2016-11-08 55 views
1

我正在处理包含用C++编写的DLL和C#代码的项目。让说该DLL具有的功能:处理C中结构的指针#

MyStruct* GetPointer(); // returns pointer to MyStruct structure 

MYSTRUCT结构如下:

struct MyStruct 
{ 
    OtherStruct1 *data1; 
    OtherStruct2 *data2; 
}; 

而且OtherStruct1和OtherStruct2结构是这样的:

struct OtherStruct1 
{ 
public: 
    double x; 
    char y; 
}; 

struct OtherStruct2 
{ 
public: 
    float a; 
    float b; 
    float c; 
    float d; 
}; 

我的问题是 - 什么是在C#代码中处理所有这些指针的最佳方法是什么?通过“处理”我的意思是阅读和写入记忆的操作。 C#中的结构不能简单地包含指针变量。我应该做些什么?什么是最优雅的方式?

+0

是您希望在C#代码中修改的对象也更新查看相同指针的C++代码?如果你不得不在指针对象之前拨打['Marshal.StructureToPtr'](https://msdn.microsoft.com/en-us/library/dn261468(v = vs.110).aspx)得到更新? –

+0

我期望有一种方法可以直接在内存中更改由DLL提供的对象的值,因此C++代码可以继续处理由C#代码更改的这些对象。这就像DLL提供了指针,C#正在用它们填充初始数据和DLL。然后C#代码继续读取由DLL修改的数据并在画布上绘制结果。 –

+0

是否有可能使'GetPointer'成为C#方法,以便托管代码创建共享内存空间?和/或者你可以使用[C++/Cli](https://msdn.microsoft.com/en-us/library/68td296t.aspx)垫片来弥补差距吗? –

回答

1

您可以使用Microsoft(现在开源)PInvoke Interop Assistant工具将您的C/C++代码转换为C#或VB。运行您的示例代码给出:

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] 
public struct MyStruct { 

    /// OtherStruct1* 
    public System.IntPtr data1; 

    /// OtherStruct2* 
    public System.IntPtr data2; 
} 

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] 
public struct OtherStruct1 { 

    /// double 
    public double x; 

    /// char 
    public byte y; 
} 

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] 
public struct OtherStruct2 { 

    /// float 
    public float a; 

    /// float 
    public float b; 

    /// float 
    public float c; 

    /// float 
    public float d; 
} 

public partial class NativeMethods { 

    /// Return Type: MyStruct* 
    [System.Runtime.InteropServices.DllImportAttribute("<Unknown>", EntryPoint="GetPointer")] 
public static extern System.IntPtr GetPointer() ; 

} 

在DllImportAttribute替换为“未知”与您的DLL名称,并确保它在该项目中引用。您现在应该可以在托管代码中访问您的结构。

然后要从内存读取/写入内存,您需要使用System.Runtime.InteropServices.Marshal命名空间中的方法。下面的代码片段展示了如何使用您的GetPointer功能,让您的非托管结构到管理的一个:

IntPtr myPtr = NativeMethods.GetPointer(); Call native code to retrieve pointer to unmanaged memory where the struct lives 
MyStruct myStruct = new MyStruct(); // Create a new managed struct 
myStruct = Marshal.PtrToStructure<MyStruct>(myPtr); 

这里是你将如何传递一个管理结构,以非托管方法:

MyStruct myStruct = new MyStruct(); // Create the managed struct 
myStruct.data1 = new OtherStruct1(); // Create a managed OtherStruct1 
myStruct.data2 = new OtherStruct2(); // Create a managed OtherStruct2 
IntPtr myStructPtr = Marshal.AllocHGlobal(Marshal.SizeOf<MyStruct>()); // Allocate unmanaged memory for the struct 
Marshal.StructureToPtr<MyStruct>(myStruct, myStructPtr, false); 
try 
{ 
    NativeMethodThatUsesMyStructPtr(myStructPtr); 
} 
finally 
{ 
    Marshal.FreeHGlobal(myStructPtr); // *** You have to free unmanaged memory manually, or you will get a memory leak! *** 
} 
+1

谢谢,这是更优雅的方式,而不使用'不安全'块:) –

+0

我编辑过,以显示完成后如何释放非托管内存。这是必须的,否则你会得到内存泄漏! – BitBot

+0

使用DLL的Clear()函数释放共享内存:)但是,无论如何,这对于未来来说是非常有用的知识:) –

2

好的,我重新设计了一个方法来做到这一点。

[StructLayout(LayoutKind.Sequential)] 
struct MyStruct 
{ 
    OtherStruct1 *data1; 
    OtherStruct2 *data2; 
}; 

[StructLayout(LayoutKind.Sequential)] 
struct OtherStruct1 
{ 
public: 
    double x; 
    char y; 
}; 

[StructLayout(LayoutKind.Sequential)] 
struct OtherStruct2 
{ 
public: 
    float a; 
    float b; 
    float c; 
    float d; 
}; 

然后:

unsafe 
{ 
    MyStruct *tmp = (MyStruct*) GetPointer(); 
    tmp->data2[0].a = 1.0F; 
} 


[DllImport(DLL_PATH, CallingConvention = CallingConvention.Cdecl)] 
    unsafe public static extern MyStruct* GetPointer(); 

就像一个魅力。 :)