Search code examples
c#windows-server-2008-r2pcscthin-client

PC/SC-Sharp GetReaders() throws InsufficientBuffer exception


I'm using PC/SC Sharp package downloaded from NuGet and there is one method GetReaders(), which is returning active readers connected to machine.

var contextFactory = ContextFactory.Instance;
using (var context = contextFactory.Establish(SCardScope.System)) {
    Console.WriteLine("Currently connected readers: ");
    var readerNames = context.GetReaders();
    foreach (var readerName in readerNames) {
        Console.WriteLine("\t" + readerName);
    }
}

When I'm calling it from my local machine (Windows 10 Pro x64), it is working correctly and returning available readers names. Anyway, when connecting through thin client to Windows Server 2008 R2, it throws a InsufficientBuffer exception

InsufficientBuffer: The data buffer to receive returned data is too small for the returned data.


Solution

  • Well, I found a solution. I've downloaded the source code of PC/SC-Sharp from their GitHub page and started analyzing, the problem was in the method SCardListReaders which has parameter pcchReaders and that parameter was to small and causing an exception, from MSDN:

    Length of the mszReaders buffer in characters. This parameter receives the actual length of the multi-string structure, including all trailing null characters. If the buffer length is specified as SCARD_AUTOALLOCATE, then mszReaders is converted to a pointer to a byte pointer, and receives the address of a block of memory containing the multi-string structure. This block of memory must be deallocated with SCardFreeMemory.

    was called like this inside pcsc-sharp's ListReaders method (WinSCardAPI):

    public SCardError ListReaders(IntPtr hContext, string[] groups, out string[] readers) {
        var dwReaders = 0;
    
        // initialize groups array
        byte[] mszGroups = null;
        if (groups != null)
        mszGroups = SCardHelper.ConvertToByteArray(groups, TextEncoding);
    
        // determine the needed buffer size
        var rc = SCardHelper.ToSCardError(
            SCardListReaders(hContext,
            mszGroups,
            null,
            ref dwReaders));
    
        if (rc != SCardError.Success) {
            readers = null;
            return rc;
        }
    
        // initialize array
        var mszReaders = new byte[dwReaders * sizeof(char)];
    
    
        rc = SCardHelper.ToSCardError(
            SCardListReaders(hContext,
            mszGroups,
            mszReaders,
            ref dwReaders));
    
        readers = (rc == SCardError.Success)
                  ? SCardHelper.ConvertToStringArray(mszReaders, TextEncoding)
                  : null;
    
        return rc;
    }
    

    First time, it was calling SCardListReaders method to determine the needed buffer size, with mszReaders parameter set to null, so according to MSDN:

    Multi-string that lists the card readers within the supplied reader groups. If this value is NULL, SCardListReaders ignores the buffer length supplied in pcchReaders, writes the length of the buffer that would have been returned if this parameter had not been NULL to pcchReaders, and returns a success code.

    So it should have assign to dwReaders the correct buffer size which will be used to get connected readers list. Well it was working correctly on my Windows 10 pro machine, with readers connected directly, but connected to Windows 2008 r2 server through thin client it was returning same value (It was 37), but that value was causing InsufficientBuffer exception.

    So I've started tweaking that value and setting manually (debugging inside Windows 2008 r2 server) and I found that if set that value to 48 (and higher) it will work. I don't know what is causing, that the SCardListReaders method returning insufficient value for that parameter, but I've managed to manually double that value before passing second time, so the new version of ListReaders() looked like this:

    public SCardError ListReaders(IntPtr hContext, string[] groups, out string[] readers) {
        var dwReaders = 0;
    
        // initialize groups array
        byte[] mszGroups = null;
        if (groups != null)
        mszGroups = SCardHelper.ConvertToByteArray(groups, TextEncoding);            
    
        // determine the needed buffer size
        var rc = SCardHelper.ToSCardError(
            SCardListReaders(hContext,
            mszGroups,
            null,
            ref dwReaders));
    
        if (rc != SCardError.Success) {
            readers = null;
            return rc;
        }
    
        //doubling buffer size to work through thin clients
        dwReaders *= 2; // <------------------ New line
    
        // initialize array
        var mszReaders = new byte[dwReaders * sizeof(char)];
    
        rc = SCardHelper.ToSCardError(
            SCardListReaders(hContext,
            mszGroups,
            mszReaders,
            ref dwReaders));
    
        readers = (rc == SCardError.Success)
                  ? SCardHelper.ConvertToStringArray(mszReaders, TextEncoding)
                  : null;
    
        return rc;
    }
    

    So it is working now, if you have any ideas and little bit less "hacky" solution, maybe I'm doing something wrong, or maybe it should work differently through thin clients and it is a bug or something, please feel free to comment!