Я работаю в системе, которая требует взаимодействия с собственным C API, используя P / Invoke. Теперь я (еще раз) наткнулся на проблему, которую никак не могу решить. Оригинальная функция предназначена для возврата 2 видов структур, основываясь на параметре, который указывает, какую структуру использовать.
Файл заголовка 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 #, метод возвращает false, а Marshal.GetLastWin32Error () возвращает -1073741737 (что для меня не имеет большого смысла). Я не вижу причин, по которым этот вызов должен завершиться неудачей, и определенно не с этим кодом ошибки. Поэтому я подозреваю, что что-то не так в моей оболочке P / Invoke.
Я знаю, что использование «byte []» в качестве типа pData, вероятно, некорректно, но, согласно некоторым поискам, «LPBYTE» переводится в «[Out] byte []». Я предполагаю, что правильный способ сделать это - использовать pData в качестве IntPtr и создать структуру, используя Marshal.PtrToStructure (...). Я пробовал это, но результат тот же. Вот код для этого сценария:
[DllImport(@"ICE_API.DLL", CharSet = CharSet.Auto, EntryPoint = "_GetCardStatus@28", 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". Этого не произошло ни с одной другой функцией, которую я обернул, поэтому я немного удивился.