2012-03-13 7 views
2

私はC#からC++の関数を呼び出すためにDllImportを使用しています。 C#でポインタ関数をC++からC#にエクスポートするには?

#if defined(__cplusplus) 
extern "C" { 
#endif 

__declspec(dllexport) int __stdcall ABC(int i); 

__declspec(dllexport) char* __stdcall C(int i); 

#if defined(__cplusplus) 
} 
#endif 

int __stdcall ABC(int i) 

{ 
    return i; 
} 

char* __stdcall C(int i) 
{ 
    char* n = new char[i]; 
    memset(n, 9, i); 
    return n; 
} 

コードである:

using System.Runtime.InteropServices; 
using System; 

namespace DepartmentStore 
{ 
    class Exercise 
    { 
     [DllImport("library.dll")] 
     public static extern int ABC(int i); 

     [DllImport("library.dll")] 
     public static extern char* C(int i); 

     static int Main() 
     { 
      int k = ABC(10); 
      byte[] b = C(1024); 

      return 0; 
     } 
    } 
} 

ABC(int i)がOKである関数が、関数C(int i)は、次のエラーを生じた場合、建物:

「ポインタと固定サイズのバッファのみかもしれません安全でない状況で使用する」

私は関数のポインタリターンをエクスポートする方法を理解していなかったと思います。

誰かが私に正しい方法を教えてください。C#は関数の戻り値の型をポインタとして呼び出しますか?

+2

戻り値の型を代わりにIntPtr、Marshal.PtrToStringAnsi()を文字列に変換して宣言します。プラグインできないメモリリークを修正することはできません。このAPIはそのまま使用できません。 –

答えて

3

後で、P/Invokeは、Cスタイルの配列をアンマネージ側からマネージ側にマーシャリングする方法を知らない。 SAFEARRAYとは異なり、後でP/Invokeがコピーするバイト数を決定するために使用できるCスタイルの配列に埋め込まれた情報はありません。あなたは、さらに

[DllImport("library.dll")] 
public static extern IntPtr C(int i); 

static int Main() 
{ 
    int k = ABC(10); 
    IntPtr b = C(1024); 

    string s = Marshal.PtrToStrAnsi(b); 

    // Problem: How do you release what is pointed to 
    // by IntPtr? 

    return 0; 
} 

:このための

、あなたはIntPtrを返し、その後、そのような管理側の文字列へのポインタを変換するMarshal.PtrToStrAnsi methodを呼び出すために、あなたのC関数を宣言したいと思いますnewで割り当てたメモリの割り当てを解除するには、IntPtrを管理されていない側に戻す必要があります。あなたがしなければ、あなたはメモリリークを持つでしょう。

簡単にオプションがそうのような呼び出しを行うと(marshal_asを使用して)String^への変換を実行する管理機能を公開してC++での管理対象外のライブラリ用のマネージラッパーを作成することです:あなたの場合

// compile with: /clr 
#include <stdlib.h> 
#include <string.h> 
#include <memory> 
#include <msclr\marshal.h> 

using namespace System; 
using namespace msclr::interop; 

String^ WrappedC(int i) { 
    // Make the call to the native function. 
    // Let's store in an auto_ptr to handle 
    // cleanup when the wrapper is exited. 
    auto_ptr<char> c(C(i)); 

    // Convert to a managed string and 
    // return. 
    return marshal_as<String^>(c.get()); 
} 

stringをマーシャリングするのではなく、byte配列をマーシャリングする必要があります。この場合でも、ラッパー方式をお勧めします(この場合、返されるマネージ配列を作成するには特定の知識が必要です):

// compile with: /clr 
#include <stdlib.h> 
#include <memory> 

using namespace System; 
using namespace msclr::interop; 

String^ WrappedC(int i) { 
    // Make the call to the native function. 
    // Let's store in an auto_ptr to handle 
    // cleanup when the wrapper is exited. 
    auto_ptr<char> c(C(i)); 

    // Copy the pointer. 
    char* p = c.get(); 

    // The byte array to return. 
    // i is the size of the array, as per the call 
    // to C. 
    array<byte>^ a = gcnew array<byte>(i); 

    // Populate. 
    for (int index = 0; index < i; ++index) 
     a[index] = (byte) *p++; 

    // Return the array. 
    return a; 
} 

これらは管理されていない領域に割り当てられたメモリへのポインタを管理するために管理対象オブジェクトと非管理対象オブジェクトを連続的にマーシャリングすることを心配する必要がないため、

+0

ありがとう、それは正常に働いた。 しかし、C#でC#関数から返される文字配列バッファを受け取ることができ、そのデータバッファをC#のbyte []に​​格納する方法はありますか? 文字列に変換したくありません。ありがとう! – TTGroup

+1

@TTGroup答えを更新しました(前回のコードサンプルを参照)。管理されたラッパーを作成し、そこでデータ操作を行い、次に管理された型を返します。 – casperOne

+0

それは非常にクールcasperOneです:) 私はもう一つ質問があります。私はキューのデータ構造を実装したい、キューの各要素は、バイナリデータバッファを含むchar []配列へのポインタです。 C++側:データをキューにプッシュします。 C#側:キューからデータをポップします。 ここで私が言及したいのは、データをコピーせずにキューからデータをポップする方法です。これは、C#が、上記のコードとして1バイトのバイトをコピーするのではなく、そのキュー内のデータへのポインタを取得することによってデータをポップすることを意味します(for int index = 0; index TTGroup