P/Invokeを使用してネイティブC APIとの対話が必要なシステムで作業しています。今私は(もう一度)私が決して解決できないような問題に遭遇しました。元の関数は、使用する構造体を指定するパラメータに基づいて、2種類の構造体を返すように設計されています。P/Invoke関数呼び出しの問題
次のようにCヘッダファイルは構造と機能を定義しています
#pragma pack(1)
typedef struct {
DWORD JobId;
DWORD CardNum;
HANDLE hPrinter;
} CARDIDTYPE, FAR *LPCARDIDTYPE;
#pragma pack()
typedef struct {
BOOL bActive;
BOOL bSuccess;
} CARD_INFO_1, *PCARD_INFO_1, FAR *LPCARD_INFO_1;
typedef struct {
DWORD dwCopiesPrinted;
DWORD dwRemakeAttempts;
SYSTEMTIME TimeCompleted;
} CARD_INFO_2, *PCARD_INFO_2, FAR *LPCARD_INFO_2;
BOOL ICEAPI GetCardId(HDC hdc, LPCARDIDTYPE pCardId);
BOOL ICEAPI GetCardStatus(CARDIDTYPE CardId, DWORD level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded);
私はこのようなP /呼び出しラッパーを実装しようとしてきた:
[StructLayout(LayoutKind.Sequential, Pack=1)]
public class CARDIDTYPE {
public UInt32 JobId;
public UInt32 CardNum;
public IntPtr hPrinter;
}
[StructLayout(LayoutKind.Sequential)]
public class CARD_INFO_1 {
public bool bActive;
public bool bSuccess;
}
[StructLayout(LayoutKind.Sequential)]
public class CARD_INFO_2 {
public UInt32 dwCopiesPrinted;
public UInt32 dwRemakeAttempts;
public Win32Util.SYSTEMTIME TimeCompleted;
}
[DllImport("ICE_API.DLL", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool GetCardId(HandleRef hDC, [Out]CARDIDTYPE pCardId);
[DllImport("ICE_API.DLL", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool GetCardStatus(CARDIDTYPE CardId, UInt32 level, [Out] byte[] pData, UInt32 cbBuf, out UInt32 pcbNeeded);
「GetCardId」は動作しているように電話良い。私はそれを呼び出した後、CARDIDTYPEインスタンスにもっともらしいデータを取得します。しかし、私が "GetCardStatus"を呼び出すと、問題が始まります。返される構造体の型は "level"パラメータによって定義され、値1は "pData"に返されるCARD_INFO_1構造体になります。
CARD_INFO_1 ci1;
DWORD cbNeeded;
ci1.bActive = TRUE;
if (GetCardStatus(*lpCardID, 1, (LPBYTE)&ci1, sizeof(ci1), &cbNeeded)) { /* success */ }
マイ同等のC#実装は次のようである:私はこのC#コードを実行すると
uint needed;
byte[] byteArray = new byte[Marshal.SizeOf(typeof(CARD_INFO_1))];
if (GetCardStatus(cardId, 1, byteArray, (uint)byteArray.Length, out needed)) { /* success */ }
は、方法が偽とMarshal.GetLastWin32Error返す(
ドキュメントは、次のCの例が含まれてい)返す-1073741737(これは私には分かりません)。この呼び出しが失敗する理由はなく、間違いなくこのエラーコードはありません。だから私はP/Invokeラッパーに何か間違っていると思う。
pDataのタイプとして「byte []」を使用するのはおそらく正しいとは限りませんが、グーグルによれば「LPBYTE」は「[Out] byte []」に変換されます。これを行う正しい方法は、pDataをIntPtrとして使用し、Marshal.PtrToStructure(...)を使用して構造体を作成することです。私はこれを試しましたが、結果は同じです。ここでは、このシナリオのためのコードは次のとおりです。
[DllImport(@"ICE_API.DLL", CharSet = CharSet.Auto, EntryPoint = "[email protected]", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool GetCardStatus(CARDIDTYPE CardId, UInt32 level, IntPtr pData, UInt32 cbBuf, out UInt32 pcbNeeded);
uint needed;
int memSize = Marshal.SizeOf(typeof(CARD_INFO_1));
IntPtr memPtr = Marshal.AllocHGlobal(memSize);
if (!GetCardStatus(cardId, 1, memPtr, (uint)memSize, out needed)) {
int lastError = Marshal.GetLastWin32Error();
// error code is -1073741737
}
CARD_INFO_1 info = (CARD_INFO_1)Marshal.PtrToStructure(memPtr, typeof(CARD_INFO_1));
Marshal.FreeHGlobal(memPtr);
編集:私は言及を忘れてしまった ことの一つは、私がエントリーポイント=「_GetCardStatusを指定しない場合は、何らかの理由でGetCardStatusコールが不明なエントリポイント例外で失敗するということです@ 28 "。これは、私がラップした他の機能には起こっていないので、ちょっと疑問に思いました。
これは実際に私が最初にしたことでした。ここで説明するように署名を使用すると、例外が発生します。 System.AccessViolationException:保護されたメモリの読み取りまたは書き込みを試みました。これはしばしば、他のメモリが壊れていることを示します。 –
私はあなたの質問をより慎重に読んで、私の答えを編集しました。 –
ご意見ありがとうございます。あなたの答えは結局問題を解決するように私を導いた。私は新しい答えで完全な解決策を投稿します: –