2015-09-14 57 views
3

我想用數據填充一個結構,所以我可以將它傳遞給我的DLL函數。 C#和C實現中的結構類似。比如像(C語言):如何在C#中使用DLL(用C語言編寫)創建結構數組?

typedef struct{ 
    apple[] apples; 
    leaf[] leaves; 
} tree; 

typedef struct{ 
    int taste; 
} apple; 

typedef struct{ 
    int color; 
} leaf; 

我想創建一個C#的樹形結構,與蘋果和樹葉填充它,然後將其發送給一個函數我有一個DLL。

怎麼樣?

+1

爲了做到這樣的東西,你需要通過編組非託管數據類型爲結構指定一個順序佈局類型,並將該數組專門編組爲C樣式數組。查看MSDN文章「平臺調用教程」,特別是標有[「爲用戶定義的結構指定自定義封送」部分]的部分(https://msdn.microsoft.com/zh-cn/library/aa288468(v = vs。 71)的.aspx#pinvoke_custommarshaling)。 –

+0

看看這個問題http://stackoverflow.com/questions/2240804/marshalling-what-is-it-and-why-do-we-need-it。 –

+0

如果有人對這個過程更加熟悉,並且可以寫出完整的答案,歡迎您加入。 –

回答

1

首先,我想你應該改變C結構一點點:

typedef struct{ 
    UINT cApples; 
    const apple *pApples; 
    UINT cLeaves; 
    const leaf *pLeaves; 
} tree; 

在C#的一面:

[StructLayout(LayoutKind.Sequential, Pack = 1)] 
internal struct Tree 
{ 
    internal uint cApples; 
    internal IntPtr pApples; 
    internal uint cLeaves; 
    internal IntPtr pLeaves; 
} 

您可以定義LeafApple類似。然後你就可以填充它們在C#方如下:

private Tree CreateTree(Leaf[] leaves, Apple[] apples) 
{ 
    var result = new Tree(); 
    result.cLeaves = (uint)leaves.Length; 
    result.cApples = (uint)apples.Length; 
    result.pLeaves = AllocateNativeMemory(leaves); 
    result.pApples = AllocateNativeMemory(apples); 
    return result; 
} 

private IntPtr AllocateNativeMemory<T>(T[] elements) where T : struct 
{ 
    int elementSize = Marshal.SizeOf(typeof(T)); 
    IntPtr result = Marshal.AllocHGlobal(elements.Length * elementSize); 

    for (int i = 0; i < elements.Length; i++) 
    { 
     Marshal.StructureToPtr(
      elements[i], new IntPtr((long)result + i * elementSize), false); 
    } 

    return result; 
} 

現在你可以通過CreateTree方法來調用C面的方法的extern的結果。

備註:分配的內存應該被釋放;否則,你的應用程序將泄漏。如果您決定了C#方負責釋放分配的內存,你應該做到底,如下所示:

private static void FreeTree(Tree tree) 
{ 
    FreeNativeMemory<Leaf>(tree.pLeaves, tree.cLeaves); 
    FreeNativeMemory<Apple>(tree.pApples, tree.cApples); 
} 

private static void FreeNativeMemory<T>(IntPtr arrayPtr, uint arrayLen) where T : struct 
{ 
    int elementSize = Marshal.SizeOf(typeof(T)); 
    for (uint i = 0; i < arrayLen; i++) 
    { 
     Marshal.DestroyStructure(new IntPtr((long)arrayPtr + i * elementSize), typeof(T)); 
    } 

    Marshal.FreeHGlobal(arrayPtr); 
}