[DllImport("QCall", CharSet = CharSet.Unicode)] [SecurityCritical] [SuppressUnmanagedCodeSecurity] private static extern bool InternalUseRandomizedHashing(); [DllImport("mscoree.dll", EntryPoint = "ND_RU1")] [SuppressUnmanagedCodeSecurity] [SecurityCritical] public static extern byte ReadByte([In] [MarshalAs(UnmanagedType.AsAny)] object ptr, int ofs);联想到上一篇阿里短信netsdk也是全用C++实现,然后用C#做一层壳,两者相互打辅助彰显更强大的威力,还有很多做物联网的朋友对这种.Net互操作技术太熟悉不过了,很多硬件,视频设备驱动都是用C/C++实现,然后用winform/WPF去做管理界面,C++还是在大学里学过,好多年没接触了,为了练手这一篇用P/Invoke来将两者相互打通。
--- Person.cpp extern "C" { _declspec(dllexport) int Sum(int a, int b); } --- Person.h #include "Person.h" #include "iostream" using namespace std; int Sum(int a, int b){ return a + b; }有一个注意的地方就是 extern "C",一定要用C方式导出,如果按照C++方式,Sum名称会被编译器自动修改,不信你把extern "C"去掉,我用ida打开给你看一下,被修改成了 ?Sum@@YAHHH@Z, 尴尬。
class Program { [DllImport("ConsoleApplication1.dll", CallingConvention = CallingConvention.Cdecl)] extern static int Sum(int a, int b); static void Main(string[] args) { var result = Sum(10, 20); Console.WriteLine($"10+20={result}"); Console.ReadLine(); } } ---- output ----- 10+20=302. 字符串的互操作
--- Person.cpp extern "C" { //字符串 _declspec(dllexport) int GetLength(char* chs); } --- Person.h #include "Person.h" #include "iostream" using namespace std; int GetLength(char* chs){ return strlen(chs); }然后我们看一下C#这边怎么写,通常string在C++中使用asc码,而C#中是Unicode,所以在DllImport中加一个CharSet指定即可。
class Program { [DllImport("ConsoleApplication1.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] extern static int GetLength([MarshalAs(UnmanagedType.LPStr)] string str); static void Main(string[] args) { var str = "hello world"; Console.WriteLine($"length={GetLength(str)}"); Console.ReadLine(); } } ---- output ----- length=113. 复杂类型的处理
--- Person.cpp extern "C" { class Person { public: char* username; char* password; }; _declspec(dllexport) char* AddPerson(Person person); } --- Person.h #include "Person.h" #include "iostream" using namespace std; char* AddPerson(Person person){ return person.username; }可以看到C++中AddPerson返回了char*,在C#中我们用IntPtr来接,然后用Marshal将指针转换string,接下来用工具生成好的C#代码拷到项目中来,如下:
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public struct Person { /// char* [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] public string username; /// char* [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] public string password; } class Program { [DllImport("ConsoleApplication1.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] extern static IntPtr AddPerson(Person person); static void Main(string[] args) { var person = new Person() { username = "dotnetfly", password = "123456" }; var ptr = AddPerson(person); var str = Marshal.PtrToStringAnsi(ptr); Console.WriteLine($"username={str}"); Console.ReadLine(); } } ---------- output ------------ username=dotnetfly4. 回调函数(异步)的处理
--- Person.cpp extern "C" { //函数指针 typedef void(_stdcall* PCALLBACK) (int result); _declspec(dllexport) void AsyncProcess(PCALLBACK ptr); } --- Person.h #include "Person.h" #include "iostream" using namespace std; void AsyncProcess(PCALLBACK ptr){ ptr(10); //回调C#的委托 } 从代码中看到,PCALLBACK就是我定义了函数指针,接受int参数。 class Program { delegate void Callback(int a); [DllImport("ConsoleApplication1.dll", CallingConvention = CallingConvention.Cdecl)] extern static void AsyncProcess(Callback callback); static void Main(string[] args) { AsyncProcess((i) => { //这里回调函数哦... // 堆代码 duidaima.com Console.WriteLine($"这是回调函数哦: {i}"); }); Console.ReadLine(); } } ------- output -------这是回调函数哦: 10