1. unit PortTalk;
  2.  
  3. // ****************************************************************************
  4. // TPortTalk - jvn@vns - 2006 June 8
  5. // ****************************************************************************
  6. // This unit is based on and uses work of Craig Peacock - 13th January 2002
  7. // 'Beyond Logic Port Talk I/O Port Driver' - http://www.beyondlogic.org
  8. // All credits to him for the PortTalk driver and associated sources.
  9. // Porttalk.sys is a kernel driver (ring 0) that allows programs access or
  10. // exclusive access to IO Ports on a Windows NT/2000/XP system.
  11. // The _PortTalk22Src.zip contains the original sources of both the driver
  12. // itself (Microsoft DDK required for compilation) docs and examples.
  13. // A monolitic test example for use with the free Borland BCC55 compiler
  14. // is added by me under the _PortTalkVnsEx subdir in the ZIP.
  15. // ****************************************************************************
  16. // Additional reference material on device drivers: www.catch22.net (tutorials)
  17. // www.osronline.com - www.sysinternals.com (utilities) - www.codeproject.com -
  18. // delphi.about.com - www.techtricks.com - www.koders.com - and of course MSDN.
  19. // ****************************************************************************
  20. // This TPortTalk class for Delphi is my own design and implements an IOCTL
  21. // call wrapper for the porttalk driver. The class is designed to automatically
  22. // create and install the driver at start of use and to clean up everything
  23. // afterwards. This requires Administration rights of the application user.
  24. // An Admin facility is present to manually load/unload the driver under Admin
  25. // log-in in order to make it available to PortTalk when running later for a
  26. // normal User without admin rights.
  27. // The driver itself is linked into the unit as a resource so that no extra
  28. // files are required.
  29. // ****************************************************************************
  30. // Changes - jvn@vns - 2006 may 27
  31. // - Added simple OS detection and port read/write for Win9x.
  32. // - Added IsWinNTAdmin detection plus improved Admin functionality.
  33. // Changes - jvn@vns - 2006 may 29
  34. // - Reporter property changed into TStrings
  35. // Changes - jvn@vns - 2006 may 30
  36. // - Further improvement of Admin options.
  37. // Changes - jvn@vns - 2006 june 8
  38. // - Added PortTalk Version; Improved mechanism for normal (non-amin) User
  39. // Changes - jvn@vns - 2006 june 21
  40. // - Added VSS keywords to complement the internal Version
  41. // ****************************************************************************
  42.  
  43. interface
  44.  
  45. uses
  46. Classes;
  47.  
  48. {*****************************************************************************}
  49. {* VISUAL SOURCESAFE EXPANDED KEYWORDS *}
  50. {*****************************************************************************}
  51.  
  52. {These entries are automatically maintained at Check-in by Visual SourceSafe
  53.   by the keyword expansion mechanism. This information is made available to
  54.   code for runtime version information}
  55.  
  56. const
  57. cptVSSArchive = '$Archive: /DRPP/VnsVcl/VnsVclSrc/PortTalk.pas $';
  58. cptVSSAuthor = '$Author: Jack Van Nuenen $';
  59. cptVSSDate = '$Date: 6/21/06 2:54p $';
  60. cptVSSRevision = '$Revision: 3 $';
  61.  
  62. {No further keyword expansion from here onwards}
  63. {-$NoKeyWords:$-}
  64.  
  65. {*****************************************************************************}
  66. {* TYPE AND CLASS DECLARATIONS *}
  67. {*****************************************************************************}
  68.  
  69. type
  70. TPortTalk = class (TObject)
  71. private
  72. fOpen: Boolean;
  73. fSrvStarted: Boolean;
  74. fSrvInstalled: Boolean;
  75. fHandle: THandle;
  76. fReporter: TStrings;
  77. fIsWinNT: Boolean;
  78. fIsWinNTAdmin: Boolean;
  79. fVersion: string;
  80. procedure Report(S: string);
  81. function ReportNotOpen(S: string): Boolean;
  82. function MyOpenSCManager: Cardinal;
  83. function MyDriverFileName: string;
  84. function StartPTService(MayInstall: Boolean): Boolean;
  85. function StopPTService(AndDelete: Boolean): Boolean;
  86. public
  87. constructor Create;
  88. destructor Destroy; override;
  89. property Reporter: TStrings read fReporter write fReporter;
  90. property Active: Boolean read fOpen;
  91. property IsWinNT: Boolean read fIsWinNT;
  92. property IsWinNTAdmin: Boolean read fIsWinNTAdmin;
  93. property Version: string read fVersion;
  94. procedure Admin;
  95. procedure Open;
  96. procedure Close;
  97. procedure OutPortB(PortAddress: Word; Data: Byte);
  98. function InPortB(PortAddress: Word; ClosedResult: Byte): Byte;
  99. end;
  100.  
  101. implementation
  102.  
  103. uses
  104. Windows,
  105. WinSvc,
  106. SysUtils,
  107. Forms, Controls, Dialogs;
  108.  
  109. // The next resource contains in fact the binary image of the 'porttalk.sys'
  110. // file. It has been created using the original sys file (here: with an
  111. // underscore prepended to the name in order to distinguish this stuff from
  112. // the delphi sources). A very simple resource script '_porttalksys.RC' has
  113. // been made (a normal text file) with one single line in it:
  114. // PortTalkSysData RCDATA "_porttalk.sys"
  115. // This defines 'PortTalkSysData' as a raw data resource (RCDATA) having the
  116. // content of our _porttalk.sys.
  117. // Next, a simple batch file '_porttalksys_rc2res.bat' has been made, again
  118. // with one single line in it:
  119. // BRCC32 -foPortTalk.RES _porttalksys.rc
  120. // calling the Borland Resource Compiler (BRCC32.exe) to compile the script
  121. // to the corresponding resource (.RES) file. The -fo switch sets the output
  122. // file name to PortTalk.RES (distributes better with PortTalk.Pas :-)
  123.  
  124. {$RESOURCE 'PortTalk.RES'}
  125.  
  126. // The next constants - after the version string for this unit - represent the
  127. // names of the porttalk system driver in various contexts (as Resource,
  128. // as Device, as Service, as Driver file name under the windows system32 dir).
  129.  
  130. const
  131. cPortTalkVerStr = 'PortTalk V1.2';
  132. cPortTalkDevStr = '\\.\PortTalk';
  133. cPortTalkSrvStr = 'PortTalkSrv';
  134. cPortTalkDrvStr = '\drivers\porttalk.sys';
  135. cPortTalkResStr = 'PortTalkSysData';
  136.  
  137. // ****************************************************************************
  138. // DetectIsNT and DetectIsNTAdmin
  139. // ****************************************************************************
  140.  
  141. // Establish whether or not we're running under NT/XP/Vista
  142. // if not, we're under Win95/98/ME or even Win3.1/Win32subsystem
  143. // In this code we refer to the first group as 'WinNT' and to
  144. // everything else as 'Win9x'.
  145.  
  146. function DetectIsNT: Boolean;
  147. begin
  148. Result:=(Win32Platform = VER_PLATFORM_WIN32_NT);
  149. end;
  150.  
  151. // Returns a boolean indicating whether or not user has admin privileges.
  152. // Based on example code at http://www.techtricks.com/delphi/isadmin.php
  153. // which itself is based on Borland's Community Article #26752 at
  154. // http://community.borland.com/article/0,1410,26752,00.html
  155.  
  156. function DetectIsNTAdmin: Boolean;
  157. const
  158. cTokenInfoLen = 1024;
  159. SECURITY_NT_AUTHORITY: SID_IDENTIFIER_AUTHORITY = (Value: (0,0,0,0,0,5));
  160. SECURITY_BUILTIN_DOMAIN_RID : DWORD = $00000020;
  161. DOMAIN_ALIAS_RID_ADMINS : DWORD = $00000220;
  162. //DOMAIN_ALIAS_RID_USERS : DWORD = $00000221;
  163. //DOMAIN_ALIAS_RID_GUESTS : DWORD = $00000222;
  164. //DOMAIN_ALIAS_RID_POWER_ : DWORD = $00000223;
  165. var
  166. TokenHandle: THandle;
  167. pTokGroups: pTokenGroups;
  168. RetLen: Cardinal;
  169. psidAdmins: PSID;
  170. I: Integer;
  171. Res: Boolean;
  172. begin
  173. Result:=False;
  174. // Can't do following stuff without having NT or up.
  175. if not DetectIsNT then Exit;
  176.  
  177. Res:=OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, TokenHandle);
  178. if not Res and (GetLastError=ERROR_NO_TOKEN)
  179. then Res:=OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, TokenHandle);
  180. if not Res then Exit;
  181.  
  182. GetMem(pTokGroups, cTokenInfoLen);
  183. try
  184. Res:=GetTokenInformation(TokenHandle, TokenGroups,
  185. pTokGroups, cTokenInfoLen, RetLen);
  186. CloseHandle(TokenHandle);
  187. if Res then
  188. begin
  189. AllocateAndInitializeSid(SECURITY_NT_AUTHORITY, 2,
  190. SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
  191. 0, 0, 0, 0, 0, 0, psidAdmins);
  192. for I:=0 to pTokGroups.GroupCount-1 do
  193. if EqualSid(psidAdmins, pTokGroups.Groups[I].Sid ) then
  194. begin
  195. Result:=True;
  196. Break;
  197. end;
  198. FreeSid(psidAdmins);
  199. end;
  200. finally
  201. FreeMem(pTokGroups);
  202. end;
  203. end;
  204.  
  205. // ****************************************************************************
  206. // Create/Destroy
  207. // ****************************************************************************
  208.  
  209. constructor TPortTalk.Create;
  210. begin
  211. inherited Create;
  212. // Adopt Version
  213. fVersion:=cPortTalkVerStr;
  214. // Detections
  215. fIsWinNT:=DetectIsNT;
  216. fIsWinNTAdmin:=DetectIsNTAdmin;
  217. // Do NOT Open automatically as it may have side-effects and
  218. // also to allow a reporter handler to be attached first
  219. end;
  220.  
  221. destructor TPortTalk.Destroy;
  222. begin
  223. // Kill reporting and then Close
  224. fReporter:=nil;
  225. Close;
  226. inherited Destroy;
  227. end;
  228.  
  229. // ****************************************************************************
  230. // Reporting
  231. // ****************************************************************************
  232.  
  233. // Report a string
  234.  
  235. procedure TPortTalk.Report(S: string);
  236. begin
  237. // Reporter handler must be assigned.
  238. if not Assigned(fReporter) then Exit;
  239. // Output string prefixed with 'PortTalk ';
  240. fReporter.Add('PortTalk '+S);
  241. end;
  242.  
  243. // Report a string when PortTalk not Open and return not Open.
  244.  
  245. function TPortTalk.ReportNotOpen(S: string): Boolean;
  246. begin
  247. // Return True when PortTalk is NOT Open
  248. Result:=not fOpen;
  249. // Report if not Open with given string
  250. if fOpen then Exit;
  251. if S<;>;'' then S:=' for '+S;
  252. S:=S+'.';
  253. Report('not Open.'+S);
  254. end;
  255.  
  256. // ****************************************************************************
  257. // Open/Close
  258. // ****************************************************************************
  259.  
  260. // Open PortTalk. This will use the PortTalk Service if it is already
  261. // present and started. If not, and the program is run by a user with
  262. // Administrative Rights, it will automatically try to start the Service.
  263. // When the Service is not present, the driver and the Service will first
  264. // be installed.
  265. // Under Win9x the Open/Close mechanism is strictly a logical one and
  266. // without any actual meaning as we won't need to install the driver.
  267.  
  268. procedure TPortTalk.Open;
  269. begin
  270. // Exit when PortTalk is already open.
  271. if fOpen then
  272. begin
  273. Report('already Open.');
  274. Exit;
  275. end;
  276. // Set logal fOpen flag as only operation when under Win9x
  277. if not fIsWinNT then
  278. begin
  279. fOpen:=True;
  280. Report('Opened for Win9X.');
  281. Exit;
  282. end;
  283. Report('Opening for WinNT/XP..');
  284. if fIsWinNTAdmin
  285. then Report('User has Administrator Rights.')
  286. else Report('User is NOT an Administrator.');
  287. // Clear flags that keep track of actions resulting from Opening PortTalk.
  288. fSrvStarted:=False;
  289. fSrvInstalled:=False;
  290. // Open PortTalk by accessing the device as an existing 'file'
  291. fHandle:=CreateFile(PChar(cPortTalkDevStr), GENERIC_READ,
  292. 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  293. // If we cannot open it, try starting the associated Service (with
  294. // enabled option of installing it first) and then retry to Open.
  295. // This will fail when not having Administrator Rights, but we
  296. // still proceed to report what happens..
  297. if (fHandle=INVALID_HANDLE_VALUE) then
  298. begin
  299. if StartPTService(True)
  300. then fHandle:=CreateFile(PChar(cPortTalkDevStr), GENERIC_READ,
  301. 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  302. end;
  303. // Evaluate result
  304. fOpen:=(fHandle<;>;INVALID_HANDLE_VALUE);
  305. if fOpen
  306. then Report('Opened for WinNT/XP.')
  307. else Report('cannot be used.');
  308. end;
  309.  
  310. // Close PortTalk. This will try to leave the system as it was encountered
  311. // when calling Open. Under Administartive Rights, it will stop the Service
  312. // when it was found existing but stopped when calling Open. It will delete
  313. // the Service and remove the driver when those were not present when
  314. // calling Open.
  315. // Under Win9x, the Close action is logical only
  316.  
  317. procedure TPortTalk.Close;
  318. begin
  319. if ReportNotOpen('') then Exit;
  320. Report('Closing..');
  321. fOpen:=False;
  322. // Nothing to do further when under Win9x
  323. if not IsWinNT then Exit;
  324. // Close the PortTalk file handle
  325. CloseHandle(fHandle);
  326. fHandle:=INVALID_HANDLE_VALUE;
  327. // Stop the Service when it was started from here
  328. // and Delete it when it was installed from here
  329. if fSrvStarted then StopPTService(fSrvInstalled);
  330. Report('Closed.');
  331. end;
  332.  
  333. // ****************************************************************************
  334. // StartPTService/StopPTService
  335. // ****************************************************************************
  336.  
  337. // Some common stuff first..
  338.  
  339. function TPortTalk.MyOpenSCManager: Cardinal;
  340. var
  341. DesiredAccess: cardinal;
  342. begin
  343. // Open Handle to Service Control Manager. MachineName=nil for local,
  344. // DatabaseName=nil for default, DesiredAccess as appropriate
  345. // Only an Admin can open the SCM with SC_MANAGER_ALL_ACCESS and have
  346. // an SCM handle that can be used with CreateService. Normal User can use
  347. // DesiredAccess:=SC_MANAGER_CONNECT or SC_MANAGER_ENUMERATE_SERVICE;
  348. // bute the SCM handle will not be usable for Service control
  349. DesiredAccess:=SC_MANAGER_ALL_ACCESS;
  350. Result:=OpenSCManager(nil, nil, DesiredAccess);
  351. // Test result
  352. if (Result>;0) then Exit;
  353. // Report when not successfull
  354. if GetLastError=ERROR_ACCESS_DENIED
  355. then Report('insufficent rights to open Service Control Manager')
  356. else Report('cannot open the Service Control Manager');
  357. end;
  358.  
  359. // Return the full file specifier of the Driver
  360. // It is supposed to reside in de drivers subdir under the
  361. // system dir (typically: c:\windows\system32\)
  362.  
  363. function TPortTalk.MyDriverFileName: string;
  364. var
  365. Buf: array[0..100] of char;
  366. begin
  367. GetSystemDirectory(Buf, SizeOf(Buf));
  368. Result:=Buf+cPortTalkDrvStr;
  369. end;
  370.  
  371. // Start the PortTalk Service. Optionally install it when not yet present.
  372. // This sets the fSrvStarted en fSrvInstalled flags when appropriate.
  373. // Return True when the Service is started from here (= value of fSrvStarted)
  374.  
  375. function TPortTalk.StartPTService(MayInstall: Boolean): Boolean;
  376. var
  377. hSCManager: Cardinal;
  378. hService: Cardinal;
  379. SArgVectors: PChar;
  380. DoInstall: Boolean;
  381. DrvFName: TFileName;
  382. rStream: TResourceStream;
  383. DriverPlaced: Boolean;
  384. begin
  385. Result:=False;
  386. // Open the Service Control Manager.
  387. hSCManager:=myOpenSCManager;
  388. // Test result
  389. if (hSCManager=0) then
  390. begin
  391. Report('Service cannot be started.');
  392. Exit;
  393. end;
  394.  
  395. // Open a Handle to the PortTalk Service Database, taking a handle to the
  396. // SCM, a pointer to the name of the service to start and the desired access.
  397. hService:=OpenService(hSCManager, PChar(cPortTalkSrvStr), SERVICE_ALL_ACCESS);
  398. // Test result of the operation
  399. DoInstall:=False;
  400. if (hService=0) then
  401. begin
  402. case GetLastError of
  403. ERROR_ACCESS_DENIED:
  404. Report('Service cannot Open; Right Denied.');
  405. ERROR_INVALID_NAME:
  406. Report('Service cannot Open; Name Invalid.');
  407. ERROR_SERVICE_DOES_NOT_EXIST:
  408. begin
  409. if MayInstall
  410. then Report('Service cannot Open. Starting Installation.')
  411. else Report('Service cannot Open.');
  412. DoInstall:=MayInstall;
  413. end;
  414. end;
  415. end;
  416.  
  417. // If we now have DoInstall then Install the driver and the Service
  418. if DoInstall then
  419. begin
  420. DrvFName:=MyDriverFileName;
  421. // Extract the driver from the exe's resources
  422. // and 'materialize' in in window's system32\drivers directory
  423. rStream:=TResourceStream.Create(hInstance, cPortTalkResStr, RT_RCDATA);
  424. try
  425. DriverPlaced:=True;
  426. try
  427. rStream.SaveToFile(DrvFName);
  428. except
  429. DriverPlaced:=False;
  430. end;
  431. finally
  432. rStream.Free;
  433. end;
  434. // Report on Driver placement
  435. if DriverPlaced
  436. then Report('Driver installed as '+DrvFName)
  437. else Report('Driver could not be installed.');
  438.  
  439. // Create the PortTalk Service. This will create the appropriate keys in
  440. // the registry at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
  441. // Note: doesn't care here whether or not driver exists or path ok
  442. // Warning: Using anything other than SERVICE_DEMAND_START for the start
  443. // type (except SERVICE_DISABLED) causes the driver to be loaded at boot
  444. // time which may be fatal if the driver contains bugs..
  445. hService:=CreateService (
  446. hSCManager, // SCManager database
  447. PChar(cPortTalkSrvStr), // name of service
  448. PChar(cPortTalkSrvStr), // name to display
  449. SERVICE_ALL_ACCESS, // desired access
  450. SERVICE_KERNEL_DRIVER, // service type
  451. SERVICE_DEMAND_START, // start type
  452. SERVICE_ERROR_NORMAL, // error control type
  453. PChar(DrvFName), // path to service's binary
  454. nil, // no load ordering group
  455. nil, // no tag identifier
  456. nil, // no dependencies
  457. nil, // LocalSystem account
  458. nil); // no password
  459.  
  460. // Report on Service installation
  461. fSrvInstalled:=(hService<;>;0);
  462. if fSrvInstalled
  463. then Report('Service successfully installed.')
  464. else if GetLastError=ERROR_SERVICE_EXISTS
  465. then Report('Service was already installed.')
  466. else Report('Service could not be created.');
  467. end;
  468.  
  469. // If we now have a valid Service Handle, then start the PortTalk
  470. // Service with no arguments (zero count, nil pointer)
  471. if (hService<;>;0) then
  472. begin
  473. SArgVectors:=nil;
  474. fSrvStarted:=StartService(hService, 0, SArgVectors);
  475. // Reporting
  476. if fSrvStarted
  477. then Report('Service has been successfully started.')
  478. else if GetLastError=ERROR_SERVICE_ALREADY_RUNNING
  479. then Report('Service is already running.')
  480. else Report('driver cannot start by unknown error.');
  481. // Return Result
  482. Result:=fSrvStarted;
  483. end;
  484.  
  485. // Close the Service Control Manager
  486. CloseServiceHandle(hService);
  487. end;
  488.  
  489. // Stop and optionally delete the PortTalk Service
  490. // Return True when the Service is actually stopped from here
  491. // This does NOT clear any of the fSrvStarted or fSrvInstalled flags.
  492.  
  493. function TPortTalk.StopPTService(AndDelete: Boolean): Boolean;
  494. var
  495. hSCManager: Cardinal;
  496. hService: Cardinal;
  497. ServiceStatus: _SERVICE_STATUS;
  498. DrvFName: TFileName;
  499. begin
  500. Result:=False;
  501. // Open the Service Control Manager.
  502. hSCManager:=myOpenSCManager;
  503. // Test result
  504. if (hSCManager=0) then
  505. begin
  506. Report('Service cannot be stopped.');
  507. Exit;
  508. end;
  509. // Open access to the PortTalk Service (if present)
  510. hService:=OpenService(hSCManager, PChar(cPortTalkSrvStr), SERVICE_ALL_ACCESS);
  511. if (hService=0) then
  512. begin
  513. case GetLastError of
  514. ERROR_ACCESS_DENIED:
  515. Report('Service cannot Open; Right Denied.');
  516. ERROR_INVALID_NAME:
  517. Report('Service cannot Open; Name Invalid.');
  518. ERROR_SERVICE_DOES_NOT_EXIST:
  519. Report('Service is not installed.');
  520. end;
  521. Exit;
  522. end;
  523.  
  524. // Arriving here, the PortTalk Service _is_ installed; Stop the Service
  525. Result:=ControlService(hService, SERVICE_CONTROL_STOP, ServiceStatus);
  526. if Result
  527. then Report('Service has been successfully stopped.')
  528. else if GetLastError=ERROR_SERVICE_NOT_ACTIVE
  529. then Report('Service was already stopped.')
  530. else Report('Service could not be stopped.');
  531.  
  532. // When having AndDelete then Delete the Service (and the registry keys)
  533. // as well as the driver file itself
  534. if AndDelete then
  535. begin
  536. // Delete the Service
  537. if DeleteService(hService)
  538. then Report('Service has been successfully deleted.')
  539. else Report('Service deletion gave an error.');
  540. // Delete the Driver file
  541. DrvFName:=MyDriverFileName;
  542. if DeleteFile(DrvFName)
  543. then Report('Driver removed - '+DrvFName)
  544. else Report('Driver could not be removed.')
  545. end;
  546.  
  547. // Close Handle to Porttalk Service Database
  548. CloseServiceHandle(hService);
  549. // Close Service Control Manager
  550. CloseServiceHandle(hSCManager);
  551. end;
  552.  
  553. // ****************************************************************************
  554. // OutPort/InPort
  555. // ****************************************************************************
  556.  
  557. // Some ported excerpts from winioctl.h
  558.  
  559. const
  560. IOCTL_USER_CODES = $0800;
  561.  
  562. METHOD_BUFFERED = 0;
  563. METHOD_IN_DIRECT = 1;
  564. METHOD_OUT_DIRECT = 2;
  565. METHOD_NEITHER = 3;
  566.  
  567. FILE_ANY_ACCESS = 0;
  568. FILE_READ_ACCESS = 1;
  569. FILE_WRITE_ACCESS = 2;
  570.  
  571. function CTL_CODE(aDeviceType, aFunction, aMethod, aAccess: Integer): Integer;
  572. begin
  573. Result:=(aDeviceType shl 16) or (aAccess shl 14)
  574. or (aFunction shl 2) or aMethod;
  575. end;
  576.  
  577. // Next couple of constants derived from PortTalk_IOCTL.h
  578. // Note: the equivalent identifiers there are in fact CTL_CODE macro's
  579.  
  580. const
  581. cPORTTALKTYPE = 40000;
  582. cIOCTL_READ_PORT_UCHAR = $904;
  583. cIOCTL_WRITE_PORT_UCHAR = $905;
  584.  
  585. // OutPort and InPort for Bytes
  586. // Using IOCTL interface with the PortTalk system driver under WinNT
  587. // Using direct port write/read under Win98
  588.  
  589. procedure TPortTalk.OutPortB(PortAddress: Word; Data: Byte);
  590. var
  591. Buf: array[0..2] of Byte;
  592. CtrlCode: Cardinal;
  593. BytesRet: Cardinal;
  594. begin
  595. if ReportNotOpen('OutPortB') then Exit;
  596. if fIsWinNT then
  597. begin
  598. Buf[0]:=Lo(PortAddress);
  599. Buf[1]:=Hi(PortAddress);
  600. Buf[2]:=Data;
  601. CtrlCode:=CTL_CODE(cPORTTALKTYPE, cIOCTL_WRITE_PORT_UCHAR,
  602. METHOD_BUFFERED, FILE_ANY_ACCESS);
  603. if not DeviceIOControl(fHandle, CtrlCode, @Buf, 3, nil, 0, BytesRet, nil)
  604. then Report('OutPortB Error: '+IntToStr(GetLastError));
  605. end else
  606. begin
  607. // Direct port access under Win9x (works in 16 and 32bit mode)
  608. asm
  609. mov dx,PortAddress
  610. mov al,Data
  611. out dx,al
  612. end;
  613. end;
  614. end;
  615.  
  616. // Read Byte from Port. The value of ClosedResult will be returned when
  617. // PortTalk is not Open.
  618.  
  619. function TPortTalk.InPortB(PortAddress: Word; ClosedResult: Byte): Byte;
  620. var
  621. Buf: array[0..2] of Byte;
  622. CtrlCode: Cardinal;
  623. BytesRet: Cardinal;
  624. begin
  625. Result:=ClosedResult;
  626. if ReportNotOpen('InPortB') then Exit;
  627. if fIsWinNT then
  628. begin
  629. CtrlCode:=CTL_CODE(cPORTTALKTYPE, cIOCTL_READ_PORT_UCHAR,
  630. METHOD_BUFFERED, FILE_ANY_ACCESS);
  631. Buf[0]:=Lo(PortAddress);
  632. Buf[1]:=Hi(PortAddress);
  633. if DeviceIOControl(fHandle, CtrlCode, @Buf, 2, @Buf, 1, BytesRet, nil)
  634. then Result:=Buf[0]
  635. else Report('InPortB Error: '+IntToStr(GetLastError));
  636. end else
  637. begin
  638. // Direct port access under Win9x (works in 16 and 32bit mode)
  639. asm
  640. mov dx,PortAddress
  641. in al,dx
  642. mov @Result,al
  643. end;
  644. end;
  645. end;
  646.  
  647. // ****************************************************************************
  648. // Permissions
  649. // ****************************************************************************
  650.  
  651. const
  652. cIOCTL_IOPM_RESTRICT_ALL_ACCESS = $900;
  653. cIOCTL_IOPM_ALLOW_EXCUSIVE_ACCESS = $901;
  654. cIOCTL_SET_IOPM = $902;
  655. cIOCTL_ENABLE_IOPM_ON_PROCESSID = $903;
  656.  
  657. { *** NOT IMPLEMENTED ***
  658.   The functionality the PortTalk.sys driver extends beyond simple port access.
  659.   It also allows modification of the I/O Permissions Bitmap and can it grant
  660.   full or specific IO permissions to all or specific processes.
  661.   The interface routines for this functionality are currently not implemented
  662.   in this TPortTalk Delphi class.
  663. }
  664.  
  665. // ****************************************************************************
  666. // Admin
  667. // ****************************************************************************
  668.  
  669. // This procedure can be used to manually install or remove the PortTalk
  670. // driver and Service while logged-in as an Administrator, in order to make
  671. // make the Service available to PortTalk while logged-in as a normal User.
  672. // This procedure is intended to be stand-alone, using it's own dialogs,
  673. // in order to offer a simple method to any program using TPortTalk to have
  674. // the possibility of separate driver/service installation/removal.
  675.  
  676. procedure TPortTalk.Admin;
  677. const
  678. cNL = #10#13;
  679. cTitle = ' - Driver and Service Administration.';
  680. var
  681. Status: string;
  682. ModalResult: Integer;
  683. begin
  684. // Report that this procedure is beginning..
  685. Report('< Admin Begin >');
  686.  
  687. // Every dialog shows a first line with Version and cTitle..
  688. Status:=Version+cTitle+cNL+cNL;
  689. if not fIsWinNTAdmin then
  690. begin
  691. // Not an Admin or not NT - PortTalk can be Active (Open) or not
  692. // but we cannot change anything regarding driver or service.
  693. if fOpen
  694. then Status:=Status+'PortTalk is currently Active.'+cNL
  695. else Status:=Status+'PortTalk is NOT Active.'+cNL;
  696. if fIsWinNT
  697. then MessageDlg(Status+
  698. 'Admin functionality requires log-in as Administrator.',
  699. mtInformation, [mbOk], 0)
  700. else MessageDlg(Status+
  701. 'Admin functionality is not required under Win9X.',
  702. mtInformation, [mbOk], 0);
  703. end else if fOpen then
  704. begin
  705. // WinNT - WinNTAdmin - PortTalk Active. Driver may or may not
  706. // have been installed from here.. Whatever the case, we can
  707. // offer to de-install. Simply call Close after setting the internal
  708. // flags to force that Close will remove service and driver.
  709. // If installed from here, we can also offer to *keep* the driver
  710. // and service resident after normal close of PortTalk (which
  711. // normally will automatically occur at program termination).
  712. Status:=Status+'PortTalk Driver IS currently installed.'+cNL;
  713. if fSrvInstalled
  714. then ModalResult:=MessageDlg(Status+
  715. 'The current process will automatically remove it again.'+cNL+
  716. 'Press [Yes] to keep installed after Close or exit.'+cNL+
  717. 'Press [No] to stop and remove the Service and Driver now.'+cNL+
  718. 'Press [Cancel] for no change.',
  719. mtInformation, [mbYes, mbNo, mbCancel], 0)
  720. else ModalResult:=MessageDlg(Status+
  721. 'This was done for permanence. Current process will keep it.'+cNL+
  722. 'Press [No] to stop and remove the Service and Driver now.'+cNL+
  723. 'Press [OK] to lift permanence. Removes driver after Close/Exit. '+cNL+
  724. 'Press [Cancel] for no change.',
  725. mtInformation, [mbNo, mbOk, mbCancel], 0);
  726. case ModalResult of
  727. mrYes:
  728. begin
  729. // Ensure that PortTalk will neither stop nor remove the
  730. // Service when closing.
  731. fSrvStarted:=False;
  732. fSrvInstalled:=False;
  733. // Report flagging change..
  734. Report('Flagging to keep resident after Close/Exit.');
  735. end;
  736. mrNo:
  737. begin
  738. // Close PortTalk now after setting the flags that will
  739. // cause complete removal
  740. fSrvStarted:=True;
  741. fSrvInstalled:=True;
  742. Close;
  743. end;
  744. mrOk:
  745. begin
  746. // Ensure that PortTalk will later stop and remove the
  747. // Service and driver when closing.
  748. fSrvStarted:=True;
  749. fSrvInstalled:=True;
  750. // Report flagging change..
  751. Report('Flagging to stop/remove after Close/Exit.');
  752. end;
  753. end;
  754. end else
  755. begin
  756. // WinNT - WinNTAdmin - PortTalk NOT Active. Driver and Service
  757. // may or may not be present. PortTalk may or may not previously
  758. // have been opened. The internal flags cannot be trusted to
  759. // contain information. We can offer to ensure (de-)installation.
  760. // Note that we *could* look in
  761. // HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
  762. // to see whether or not the service is present..
  763. ModalResult:=MessageDlg(Status+
  764. 'PortTalk is currently NOT Active.'+cNL+
  765. 'Presence of Driver and Service is not determined.'+cNL+
  766. 'Press [Yes] to activate and ensure permanent installation.'+cNL+
  767. 'Press [No] to ensure stop and removal of Service and Driver.'+cNL+
  768. 'Press [OK] to activate/install for the current process.'+cNL+
  769. 'Press [Cancel] for no change.',
  770. mtInformation, [mbYes, mbNo, mbOk, mbCancel], 0);
  771. case ModalResult of
  772. mrYes:
  773. begin
  774. // Open PortTalk (also for the current process) and immediately
  775. // clear the flag that we have installed (if so) to keep the
  776. // installation present after next Close/Exit.
  777. Open;
  778. fSrvStarted:=False;
  779. fSrvInstalled:=False;
  780. end;
  781. mrNo:
  782. begin
  783. // Remove the Service and Driver
  784. StopPTService(True);
  785. end;
  786. mrOk:
  787. begin
  788. // Open PortTalk (also for the current process) in standard way.
  789. // Automatically Stops/Removes at next Close/Exit.
  790. Open;
  791. end;
  792. end;
  793. end;
  794.  
  795. // Report that this procedure has ended..
  796. Report('< Admin End >');
  797. end;
  798.  
  799.  
  800. end.
  801.  
  802.  
  803.