How to handle optional struct parameters when calling C functions from C# [duplicate]

StackOverflow https://stackoverflow.com/questions/11901534

  •  25-06-2021
  •  | 
  •  

Frage

How can i skip the optional parameter (pioRecvPci) in C#? I think the main problem is that in C the parameter is a pointer so it is possible to supply NULL while in C# the ref keyword on a struct is used which can't be null by definition.

C Code

typedef struct {
  DWORD dwProtocol;
  DWORD cbPciLength;
} SCARD_IO_REQUEST;

LONG WINAPI SCardTransmit(
      __in         SCARDHANDLE hCard,
      __in         LPCSCARD_IO_REQUEST pioSendPci,
      __in         LPCBYTE pbSendBuffer,
      __in         DWORD cbSendLength,
      __inout_opt  LPSCARD_IO_REQUEST pioRecvPci,
      __out        LPBYTE pbRecvBuffer,
      __inout      LPDWORD pcbRecvLength
    );

C# Code

[StructLayout(LayoutKind.Sequential)]
public struct SCARD_IO_REQUEST
{
    public int dwProtocol;
    public int cbPciLength;
}

[DllImport("winscard.dll")]
public static extern int SCardTransmit(
     int hCard, 
     ref SCARD_IO_REQUEST pioSendRequest, 
     ref byte SendBuff, 
     int SendBuffLen, 
     ref SCARD_IO_REQUEST pioRecvRequest, 
     ref byte RecvBuff, 
     ref int RecvBuffLen);
War es hilfreich?

Lösung

You can change the struct to a class and then pass null. Remember that a C# struct is quite different from a C++ struct, and here your really want to use a C# class.

Or if you always want to ignore pioRecvRequest change the signature of SCardTransmit so that pioRecvRequest is of type IntPtr. Then pass IntPtr.Zero for the value.

Actually, the SCARD_IO_REQUEST is just a header and if you want to pass it in the call you will have to manage this structure and the additional buffer space yourself anyway so IntPtr is the right choice. You will then have to use the Marshal functions to allocate and fill the structure before the call and unmarshal the data and free it after the call.

Andere Tipps

C# supports method overloads. Something you can take advantage of here, redeclare the method but now give it an IntPtr argument type (no ref). And pass IntPtr.Zero.

Second way is to marshal the structure yourself. Declare the argument type IntPtr. And use Marshal.AllocHGlobal() to allocate memory for the structure, Marshal.StructureToPtr() to copy the structure into it. Marshal.FreeHGlobal() after the call. Or pass IntPtr.Zero. Clearly the overload trick is much less painful.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top