wincd.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. {
  2. }
  3. {$IFNDEF FPC_DOTTEDUNITS}
  4. unit WinCD;
  5. {$ENDIF FPC_DOTTEDUNITS}
  6. {$mode objfpc}
  7. {$h+}
  8. interface
  9. {$IFDEF FPC_DOTTEDUNITS}
  10. uses WinApi.Windows,System.SysUtils;
  11. {$ELSE FPC_DOTTEDUNITS}
  12. uses Windows,SysUtils;
  13. {$ENDIF FPC_DOTTEDUNITS}
  14. Type
  15. TCDAccessMethod = (camNone,camASPI,camSPTI,camIOCTL);
  16. {$packrecords c}
  17. TTOCTrack = packed record
  18. rsvd,
  19. ADR,
  20. trackNumber,
  21. rsvd2 : Byte;
  22. addr : Array[0..3] of byte;
  23. end;
  24. TTOC = packed Record
  25. toclen : word;
  26. firsttrack,
  27. lastTrack : byte;
  28. toctrack: Array[0..99] of TTocTrack;
  29. end;
  30. Const
  31. AccessMethodNames : Array[TCDAccessMethod] of AnsiString
  32. = ('None','ASPI','SPTI','IOCTL');
  33. Function GetCDAccessMethod : TCDAccessMethod;
  34. Procedure SetCDAccessMethod (Value : TCDAccessMethod);
  35. Function ReadTOC(Device : AnsiString; Var TOC : TTOc) : Integer;
  36. Function EnumCDDrives(Var Drives : Array of AnsiString) : Integer;
  37. Function GetNumDrives : Integer;
  38. implementation
  39. {$IFDEF FPC_DOTTEDUNITS}
  40. uses System.CdRom.Windows.IoCtl, System.CdRom.Windows.Aspi32, System.CdRom.ScsiDefs;
  41. {$ELSE FPC_DOTTEDUNITS}
  42. uses cdromioctl,wnaspi32,scsidefs;
  43. {$ENDIF FPC_DOTTEDUNITS}
  44. Var
  45. CurrentAccessMethod : TCDAccessMethod;
  46. CDOSVer : Integer;
  47. { ---------------------------------------------------------------------
  48. SPTI Defines.
  49. ---------------------------------------------------------------------}
  50. Type
  51. {$packrecords C}
  52. SCSI_PASS_THROUGH = record
  53. Length : USHORT;
  54. ScsiStatus : UCHAR;
  55. PathId : UCHAR;
  56. TargetId : UCHAR;
  57. Lun : UCHAR;
  58. CdbLength : UCHAR;
  59. SenseInfoLength : UCHAR;
  60. DataIn : UCHAR;
  61. DataTransferLength : ULONG;
  62. TimeOutValue : ULONG;
  63. DataBufferOffset : ULONG;
  64. SenseInfoOffset : ULONG;
  65. Cdb : array[0..15] of UCHAR;
  66. end;
  67. TSCSI_PASS_THROUGH = SCSI_PASS_THROUGH;
  68. PSCSI_PASS_THROUGH = ^TSCSI_PASS_THROUGH;
  69. SCSI_PASS_THROUGH_DIRECT = record
  70. Length : USHORT;
  71. ScsiStatus : UCHAR;
  72. PathId : UCHAR;
  73. TargetId : UCHAR;
  74. Lun : UCHAR;
  75. CdbLength : UCHAR;
  76. SenseInfoLength : UCHAR;
  77. DataIn : UCHAR;
  78. DataTransferLength : ULONG;
  79. TimeOutValue : ULONG;
  80. DataBuffer : PVOID;
  81. SenseInfoOffset : ULONG;
  82. Cdb : array[0..15] of UCHAR;
  83. end;
  84. TSCSI_PASS_THROUGH_DIRECT = SCSI_PASS_THROUGH_DIRECT;
  85. PSCSI_PASS_THROUGH_DIRECT = ^SCSI_PASS_THROUGH_DIRECT;
  86. SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER = record
  87. spt : SCSI_PASS_THROUGH_DIRECT;
  88. Filler : ULONG;
  89. ucSenseBuf : array[0..31] of UCHAR;
  90. end;
  91. TSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER = SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
  92. PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER = ^SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
  93. const
  94. METHOD_BUFFERED = 0;
  95. METHOD_IN_DIRECT = 1;
  96. METHOD_OUT_DIRECT = 2;
  97. METHOD_NEITHER = 3;
  98. FILE_ANY_ACCESS = 0;
  99. FILE_READ_ACCESS = $0001;
  100. FILE_WRITE_ACCESS = $0002;
  101. IOCTL_CDROM_BASE = $00000002;
  102. IOCTL_SCSI_BASE = $00000004;
  103. SCSI_IOCTL_DATA_OUT = 0;
  104. SCSI_IOCTL_DATA_IN = 1;
  105. SCSI_IOCTL_DATA_UNSPECIFIED = 2;
  106. { ---------------------------------------------------------------------
  107. Initialization code.
  108. ---------------------------------------------------------------------}
  109. procedure InitWinCD;
  110. Var
  111. TheCDOSVER : TOSVersionInfo;
  112. begin
  113. TheCDOSVer.dwOSVersionInfoSize:=SizeOf(TheCDOSver);
  114. GetVersionEx(TheCDOSVer);
  115. CDOSVer:=TheCDOSVer.dwMajorVersion;
  116. If AspiLoaded then
  117. CurrentAccessMethod := camASPI
  118. else
  119. begin
  120. if (CDOSver<1) then
  121. CurrentAccessMethod := camNone
  122. else
  123. {
  124. It is better to use SPTI on windows, but the problem with that
  125. is that administrative priviledges are needed. A detection
  126. algorithm for these priviledges here would be nice.
  127. }
  128. CurrentAccessMethod := camSPTI;
  129. end;
  130. end;
  131. { ---------------------------------------------------------------------
  132. Actual reading of table of contents.
  133. ---------------------------------------------------------------------}
  134. { ---------------------------------------------------------------------
  135. 1. SPTI
  136. ---------------------------------------------------------------------}
  137. Function sptiReadTOC(Device : AnsiString; var TOC: TToC) : Integer;
  138. Var
  139. DriveHandle : THandle;
  140. len : Cardinal;
  141. buf : Array[0..31] of AnsiChar;
  142. ID,retVal : Integer;
  143. Returned,Flags : Cardinal;
  144. swb : TSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
  145. begin
  146. Flags := Cardinal(GENERIC_READ);
  147. if (CDOSVer>4) then
  148. Flags:=Flags or Cardinal(GENERIC_WRITE);
  149. Device:=Upcase('\\.\'+Device);
  150. DriveHandle:=CreateFileA(PAnsiChar(Device),Flags,FILE_SHARE_READ,
  151. nil,OPEN_EXISTING, 0, 0 );
  152. if (DriveHandle=INVALID_HANDLE_VALUE) then
  153. begin
  154. Result:=-1;
  155. Exit;
  156. end;
  157. Try
  158. Returned:= 0;
  159. len:= sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
  160. FillChar(swb, len ,0);
  161. With swb.spt do
  162. begin
  163. Length := sizeof(swb.spt); // SCSI_PASS_THROUGH);
  164. CdbLength := 10;
  165. DataIn := SCSI_IOCTL_DATA_IN;
  166. DataTransferLength := SizeOf(tOC);
  167. TimeOutValue := 5;
  168. DataBuffer := @TOC;
  169. SenseInfoOffset := @swb.ucSenseBuf[0]-pbyte(@swb.spt);
  170. Cdb[0] := $43; // read TOC
  171. Cdb[1] := $02; // MSF mode
  172. Cdb[7] := $03;
  173. Cdb[8] := $24;
  174. end;
  175. if (Not DeviceIoControl(DriveHandle,
  176. IOCTL_SCSI_PASS_THROUGH_DIRECT,
  177. @swb,
  178. len,
  179. @swb,
  180. len,
  181. @Returned,
  182. Nil)) then
  183. begin
  184. Result:=-1;
  185. Exit;
  186. end;
  187. With TOC do
  188. Result:=LastTrack-FirstTrack+1;
  189. finally
  190. CloseHandle(DriveHandle);
  191. end;
  192. end;
  193. { ---------------------------------------------------------------------
  194. 2. ASPI
  195. ---------------------------------------------------------------------}
  196. Function AspiGetNumAdapters : Integer;
  197. Var
  198. D : DWORD;
  199. Count, Status : Byte;
  200. begin
  201. d:= GetASPI32SupportInfo();
  202. Count:=D and $FF;
  203. Status:=(D shr 8) and $ff;
  204. if (Status<>SS_COMP) and (Status<>SS_NO_ADAPTERS) then
  205. Result:=-1
  206. else
  207. Result:=Count;
  208. end;
  209. Function DriveToSCSIParm (Device : AnsiString; Var HID,TGT,LUN : Byte) : Boolean;
  210. Var
  211. Code : Integer;
  212. begin
  213. Result:=False;
  214. Code:=Pos('[',Device);
  215. if Code<>0 then
  216. begin
  217. Delete(Device,1,Code);
  218. Code:=Pos(';',Device);
  219. HID:=StrToIntDef(Copy(Device,1,Code-1),-1);
  220. Result:=HID<>-1;
  221. If result then
  222. begin
  223. Delete(DEvice,1,Code);
  224. Code:=Pos(';',Device);
  225. Tgt:=StrToIntDef(Copy(Device,1,Code-1),-1);
  226. Result:=tgt<>-1;
  227. If result then
  228. begin
  229. Delete(DEvice,1,Code);
  230. Code:=Pos(']',Device);
  231. Lun:=StrToIntDef(Copy(Device,1,Code-1),-1);
  232. Result:=Lun<>-1;
  233. end;
  234. end;
  235. end;
  236. end;
  237. Var
  238. Atoc : TTOc;
  239. Function AspiReadTOC(Device : AnsiString; Var TOC : TTOC) : Integer;
  240. Var
  241. HAID,TGT,LUN : Byte;
  242. Status : DWord;
  243. S,T : SRB_ExecSCSICmd;
  244. HEvent : THANDLE;
  245. begin
  246. If Not DriveToSCSIParm(Device,HAID,TGT,lun) then
  247. begin
  248. Result:=-1;
  249. Exit;
  250. end;
  251. Writeln('About to read toc from ',haid,' ',tgt,' ',lun);
  252. hEvent:=CreateEvent( nil, TRUE, FALSE, nil );
  253. Writeln('Resetting event');
  254. ResetEvent(hEvent);
  255. Writeln('Reset event');
  256. Try
  257. FillChar(S,sizeof(s),0);
  258. s.SRB_Cmd := SC_EXEC_SCSI_CMD;
  259. s.SRB_HaID := HaID;
  260. s.SRB_Target := Tgt;
  261. s.SRB_Lun := lun;
  262. s.SRB_Flags := SRB_DIR_IN or SRB_EVENT_NOTIFY;
  263. s.SRB_BufLen := SizeOf(Toc);
  264. s.SRB_BufPointer := @TOC;
  265. s.SRB_SenseLen := SENSE_LEN;
  266. s.SRB_CDBLen := $0A;
  267. s.SRB_PostProc := LPVOID(hEvent);
  268. s.CDBByte[0] := SCSI_READ_TOC; // read TOC command
  269. s.CDBByte[1] := $02; // MSF mode
  270. s.CDBByte[7] := HiByte(Word(S.SRB_BufLen)); // high-order byte of buffer len
  271. s.CDBByte[8] := LoByte(Word(S.SRB_BUFLEN)); // low-order byte of buffer len
  272. Writeln('Sending Command');
  273. SendASPI32Command(LPSRB(@s));
  274. Writeln('Sent Command');
  275. Status:=S.SRB_STATUS;
  276. Writeln('Command status,',Status);
  277. if (Status=SS_PENDING ) then
  278. begin
  279. Writeln('Waiting for object');
  280. WaitForSingleObject( hEvent, 10000 ); // wait up to 10 secs
  281. Writeln('Waiting ended');
  282. end;
  283. Finally
  284. CloseHandle( hEvent );
  285. end;
  286. if (S.SRB_Status<>SS_COMP ) then
  287. begin
  288. Result:=-1;
  289. Exit;
  290. end;
  291. Writeln('Command completed');
  292. With TOC do
  293. Result:=LastTrack-FirstTrack+1;
  294. end;
  295. { ---------------------------------------------------------------------
  296. 3. IOCTL
  297. ---------------------------------------------------------------------}
  298. Function ioctlReadTOC(Device : AnsiString; Var TOC : TTOC) : Integer;
  299. Var
  300. DriveHandle : Thandle;
  301. Retval : Longint;
  302. Returned : DWord;
  303. Flags : Cardinal;
  304. begin
  305. Flags:=Cardinal(GENERIC_READ);
  306. device:=Upcase('\\.\'+device);
  307. DriveHandle:=CreateFileA(PAnsiChar(Device), Flags,
  308. FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0 );
  309. if (DriveHandle = INVALID_HANDLE_VALUE) then
  310. begin
  311. result:=-1;
  312. exit;
  313. end;
  314. Try
  315. Returned := 0;
  316. FillChar(Toc, sizeof(TOC),0 );
  317. if Not DeviceIoControl(DriveHandle,
  318. IOCTL_CDROM_READ_TOC,
  319. Nil,
  320. 0,
  321. @TOC,
  322. sizeof(TTOC),
  323. @Returned,
  324. NiL) then
  325. begin
  326. Result:=-1;
  327. exit;
  328. end;
  329. With TOC do
  330. Result:=LastTrack-FirstTrack+1;
  331. Finally
  332. CloseHandle(DriveHandle);
  333. end;
  334. end;
  335. Function NtDriveInfo(CopyDrives : Boolean;Var CDDrives : Array of AnsiString): Integer;
  336. var
  337. I : Integer;
  338. Drives : Array[0..105] of AnsiChar;
  339. P : PAnsiChar;
  340. begin
  341. FillChar(Drives,SizeOf(Drives),0);
  342. GetLogicalDriveStringsA(105,Drives);
  343. P:=@Drives[0];
  344. Result:=0;
  345. While P[0]<>#0 do
  346. begin
  347. If GetDriveTypeA(p)=DRIVE_CDROM then
  348. begin
  349. If CopyDrives and (Result<High(CDDrives)) then
  350. CDDrives[Result]:=Upcase(P[0])+':';
  351. Inc(Result);
  352. end;
  353. P:=P+Strlen(P)+1;
  354. end;
  355. end;
  356. Function NTGetNumDrives: Integer;
  357. Var A : Array[1..1] of AnsiString;
  358. begin
  359. Result:=NTDriveInfo(False,A);
  360. end;
  361. Function ioctlEnumDrives(Var Drives : Array of AnsiString) : Integer;
  362. begin
  363. result:=NTDriveInfo(True,Drives);
  364. end;
  365. { ---------------------------------------------------------------------
  366. 3. Generic
  367. ---------------------------------------------------------------------}
  368. Function ReadTOC(Device : AnsiString; Var TOC : TTOc) : Integer;
  369. begin
  370. Case CurrentAccessMethod of
  371. camNone : Result:=-1;
  372. camASPI : Result:=AspiReadTOC(Device,TOC);
  373. camSPTI : Result:=SptiReadTOC(Device,TOC);
  374. camIOCTL : Result:=IOCTLReadTOC(Device,TOC);
  375. end;
  376. end;
  377. Function GetCDAccessMethod : TCDAccessMethod;
  378. begin
  379. Result:=CurrentAccessMethod;
  380. end;
  381. Procedure SetCDAccessMethod (Value : TCDAccessMethod);
  382. begin
  383. CurrentAccessMethod:=Value;
  384. end;
  385. Function ASPIDriveInfo(CopyInfo : Boolean; Var Drives : Array of AnsiString) : Integer;
  386. var
  387. sh : SRB_HAInquiry;
  388. sd : SRB_GDEVBlock;
  389. numAdapters, maxTgt : Byte;
  390. i, j, k : byte;
  391. idx : Integer;
  392. begin
  393. Result:=0;
  394. numAdapters := AspiGetNumAdapters;
  395. if (numAdapters=0) then
  396. exit;
  397. For I:=0 to NumAdapters-1 do
  398. begin
  399. FillChar(sh,sizeof(sh),0);
  400. sh.SRB_Cmd := SC_HA_INQUIRY;
  401. sh.SRB_HaID := i;
  402. SendASPI32Command(LPSRB(@sh));
  403. if (sh.SRB_Status=SS_COMP) then
  404. begin
  405. maxTgt:=sh.HA_Unique[3];
  406. if (maxTgt=0) then
  407. maxTgt:=MAXTARG;
  408. For J:=0 to Maxtgt-1 do
  409. For k:=0 to MAXLUN-1 do
  410. begin
  411. FillChar(sd,sizeof(sd),0);
  412. sd.SRB_Cmd := SC_GET_DEV_TYPE;
  413. sd.SRB_HaID := i;
  414. sd.SRB_Target := j;
  415. sd.SRB_Lun := k;
  416. SendASPI32Command(LPSRB(@sd));
  417. if (sd.SRB_Status=SS_COMP) and
  418. (sd.SRB_DeviceType=DTYPE_CDROM) then
  419. begin
  420. If CopyInfo and (Result<High(Drives)) then
  421. Drives[Result]:=Format('ASPI[%d;%d;%d]',[I,J,K]);
  422. Inc(Result);
  423. end;
  424. end;
  425. end;
  426. end;
  427. end;
  428. Function ASPIGetNumDrives: Integer;
  429. Var
  430. A : Array[1..1] of AnsiString;
  431. begin
  432. Result:=AspiDriveInfo(False,A);
  433. end;
  434. Function GetNumDrives : Integer;
  435. begin
  436. If CurrenTAccessMethod=camASPI then
  437. Result:=AspiGetNumDrives
  438. else
  439. Result:=NTGetNumDrives;
  440. end;
  441. Function EnumCDDrives(Var Drives : Array of AnsiString) : Integer;
  442. begin
  443. If CurrenTAccessMethod=camASPI then
  444. Result:=AspiDriveInfo(True,Drives)
  445. else
  446. Result:=ioctlEnumDrives(Drives);
  447. end;
  448. Initialization
  449. InitWinCD;
  450. end.