📄 pcscconnector.pas
字号:
{******************************************************************}
{ }
{ PC/SC Interface component }
{ Helps you access a cardreader through Microsofts SmartCard API }
{ }
{ The Original Code is PCSCConnector.pas }
{ }
{ The Initial Developer of the Original Code is }
{ Norbert Huettisch (nobbi(at)nobbi.com) }
{ }
{ Any suggestions and improvements to the code are appreciated }
{ }
{ This Code uses a modified SCardErr.pas (included) }
{ This Code uses a modified WinSCard.pas (included) }
{ This code uses the original WinSmCrd.pas (included) }
{ }
{ All originally made by Chris Dickerson (chrisd(at)tsc.com), }
{ available as 'Interface units for the Microsoft Smart Card API' }
{ at the Project JEDI Homepage http://www.delphi-jedi.org }
{ }
{ Version info: }
{ 021230 - initial version }
{ 030101 - routed errors from 'init' to the OnError event }
{ }
{ }
{******************************************************************}
{ }
{ The contents of this file are subject to the }
{ }
{ Mozilla Public License Version 1.1 (the "License") }
{ }
{ You may not use this file except in compliance with the License. }
{ You may obtain a copy of the License at }
{ http://www.mozilla.org/MPL/ }
{ }
{ Software distributed under the License is distributed on an }
{ "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or }
{ implied. See the License for the specific language governing }
{ rights and limitations under the License. }
{ }
{******************************************************************}
unit PCSCConnector;
interface
uses
Windows, Messages, Forms, Classes, SysUtils,
SCardErr, WinSCard, WinSmCrd;
type
TErrSource = (esInit, esConnect, esGetStatus, esTransmit);
TNeededPIN = (npPIN1, npPIN2, npPUK1, npPUK2);
TDelimiters = set of Char;
TPCSCErrorEvent = procedure(Sender: TObject; ErrSource: TErrSource; ErrCode: cardinal) of object;
TPCSCPinEvent = procedure(Sender: TObject; NeedPIN: TNeededPIN) of object;
const
MAXAPDULENGTH = 260; // CLA + INS + P1..3 + 255Bytes
NOREADERSELECTED = -1;
SCARD_PCI_T0 : SCARD_IO_REQUEST = (dwProtocol:1; dbPciLength:8);
SCARD_PCI_T1 : SCARD_IO_REQUEST = (dwProtocol:2; dbPciLength:8);
SCARD_PROTOCOL_T0 = $00000001;
SCARD_PROTOCOL_T1 = $00000002;
SCARD_PROTOCOL_RAW = $00010000;
SCARD_PROTOCOL_UNK = $00000000;
WM_CARDSTATE = WM_USER + 42;
GSMStatusOK = $9000;
GSMStatusMemoryError = $9240;
GSMStatusNoEFSelected = $9400;
GSMStatusOutOfRange = $9402;
GSMStatusNotFound = $9404;
GSMStatusFCDoNotMatch = $9408;
GSMStatusCHVNeeded = $9802;
GSMStatusAuthFailed = $9804;
GSMStatusAuthFailedBl = $9840;
GSMStatusTechProblem = $6F00;
GSMStatusResponseData = $9F;
GSMFileTypeRFU = 0;
GSMFileTypeMF = 1;
GSMFileTypeDF = 2;
GSMFileTypeEF = 4;
GSMEfTransp = 0;
GSMEfLinFixed = 1;
GSMEfCyclic = 3;
type
TPCSCConnector = class(TComponent)
protected
FContext : cardinal;
FCardHandle : integer;
FConnected : boolean;
FNumReaders : integer;
FUseReaderNum : integer;
FReaderList : TStringlist;
FAttrProtocol : integer;
FAttrICCType : string;
FAttrCardATR : string;
FAttrVendorName : string;
FAttrVendorSerial : string;
FGSMCurrentFile : string;
FGSMFileInfo : string;
FGSMDirInfo : string;
FGSMVoltage30 : boolean;
FGSMVoltage18 : boolean;
FOnReaderWaiting : TNotifyEvent;
FOnReaderListChange : TNotifyEvent;
FOnCardInserted : TNotifyEvent;
FOnCardActive : TNotifyEvent;
FOnCardRemoved : TNotifyEvent;
FOnCardInvalid : TNotifyEvent;
FOnError : TPCSCErrorEvent;
FOnCHVNeeded : TPCSCPinEvent;
procedure SetReaderNum(Value: integer);
procedure MessageWndProc(var Msg: TMessage);
function ConnectSelectedReader: boolean;
procedure ProcessReaderState(const OldState,NewState: cardinal);
procedure GetReaderAttributes;
procedure GetCardAttributes;
procedure ClearReaderAttributes;
procedure ClearCardAttributes;
function IsReaderOpen: boolean;
function GetReaderState: cardinal;
procedure CloseAndDisconnect;
procedure CardInsertedAction;
procedure CardActiveAction;
procedure CardRemovedAction;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function Init: boolean;
function Open: boolean;
procedure Close;
function Connect: boolean;
procedure Disconnect;
function GetResponseFromCard(const apdu: string): string; overload;
function GetResponseFromCard(const command: string;
var data: string;
var sw1, sw2: byte): boolean; overload;
function GSMStatus: integer;
function GSMSelect(const FileID: string): integer;
function GSMReadBinary(const Offset, Length: integer; var Data: string): integer;
published
property UseReaderNum: integer read FUseReaderNum write SetReaderNum default -1;
property OnCardInserted: TNotifyEvent read FOnCardInserted write FOnCardInserted;
property OnCardActive: TNotifyEvent read FOnCardActive write FOnCardActive;
property OnCardRemoved: TNotifyEvent read FOnCardRemoved write FOnCardRemoved;
property OnCardInvalid: TNotifyEvent read FOnCardInvalid write FOnCardInvalid;
property OnReaderWaiting: TNotifyEvent read FOnReaderWaiting write FOnReaderWaiting;
property OnReaderListChange: TNotifyEvent read FOnReaderListChange write FOnReaderListChange;
property OnError: TPCSCErrorEvent read FOnError write FOnError;
property OnCHVNeeded: TPCSCPinEvent read FOnCHVNeeded write FOnCHVNeeded;
property ReaderList: TStringList read FReaderList;
property NumReaders: integer read FNumReaders;
property Connected: boolean read FConnected;
property Opened: boolean read IsReaderOpen;
property ReaderState: cardinal read GetReaderState;
property AttrProtocol: integer read FAttrProtocol;
property AttrICCType: string read FAttrICCType;
property AttrCardATR: string read FAttrCardATR;
property AttrVendorName: string read FAttrVendorName;
property AttrVendorSerial: string read FAttrVendorSerial;
property GSMCurrentFile: string read FGSMCurrentFile;
property GSMFileInfo: string read FGSMFileInfo;
property GSMDirInfo: string read FGSMDirInfo;
property GSMVoltage30: boolean read FGSMVoltage30;
property GSMVoltage18: boolean read FGSMVoltage18;
end;
procedure Register;
implementation
var
ActReaderState : cardinal;
LastReaderState : cardinal;
SelectedReader : PChar;
ReaderOpen : boolean;
NotifyHandle : HWND;
const
// GSM Commands
GCGetStatus = #$A0#$F2#$00#$00#$16;
GCGetResponse = #$A0#$C0#$00#$00;
GCSelectFile = #$A0#$A4#$00#$00#$02;
GCReadBinary = #$A0#$B0;
GSMMasterFile = #$3f#$00;
DFgsm900 = #$7f#$20;
DFgsm1800 = #$7f#$21;
procedure Register;
begin
RegisterComponents('More...', [TPCSCConnector]);
end;
function SortOutSubstrings(const From:string; var t:array of string; const Delim:TDelimiters = [' ',';']; const ConcatDelim:boolean = true):integer;
var a,b,s,i : integer;
sep : boolean;
begin
a := 1;
b := Low(t);
s := 1;
i := 0;
sep := ConcatDelim;
t[b] := '';
while a <= Length(From) do
begin
if not (From[a] in Delim) then
begin
Inc(i);
sep := false;
end else
begin
if not sep then
begin
t[b] := Copy(From, s, i);
Inc(b);
if b > High(t) then Break;
t[b] := '';
end;
if ConcatDelim then sep := true;
s := a + 1;
i := 0;
end;
Inc(a);
end;
if (b <= High(t)) and (i > 0) then
begin
t[b] := Copy(From, s, i);
Inc(b);
end;
for a := b + 1 to High(t) do t[a] := '';
Result := b;
end;
function OrdD(const From: string; const Index: integer): integer;
begin
if Index <= Length(From) then Result := Ord(From[Index])
else Result := 0;
end;
function CardWatcherThread(PContext: pointer): integer;
var
RetVar : cardinal;
RContext : cardinal;
RStates : array[0..1] of SCARD_READERSTATEA;
begin
try
RContext := cardinal(PContext^);
FillChar(RStates,SizeOf(RStates),#0);
RStates[0].szReader := SelectedReader;
RStates[0].pvUserData := nil;
RStates[0].dwEventState := ActReaderState;
while ReaderOpen do
begin
RStates[0].dwCurrentState := RStates[0].dwEventState;
RetVar := SCardGetStatusChangeA(RContext, -1, RStates, 1);
ActReaderState := RStates[0].dwEventState;
PostMessage(NotifyHandle, WM_CARDSTATE, RetVar, 0);
end;
finally
Result := 0;
end;
end;
procedure TPCSCConnector.MessageWndProc(var Msg: TMessage);
begin
if (Msg.Msg = WM_CARDSTATE) then
begin
if Msg.WParam <> SCARD_S_SUCCESS then
if Assigned(FOnError) then FOnError(Self, esGetStatus, Msg.WParam);
if ActReaderState <> LastReaderState then
begin
ProcessReaderState(LastReaderState, ActReaderState);
end;
end
else Msg.Result := DefWindowProc(NotifyHandle, Msg.Msg, Msg.WParam, Msg.LParam);
end;
constructor TPCSCConnector.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FReaderList := TStringlist.Create;
FContext := 0;
FCardHandle := 0;
FNumReaders := 0;
FUseReaderNum := -1;
FConnected := false;
ActReaderState := SCARD_STATE_UNAWARE;
LastReaderState := SCARD_STATE_UNAWARE;
ReaderOpen := false;
ClearReaderAttributes;
ClearCardAttributes;
if not (csDesigning in ComponentState) then NotifyHandle := AllocateHWnd(MessageWndProc);
end;
destructor TPCSCConnector.Destroy;
begin
CloseAndDisconnect;
SCardReleaseContext(FContext);
FReaderList.Free;
if not (csDesigning in ComponentState) then DeallocateHWnd(NotifyHandle);
inherited Destroy;
end;
function TPCSCConnector.Init: boolean;
var
RetVar : cardinal;
ReaderList : string;
ReaderListSize : integer;
v : array[0..MAXIMUM_SMARTCARD_READERS] of string;
i : integer;
begin
Result := false;
FNumReaders := 0;
CloseAndDisconnect;
if SCardIsValidContext(FContext) = SCARD_S_SUCCESS then SCardReleaseContext(FContext);
RetVar := SCardEstablishContext(SCARD_SCOPE_USER, nil, nil, @FContext);
if RetVar = SCARD_S_SUCCESS then
begin
ReaderListSize := 0;
RetVar := SCardListReadersA(FContext, nil, nil, ReaderListSize);
if RetVar = SCARD_S_SUCCESS then
begin
SetLength(ReaderList, ReaderListSize);
SCardListReadersA(FContext, nil, Pointer(ReaderList), ReaderListSize);
FReaderList.Clear;
SortOutSubstrings(ReaderList,v,[#0]);
for i := 0 to MAXIMUM_SMARTCARD_READERS do
if v[i] <> '' then FReaderList.Add(v[i]);
FNumReaders := FReaderList.Count;
if FNumReaders > 0 then
begin
if Assigned(FOnReaderListChange) then FOnReaderListChange(Self);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -