VS dll【C++创建DLL并用C#调用且同时实现对DLL的调试】【C#和dll之间传递数组】【System.AccessViolationException: 尝试读取或写入受保护的内存。】

SYD8821是具有全球领先低功耗(RX 2.4mA @-94.5dBm灵敏度,TX 4.3mA @0dBm输出功率)的蓝牙低功耗SOC芯片,在极低电流下实现了优异的射频性能,搭配176kB SRAM,512kB flash,非常适合中高阶可穿戴、智能家居、物联网等低功耗应用。具体可咨询:http://www.sydtek.com/

 

本文摘录于:https://blog.csdn.net/songyi160/article/details/51075023/,这里只是作为一个我的一个抄录版,绝对没有盗用前辈知识的意图。

在VS2015中先创建C#项目,然后再创建要编写的动态库DLL项目,这样做的好处是整个解决方案的编程环境是C#模式,这样就可以有很多智能的提示或快捷的编程方式在整个解决方案中都可以使用。

一:创建C#控制台应用程序:用于调用C++编写的DLL

(1)启动VS2015》文件》新建》项目,在弹出的新建项目对话框中按下图进行选择填写,先填写项目的名称,再修改解决方案的名称。

(2)项目创建后,整体效果如下图:

二:用C++创建DLL

下面仅仅创建了DLL,具体创建DLL的讲解请参考【在VS2015中用C++编写可被其它语言调用的动态库DLL

(1)在解决方案上右击选择【添加】>【新建项目】

(2)在添加新项目对话框中,按下图进行选择填写

(3)在下图的创建向导对话框中进行如下选择

(4)在下图的创建向导对话框中进行如下选择

(5)项目创建后,整体效果如下图:

(6)在【头文件】上右击进行如下选择

(7)在添加新项对话框中进行如下选择填写

(8)在CreateDLL.cpp文件中添加对CreateDLL.h的引用并且添加下图所示测试代码

(9)在CreateDLL.h文件中添加下图所示测试代码

(10)在【源文件】上右击进行如下选择

(11)在添加新项对话框中进行如下选择填写

(12)在CreateDLL.def文件中添加下图所示测试代码

(13)先将解决方案切换到Release模式,再在CreateDLL项目名称上右击选择【生成】或【重新生成】

            注:Release模式下生成的DLL才是最终的,Debug模式下生成的DLL有时会出问题

(14)在解决方案所在的目录中打开Release文件夹即可看到生成的DLL

(15)用Dependency Walker查看导出函数名是否正确,直接将CreateDLL.dll拖到Dependency Walker软件界面即可,如下图

(16)使用DLL函数查看器(3.5)查看导出函数名和参数数量是否正确,直接将CreateDLL.dll拖到DLL函数查看器(3.5)软件界面即可,如下图

三:用C#项目调用C++创建DLL

(1)将C#项目设置为启动项目,并且将解决方案设置为Debug模式

(2)在C#项目的program.cs文件中编写如下代码,代码中DLL的路径中的斜杠,测试发现左斜、右斜都可以,代码中的相对路径指的是:编译后的exe程序相对于DLL的路径

 

 
  1. using System;

  2. using System.Collections.Generic;

  3. using System.Linq;

  4. using System.Runtime.InteropServices;

  5. using System.Text;

  6. using System.Threading.Tasks;

  7.  
  8. namespace CSharpCallDLL

  9. {

  10. class Program

  11. {

  12. [DllImport(@"../../../Debug/CreateDLL.dll", EntryPoint = "test01", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]

  13. extern static int test01(int a, int b, int c);

  14.  
  15. [DllImport(@"../../../Debug/CreateDLL.dll", EntryPoint = "test02", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]

  16. extern static int test02(int a, int b);

  17.  
  18. static void Main(string[] args)

  19. {

  20. int r1 = test01(1, 2, 3);

  21. int r2 = test02(5, 2);

  22. Console.WriteLine("test01结果:" + r1.ToString());

  23. Console.WriteLine("test02结果:" + r2.ToString());

  24. Console.ReadKey();

  25. }

  26. }

  27. }

(3)运行C#项目输出结果如下

 


(4)若想实现C#调用DLL时自动进入到C++项目设置的断点,需在C#工程右键【属性】->【调试】->【启动调试器】中选中【启动本机代码调试】

(5)在C++项目的源码中设置断点,在Debug模式下运行C#程序会自动跳到断点处,如下图,从中可以看到C#项目中的值已经传到C++项目中了

(6)源码下载地址:在VS2015中用C++创建DLL并用C#调用且同时实现对DLL的调试源码

(7)Dependency Walker软件下载地址:Dependency Walker动态库导出函数查看器

(8)DLL函数查看器(3.5)软件下载地址:DLL函数查看器(3.5)

 

 

C#和dll之间传递数组

这里有一篇文章作参考:https://blog.csdn.net/xiaoyong_net/article/details/50178021

 用C#调用VC写的dll,传递参数给dll,如果是非托管代码,调用起来有点麻烦。所以把把参数转换成非托管代码的指针参数,然后再传递给dll。

  1. using System.Runtime.InteropServices; //操作Dll的类空间

  2.  
  3. class CGicomIndex

  4. {

  5. [DllImport("Index_dll.dll")]

  6. unsafe private static extern Int32 create_index_file(IntPtr filename, IntPtr fieldname, UInt32 tongshu, IntPtr err);

  7. //VC原型 create_index_file( char*filename, char * fieldname, unsigned long tongshu, char *err);

  8.  
  9. #region " bool CreateIndexFile( string m_strFileName, string m_strFieldName, UInt32 m_key, ref string strErr ) 对DBF文件按照指定的字段创建索引"

  10. /// <summary>

  11. /// 对DBF文件按照指定的字段创建索引

  12. /// </summary>

  13. /// <param name="m_strFileName">DBF文件路径</param>

  14. /// <param name="m_strFieldName">创建索引的字段名称</param>

  15. /// <param name="m_key">索引桶数(文件记录的最大数)</param>

  16. /// <param name="strErr">出错信息</param>

  17. /// <returns>成功 true</returns>

  18. public static bool CreateIndexFile( string m_strFileName, string m_strFieldName, UInt32 m_key, ref string strErr )

  19. {

  20. try

  21. {

  22. IntPtr ptrFileName, ptrFieldName, ptrErr;

  23.  
  24. //根据数据的长度申请非托管空间

  25. ptrFileName = mallocIntptr(m_strFileName);

  26. ptrFieldName = mallocIntptr(m_strFieldName);

  27. ptrErr = mallocIntptr(300);

  28.  
  29. //创建索引

  30. if (create_index_file(ptrFileName, ptrFieldName, m_key, ptrErr) != 1)

  31. {

  32. strErr = Marshal.PtrToStringAnsi(ptrErr);

  33. Marshal.FreeHGlobal(ptrFileName);

  34. Marshal.FreeHGlobal(ptrFieldName);

  35. Marshal.FreeHGlobal(ptrErr);

  36. return false;

  37. }

  38. Marshal.FreeHGlobal(ptrFileName);

  39. Marshal.FreeHGlobal(ptrFieldName);

  40. Marshal.FreeHGlobal(ptrErr);

  41. return true;

  42. }

  43. catch (System.Exception ex)

  44. {

  45. strErr = ex.Message;

  46. return false;

  47. }

  48. }

  49. #endregion

  50.  
  51. #region " IntPtr mallocIntptr( string strData ) 根据数据的长度申请非托管空间"

  52. /// <summary>

  53. /// 根据数据的长度申请非托管空间

  54. /// </summary>

  55. /// <param name="strData">要申请非托管空间的数据</param>

  56. /// <returns>指向非拖管空间的指针</returns>

  57. private static IntPtr mallocIntptr( string strData )

  58. {

  59. //先将字符串转化成字节方式

  60. Byte[] btData = System.Text.Encoding.Default.GetBytes(strData);

  61.  
  62. //申请非拖管空间

  63. IntPtr m_ptr = Marshal.AllocHGlobal(btData.Length);

  64.  
  65. //给非拖管空间清0

  66. Byte[] btZero = new Byte[btData .Length+ 1]; //一定要加1,否则后面是乱码,原因未找到

  67. Marshal.Copy(btZero, 0, m_ptr, btZero.Length);

  68.  
  69. //给指针指向的空间赋值

  70. Marshal.Copy(btData, 0, m_ptr, btData.Length);

  71.  
  72. return m_ptr;

  73. }

  74.  
  75. /// <summary>

  76. /// 根据长度申请非托管空间

  77. /// </summary>

  78. /// <param name="strData">要申请非托管空间的大小</param>

  79. /// <returns>指向非拖管空间的指针</returns>

  80. private static IntPtr mallocIntptr( int length )

  81. {

  82. //申请非拖管空间

  83. IntPtr m_ptr = Marshal.AllocHGlobal(length);

  84.  
  85. //给非拖管空间清0

  86. Byte[] btZero = new Byte[length + 1]; //一定要加1,否则后面是乱码,原因未找到

  87. Marshal.Copy(btZero, 0, m_ptr, btZero.Length);

  88.  
  89. //给指针指向的空间赋值

  90. Marshal.Copy(btZero, 0, m_ptr, length);

  91.  
  92. return m_ptr;

  93. }

  94. #endregion

  95. }

     C#并不支持指针的操作,所以如果DLL中的函数有指针的话就比较麻烦了,这里我尝试各种各样的方式都失败了,最后只能够把函数写成返回变量的形式,一个一个的传输出来!上面的方法第一次有效,第二次就不行了。

 

 

System.AccessViolationException: 尝试读取或写入受保护的内存。

本文摘录于:http://www.cnblogs.com/hfzsjz/archive/2010/01/19/1651406.html

昨天写的RC4的加解密程序,当C#调用C的dll时就会出现这个提示,尝试怎么修改都不行。Debug跟踪显示,应该返回string类型的函数返回值为空。也不知道怎么调试dll,dll里下的断点始终提示无效。单独运行dll里的函数,结果没有问题。几乎可以断定是函数返回的地方出错了。

终于搞清楚错误的真正原因了。诡异的是,这个错误是我趴在桌子上半梦半醒的状态下想到的。更诡异的是,睡醒了之后还记得睡梦中的想法。尝试修改错误,竟然真的就成功了。有点玄......

这个错误其实不应该犯,《C专家编程》和《C陷阱与缺陷》里都提到过,自己也做了这条的笔记。只是之前几乎一样的dll调用都做过,也都成功运行得到了正确的结果,就以为dll里不会出什么错,一直在C#的代码里找错。而且同样的错误提示也见过,只是那时是糊里糊涂的把数组赋个初始值就解决了,也就没太在意。

错误提示: 

有关调用实时(JIT)调试而不是此对话框的详细信息,
请参见此消息的结尾。

************** 异常文本 **************
System.AccessViolationException: 尝试读取或写入受保护的内存。这通常指示其他内存已损坏。
   在 RC4_GUI.RC4_form.RC4_decrypt(String strings_hex, Int32 start, Int32 length)
   在 RC4_GUI.RC4_form.decrypt_button1_Click(Object sender, EventArgs e) 位置 F:\Crypt\Modern Cryptology\GUI\RC4_GUI\Form1.cs:行号 136
   在 System.Windows.Forms.Control.OnClick(EventArgs e)
   在 System.Windows.Forms.Button.OnClick(EventArgs e)
   在 System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
   在 System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   在 System.Windows.Forms.Control.WndProc(Message& m)
   在 System.Windows.Forms.ButtonBase.WndProc(Message& m)
   在 System.Windows.Forms.Button.WndProc(Message& m)
   在 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   在 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   在 System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)


************** 已加载的程序集 **************
mscorlib
    程序集版本: 2.0.0.0
    Win32 版本: 2.0.50727.4927 (NetFXspW7.050727-4900)
    基本代码: file:///C:/Windows/Microsoft.NET/Framework/v2.0.50727/mscorlib.dll
----------------------------------------
RC4_GUI
    程序集版本: 1.0.0.0
    Win32 版本: 1.0.0.0
    基本代码: file:///F:/Crypt/Modern%20Cryptology/GUI/RC4_GUI/bin/Debug/RC4_GUI.exe
----------------------------------------
System.Windows.Forms
    程序集版本: 2.0.0.0
    Win32 版本: 2.0.50727.4927 (NetFXspW7.050727-4900)
    基本代码: file:///C:/windows/assembly/GAC_MSIL/System.Windows.Forms/2.0.0.0__b77a5c561934e089/System.Windows.Forms.dll
----------------------------------------
System
    程序集版本: 2.0.0.0
    Win32 版本: 2.0.50727.4927 (NetFXspW7.050727-4900)
    基本代码: file:///C:/windows/assembly/GAC_MSIL/System/2.0.0.0__b77a5c561934e089/System.dll
----------------------------------------
System.Drawing
    程序集版本: 2.0.0.0
    Win32 版本: 2.0.50727.4927 (NetFXspW7.050727-4900)
    基本代码: file:///C:/windows/assembly/GAC_MSIL/System.Drawing/2.0.0.0__b03f5f7f11d50a3a/System.Drawing.dll
----------------------------------------
System.Configuration
    程序集版本: 2.0.0.0
    Win32 版本: 2.0.50727.4927 (NetFXspW7.050727-4900)
    基本代码: file:///C:/windows/assembly/GAC_MSIL/System.Configuration/2.0.0.0__b03f5f7f11d50a3a/System.Configuration.dll
----------------------------------------
System.Xml
    程序集版本: 2.0.0.0
    Win32 版本: 2.0.50727.4927 (NetFXspW7.050727-4900)
    基本代码: file:///C:/windows/assembly/GAC_MSIL/System.Xml/2.0.0.0__b77a5c561934e089/System.Xml.dll
----------------------------------------
mscorlib.resources
    程序集版本: 2.0.0.0
    Win32 版本: 2.0.50727.4927 (NetFXspW7.050727-4900)
    基本代码: file:///C:/Windows/Microsoft.NET/Framework/v2.0.50727/mscorlib.dll
----------------------------------------
System.Windows.Forms.resources
    程序集版本: 2.0.0.0
    Win32 版本: 2.0.50727.4927 (NetFXspW7.050727-4900)
    基本代码: file:///C:/windows/assembly/GAC_MSIL/System.Windows.Forms.resources/2.0.0.0_zh-CHS_b77a5c561934e089/System.Windows.Forms.resources.dll
----------------------------------------

************** JIT 调试 **************
要启用实时(JIT)调试,
该应用程序或计算机的 .config 文件(machine.config)的 system.windows.forms 节中必须设置
jitDebugging 值。
编译应用程序时还必须启用
调试。

例如: 

<configuration>
    <system.windows.forms jitDebugging="true" />
</configuration>

启用 JIT 调试后,任何无法处理的异常
都将被发送到在此计算机上注册的 JIT 调试器,
而不是由此对话框处理。

解决方案: 

将dll里函数需要返回的局部变量声明为static。(就这么简单。原因就不重复了)

 

教训: 

1、不要犯经验主义的错误,有些经验可能是错误的。 

2、发现bug要及时处理,不可为了赶工而忽略。随机性出现的bug更要注意,因为这类bug更难发现,也就更能解决。

 

这个方法我没试,这里只是做一个备份。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值