P / Invoke funksjonskall problem

stemmer
0

Jeg arbeider på et system som krever interaksjon med en native C API hjelp av P / Invoke. Nå har jeg (nok en gang) snublet over et problem som jeg ikke kan synes å løse på noen måte. Den opprinnelige funksjon er konstruert for å returnere 2 typer strukturer, basert på en parameter som angir hvilken struktur til bruk.

Den C-topptekstfilen definerer strukturer og funksjon som følger:

#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 );

Jeg har forsøkt å implementere P / Invoke pakkere som dette:

[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);

Kalle GetCardId synes å fungere fint. Jeg får plausible data i CARDIDTYPE eksempel etter å kalle det. Men når jeg kaller GetCardStatus problemene begynner. Den konstruksjonstype som skal returneres er definert ved nivå parameter, og en verdi på 1 bør resultere i en CARD_INFO_1 struktur for å være returnes i pData.

Dokumentasjonen inneholder følgende C eksempel:

CARD_INFO_1 ci1;
DWORD cbNeeded;
ci1.bActive = TRUE;
if (GetCardStatus(*lpCardID, 1, (LPBYTE)&ci1, sizeof(ci1), &cbNeeded )) { /* success */ }

Min tilsvarende C # implementering er som dette:

uint needed;
byte[] byteArray = new byte[Marshal.SizeOf(typeof(CARD_INFO_1))];
if (GetCardStatus(cardId, 1, byteArray, (uint)byteArray.Length, out needed)) { /* success */ }

Når jeg utfører denne C # -kode, returnerer metoden falske og Marshal.GetLastWin32Error () returnerer -1073741737 (som gjør ikke mye mening for meg). Jeg ser ingen grunn til at denne samtalen skulle svikte, og definitivt ikke med denne feilkoden. Så jeg mistenker at jeg har fått noe galt i mitt P / Invoke wrapper.

Jeg vet at det å bruke byte [] som type pData er nok ikke riktig, men ifølge noen googling en LPBYTE oversettes til [Ut] byte []. Jeg antar den riktige måten å gjøre dette på er å ha pData som IntPtr, og skape struktur ved hjelp Marshal.PtrToStructure (...). Jeg har prøvd dette, men resultatet er det samme. Her er koden for dette scenariet:

[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: En ting jeg glemte å nevne er at for noen grunn GetCardStatus anrop mislykkes med en ukjent inngangspunkt unntak hvis jeg ikke angir Entrypoint = _GetCardStatus @ 28. Dette har ikke skjedd med noen annen funksjon jeg har pakket, så det fikk meg lurer litt.

Publisert på 29/03/2009 klokken 14:12
kilden bruker
På andre språk...                            


3 svar

stemmer
3

_GetCardStatus@28ga meg en idé. Med mindre du kjører på 64-bits Windows, har du fått det antall argumenter feil. P / Invoke for GetCardStatusville være _GetCardStatus@20, fordi den har 5 32-bits argumenter. Din C erklæring GetCardStatussynes å akseptere cardIdav verdien snarere enn som referanse. Da CARDIDTYPEer 12 bytes lang, vil dette gi den riktige lengde av argumentlisten (28). Dessuten vil dette forklare både mottar en feilkode på -1073741737 (C0000057, STATUS_INVALID_PARAMETER) - siden du ikke passerer en gyldig cardId- og aksess brudd - GetCardStatusprøver å skrive til pcbNeeded, som er søppel fordi marshaler ikke har selv dyttet den !

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) ;

Merk omvendt rekkefølge av de tre CARDIDTYPEmedlemmer: stdcallskyver parametrene venstre mot høyre (dvs. mot lavere adresser), og min gjetning er at en structer "presset" som en enhet.

Også, hvis du senere lukker skriveren håndtak med CloseHandle, vil jeg foreslå å motta håndtaket CARDIDTYPEi en passende SafeHandle, ikke inn i et nakent IntPtr, og erklære GetCardStatuså motta den sikre håndtaket.

Svarte 29/03/2009 kl. 14:22
kilden bruker

stemmer
2

Som Anton antyder at problemet ligger i parametere som sendes til funksjonen. Jeg la ikke merke til dette i går, men CARDIDTYPE strukturen er vedtatt av pekeren i GetCardID funksjon, og ved verdi i GetCardStatus funksjon. I mine samtaler jeg passerte CARDIDTYPE av pekeren til GetCardStatus også, tvinger P / Invoke rammeverk for å finne riktig funksjon ved å spesifisere den nøyaktige funksjonen navn som finnes i-avhengighet Walker.

I løst dette ved å definere CARDIDTYPE som en struct i stedet for en klasse, og passere det ved henvisning til den GetCardId funksjon. Videre er CARDIDTYPE blir formidlet som en Struct da sendes til GetCardStatus funksjon. Dette i tillegg til Antons teknikken med å bruke to funksjonsdefinisjoner med forskjellige typer pData (CARD_INFO_1 og CARD_INFO_2) arbeider nå på riktig måte. Her er de siste definisjoner:

[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);

Takk for ditt bidrag til å løse dette problemet dere begge :-)

Svarte 30/03/2009 kl. 10:26
kilden bruker

stemmer
1

Problemet er at du bruker [Ut] hvor du bør bruke noe

[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);

The Out / In attributter fortelle CLR Marshaller i hvilken retning umiddelbar variable vil bli formidlet. I tilfelle av byte [], er parameteren egentlig gjør ingenting. En av det sub-elementer beveges.

Formidlinger arrays er et kinkig skjønt, spesielt når det brukes direkte i en signatur kontra en struktur. Det kan være bedre for deg å bruke en IntPtr, allokere minne der og manuelt Marshal rekken ut av 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);
}
Svarte 29/03/2009 kl. 14:16
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more