尝试1:调用存储ref参数的非托管函数.然后,一旦该函数返回到托管代码,调用另一个非托管函数,该函数调用将char *转换为托管字符串的回调函数.
C++ typedef void (_stdcall* CallbackFunc)(void* ManagedString,char* UnmanagedString); CallbackFunc UnmanagedToManaged = 0; void* ManagedString = 0; extern "C" __declspec(dllexport) void __stdcall StoreCallback(CallbackFunc X) { UnmanagedToManaged = X; } extern "C" __declspec(dllexport) void __stdcall StoreManagedStringRef(void* X) { ManagedString = X; } extern "C" __declspec(dllexport) void __stdcall CallCallback() { UnmanagedToManaged(ManagedString,"This is an unmanaged string produced by unmanaged code"); } C# [DllImport("Name.dll",CallingConvention = CallingConvention.StdCall)] public static extern void StoreCallback(CallbackFunc X); [DllImport("Name.dll",CallingConvention = CallingConvention.StdCall)] public static extern void StoreManagedStringRef(ref string X); [DllImport("Name.dll",CallingConvention = CallingConvention.StdCall)] public static extern void CallCallback(); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate void CallbackFunc(ref string Managed,IntPtr Native); static void Main(string[] args) { string a = "This string should be replaced"; StoreCallback(UnmanagedToManaged); StoreManagedStringRef(ref a); CallCallback(); } static void UnmanagedToManaged(ref string Managed,IntPtr Unmanaged) { Managed = Marshal.PtrToStringAnsi(Unmanaged); }
尝试2:将字符串ref传递给将字符串ref传递给托管回调的非托管函数.
C++ typedef void (_stdcall* CallbackFunc)(void* ManagedString,char* UnmanagedString); CallbackFunc UnmanagedToManaged = 0; extern "C" __declspec(dllexport) void __stdcall StoreCallback(CallbackFunc X) { UnmanagedToManaged = X; } extern "C" __declspec(dllexport) void __stdcall DoEverything(void* X) { UnmanagedToManaged(X,CallingConvention = CallingConvention.StdCall)] public static extern void DoEverything(ref string X); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate void CallbackFunc(ref string Managed,IntPtr Unmanaged); static void Main(string[] args) { string a = "This string should be replaced"; StoreCallback(UnmanagedToManaged); DoEverything(ref a); } static void UnmanagedToManaged(ref string Managed,IntPtr Unmanaged) { Managed = Marshal.PtrToStringAnsi(Unmanaged); }
尝试1不起作用但尝试2起作用.在尝试1中,似乎只要在存储ref之后非托管代码返回,ref就变为无效.为什么会这样?
鉴于尝试1的结果,我怀疑尝试2是否可行.那么,当与非托管代码一起使用时,代码的非托管端是否安全?或者换句话说,在使用ref时,什么在非托管代码中不起作用?
我想知道的是:
在非托管代码中使用ref时,它是否保证对象将保留在内存中的当前位置?
在非托管代码中,ref(我不能用ref做什么)有什么限制?
解决方法
在您的示例中,您都没有将托管变量的地址传递给非托管代码. p / invoke层包括编组逻辑,用于将托管数据转换为非托管代码可用的内容,然后在非托管代码返回时进行转换.
在这两个示例中,p / invoke层必须创建一个中间对象以用于编组.在第一个示例中,当您再次调用非托管代码时,此对象已消失.当然,在第二个例子中,它并非如此,因为所有的工作都是同时发生的.
我相信你的第二个例子应该是安全的.也就是说,p / invoke层足够聪明,可以在这种情况下正确处理ref.第一个例子是不可靠的,因为p / invoke被滥用,而不是因为ref参数的任何基本限制.
还有几点要点:
>我不会在这里使用“不安全”这个词.是的,调用非托管代码在某些方面是不安全的,但在C#中,“unsafe”具有非常特定的含义,与使用unsafe关键字有关.我的代码示例中没有看到任何实际使用不安全的内容.>在这两个示例中,您都有一个与使用传递给非托管代码的委托相关的错误.特别是,虽然p / invoke层可以将托管委托引用转换为非托管代码可以使用的函数指针,但它并不知道委托对象的生命周期.它会使对象保持足够长的时间以使p /被调用的方法调用完成,但是如果你需要它比它更长寿(就像这里的情况那样),你需要自己做.例如,对存储引用的变量使用GC.KeepAlive(). (您可能通过在调用StoreCallback()和稍后调用非托管代码(将使用函数指针)之间插入对GC.Collect()的调用来重现崩溃.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。