Search code examples
c#pinvokesmartcardmarshalbyrefobjectwinscard

SCardGetCardTypeProviderName return empty results


I'm trying to use the SCardGetCardTypeProviderName using interop in C#. One of the parameters is a reference, that is supposed to return the name of the provider for a smart card according to the card context that is passed in. This is the code I'm using:

IntPtr hSC = { value comes from call using SCardEstablishContext }
string cardName = { value comes from SCardUIDlgSelectCard }
int providerNameLength = 256;
string providerName = string.Empty;  //doesn't matter how I initialize this

[DllImport("winscard.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern Int32 SCardGetCardTypeProviderName(IntPtr hContext, string szCardName, uint dwProviderId, ref string szProvider, ref int pcchProvider);

int lReturn = SCardGetCardTypeProviderName(hSC, cardName, SCARD_PROVIDER_CSP, ref providerName, ref providerNameLength);

But the providerName always comes back empty, while the providerNameLength changes to 43, which makes me think it's a marshalling problem. But I've tried marshalling providerName as:

  • LPWStr
  • LPTStr
  • LPStr

In the case of the latter two, instead of an empty string, I get a string of strange characters, again, making me think it's a marshalling/translation issue.


Solution

  • Declare the string parameter as StringBuilder:

    [DllImport("winscard.dll"]
    public static extern int SCardGetCardTypeProviderName(
        IntPtr hContext,
        string szCardName, 
        uint dwProviderId, 
        StringBuilder szProvider, 
        ref int pcchProvider
    );
    ....
    StringBuilder providerName = new StringBuilder(providerNameLength);
    int lReturn = SCardGetCardTypeProviderName(
        hSC, 
        cardName, 
        SCARD_PROVIDER_CSP, 
        providerName, 
        ref providerNameLength
    );
    

    No need to repeat the default value of CharSet, and these API functions don't use the Win32 last error mechanism.