I've been working on a small smart card scanner application using the pcsc-sharp library. The application works fine when running as a console application, the code is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Drawing.Printing;
using System.Net;
using System.Net.Sockets;
using System.Data.SqlClient;
using System.Threading;
using System.IO.Ports;
using System.Text.RegularExpressions;
using System.Speech.Synthesis;
using System.Diagnostics;
using PCSC;
using System.Media;
namespace MeterTestingApplication
{
class Program
{
static void Main(string[] args)
{
// Retrieve the names of all installed readers.
string[] readerNames;
using (var context = new SCardContext())
{
context.Establish(SCardScope.System);
readerNames = context.GetReaders();
context.Release();
}
if (readerNames == null || readerNames.Length == 0)
{
Console.WriteLine("There are currently no readers installed.");
return;
}
// Create a monitor object with its own PC/SC context.
// The context will be released after monitor.Dispose()
using (var monitor = new SCardMonitor(new SCardContext(), SCardScope.System))
{
// Point the callback function(s) to the anonymous & static defined methods below.
monitor.CardInserted += (sender, args0) => DisplayEvent("CardInserted", args0);
//monitor.CardRemoved += (sender, args0) => DisplayEvent("CardRemoved", args0);
//monitor.Initialized += (sender, args0) => DisplayEvent("Initialized", args0);
//monitor.StatusChanged += StatusChanged;
monitor.MonitorException += MonitorException;
monitor.Start(readerNames);
// Keep the window open
Console.ReadLine();
}
}
private static void DisplayEvent(string eventName, CardStatusEventArgs unknown)
{
Console.WriteLine(">> {0} Event for reader: {1}", eventName, unknown.ReaderName);
Console.WriteLine("ATR: {0}", BitConverter.ToString(unknown.Atr ?? new byte[0]));
Console.WriteLine("State: {0}\n", unknown.State);
//Works fine here
}
private static void StatusChanged(object sender, StatusChangeEventArgs args)
{
//Console.WriteLine(">> StatusChanged Event for reader: {0}", args.ReaderName);
//Console.WriteLine("ATR: {0}", BitConverter.ToString(args.Atr ?? new byte[0]));
//Console.WriteLine("Last state: {0}\nNew state: {1}\n", args.LastState, args.NewState);
}
private static void MonitorException(object sender, PCSCException ex)
{
Console.WriteLine("Monitor exited due an error:");
Console.WriteLine(SCardHelper.StringifyError(ex.SCardError));
}
}
}
However, when trying to run it as a Windows service - to allow scanning of cards to update a database - the event never fires and it appears to crash when the service is started. The event log shows a PCSC.InvalidContextException error. The code for the service is near enough identical, but here is the sample.
using System;
using System.Diagnostics;
using System.ServiceProcess;
using System.Timers;
using System.Configuration;
using System.Linq;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Drawing.Printing;
using System.Threading;
using System.Speech.Synthesis;
using System.IO.Ports;
using System.Text.RegularExpressions;
using PCSC;
using System.Media;
using log4net;
namespace CardScannerService
{
public partial class CardScanner : ServiceBase
{
private ILog logger;
private SCardContext context;
private string[] readerNames;
private SCardMonitor monitor;
private static System.Timers.Timer aTimer;
public CardScanner()
{
InitializeComponent();
}
static void Main()
{
ServiceBase.Run(new CardScanner());
}
//
// Start Service
//
protected override void OnStart(string[] args)
{
aTimer = new System.Timers.Timer();
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Interval = (1000 * 60 * 60 * 24); // Once a day; 1000ms * 60s * 60m * 24h
aTimer.Enabled = true;
//// Entry point
using (context = new SCardContext())
{
context.Establish(SCardScope.System);
readerNames = context.GetReaders();
context.Release();
}
if (readerNames == null || readerNames.Length == 0)
{
EventLog.WriteEntry("CardReaderService", "There are currently no readers installed.");
return;
}
// Create a monitor object with its own PC/SC context.
// The context will be released after monitor.Dispose()
using (monitor = new SCardMonitor(new SCardContext(), SCardScope.System))
{
// Point the callback function(s) to the anonymous & static defined methods below.
monitor.CardInserted += (sender, args0) => DisplayEvent("CardInserted", args0);
//monitor.CardRemoved += (sender, args0) => DisplayEvent("CardRemoved", args0);
//monitor.Initialized += (sender, args0) => DisplayEvent("Initialized", args0);
//monitor.StatusChanged += StatusChanged;
monitor.MonitorException += MonitorException;
monitor.Start(readerNames);
// Keep the window open
//Console.ReadLine();
}
logger.InfoFormat("CardScannerService Started at {0}", DateTime.Now.ToLongTimeString());
}
//
// Stop Service
//
protected override void OnStop()
{
logger.InfoFormat("CardScannerService Stopped at {0}", DateTime.Now.ToLongTimeString());
}
//
// Execute timed event every hour and half hour
//
void OnTimedEvent(object source, ElapsedEventArgs e)
{
// Card scanner service entry point for timed event - any cleanup code can go here
}
private static void DisplayEvent(string eventName, CardStatusEventArgs unknown)
{
EventLog.WriteEntry("CardReaderService", ">> " + eventName + "v Event for Reader: " + unknown.ReaderName);
EventLog.WriteEntry("CardReaderService", "ATR: " + BitConverter.ToString(unknown.Atr ?? new byte[0]));
EventLog.WriteEntry("CardReaderService", "State: " + unknown.State);
//Not firing
}
private static void StatusChanged(object sender, StatusChangeEventArgs args)
{
//Console.WriteLine(">> StatusChanged Event for reader: {0}", args.ReaderName);
//Console.WriteLine("ATR: {0}", BitConverter.ToString(args.Atr ?? new byte[0]));
//Console.WriteLine("Last state: {0}\nNew state: {1}\n", args.LastState, args.NewState);
}
private static void MonitorException(object sender, PCSCException ex)
{
EventLog.WriteEntry("CardReaderService", "Monitor exited due an error:");
EventLog.WriteEntry("CardReaderService", SCardHelper.StringifyError(ex.SCardError));
}
}
}
I have stripped out the actual database logic as that appears to work fine, I believe it is crashing when trying to either release the context variable or attaching the monitor to the smart cards.
It must also be stated that I have tried changing the service from starting using the Local System account to using Local Service, in case it was some sort of access permissions error.
If anyone can shed some light on where I'm going wrong I'd greatly appreciate the help. I'm relatively new to working with PC/SC and appear to have hit a brick wall with this project for now.
UPDATE
I have now solved the issue; it turns out Windows services don't like using statements. The variables are disposed at the end of the block and therefore the context was becoming invalid.
The using blocks were commented out and declarations for the variables were done in their place. New code for the OnStart method is a is as follows:
protected override void OnStart(string[] args)
{
logger = LogManager.GetLogger(this.GetType().Name);
aTimer = new System.Timers.Timer();
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Interval = (1000 * 60 * 60 * 24); // Once a day; 1000ms * 60s * 60m * 24h
aTimer.Enabled = true;
// Entry point
//using (context = new SCardContext())
//{
context = new SCardContext();
context.Establish(SCardScope.System);
readerNames = context.GetReaders();
context.Release();
//}
if (readerNames == null || readerNames.Length == 0)
{
EventLog.WriteEntry("CardReaderService", "There are currently no readers installed.");
return;
}
// Create a monitor object with its own PC/SC context.
// The context will be released after monitor.Dispose()
//using (monitor = new SCardMonitor(new SCardContext(), SCardScope.System))
//{
monitor = new SCardMonitor(new SCardContext(), SCardScope.System);
// Point the callback function(s) to the anonymous & static defined methods below.
monitor.CardInserted += (sender, args0) => DisplayEvent("CardInserted", args0);
//monitor.CardRemoved += (sender, args0) => DisplayEvent("CardRemoved", args0);
//monitor.Initialized += (sender, args0) => DisplayEvent("Initialized", args0);
//monitor.StatusChanged += StatusChanged;
monitor.MonitorException += MonitorException;
monitor.Start(readerNames);
// Keep the window open
//Console.ReadLine();
//}
logger.InfoFormat("CardScannerService Started at {0}", DateTime.Now.ToLongTimeString());
}
Hopefully this information helps someone else out.
I managed to solve the problem - the question has been updated to reflect the changes.
来源:https://stackoverflow.com/questions/26233593/pcsc-invalidcontextexception-when-running-as-a-windows-service