Проблема с преобразованием программы смарт-карты в UWP

Я работаю над преобразованием того, что изначально было приложением .NET для веб-форм, в UWP. Приложение предназначено для использования бейджей, выпущенных компанией, для регистрации посещаемости собраний вместо наших текущих бумажных листов для подписи. Оригинальная программа использует Omnikey 5427 CK в режиме клавиатуры и не имеет проблем. Для нового, работающего под управлением Windows 10 IoT Core на Raspberry Pi 3B, я пытаюсь установить более прямую связь с устройством чтения карт, чтобы мне не приходилось полагаться на клавиатуру и быстрее собирать больше информации с карты.

Я пытался использовать образец смарт-карты Microsoft по адресу https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/SmartCard/cs. Ближайший существующий ответ SO на Бесконтактная карта через OmniKey, как получить "UID"? привел меня к репозиторию GitHub https://github.com/StormRider01/ProxCardDemo2/blob/master/prox.cs. Оба репозитория видят ридер на моем компьютере с Windows 10, но не на устройстве IoT. Как видно из этого снимка экрана (https://github.com/NevadaDonorNetwork/FSK-Prox-Card-Demo/blob/master/FSK%20Prox%20Card%20Demo/Screenshot_2019-04-04_13-49-14.jpg), устройство IoT обнаруживает устройство чтения карт.

Я собрал репозиторий в https://github.com/NevadaDonorNetwork/FSK-Prox-Card-Demo для текста на основе решения SO, которое я нашел.

// MainPage.xaml.cs
using System;
using System.Diagnostics;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace FSK_Prox_Card_Demo
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            InitializeComponent();
            LoadCardReaders();
        }
        private void BtnRefreshReaders_Click(object sender, RoutedEventArgs e)
        {
            LoadCardReaders();
        }
        void X_CardPresented(string reader, byte[] cardData)
        {
            // Clear form labels
            TxtFacilityCode.Text = string.Empty;
            TxtBadgeNumber.Text = string.Empty;
            try
            {
                // load and parse ATR for facility code and and badge number
                string ATR = SCARD.ToHex(cardData, string.Empty);
                string CardFormat = ATR.Substring(0, 4);
                string ProxFormatSetting = ATR.Substring(4, 2);
                string StrFacilityCode = ATR.Substring(6, 4);
                string StrCardNumber = ATR.Substring(10, 6);
                // ensure facility code and and badge number are integers
                if (!int.TryParse(StrFacilityCode, out int FacilityCode))
                {
                    Debug.WriteLine("Invalid Facility Code:" + StrFacilityCode);
                    return;
                }
                if (!int.TryParse(StrCardNumber, out int CardNumber))
                {
                    Debug.WriteLine("Invalid Card Number: " + StrCardNumber);
                    return;
                }
                // write facility code and and badge number to output
                TxtFacilityCode.Text = FacilityCode.ToString();
                TxtBadgeNumber.Text = CardNumber.ToString();
                Debug.WriteLine("Facility Code (FAC): " + FacilityCode.ToString());
                Debug.WriteLine("Card Number (CN): " + CardNumber.ToString());
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
                TxtOutput.Text += ex.Message + "\n";
            }
        }
        void LoadCardReaders()
        {
            // query readers
            ReaderList x = new ReaderList();
            x.CardPresented += X_CardPresented;
            x.Refresh();
            TxtOutput.Text += x.ReaderCount.ToString() + " readers found.\n";
        }
    }
}
// ProxCard2.cs
// based on Chris's response here: http://stackoverflow.com/questions/4497671/contact-less-card-through-an-omnikey-how-to-get-uid/14939168#14939168
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace FSK_Prox_Card_Demo
{
    public delegate void VoidDelegate();
    public delegate void CardPresented(string reader, byte[] cardData);

    public class ReaderList : IDisposable, IEnumerable<string>
    {
        public ReaderList()
        { }

        public void Dispose()
        {
            StopThread();
        }

        private Thread thread;
        private void StartThread()
        {
            if (thread != null)
                StopThread();

            thread = new Thread(Run)
            {
                IsBackground = true
            };
            bStopThread = false;
            thread.Start();
        }

        private void StopThread()
        {
            if (thread != null)
            {
                bStopThread = true;
                Thread.Sleep(50);
            }
            if (thread != null)
                thread.Abort();
            if (thread != null)
                thread.Join();
            thread = null;
        }

        private List<string> readerNames = new List<string>();
        private Dictionary<string, string> lastCardFound = new Dictionary<string, string>();
        public int ReaderCount
        { get { return readerNames.Count; } }

        public void Refresh()
        {
            if (thread == null)
                StartThread();
        }

        public event VoidDelegate ListChanged;
        public event CardPresented CardPresented;

        private bool bStopThread = true;
        private void Run()
        {
            IntPtr hContext = IntPtr.Zero;

            try
            {
                uint result = SCARD.EstablishContext(SCARD.SCOPE_SYSTEM, IntPtr.Zero, IntPtr.Zero, ref hContext);
                if (result != SCARD.S_SUCCESS)
                {
                    thread = null;
                    return;
                }

                uint notification_state = SCARD.STATE_UNAWARE;

                while (!bStopThread)    // loop 1 - build list, then iterate
                {
                    SCARD.ReaderState[] states = new SCARD.ReaderState[ReaderCount + 1];
                    states[0] = new SCARD.ReaderState(@"\\?PNP?\NOTIFICATION")
                    {
                        dwCurrentState = notification_state
                    };

                    int iState = 0;
                    if (readerNames != null)
                        foreach (string s in readerNames)
                        {
                            iState++;
                            states[iState] = new SCARD.ReaderState(s)
                            {
                                dwCurrentState = SCARD.STATE_UNAWARE
                            };
                        }

                    while (!bStopThread)    // loop 2 - iterate over list built above
                    {
                        result = SCARD.GetStatusChange(hContext, 250, states, (uint)states.Length);
                        if (result == SCARD.E_TIMEOUT)
                            continue;
                        if (result != SCARD.S_SUCCESS)
                            break;

                        bool bReaderListChanged = false;
                        for (int i = 0; i < states.Length; i++)
                            if ((states[i].dwEventState & SCARD.STATE_CHANGED) != 0)
                                if (i == 0)
                                {
                                    // reader added or removed
                                    notification_state = states[0].dwEventState;

                                    // we want to replace the member in one step, rather than modifying it...
                                    List<string> tmp = GetReaderList(hContext, SCARD.GROUP_ALL_READERS);
                                    if (tmp == null)
                                        readerNames.Clear();
                                    else
                                        readerNames = tmp;

                                    ListChanged?.Invoke();
                                    bReaderListChanged = true;
                                }
                                else
                                {
                                    // card added or removed
                                    states[i].dwCurrentState = states[i].dwEventState;

                                    if ((states[i].dwEventState & SCARD.STATE_PRESENT) != 0)
                                    {
                                        byte[] cardData = new byte[states[i].cbATR];
                                        for (int j = 0; j < cardData.Length; j++)
                                            cardData[j] = states[i].rgbATR[j];
                                        string thisCard = SCARD.ToHex(cardData, "");
                                        lastCardFound.TryGetValue(states[i].szReader, out string lastCard);
                                        if (thisCard != lastCard)
                                        {
                                            lastCardFound[states[i].szReader] = thisCard;
                                            CardPresented?.Invoke(states[i].szReader, cardData);
                                        }
                                    }
                                    else
                                        lastCardFound[states[i].szReader] = "";
                                }

                        if (bReaderListChanged)
                            break;  // break out of loop 2, and re-build our 'states' list

                    } // end loop 2
                } // end loop 1
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
            finally
            {
                if (hContext != IntPtr.Zero)
                    SCARD.ReleaseContext(hContext);
                thread = null;
            }
        }

        private List<string> GetReaderList(IntPtr hContext, string sGroup)
        {
            uint nStringLength = 0;
            uint result = SCARD.ListReaders(hContext, sGroup, null, ref nStringLength);
            if (result != SCARD.S_SUCCESS)
                return null;

            string sReaders = new string(' ', (int)nStringLength);
            result = SCARD.ListReaders(hContext, sGroup, sReaders, ref nStringLength);
            if (result != SCARD.S_SUCCESS)
                return null;
            List<string> list = new List<string>(sReaders.Split('\0'));
            for (int i = 0; i < list.Count;)
                if (list[i].Trim().Length > 0)
                    i++;
                else
                    list.RemoveAt(i);
            return list;
        }

        public IEnumerator<string> GetEnumerator()
        { return readerNames.GetEnumerator(); }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        { return readerNames.GetEnumerator(); }

    }

    public class SCARD
    {
        [DllImport("WinScard.dll", EntryPoint = "SCardEstablishContext")]
        public static extern uint EstablishContext(
            uint dwScope,
            IntPtr nNotUsed1,
            IntPtr nNotUsed2,
            ref IntPtr phContext);

        [DllImport("WinScard.dll", EntryPoint = "SCardReleaseContext")]
        public static extern uint ReleaseContext(
            IntPtr hContext);

        [DllImport("winscard.dll", EntryPoint = "SCardGetStatusChangeW", CharSet = CharSet.Unicode)]
        public static extern uint GetStatusChange(
            IntPtr hContext,
            uint dwTimeout,
            [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)]
                SCARD.ReaderState[] rgReaderState,
            uint cReaders);

        [DllImport("winscard.dll", EntryPoint = "SCardListReadersW", CharSet = CharSet.Unicode)]
        public static extern uint ListReaders(
            IntPtr hContext,
            string groups,
            string readers,
            ref uint size);

        #region Error codes
        public const uint S_SUCCESS = 0x00000000;
        public const uint F_INTERNAL_ERROR = 0x80100001;
        public const uint E_CANCELLED = 0x80100002;
        public const uint E_INVALID_HANDLE = 0x80100003;
        public const uint E_INVALID_PARAMETER = 0x80100004;
        public const uint E_INVALID_TARGET = 0x80100005;
        public const uint E_NO_MEMORY = 0x80100006;
        public const uint F_WAITED_TOO_LONG = 0x80100007;
        public const uint E_INSUFFICIENT_BUFFER = 0x80100008;
        public const uint E_UNKNOWN_READER = 0x80100009;
        public const uint E_TIMEOUT = 0x8010000A;
        public const uint E_SHARING_VIOLATION = 0x8010000B;
        public const uint E_NO_SMARTCARD = 0x8010000C;
        public const uint E_UNKNOWN_CARD = 0x8010000D;
        public const uint E_CANT_DISPOSE = 0x8010000E;
        public const uint E_PROTO_MISMATCH = 0x8010000F;
        public const uint E_NOT_READY = 0x80100010;
        public const uint E_INVALID_VALUE = 0x80100011;
        public const uint E_SYSTEM_CANCELLED = 0x80100012;
        public const uint F_COMM_ERROR = 0x80100013;
        public const uint F_UNKNOWN_ERROR = 0x80100014;
        public const uint E_INVALID_ATR = 0x80100015;
        public const uint E_NOT_TRANSACTED = 0x80100016;
        public const uint E_READER_UNAVAILABLE = 0x80100017;
        public const uint P_SHUTDOWN = 0x80100018;
        public const uint E_PCI_TOO_SMALL = 0x80100019;
        public const uint E_READER_UNSUPPORTED = 0x8010001A;
        public const uint E_DUPLICATE_READER = 0x8010001B;
        public const uint E_CARD_UNSUPPORTED = 0x8010001C;
        public const uint E_NO_SERVICE = 0x8010001D;
        public const uint E_SERVICE_STOPPED = 0x8010001E;
        public const uint E_UNEXPECTED = 0x8010001F;
        public const uint E_ICC_INSTALLATION = 0x80100020;
        public const uint E_ICC_CREATEORDER = 0x80100021;
        public const uint E_UNSUPPORTED_FEATURE = 0x80100022;
        public const uint E_DIR_NOT_FOUND = 0x80100023;
        public const uint E_FILE_NOT_FOUND = 0x80100024;
        public const uint E_NO_DIR = 0x80100025;
        public const uint E_NO_FILE = 0x80100026;
        public const uint E_NO_ACCESS = 0x80100027;
        public const uint E_WRITE_TOO_MANY = 0x80100028;
        public const uint E_BAD_SEEK = 0x80100029;
        public const uint E_INVALID_CHV = 0x8010002A;
        public const uint E_UNKNOWN_RES_MNG = 0x8010002B;
        public const uint E_NO_SUCH_CERTIFICATE = 0x8010002C;
        public const uint E_CERTIFICATE_UNAVAILABLE = 0x8010002D;
        public const uint E_NO_READERS_AVAILABLE = 0x8010002E;
        public const uint E_COMM_DATA_LOST = 0x8010002F;
        public const uint E_NO_KEY_CONTAINER = 0x80100030;
        public const uint W_UNSUPPORTED_CARD = 0x80100065;
        public const uint W_UNRESPONSIVE_CARD = 0x80100066;
        public const uint W_UNPOWERED_CARD = 0x80100067;
        public const uint W_RESET_CARD = 0x80100068;
        public const uint W_REMOVED_CARD = 0x80100069;
        public const uint W_SECURITY_VIOLATION = 0x8010006A;
        public const uint W_WRONG_CHV = 0x8010006B;
        public const uint W_CHV_BLOCKED = 0x8010006C;
        public const uint W_EOF = 0x8010006D;
        public const uint W_CANCELLED_BY_USER = 0x8010006E;
        public const uint W_CARD_NOT_AUTHENTICATED = 0x8010006F;
        #endregion

        public const uint SCOPE_USER = 0;
        public const uint SCOPE_TERMINAL = 1;
        public const uint SCOPE_SYSTEM = 2;

        public const string GROUP_ALL_READERS = "SCard$AllReaders\0\0";
        public const string GROUP_DEFAULT_READERS = "SCard$DefaultReaders\0\0";
        public const string GROUP_LOCAL_READERS = "SCard$LocalReaders\0\0";
        public const string GROUP_SYSTEM_READERS = "SCard$SystemReaders\0\0";

        public const uint STATE_UNAWARE = 0x00000000;
        public const uint STATE_IGNORE = 0x00000001;
        public const uint STATE_CHANGED = 0x00000002;
        public const uint STATE_UNKNOWN = 0x00000004;
        public const uint STATE_UNAVAILABLE = 0x00000008;
        public const uint STATE_EMPTY = 0x00000010;
        public const uint STATE_PRESENT = 0x00000020;
        public const uint STATE_ATRMATCH = 0x00000040;
        public const uint STATE_EXCLUSIVE = 0x00000080;
        public const uint STATE_INUSE = 0x00000100;
        public const uint STATE_MUTE = 0x00000200;
        public const uint STATE_UNPOWERED = 0x00000400;

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct ReaderState
        {
            public ReaderState(string sName)
            {
                szReader = sName;
                pvUserData = IntPtr.Zero;
                dwCurrentState = 0;
                dwEventState = 0;
                cbATR = 0;
                rgbATR = null;
            }

            internal string szReader;
            internal IntPtr pvUserData;
            internal uint dwCurrentState;
            internal uint dwEventState;
            internal uint cbATR;    // count of bytes in rgbATR
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x24, ArraySubType = UnmanagedType.U1)]
            internal byte[] rgbATR;
        }

        public static string ToHex(byte[] ab, string sDelim)
        {
            if (ab == null) return "<NULL>";
            return ToHex(ab, 0, ab.Length, sDelim);
        }

        public static string ToHex(byte[] ab, int offset, int len, string sDelim)
        {
            if (ab == null) return "<NULL>";
            StringBuilder sb = new StringBuilder();
            len = Math.Min(offset + len, ab.Length);
            for (int i = offset; i < len; i++)
                sb.Append(String.Format("{0:x02}", ab[i]).ToUpper() + sDelim);
            return sb.ToString();
        }
    }
}

Я надеюсь, что устройство IoT будет считывать код объекта и номер значка с бесконтактной карты при считывании.

Привет, Дон Книсли, смарт-карта не указана в Windows IoT Core список совместимого оборудования. Вы установили какой-либо драйвер для вашей смарт-карты, работающей на рабочем столе?

Rita Han 05.04.2019 10:48

Вы тестировали Официальный образец UWP? Есть некоторые возможности устройства, которые я не могу найти в ваше приложение.

Rita Han 05.04.2019 10:55

@RitaHan-MSFT Я обновил манифест своего приложения и запустил программу, без радости. Я также первоначально использовал официальный образец, который вы упомянули, без изменений. Он обнаружил устройство чтения на моем рабочем столе, но не устройство IoT. Я понимаю, что в списке совместимого оборудования нет считывателей карт, но есть поддержка CCID, а считыватель поддерживает драйвер CCID, входящий в состав Windows. Я чувствую себя очень близко, поскольку приложение IoT Core по умолчанию действительно видит читателя на примере UWP.

Don Knisley 05.04.2019 20:47
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
316
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

I feel very close as the IoT Core Default Application does see the reader by the UWP example does not.

Да, ты прав. Приложение по умолчанию также является приложением UWP.

Пожалуйста, проверьте возможности и ConnectedDevicePresenter.cs приложения по умолчанию в качестве справки для изменения вашего приложения.

теперь мы получаем где-то. Из ваших ссылок я смог увидеть ридер со следующими свойствами. Теперь мне просто нужна помощь в общении с ним. Допрос его вернулся со следующими свойствами.<br/> добавлено 5427 CK System.ItemNameDisplay: 5427 CK System.Devices.DeviceInstanceId: USB\VID_076B&PID_5427\010100534859383439013B8448175830 System.Devices.Icon: System.Devices.GlyphIcon: System.Devices.GlyphIcon: .InterfaceEnabled: True System.Devices.IsDefault: False System.Devices.PhysicalDeviceLocation: System.Devices.ContainerId: 1d01a7c8-e66f-5b20-a4b6-24779711d79e

Don Knisley 08.04.2019 21:29

@DonKnisley Итак, вы пробовали какие-либо операции на подключенном смарт-устройстве, например, такие как RequestPinChangeAsync, используемый в официальный образец?

Rita Han 09.04.2019 09:31

@rita-han-msft У меня есть, но официальный образец даже не признает читателя. Я подозреваю, что, возможно, понял, что происходит. Этот кард-ридер имеет несколько особенностей. Он имеет прямой интерфейс веб-администрирования и вариант с клавиатурой. Приложение по умолчанию видит устройства, но не знает, что это за устройство. Возможно, он указан, потому что Windows обнаруживает виртуальную клавиатуру или функции Ethernet, а не устройство чтения карт вообще. Как вы думаете?

Don Knisley 09.04.2019 21:06

@DonKnisley Это устройство не входит в официальный список совместимых устройств, и у меня нет этого устройства для тестирования. Я предлагаю попытаться заставить приложение UWP работать на рабочем столе с Windows 10, а затем с Windows IoT Core.

Rita Han 10.04.2019 07:54

@rita-han-msft UWP работает на рабочем столе, поэтому мне просто придется списать это на несовместимость и вернуться к режиму заклинивания клавиатуры. С помощью дыма и зеркал я могу сделать так, чтобы это выглядело как прямая связь. Спасибо за уделенное время.

Don Knisley 10.04.2019 16:25

Вы переводили ридер в режим CCID, а не в KBW (я полагаю, это означает Keyboard Wedge)? Моя не появлялась, пока я не появлялся. Вы можете сделать это с помощью инструмента omnikey workbench.

Jonas Stensved 22.09.2021 21:41

Другие вопросы по теме