C-langで記述されたdllに次のコードがあるとしましょう。ここで、dllで定義されたいくつかの関数を関数ポインターとしてマップし、実際の関数にマップしようとします。次のリンクをたどって、ここまで到達しますhttps://docs.microsoft .com / en-us / dotnet / framework / interop / marshaling-different-types-of-arrays
Dlltest.h
typedef struct VersionInfo
{
UINT uMajor;
UINT uMinor;
UINT uMRevision;
} STRUCT_VERSION_INFO;
typedef struct DllTestFPStruct
{
int(*Close) (void);
int(*Init) (STRUCT_VERSION_INFO *sVersInfo);
int(*RegisterClient) (int id);
} STRUCT_DLL_TEST_FP_STRUCT;
typedef struct DllTestMgr
{
STRUCT_DLL_TEST_FP_STRUCT *psItf;
int iDummy;
} STRUCT_DLL_TEST_MGR;
extern "C"
{ __declspec(dllexport) void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP); }
Dlltest.c
static int Close(void);
static int Init(STRUCT_VERSION_INFO *sVersInfo);
static int RegisterClient(int id);
STRUCT_DLL_TEST_FP_STRUCT sFP =
{
&Close,
&Init,
&RegisterClient,
};
DLLTESTC_API void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP)
{ psFP->psItf = &sFP; }
static int Close(void)
{ printf("Closed called.\n"); }
static int Init(STRUCT_VERSION_INFO *sVersInfo)
{ printf("Init called.\n");}
static int RegisterClient(STRUCT_VERSION_INFO *sVersInfo)
{ printf("RegisterClient called.\n");}
ここで、このDLLを使用するac#アプリケーションを作成したいと思います。特に、「GetDllTestFP」(関数ポインターを実際の関数にマップする関数)を使用する必要があります。現在、私のC#アプリケーションは次のとおりです。
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_CLOSE(ref VersionInfo sVersInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_INIT(ref VersionInfo sVersInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_RC(ref VersionInfo sVersInfo);
[StructLayout(LayoutKind.Sequential)]
public struct DllTestFPStruct
{
public FP_DLL_TEST_CLOSE Close;
public FP_DLL_TEST_INIT Init;
public FP_DLL_TEST_RC RegisterClient;
}
[StructLayout(LayoutKind.Sequential)]
public struct DllTestMgr
{
public IntPtr psItf;
public int iDummy;
}
[DllImport("DllTestC.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void GetDllTestFP(ref DllTestMgr ps);
static void Main(string[] args)
{
VersionInfo vInfo = new VersionInfo();
DllTestFPStruct dllFPs = new DllTestFPStruct();
DllTestMgr dllTest = new DllTestMgr();
IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(dllFPs));
Marshal.StructureToPtr(dllFPs, buffer, false);
dllTest.psItf = buffer;
GetDllTestFP(ref dllTest); // Funtionpointers are not mapped, still null
dllFPs.Close(ref vInfo);
}
問題は、関数がdll内の実際の関数にマップされないことです。どうすれば目標を達成できますか?
ありがとう
C#コードは次のとおりです。
DllTestMgr dllTest = new DllTestMgr();
GetDllTestFP(ref dllTest);
DllTestFPStruct dllFPs = (DllTestFPStruct)Marshal.PtrToStructure(dllTest.psItf, typeof(DllTestFPStruct));
VersionInfo vInfo = new VersionInfo();
dllFPs.Close(ref vInfo);
次のように、dllTest.psItf
becaseを割り当てる必要はありGetDllTestFP
ません。
DLLTESTC_API void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP)
{
psFP->psItf = &sFP;
}
だから、コピーしたアドレスのをsFP
。
データ(sFP
構造体)への直接アクセスを「クライアント」に与えるため、一般的にこれは悪い考えであることに注意してください。別の方法は、クライアントが(前に書いたように)メモリを渡してから、次のことを行うことです。
(*psFP->psItf) = sFP;
(ただし、割り当てられたメモリを解放することを忘れないでください!)
3番目の方法として、C側は共有アロケーター(C#で使用できるため、ここではmalloc
/new
ここでは使用できません)を介してメモリブロックを割り当て、C#はそれを割り当て解除する必要があります。
間違った解決策は
STRUCT_DLL_TEST_FP_STRUCT sFP2 = sFP;
psFP->psItf = &sFP2;
の存続期間はsFP2
、メソッドが戻るときに終了します。psFP->psItf
は、もう存在しないスタックの一部を指しています。しないでください!
ああ... @Hansアンパッサンによって書かれたように、メモリを割り当て、誰に応じて、GetDllTestFP
可能ref
かout
。メモリがC#によって割り当てられている場合は、ref
(現在のように)割り当てられていないか、C ++によって割り当てられている場合は、問題out
ありません。一方向のマーシャリングを節約できます。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加