2009-03-29 2 views
0

Я работаю над системой, которая требует взаимодействия с собственным 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" . Это не случилось с какой-либо другой функцией, которую я обернул, так что мне стало интересно.

ответ

3

[email protected] дал мне представление. Если вы не работаете в 64-разрядной Windows, у вас неверно количество аргументов. Ваш P/Invoke для GetCardStatus будет [email protected], поскольку он имеет 5 32-битных аргументов.Ваше объявление C GetCardStatus, по-видимому, принимает значение cardId по значению, а не по ссылке. Поскольку CARDIDTYPE имеет длину 12 байтов, это даст правильную длину списка аргументов (28). Кроме того, это могло бы объяснить, как ваш получать код ошибки -1073741737 (C0000057, STATUS_INVALID_PARAMETER) - так как вы не передаете действующий cardId - и нарушения прав доступа - GetCardStatus пытается написать pcbNeeded, который является мусором, потому что в маршалинга даже не подтолкнул его!

Ergo:

[DllImport("ICE_API.DLL", CharSet = CharSet.Auto, 
    CallingConvention = CallingConvention.Winapi, SetLastError = true)] 
public static extern bool GetCardStatus (
    IntPtr hPrinter, UInt32 cardNum, UInt32 jobId, UInt32 level, 
    [In, Out] CARD_INFO_1 data, UInt32 cbBuf, out UInt32 pcbNeeded) ; 
[DllImport("ICE_API.DLL", CharSet = CharSet.Auto, 
    CallingConvention = CallingConvention.Winapi, SetLastError = true)] 
public static extern bool GetCardStatus (
    IntPtr hPrinter, UInt32 cardNum, UInt32 jobId, UInt32 level, 
    [In, Out] CARD_INFO_2 data, UInt32 cbBuf, out UInt32 pcbNeeded) ; 

Примечание обратный порядок трех CARDIDTYPE членов: stdcall выталкивает параметры слева направо (т.е. в сторону более низких адресов), и я думаю, что struct «выталкивается», как единица.

Кроме того, если вы позже закрыть дескриптор принтера с CloseHandle, я предложил бы получать ручку в CARDIDTYPE в соответствующий SafeHandle, а не в голом IntPtr, и объявление GetCardStatus получить безопасную ручку.

+0

Это было то, что я сделал вначале. Использование подписи при описании здесь приводит к исключению: System.AccessViolationException: Попытка чтения или записи защищенной памяти. Это часто свидетельствует о том, что другая память повреждена. –

+0

Я внимательно прочитал ваш вопрос и отредактировал свой ответ. –

+0

Спасибо за ваши отзывы. Ваш ответ привел меня к решению проблемы в конце концов. Я отправлю полное решение в новом ответе :-) –

1

Проблема заключается в использовании [Out], где вы не должны использовать ничего

[DllImport("ICE_API.DLL", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)] 
public static extern bool GetCardStatus(CARDIDTYPE CardId, UInt32 level, byte[] pData, UInt32 cbBuf, out UInt32 pcbNeeded); 

Командлет Out/In атрибуты сказать CLR ИАС, в каком направлении будет выстроил немедленным переменной. В случае байта [] параметр действительно ничего не делает. Один из его подэлементов перемещается.

Маршаллинговые массивы - дело сложное, особенно, когда оно используется непосредственно в сигнатуре против структуры. Возможно, вам лучше использовать IntPtr, выделить там память и вручную вывести массив из IntPtr.

[DllImport("ICE_API.DLL", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)] 
public static extern bool GetCardStatus(CARDIDTYPE CardId, UInt32 level, IntPtr pData, UInt32 cbBuf, out UInt32 pcbNeeded); 

public void Example(uint size) { 
    // Get other params 
    var ptr = Marshal.AllocHGlobal(size); 
    GetCardStatus(cardId, level, ptr, size, out needed); 
    // Marshal back the byte array here 
    Marshal.FreeHGlobal(ptr); 
} 
+0

меня пытается сделать это, но результат тот же. Функция retuns false, а код ошибки - то же самое. –

+0

@ Джонни, первое или второе решение? – JaredPar

+0

Извините ... Второе решение. Я буду редактировать свой пост, чтобы включить этот код. –

2

Как утверждает Антон, проблема заключается в параметрах, переданных функции. Я не заметил этого вчера, но структура CARDIDTYPE передается указателем в функции GetCardID и по значению в функции GetCardStatus. В моих вызовах я передал CARDIDTYPE также указателем на GetCardStatus, заставив фреймворк P/Invoke найти правильную функцию, указав точное имя функции, которое найдено в Dependecy Walker.

Я решил это, определив CARDIDTYPE как структуру вместо класса и передав ее по ссылке на функцию GetCardId. Далее CARDIDTYPE маршалируется как Struct при передаче функции GetCardStatus. Это в дополнение к технологии Antons использования двух определений функций с различными типами pData (CARD_INFO_1 и CARD_INFO_2) теперь работает правильно. Вот заключительные определения:

[StructLayout(LayoutKind.Sequential, Pack = 1)] 
public struct 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, ref CARDIDTYPE pCardId); 

[DllImport(@"ICE_API.DLL", EntryPoint = "GetCardStatus", CallingConvention = CallingConvention.Winapi, SetLastError = true)] 
public static extern bool GetCardStatus([MarshalAs(UnmanagedType.Struct)]CARDIDTYPE CardId, UInt32 level, 
    [In, Out] CARD_INFO_1 pData, UInt32 cbBuf, out UInt32 pcbNeeded); 

[DllImport(@"ICE_API.DLL", EntryPoint = "GetCardStatus", CallingConvention = CallingConvention.Winapi, SetLastError = true)] 
public static extern bool GetCardStatus([MarshalAs(UnmanagedType.Struct)]CARDIDTYPE CardId, UInt32 level, 
    [In, Out] CARD_INFO_2 pData, UInt32 cbBuf, out UInt32 pcbNeeded); 

Благодарим вас за ваш вклад в решение этой проблемы :-)

+0

Хех, я не знал об UnmanagedType.Struct. Спасибо за совет! :) –

Смежные вопросы