Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/325.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当与不安全代码一起使用时,ref的安全性如何?_C#_Unsafe - Fatal编程技术网

C# 当与不安全代码一起使用时,ref的安全性如何?

C# 当与不安全代码一起使用时,ref的安全性如何?,c#,unsafe,C#,Unsafe,使用Microsoft Visual C#2010,我最近注意到可以通过引用将对象传递给非托管代码。因此,我尝试编写一些非托管代码,这些代码使用回调到托管代码将C++ Car *转换为C字符串。我试了两次 尝试1:调用存储ref参数的非托管函数。然后,在该函数返回托管代码后,调用另一个非托管函数,该函数调用将char*转换为托管字符串的回调函数 C++ typedef void (_stdcall* CallbackFunc)(void* ManagedString, char* Unmanag

使用Microsoft Visual C#2010,我最近注意到可以通过引用将对象传递给非托管代码。因此,我尝试编写一些非托管代码,这些代码使用回调到托管代码将C++ Car *转换为C字符串。我试了两次

尝试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:将字符串引用传递给将字符串引用传递给托管回调的非托管函数

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, "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 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时,对象将保持在内存中的当前位置


非托管代码中ref(我不能用ref做什么)的限制是什么?

关于p/invoke如何工作的完整讨论超出了堆栈溢出的适当范围问与答。但简单地说:

在这两个示例中,您都没有将托管变量的地址真正传递给非托管代码。p/invoke层包括封送逻辑,该逻辑将托管数据转换为非托管代码可用的数据,然后在非托管代码返回时转换回

在这两个示例中,p/invoke层都必须创建一个中间对象以进行封送处理。在第一个示例中,当您再次调用非托管代码时,该对象已消失。当然,在第二个例子中,情况并非如此,因为所有的工作同时发生

我相信你的第二个例子应该是安全的。也就是说,在这种情况下,p/invoke层足够智能,可以正确处理
ref
。第一个例子是不可靠的,因为p/invoke被误用,而不是因为
ref
参数的任何基本限制


还有几点:

  • 我不会在这里用“不安全”这个词。是的,调用非托管代码在某些方面是不安全的,但在C#中,“不安全”有一个非常具体的含义,与
    unsafe
    关键字的使用有关。我在您的代码示例中没有看到任何实际使用
    不安全的
  • 在这两个示例中,您都有一个与使用传递给非托管代码的委托相关的bug。特别是,尽管p/invoke层可以将托管委托引用转换为非托管代码可以使用的函数指针,但它对委托对象的生存期一无所知。它将使对象保持足够长的生存时间,以使p/invoked方法调用得以完成,但是如果您需要它的生存时间更长(这里就是这种情况),那么您需要自己完成。例如,对存储了引用的变量使用
    GC.KeepAlive()
    。(通过在调用
    StoreCallback()
    和稍后调用将使用函数指针的非托管代码之间插入对
    GC.Collect()
    的调用,可能会重现崩溃)

不安全代码之所以称为“不安全”,是因为。。。这是不安全的关于第二点,你是对的。插入GC.Collect()导致崩溃。我假设保持委托的静态引用也会起作用。