あなたの2番目のアプローチは私には正しいように見えます。あなたが持っている場所:
FreePointerHandler(pointerToArray.ToPointer());
私はあなたがただできると思います:
FreePointerHandler(pointerToArray);
私はテストを書いていて、編集を投稿します。
ref
対。out
ref
vs.に関してout
は、これはC++参照に正確にマップされていません。C#がC#を呼び出す場合out
、呼び出された関数によってパラメーターを設定する必要があります。相互運用呼び出しでは、これを強制することはできません。ドキュメントの価値のためにout
、この場合は私が好みます。
本当の問題
本当の問題は、.Net pinvokeの経験がない場合、すぐに目に見える副作用なしに関数を呼び出すときに自信を持てないことです。
@Hans Passantは、メモリリークを明らかにするために、「100万回呼び出すことでテストする」ことを推奨しました。この場合、このアプローチはおそらく最も単純ですが、関連付けられたメモリリソースがない場合には一般化されず、失敗しても追加情報は提供されません。.dllのソースコードがある場合はpinvoke呼び出しを介してデバッグするか、ない場合はモックAPI実装を作成することを好みます。そうすれば、新しいピンボーク機能を試しているときに、何が起こっているのかを正確に確認できます。また、問題がある場合は、デバッガーでトレースすると、どこが間違っているかを確認するのに役立ちます。
完全なピンボークの例
これは、元の質問に基づく完全な例です。戻り値も、myFunctionの詳細も文書化されていません。この例では、割り当てられた長さ3の配列を(0、1、2)で埋めるだけです。
DLLのヘッダーファイルは次のとおりです。
// testlib.h
#ifdef TESTLIB_EXPORTS
#define TESTLIB_API __declspec(dllexport)
#else
#define TESTLIB_API __declspec(dllimport)
#endif
extern "C" {
TESTLIB_API int myFunction(int someNumber, int &arraySize, signed char *&array);
TESTLIB_API int freePointer(void* myPointer);
}
.DLLの.cppソースファイル:
// testlib.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "testlib.h"
#include <iostream>
using namespace std;
TESTLIB_API int myFunction(int someNumber, int &arraySize, signed char *&array)
{
#ifdef _DEBUG
cout << "myFunction enter " << someNumber << ", " << arraySize << ", 0x" << &array << endl;
#endif
arraySize = 3;
array = (signed char*)malloc(arraySize);
#ifdef _DEBUG
cout << "myFunction array: 0x" << (void *)array << endl;
#endif
for (int i = 0; i < arraySize; ++i)
{
array[i] = i;
}
#ifdef _DEBUG
cout << "myFunction exit " << someNumber << ", " << arraySize << ", 0x" << &array << endl;
#endif
return 0;
}
TESTLIB_API int freePointer(void* myPointer)
{
#ifdef _DEBUG
cout << "freePointer: 0x" << myPointer << endl;
#endif
free(myPointer);
return 0;
}
C#ソース:
using System;
using System.Runtime.InteropServices;
namespace InteropTest
{
class InteropTest
{
[DllImport("testlib.dll")]
private static extern int myFunction(int someNumber, out int arraySize, out IntPtr array);
[DllImport("testlib.dll")]
public static extern int freePointer(IntPtr pointer);
public static byte[] myFunction(int myNumber)
{
int myArraySize;
IntPtr pointerToArray;
int iRet = myFunction(myNumber, out myArraySize, out pointerToArray);
// should check iRet and if needed throw exception
#if (DEBUG)
Console.WriteLine();
Console.WriteLine("InteropTest.myFunction myArraySize: {0}", myArraySize);
Console.WriteLine();
#endif
byte[] myArray = new byte[myArraySize];
Marshal.Copy(pointerToArray, myArray, 0, myArraySize);
freePointer(pointerToArray);
#if (DEBUG)
Console.WriteLine();
#endif
return myArray;
}
static void Main(string[] args)
{
#if (DEBUG)
Console.WriteLine("Start InteropTest");
Console.WriteLine();
#endif
int myNumber = 123;
byte[] myArray = myFunction(myNumber);
for (int i = 0; i < myArray.Length; ++i)
{
Console.WriteLine("InteropTest myArray[{0}] = {1}", i, myArray[i]);
}
}
}
}
デバッグビルドはステータスメッセージを出力します。それらは、myFunction
のarray
値がに渡されるものであることを示していfreePointer
ます。
のC#インターフェイス関数も作成しましたmyFunction
。pinvoke呼び出しが簡単ではない場合、通常は詳細を1回計算し、それを関数にカプセル化して再利用する必要があります。