0

I'm building a C# Win Form application with Visual Studio 2019, that aims to communicate with a SmartCards (ACR 122U).

To be short: the type of tags I'm working with are passive tags that aren't recognized by the reader. The only way to work with these tags is to send direct commands to the chip inside the reader.

Using winscard.dll I managed to do it.

I'm trying to make a backup of the tag: the procedure have to read and store 128 blocks (so 128 SCardControl will be called).

If I do it in the UI thread, it works like a charm (5 seconds max, 30MB of memory usage for the process during the procedure). But if I try to do it in a background thread, it takes forever (25 seconds min, and 150MB of memory usage for the process during the procedure).

Why it goes like that?

EDIT:

Here's some code of the thread


//All the DllImport directives:
[DllImport("winscard.dll")]
public static extern int SCardEstablishContext(uint dwScope, IntPtr notUsed1, IntPtr notUsed2, out IntPtr phContext);

[DllImport("winscard.dll", EntryPoint = "SCardListReadersA", CharSet = CharSet.Ansi)]
public static extern int SCardListReaders(IntPtr hContext, byte[] mszGroups, byte[] mszReaders, ref UInt32 pcchReaders);

//... Other working method delcarations

//Declaration of the "slow" method SCardControl
[DllImport("winscard.dll")]
public static extern int SCardControl(IntPtr hCard, int dwControlCode, byte[] lpInBuffer, uint cbInBufferSize, byte[] lpOutBuffer, ref uint cbOutBufferSize, ref int lpBytesReturned);

//... Other declarations.

private void btnBackup_Click(object sender, EventArgs e)
{
    Thread t = new Thread(bgWorker);
    t.Start();
    //bgWorker();
}

public void bgWorker() {
    int res;
    
    uint SystemScope = 2; //From the documentation. Tried both 1 and 2 (User scope and System Scope)
    uint readerNum = 0;
    uint controlMode = 3; //Using SCARD_SHARE_DIRECT as control mode in order to communicate with the reader
    uint protocolToUse = 0; //Using SCARD_PROTOCOL_UNDEFINED as protocol (must use it in order to use SCARD_SHARE_DIRECT)
    int EscapeCode = 3225264; //Suggested by ACR 122 U: Escape code in order to send commands to the reader, computes as SCARD_CTL_CODE(3500) in C.
    string ReadBlockCmd = "FF000000..." //Standard read command in hex for the tag from dtatasheet
    
    IntPtr context = IntPtr.Zero;
    IntPtr simulatedCard = IntPtr.Zero;
    IntPtr protocolEstablished = IntPtr.Zero;

    byte[] readerStream;
    string[] readerList;
    byte[] retStream = new byte[1000];
    uint sizeRetStream = 0;
    int sizeResponse = 0;

    List<string> cardBlocks = new List<string>();

    //NOTE: "res" is always = 0 when everything works. I cutted off the parts where i check the value of "res".

    //Establishing context
    res = SCardEstablishContext(SystemScope, IntPtr.Zero, IntPtr.Zero, out context);

    //Getting the reader list (the return is in "readerStream", with a multistring value)
    res = SCardListReaders(context, null, null, ref readerNum);
    readerStream = new byte[readerNum];
    res = SCardListReaders(context, null, readerStream, ref readerNum);
    readerList = Encoding.ASCII.GetString(readerStream).Split('\0');

    //Connect to the reader
    res = SCardConnect(context, readerList[0], controlMode, protocolToUse, ref simulatedCard, ref protocolEstablished);
    
    /* 
     * Some calls to SCardControl to configure the reader in order to work with my tags
     */
     
    //Backup procedure
    for(int i = 0; i < 128; i++)
    {
        SCardControl(simulatedCard, EscapeCode, HexStrToByteArray(ReadBlockCmd + i.ToString("X2")), (uint)ReadBlockCmd.Length / 2 + 1, retStream, ref sizeRetStream, ref sizeResponse);
        cardBlocks.Add(ByteArrayToHexStr(retStream, sizeResponse));
    }

    //Here it supposed to do the backup on file, but i removed it in order to limit the IO ops
}

If you comment the thread launching part and uncomment the call to bgWorker() the procedure lasts like 2.1 seconds. If you keep it like this (using the thread), the procedure lasts more than 20 seconds (no matter if in debug mode, in release, within Visual Studio or outside).

Lorenzo
  • 1
  • 1
  • Is there any excessive communication between the background thread and the UI thread, that could cause the delay? I suggest to update your answer by including the background thread version of your code, so that we have something to see and comment on. – Theodor Zoulias Aug 14 '20 at 00:05
  • Actually, there is no communication between the two threads: the worker thread exists only to read the card content and save it into a file. – Lorenzo Aug 14 '20 at 09:56
  • I see no reason for your code to run slower in a background thread. I suggest to make performance measurements both ways, while iteratively stripping your code line by line, until you find the offending line (assuming that there is a single line that causes the discrepancy). – Theodor Zoulias Aug 14 '20 at 17:27
  • The line that runs slower on thread is where i call che SCardControl function, but it's a Microsoft lib, and I can't check inside its code. Probably i should try to replicate this situation in a C++ code (maybe there are struggles with .NET and its resousce handling). – Lorenzo Aug 15 '20 at 08:42
  • The `SCardControl` line is composed by two method calls. It includes the call to `HexStrToByteArray` method. I suggest to split this line to two, to narrow down the search for the culprit. – Theodor Zoulias Aug 15 '20 at 09:08
  • The `SCardControl` is an `extern` method? – Theodor Zoulias Aug 15 '20 at 09:12
  • SCardControl is an extern method, yes. It's the execution of SCardControl the problem. In fact, if I precompute the byte array with the command (whish is identical for all the 128 iterations) and add just "i" as last element of the array the result is the same... – Lorenzo Aug 15 '20 at 13:24
  • Strange. Do you have the source code of the external `SCardControl` method? – Theodor Zoulias Aug 15 '20 at 13:33
  • 1
    Sadly no. It comes from the standard windows WinScard.dll. I just edited the code in the post with the declaration. Oh and, thanks for your time! – Lorenzo Aug 15 '20 at 13:48
  • You could try configuring the background `Thread` with `.SetApartmentState(ApartmentState.STA)`, to see if it makes any difference. – Theodor Zoulias Aug 15 '20 at 18:05

0 Answers0