Я работаю над системой, которая требует взаимодействия с собственным C API с использованием P/Invoke. Теперь я (еще раз) наткнулся на проблему, которую я никак не могу решить. Исходная функция предназначена для возврата двух типов структур, основанных на параметре, который указывает, какую структуру использовать.Проблема с вызовом функции 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/Invoke оберток, как это:
[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 должно приводить к тому, что структура CARD_INFO_1 будет возвращаться в «pData».
Документация содержит следующий пример C:
CARD_INFO_1 ci1;
DWORD cbNeeded;
ci1.bActive = TRUE;
if (GetCardStatus(*lpCardID, 1, (LPBYTE)&ci1, sizeof(ci1), &cbNeeded)) { /* success */ }
Мой эквивалент C# реализация выглядит так:
uint needed;
byte[] byteArray = new byte[Marshal.SizeOf(typeof(CARD_INFO_1))];
if (GetCardStatus(cardId, 1, byteArray, (uint)byteArray.Length, out needed)) { /* success */ }
Когда я выполняю этот C# код, метод возвращает ложь и Marshal.GetLastWin32Error () return -1073741737 (что для меня не имеет смысла). Я не вижу причин, почему этот вызов должен завершиться неудачно, и определенно не с этим кодом ошибки. Поэтому я подозреваю, что у меня что-то не так в моей обертке P/Invoke.
Я знаю, что использование «byte []» как типа pData, вероятно, неверно, но, согласно некоторым сообщениям, «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);
Edit: Одна вещь, которую я забыл упомянуть о том, что по какой-то причине вызов GetCardStatus завершается с неизвестной точки входа за исключением, если не указать EntryPoint = «_GetCardStatus @ 28" . Это не случилось с какой-либо другой функцией, которую я обернул, так что мне стало интересно.
Это было то, что я сделал вначале. Использование подписи при описании здесь приводит к исключению: System.AccessViolationException: Попытка чтения или записи защищенной памяти. Это часто свидетельствует о том, что другая память повреждена. –
Я внимательно прочитал ваш вопрос и отредактировал свой ответ. –
Спасибо за ваши отзывы. Ваш ответ привел меня к решению проблемы в конце концов. Я отправлю полное решение в новом ответе :-) –