2012-02-17 12 views
1

構造体へのポインタをDLLに渡す必要があります。どのようにすればよいでしょうか?私のC#のコードでC#プログラムからC DLLを呼び出す

typedef struct 
{ 
    int length; 
    unsigned char *value; 
} Sample; 


__declspec(dllexport) void __stdcall helloWorld(Sample *sample); 

:私のCのDLLで

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Runtime.InteropServices; 

namespace CSharpConsole 
{ 
    class Program 
    { 
     [StructLayout(LayoutKind.Sequential, Pack = 1)] 
     private struct Sample 
     { 
      public Int32 length; 
// What Should I Declare Here? 
     } 

     [DllImport("C:\\CTestDLL.dll")] 
     private static extern void helloWorld(Sample sample); // How would I make this a pointer? 

     void HelloWorld() 
     { 
      Sample sample = new Sample(); 
      sample .length = 20; 
      // How can I fill up the values of value? 
      helloWorld(sample); // How should I pass it inside here 
      return; 
     } 

     static void Main(string[] args) 
     { 
      Program program = new Program(); 
      program.HelloWorld(); 
     } 
    } 
} 
+0

IntPtr(プラットフォームセーフポインタ)を使用し、場合によってはデータのマーシャリングを使用します。安全でないコードブロックが必要な場合があります。 – user978122

答えて

1

Default Marshaling for Value Types - マーシャル構造体に関する情報をいくつか示します(実際にはコンバージョンはまったくありません)。
Calling Win32 DLLs in C# with P/Invoke - ページの半分を少し下に、標準の管理されていないタイプと管理されたタイプの間のタイプ変換を示す表があります。

型を渡して変換する方法はいくつかあります。
マイケル・エデンフィールドが指摘したように、基本的にポインタである参照によって渡されるべきであることを示すために、一般にrefキーワードを使用することができます。

しかし、文字列や複雑なデータ型の場合は特にそうではないことがありますので、ここでIntPtr型が入ります。 IntPtrsを使用するためのオプションがいくつかあります。
あなたは使用して、指定したサイズのアンマネージメモリのブロックへのIntPtrを作成することができます:あなたは手動で管理されていないメモリを割り当てているので

IntPtr pointer = Marshal.AllocHGlobal(sizeOfBufferInBytes); 
//Do stuff with the pointer 
Marshal.FreeHGlobal(pointer); //Don't forget to release the memory 

これは明らかに少し危険です。
あなたがMarshal.Copy()、Marshal.PtrToStructure()、Buffer.BlockCopy()のようなものを使用して、バックマネージ型にバッファからデータをマーシャリングする必要があります、など

また、作成することができます管理オブジェクトを作成し、必要なときにメモリに固定し、ポインタを取得してメソッドに渡します。

MyObject instance = new MyObject(); 
GCHandle gch = GCHandle.Alloc(instance, GCHandleType.Pinned); 
importedMethod(gch.AddrOfPinnedObject()); //AddrOfPinnedObject() gives you an IntPtr 
gch.Free(); //Release the pinned memory so the garbage collector can deal with it 

これは、手動でバック正しいデータ型へのマーシャリングのための必要性を回避するが、これは常にMyObjectにの種類と、それはblittable型だかどうかによって、オプションではありません。

1

これが私の作品:

DLL:

typedef struct 
{ 
    int length; 
    unsigned char *value; 
} Sample; 

extern "C" __declspec(dllexport) void __stdcall helloWorld(Sample *sample) 
{ 
    MessageBoxA(NULL, (LPCSTR) sample->value, (LPCSTR) sample->value, 0); 
} 

のC#:

class Program 
{ 
    [StructLayout(LayoutKind.Sequential, Pack = 1)] 
    private class Sample 
    { 
     public Int32 length; 
     public String value; 
    } 

    [DllImport("C:\\Users\\Kep\\Documents\\Visual Studio 2010\\Projects\\SODLL\\Debug\\DLL.dll")] 
    private static extern void helloWorld(Sample sample); 

    static void Main(string[] args) 
    { 
     Sample s = new Sample(); 
     s.length = 10; 
     s.value = "Huhu"; 
     helloWorld(s); 
    } 
} 

重要なことは、C#の構造体ではなくクラスとしてマークすることです。これにより

、あなたはまた、C#でコンストラクタなどを使用することができます。

class Program 
{ 
    [StructLayout(LayoutKind.Sequential, Pack = 1)] 
    private class Sample 
    { 
     public Int32 length; 
     public String value; 

     public Sample(String s) 
     { 
      length = s.Length; 
      value = s; 
     } 
    } 

    [DllImport("C:\\Users\\Kep\\Documents\\Visual Studio 2010\\Projects\\SODLL\\Debug\\DLL.dll")] 
    private static extern void helloWorld(Sample sample); 

    static void Main(string[] args) 
    { 
     Sample s = new Sample("Huhu"); 
     helloWorld(s); 
    } 
} 
+0

C#の値型はコンストラクタを持つことができます。また、値型を参照型に変更するだけで、interopポインタの動作を取得することは、過剰なIMHOのようです。 –

+0

実際に私は、どのアイデアのファイルから単純にバイトを読んでみたかったのですか? byte []をunsigned char *に変換しますか? – shawn

3

だけrefまたはoutなどのパラメータを宣言P /呼び出し関数に値型へのポインタを渡すには。これは、暗黙的パラメータへの参照を取得していることを渡し:あなたの構造がそれで配列を持っているので、あなたはこれが機能するために、適切にそれを宣言するために世話をする必要があります

[DllImport("C:\\CTestDLL.dll")] 
private static extern void helloWorld(ref Sample sample); 

。可能であれば私は強くはずっと幸せ、それがマーシャラーがはるかになりますようあなたは、固定長の配列にそれを回すことを、お勧めします

typedef struct 
{ 
    int length; 
    unsigned char value[MAX_LENGTH]; 
} Sample; 

これは次のようになります。

public struct Sample 
{ 
    int length; 
    [MarshalAs(UnmanagedType.LPArray, SizeConst = MAX_LENGTH)] byte[] value; 
} 

いる場合実行不可能な場合、ランタイムはマーシャリングバックするデータの量を知る方法がありません。その場合、あなたはおそらく、あなたのデータを手動でマーシャリングに頼る必要があります:あなただけのバイトのブロックを取得する必要があるよう

public struct Sample 
{ 
    int length; 
    IntPtr value; 
} 

var sample = new Sample(); 
helloWorld(ref sample); 

byte[] value = new byte[sample.length]; 
Marshal.Copy(sample.value, value, 0, sample.Length); 

はしかし、別の答えにあなたのコメントに基づいて、それが見えますC DLLのC#への変換。そのためには、構造体がまったく必要なくなり、それをなくすことで多くのことが簡単になります。

(これはCとC#の両方のコードベースを制御していることを前提としています;もしそうでなければ、refの提案は、次のようになります。 8ビット、符号なし - あなたが必要なものを達成するための方法)

// In C# code: 
[DllImport(@"C:\CTestDll.dll")] 
private static extern void helloWorld(
    int length, 
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] byte[] buffer); 

byte[] buffer = new byte[1024 * 8]; 
helloWorld(1024 * 8, buffer); 


// In C: 
__declspec(dllexport) void __stdcall helloWorld(int, unsigned char *); 

void helloWorld(int cb, unsigned char *buf) 
{ 
    memcpy(buf, DATASRC, cb); 
} 

C、unsigned char型では、定義によって、C#のバイトと同じサイズです。 C#では、charは実際には2バイトです。ランタイムは自動的にunsigned char *byte[]に「変換」します。

+0

私はメンバとしてunsigned char *を持つC構造体を持っていますが、C#コードでbyte []を使用しています。値はファイルbtwから読み込まれます。 – shawn

+0

ああ、はい、私は内部構造を扱う配列がちょっと怪しいことを忘れていました。 Lemmeは私の答えを更新する。 –

関連する問題