System.AccessViolationException:shared_ptr 在 C# .NET 和 C++ 应用程序之间
System.AccessViolationException: shared_ptr between C# .NET and C++ applications
在我们的项目中,我们通过命名管道与两个应用程序通信,一个使用 C#,另一个使用 C++。我们的目的是在它们之间传递内存指针,并能够在任一应用程序中访问它们指向的对象。我们现在的代码上升了一个System.AccessViolationException:
System.AccessViolationException:
Attempted to read or write protected memory. This is often an
indication that other memory is corrupt.
到目前为止,我们正在使用指向自定义结构的 shared_ptr 并在 C++ 中编写指向缓冲区的指针,如下所示:
typedef struct {
int one;
int a;
int two;
int b;
} DATA_STRUCT; // C++ struct
DATA_STRUCT ds;
ds.one = 10;
ds.a = 5;
ds.two = 99;
ds.b = 0;
shared_ptr<DATA_STRUCT> ptr_ds(new DATA_STRUCT);
shared_ptr<DATA_STRUCT> p(ptr_ds);
*ptr_ds = ds;
const int size = BUFFER_SIZE;
char buf[size];
memset(buf, 0xCC, 100);
while (keepReading)
{
printf("Write message:");
scanf("%s", buf);
memcpy(buf, &p, sizeof(shared_ptr<DATA_STRUCT>));
if (strcmp(buf, "quit") == 0)
keepReading = false;
else
{
WriteFile(hPipe1, buf, dwBytesToWrite, &cbWritten, NULL);
memset(buf, 0xCC, 100);
}
}
然后,在 C# 中,我们读取整个缓冲区,将包含相关信息的字节保存在另一个缓冲区 (Rc) 中,并使用不安全的 IntPtr 将字节数组转换为我们的自定义数据结构,如下所示:
buffer = new byte[BUFFER_SIZE];
bytesRead = clientCSharp.stream.Read(buffer, 0, BUFFER_SIZE);
public struct DATA_STRUCT
{
public int one;
public int a;
public int two;
public int b;
}; // C# struct
unsafe
{
Buffer.BlockCopy(buffer, 0, Rc, 0, ReadLength);
DATA_STRUCT ds = new DATA_STRUCT();
IntPtr aux_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
IntPtr final_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
Marshal.Copy(Rc, 0, aux_ptr, 4);
final_ptr = (IntPtr)Marshal.PtrToStructure(aux_ptr, typeof(IntPtr));
ds = (DATA_STRUCT)Marshal.PtrToStructure(final_ptr, typeof(IntPtr));
}
当我们尝试访问 final_ptr 以加载 DATA_STRUCT 时出现异常,这是上面显示的最后一行代码。这里我给出一些调试图片:
C++ 调试图像 Pointer value written to the named pipe buffer
C# 调试图像 Pointer value read from the named pipe reduced buffer (Rc)
会不会跟指针长度有关?在我看来,在 C++ 应用程序中它有 8 个字节,而在 C# 应用程序中它有 16 个字节?我们应该为 C# 和 C++ 声明一个安全的内存位置吗?如果是,那怎么办?
注意:我们的目标是在 C# 应用程序中使用不安全的 IntPtr。在此示例中,我们正在加载 DATA_STRUCT 对象,因为我们希望确保在 C# 应用程序中我们正在检索在 C++ 应用程序中传递的同一对象。最终应用程序旨在用于 Windows.
应用程序数据空间是完全不同的,并且已经存在多年。您不能简单地在应用程序之间传递原始指针并期望访问相同的内存。
通常的方法是序列化对象的内容,将其通过管道喷射,然后在接收方重建对象。
您可以设置命名的共享内存区域,这样共享大对象会更快(在 unix 中,我假设在 windows 中),但这些共享区域可能不会位于同一地址, 所以仍然只适用于原始数据。
在我们的项目中,我们通过命名管道与两个应用程序通信,一个使用 C#,另一个使用 C++。我们的目的是在它们之间传递内存指针,并能够在任一应用程序中访问它们指向的对象。我们现在的代码上升了一个System.AccessViolationException:
System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
到目前为止,我们正在使用指向自定义结构的 shared_ptr 并在 C++ 中编写指向缓冲区的指针,如下所示:
typedef struct {
int one;
int a;
int two;
int b;
} DATA_STRUCT; // C++ struct
DATA_STRUCT ds;
ds.one = 10;
ds.a = 5;
ds.two = 99;
ds.b = 0;
shared_ptr<DATA_STRUCT> ptr_ds(new DATA_STRUCT);
shared_ptr<DATA_STRUCT> p(ptr_ds);
*ptr_ds = ds;
const int size = BUFFER_SIZE;
char buf[size];
memset(buf, 0xCC, 100);
while (keepReading)
{
printf("Write message:");
scanf("%s", buf);
memcpy(buf, &p, sizeof(shared_ptr<DATA_STRUCT>));
if (strcmp(buf, "quit") == 0)
keepReading = false;
else
{
WriteFile(hPipe1, buf, dwBytesToWrite, &cbWritten, NULL);
memset(buf, 0xCC, 100);
}
}
然后,在 C# 中,我们读取整个缓冲区,将包含相关信息的字节保存在另一个缓冲区 (Rc) 中,并使用不安全的 IntPtr 将字节数组转换为我们的自定义数据结构,如下所示:
buffer = new byte[BUFFER_SIZE];
bytesRead = clientCSharp.stream.Read(buffer, 0, BUFFER_SIZE);
public struct DATA_STRUCT
{
public int one;
public int a;
public int two;
public int b;
}; // C# struct
unsafe
{
Buffer.BlockCopy(buffer, 0, Rc, 0, ReadLength);
DATA_STRUCT ds = new DATA_STRUCT();
IntPtr aux_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
IntPtr final_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
Marshal.Copy(Rc, 0, aux_ptr, 4);
final_ptr = (IntPtr)Marshal.PtrToStructure(aux_ptr, typeof(IntPtr));
ds = (DATA_STRUCT)Marshal.PtrToStructure(final_ptr, typeof(IntPtr));
}
当我们尝试访问 final_ptr 以加载 DATA_STRUCT 时出现异常,这是上面显示的最后一行代码。这里我给出一些调试图片:
C++ 调试图像 Pointer value written to the named pipe buffer
C# 调试图像 Pointer value read from the named pipe reduced buffer (Rc)
会不会跟指针长度有关?在我看来,在 C++ 应用程序中它有 8 个字节,而在 C# 应用程序中它有 16 个字节?我们应该为 C# 和 C++ 声明一个安全的内存位置吗?如果是,那怎么办?
注意:我们的目标是在 C# 应用程序中使用不安全的 IntPtr。在此示例中,我们正在加载 DATA_STRUCT 对象,因为我们希望确保在 C# 应用程序中我们正在检索在 C++ 应用程序中传递的同一对象。最终应用程序旨在用于 Windows.
应用程序数据空间是完全不同的,并且已经存在多年。您不能简单地在应用程序之间传递原始指针并期望访问相同的内存。
通常的方法是序列化对象的内容,将其通过管道喷射,然后在接收方重建对象。
您可以设置命名的共享内存区域,这样共享大对象会更快(在 unix 中,我假设在 windows 中),但这些共享区域可能不会位于同一地址, 所以仍然只适用于原始数据。