Unit FileUtilsUnit; // NewView - a new OS/2 Help Viewer // Copyright 2003-2006 Aaron Lawrence // Copyright 2006-2009 Ronald Brill (rbri at rbri dot de) // This software is released under the GNU Public License - see readme.txt // Helper functions for file handling Interface uses Classes, ACLUtility; const DIRECTORY_SEPARATOR = '\'; PATH_SEPARATOR = ';'; CURRENT_DIRECTORY = '.'; PARENT_DIRECTORY = '..'; FILE_EXTENSION_DELIMITER = '.'; // Drive numbers are one based MinDriveNumber = 1; MaxDriveNumber = 26; // TODO HelpPathEnvironmentVar = 'HELP'; BookshelfEnvironmentVar = 'BOOKSHELF'; LanguageEnvironmentVar = 'LANG'; DEFAULT_LANGUAGE = 'EN_US'; HELP_FILE_DELIMITER = '+'; HELP_FILE_EXTENSION = FILE_EXTENSION_DELIMITER + 'hlp'; INF_FILE_EXTENSION = FILE_EXTENSION_DELIMITER + 'inf'; type TDriveType = ( dtNone, dtFloppy, dtHard, dtCD, dtNetwork, dtRemovable ); // Adds a slash to the end of dir if not present // if aDir is empty this returns '\' Function AddDirectorySeparator(aDirectory : String) : String; // Adds a slash to the end of dir if not present // if aDir is empty this returns '' Function AddDirectorySeparatorIfNotEmpty(aDirectory: String) : String; // Removes a directory seperator from the end of aDirectory // (if present) Function RemoveRightDirectorySeparator(aDirectory : String) : String; // Expands the path given, relative to aBaseDirectory // Handles leading \ for root dir, // .. for parent, . (ignored), // drive spec at start, // ignores repeated \ e.g. \\ Function ExpandPath(aBaseDirectory : String; aPath : String): String; Function GetLogFilesDir: String; Function SearchPath(const aPathEnvVar: String; const aFilename: String; var aResultFilename: String) : boolean; Function SearchHelpPaths(const aFilename: String; var aResultFilename: String; const anIncludeAppDir: boolean) : boolean; // Find the help file for the current app based on LANG Function FindDefaultLanguageHelpFile(const anApplicationName: String; const aLanguage : String) : String; // Breaks up specified Env var path // this always clears the list at the beginning Procedure GetDirsInPath(const aPathEnvVar: String; var aList: TStrings); // Breaks up specified Env var path // then adds all matching files to the list Procedure GetFilesInPath( const aPathEnvVar: String; const aFilter: String; var aList: TStrings ); // searches for all files in aDirectory matching aFilter and add // them to aList // it is possible to define different filter if you separate them by semicolon Procedure ListFilesInDirectory( const aDirectory : String; const aFilter : String; const aWithDirectoryFlag : boolean; var aList : TStrings); // searches for all directories in aDirectory and add them to aList Procedure ListSubDirectories(const aDirectory: String; const anIncludeSystemAndHiddenFlag: boolean; var aList: TStrings); Procedure ListFilesInDirectoryRecursiveWithTermination(const aDirectory : String; const aFilter : String; const aWithDirectoryFlag : boolean; const anIncludeSystemAndHiddenFlag: boolean; var aList : TStrings; const aTerminateCheck : TTerminateCheck; const aUseTerminateCheck : boolean); Function ParentDir(const aDirectory : String) : String; // In the directory startpath, create directory and subdirectories // specified in DirsString // e.g. bob\bill\fred will make bob, then bill in bob, then fred in bob // returns path to lowest dir created Function MakeDirs(const aFullDirectoryPath: String) : String; // Checks if a directory exists Function DirectoryExists(const aDirectory : String) : boolean; Function DriveLetterToDriveNumber(const aDriveLetter : char) : longint; Function DriveNumberToDriveLetter(const aDriveNumber : longint) : char; Function GetVolumeLabel(aDrive: char) : String; Function GetBootDriveLetter: char; // Returns true if file exists and is read only Function FileIsReadOnly(const aFilename : String) : boolean; // TODO Function IsFloppyDrive( DriveNumber: longint ): Boolean; Function GetLocalDriveType( DriveNumber: longint ): TDriveType; Function GetDriveType( DriveNumber: longint ): TDriveType; Function GetNetworkDriveRemotePath( DriveNumber: longint ): String; Implementation uses Dos, BseDos, BseErr, BseDev, Os2Def, SysUtils, StringUtilsUnit, CharUtilsUnit; imports FUNCTION _DosQueryFSAttach( VAR pszDeviceName: CSTRING; ulOrdinal: ULONG; ulFSAInfoLevel:ULONG; pfsqb: PFSQBUFFER2; VAR pcbBuffLength: ULONG ): APIRET; APIENTRY; 'DOSCALLS' index 277; end; type TWord = Record // Bytes of a Word LoByte, HiByte : Byte; End; TBPB = Array[0..30] Of Byte; // Puffer fuer BPB-Struktur TDeviceParameters = Record BPB: TBPB; Cylinders: word; DeviceType: Byte; Attributes: Word; End; Function AddDirectorySeparator(aDirectory : String) : String; begin if aDirectory = '' then begin Result:= DIRECTORY_SEPARATOR; exit; end; if aDirectory[length(aDirectory)] <> DIRECTORY_SEPARATOR then begin Result := aDirectory + DIRECTORY_SEPARATOR; exit; end; Result := aDirectory; end; Function AddDirectorySeparatorIfNotEmpty(aDirectory: String): String; begin if aDirectory = '' then begin Result := ''; exit; end; Result := AddDirectorySeparator(aDirectory); end; Function RemoveRightDirectorySeparator(aDirectory : String) : String; begin Result := StrTrimRightChars(aDirectory, [DIRECTORY_SEPARATOR]); end; Function ExpandPath(aBaseDirectory : String; aPath : String): String; var tmpDirectory: String; tmpDirectories : TStringList; i : integer; begin Result:= aBaseDirectory; if aPath = '' then begin Result := StrTrimRightChars(Result, [DIRECTORY_SEPARATOR]); exit; end; aPath := trim(aPath); if Length(aPath) > 1 then begin // check for drive spec if aPath[2] = ':' then begin Result := AddDirectorySeparator(aPath); if Length(aPath) > 3 then begin Result := StrTrimRightChars(Result, [DIRECTORY_SEPARATOR]); end; exit; end end; if Length(aPath) > 0 then begin // check for root dir spec if aPath[1] = DIRECTORY_SEPARATOR then begin // take just the drive from the basedir if aBaseDirectory[2] = ':' then begin Result := StrLeft(aBaseDirectory, 2); end else begin Result := DIRECTORY_SEPARATOR; end; aPath := StrTrimLeftChars(aPath, [DIRECTORY_SEPARATOR]); end; end; tmpDirectories := TStringList.Create; StrExtractStringsIgnoreEmpty(tmpDirectories, aPath, [DIRECTORY_SEPARATOR], #0); for i := 0 to tmpDirectories.count-1 do begin tmpDirectory := tmpDirectories[i]; if tmpDirectory = PARENT_DIREcTORY then begin if NOT ((Length(Result) = 2) AND (Result[2] = ':')) then begin Result := ParentDir(Result); end; end else if tmpDirectory = CURRENT_DIRECTORY then begin ; // nothing to do end else begin Result := AddDirectorySeparator(Result) + tmpDirectory; end; // strip any extra leading slashes aPath := StrTrimLeftChars(aPath, [DIRECTORY_SEPARATOR]); end; tmpDirectories.Destroy; if Length(Result) = 2 then begin if Result[2] = ':' then begin // just a drive spec X:, so add a slash Result := Result + DIRECTORY_SEPARATOR; end; end; end; Function GetLogFilesDir: String; begin // ecomstation 1.1 compat Result := GetEnv('LOGFILES'); if Result <> '' then begin Result := AddDirectorySeparator(Result); exit; end; // TODO Result := AddDirectorySeparator(GetApplicationDir); end; Function SearchPath(const aPathEnvVar: String; const aFilename: String; var aResultFilename: String) : boolean; var tmpSzEnvVar : CString; tmpSzFilename : CString; tmpSzFilenameFound : CString; tmpRC: APIRET; begin Result := false; aResultFilename := ''; tmpSzEnvVar := aPathEnvVar; tmpSzFilename := aFilename; tmpRC := DosSearchPath( SEARCH_IGNORENETERRS + SEARCH_ENVIRONMENT + SEARCH_CUR_DIRECTORY, tmpSzEnvVar, tmpSzFilename, tmpSzFilenameFound, sizeof(tmpSzFilenameFound)); if tmpRC = 0 then begin Result := true; aResultFilename := tmpSzFilenameFound; end; end; Function SearchHelpPaths(const aFilename: String; var aResultFilename: String; const anIncludeAppDir: boolean) : boolean; begin Result := SearchPath(HelpPathEnvironmentVar, aFileName, aResultFilename); if not Result then begin Result := SearchPath(BookshelfEnvironmentVar, aFileName, aResultFilename); end; if (not Result) and anIncludeAppDir then begin aResultFilename := AddDirectorySeparator(GetApplicationDir) + aFilename; Result := FileExists(aResultFilename); if not Result then begin aResultFilename := ''; end; end; end; Function FindDefaultLanguageHelpFile(const anApplicationName: String; const aLanguage : String) : String; var tmpLanguage : String; tmpLanguageParts : TStringList; tmpMajorLanguage : String; tmpMinorLanguage : String; begin Result := ''; tmpLanguage := aLanguage; if aLanguage = '' then begin tmpLanguage := DEFAULT_LANGUAGE; end; tmpLanguageParts := TStringList.Create; StrExtractStrings(tmpLanguageParts, tmpLanguage, ['_'], #0); tmpMajorLanguage := ''; if tmpLanguageParts.count > 0 then begin tmpMajorLanguage := tmpLanguageParts[0]; end; tmpMinorLanguage := ''; if tmpLanguageParts.count > 1 then begin tmpMinorLanguage := tmpMinorLanguage[1]; end; tmpLanguageParts.Destroy; // note there might be some other stuff on the end of LANG // such as ES_ES_EURO... if tmpMinorLanguage <> '' then begin if SearchHelpPaths( anApplicationName + '_' + tmpMajorLanguage + '_' + tmpMinorLanguage + HELP_FILE_EXTENSION, Result, true ) then begin // found a specifc language exit; end; end; // try generic language? if SearchHelpPaths( anApplicationName + '_' + tmpMajorLanguage + HELP_FILE_EXTENSION, Result, true ) then begin exit; end; // nothing specific, search for default SearchHelpPaths(anApplicationName + HELP_FILE_EXTENSION, Result, true); end; Procedure GetDirsInPath(const aPathEnvVar: String; var aList: TStrings); var tmpRC : APIRET; tmpPszPathEnvVar : PChar; tmpSzEnvVar : CString; begin // do this in any case also if there is an error // to garantie a defined behavior aList.Clear; tmpSzEnvVar := aPathEnvVar; tmpRC := DosScanEnv(tmpSzEnvVar, tmpPszPathEnvVar); if tmpRC <> 0 then begin exit; end; StrExtractStringsIgnoreEmpty(aList, StrPas(tmpPszPathEnvVar), [PATH_SEPARATOR], #0); end; Procedure GetFilesInPath( const aPathEnvVar: String; const aFilter: String; var aList: TStrings ); var tmpDirectories : TStringList; i : integer; begin tmpDirectories := TStringList.Create; GetDirsInPath(aPathEnvVar, tmpDirectories); for i:=0 to tmpDirectories.count-1 do begin ListFilesInDirectory(tmpDirectories[i], aFilter, false, aList); end; tmpDirectories.Destroy; end; Procedure ListFilesInDirectory( const aDirectory: String; const aFilter: String; const aWithDirectoryFlag: boolean; var aList: TStrings); var tmpRC : APIRET; tmpSearchResults: TSearchRec; tmpMask: String; tmpFilterParts : TStringList; tmpDirectory : String; i : integer; begin tmpFilterParts := TStringList.Create; StrExtractStrings(tmpFilterParts, aFilter, [PATH_SEPARATOR], #0); for i:=0 to tmpFilterParts.count-1 do begin tmpMask := tmpFilterParts[i]; tmpDirectory := AddDirectorySeparator(aDirectory); tmpRC := FindFirst(tmpDirectory + tmpMask, faAnyFile, tmpSearchResults); while tmpRC = 0 do begin if tmpSearchResults.Attr And faDirectory = 0 then begin if (aWithDirectoryFlag) then begin aList.Add(tmpDirectory + tmpSearchResults.Name); end else begin aList.Add(tmpSearchResults.Name); end; end; tmpRC := FindNext(tmpSearchResults); end; FindClose(tmpSearchResults); end; tmpFilterParts.Destroy; end; Procedure ListSubDirectories(const aDirectory: String; const anIncludeSystemAndHiddenFlag: boolean; var aList: TStrings); var tmpRC : APIRET; tmpSearchResults: TSearchRec; tmpName : String; tmpFileAttributes : ULONG; begin if anIncludeSystemAndHiddenFlag then begin tmpFileAttributes := faArchive or faReadonly or faHidden or faSysFile or faDirectory or faMustDirectory; end else begin tmpFileAttributes := faArchive or faReadonly or faDirectory or faMustDirectory; end; tmpRC := FindFirst(AddDirectorySeparator(aDirectory) + '*', tmpFileAttributes, tmpSearchResults); if (tmpRC <> 0) then begin exit; end; while tmpRC = 0 do begin tmpName := tmpSearchResults.Name; if (tmpName <> CURRENT_DIRECTORY) AND (tmpName <> PARENT_DIRECTORY) then begin aList.Add(AddDirectorySeparatorIfNotEmpty(aDirectory) + tmpSearchResults.Name ); end; tmpRC := FindNext(tmpSearchResults); end; FindClose(tmpSearchResults); end; Procedure ListFilesInDirectoryRecursiveWithTermination(const aDirectory : String; const aFilter : String; const aWithDirectoryFlag : boolean; const anIncludeSystemAndHiddenFlag: boolean; var aList : TStrings; const aTerminateCheck : TTerminateCheck; const aUseTerminateCheck : boolean); var i : integer; tmpSubDirectories : TStringList; tmpSubDirectory : String; begin // at first add all files from the directory itself ListFilesInDirectory(aDirectory, aFilter, aWithDirectoryFlag, aList); // now determine all subdirectories tmpSubDirectories := TStringList.Create; ListSubDirectories(aDirectory, anIncludeSystemAndHiddenFlag, tmpSubDirectories); for i := 0 to tmpSubDirectories.Count - 1 do begin // if Assigned( TerminateCheck ) then - doesn't work in sibyl if aUseTerminateCheck then if aTerminateCheck then break; tmpSubDirectory := tmpSubDirectories[i]; ListFilesInDirectoryRecursiveWithTermination( tmpSubDirectory, aFilter, aWithDirectoryFlag, anIncludeSystemAndHiddenFlag, aList, aTerminateCheck, aUseTerminateCheck); end; tmpSubDirectories.Destroy; end; Function ParentDir(const aDirectory : String) : String; var tmpPos: integer; begin tmpPos := Length(aDirectory); // ends with slash while (aDirectory[tmpPos] = DIRECTORY_SEPARATOR) AND (tmpPos > 0) do begin dec(tmpPos); end; // find slash while (aDirectory[tmpPos] <> DIRECTORY_SEPARATOR) AND (tmpPos > 0) do begin dec(tmpPos); end; result:= StrLeft(aDirectory, tmpPos-1); end; Function MakeDirs(const aFullDirectoryPath: String) : String; Var tmpDirectoryParts : TStringList; tmpDirectoryPart : String; tmpCompletePart : String; i : integer; begin tmpDirectoryParts := TStringList.Create; StrExtractStringsIgnoreEmpty(tmpDirectoryParts, aFullDirectoryPath, [DIRECTORY_SEPARATOR], #0); tmpCompletePart := ''; for i:=0 to tmpDirectoryParts.count-1 do begin tmpDirectoryPart := trim(tmpDirectoryParts[i]); if tmpDirectoryPart <> '' then begin tmpCompletePart := AddDirectorySeparatorIfNotEmpty(tmpCompletePart) + tmpDirectoryPart; if not DirectoryExists(tmpCompletePart) then begin MkDir(tmpCompletePart); end; end; end; Result := tmpCompletePart; end; Function DirectoryExists(const aDirectory : String) : boolean; Var tmpRC : APIRET; tmpSearchResults : TSearchRec; tmpDriveMap : ULONG; tmpActualDrive : ULONG; tmpDrive : Char; tmpDriveNum : integer; tmpDriveBit : longword; tmpDirectory : String; Begin Result := false; tmpDirectory := RemoveRightDirectorySeparator(aDirectory); if tmpDirectory = '' then begin Result := true; exit; end; if Length(tmpDirectory) = 2 then begin if tmpDirectory[2] = ':' then begin // a drive only has been specified tmpDrive := UpCase(tmpDirectory[1] ); if (tmpDrive < 'A') or (tmpDrive > 'Z') then begin // invalid drive; return false; exit; end; DosQueryCurrentDisk(tmpActualDrive, tmpDriveMap); tmpDriveNum := Ord(tmpDrive) - Ord('A') + 1; // A -> 1, B -> 2... tmpDriveBit := 1 shl (tmpDriveNum-1); // 2^DriveNum Result := tmpDriveMap and (tmpDriveBit) > 0; exit; end; end; tmpRC := FindFirst( tmpDirectory, faArchive or faReadonly or faHidden or faSysFile or faDirectory or faMustDirectory, tmpSearchResults); if tmpRC = 0 then begin Result:= true; FindClose(tmpSearchResults); end; end; Function DriveLetterToDriveNumber(const aDriveLetter : char) : longint; begin if (aDriveLetter >= 'a') and (aDriveLetter <= 'z') then begin Result := Ord(aDriveLetter) - Ord('a') + 1; exit; end; if (aDriveLetter >= 'A') and (aDriveLetter <= 'Z') then begin Result := Ord(aDriveLetter) - Ord('A') + 1; exit; end; // not a valid drive letter Result := 0; end; Function DriveNumberToDriveLetter(const aDriveNumber: longint) : char; begin Result := Chr(aDriveNumber - 1 + Ord('A')); end; Function GetVolumeLabel(aDrive: char) : String; var tmpRC : APIRET; tmpFileSystemInfo : FSINFO; e : EInOutError; begin DosErrorAPI( FERR_DISABLEHARDERR ); Result := ''; tmpRC := DosQueryFSInfo( DriveLetterToDriveNumber(aDrive), FSIL_VOLSER, tmpFileSystemInfo, sizeof(tmpFileSystemInfo)); if tmpRC = 0 then begin Result := StrPasWithLength(Addr(tmpFileSystemInfo.vol.szVolLabel), tmpFileSystemInfo.vol.cch); Result := LowerCase(Result); end; DosErrorAPI( FERR_ENABLEHARDERR ); if tmpRC <> 0 then begin e := EInOutError.Create( 'Cannot read drive ' + aDrive + ':'); e.ErrorCode := tmpRC; raise e; end; end; Function GetBootDriveLetter: char; var tmpBuffer: longword; begin DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, tmpBuffer, sizeof(tmpBuffer)); Result := Chr(ord('A') + tmpBuffer - 1); end; Function FileIsReadOnly(const aFilename : String ) : boolean; begin Result :=(FileGetAttr(aFilename) AND faReadonly) > 0; end; // TODO Function IsFloppyDrive( DriveNumber: longint ): Boolean; Var bResult : Byte; Begin DosDevConfig( bResult, DEVINFO_FLOPPY ); Result := ( Abs( DriveNumber ) <= bResult); End; // ------------------------------------------------------------------------- // Funktion/Function: QueryCDRoms() // // Beschreibung: // Die Funktion QueryCDRom ermittelt ueber eine nicht dokumentierte // Schnittstelle die Anzahl der CDRom-Laufwerke und den ersten, fuer // ein CDRom-Laufwerk, vergebenen UnitIdentifier. // Der Treiber OS2CDROM.DMD stellt dem System zwei Devices (CD-ROM1$ // und CD-ROM2$) zur Verfuegung. Die beiden Devices unterscheiden sich // durch DeviceAttribute. Beide Devices unterstuetzen (zumindest unter // Warp) den undokumentierten Generic IOCtl 0x82/0x60, welcher Infor- // mationen ueber die angeschlossenen CDRom-Laufwerke liefert. // // Description: // This Functions finds out how many CD-Rom Drives are present in System // and which Drive Letter is the first occupied by a CD-Rom. It uses an // undocumented Interface to OS2CDROM.DMD. // OS2CDROM.DMD presents two Devices (CD-ROM1$ and CD-ROM2$). These De- // vices are distinguished by their Device-Attributes. Both Devices sup- // port (under Warp) the undocumented generic IOCtl-Call 0x82/0x60 which // deliver some Information about the connected CD-Rom Drives. // // Parameter: // Var ulCDRomCount ULONG Anzahl CD-Rom Laufwerke im System // Number of CD-Rom Drives in System // // Var ulFirstCDRomDiskNo ULONG erste Laufwerksnummer, die an ein // CD-Rom vergeben ist // first Drive-Letter occupied by a // CD-Rom Drive // // Rueckgabe/Returnvalue: keine/none // ------------------------------------------------------------------------- Procedure QueryCDRoms(Var ulCDRomCount, ulFirstCDRomDiskNo: ULONG); Const cszDriverName : CSTRING = 'CD-ROM?$'; Var cCurDriver : Char; // Indexvariable fuer aktuell bearbeites Device (1 oder 2) // Index for current Device (1 or 2) hDevHandle : HFILE; // Handle fuer Device // Device handle ulAction : ULONG; // Aktionscode (DosOpen()) // Actioncode (DosOpen()) ulParams : ULONG; // Anzahl Bytes von IOCtl gelieferter Parameterdaten // Number of Bytes for delivered Parameterdata ulData : ULONG; // Anzahl Bytes von IOCtl gelieferter Daten // Number of Bytes delivered by IOCtl rCDInfo : Record // Ergebnisstruktur der IOCtl-Funktion (s.o.) // Record for Results of IOCtl-Call (see above) usCDRomCount : USHORT; // Anzahl CD-Roms / Number of CD-Rom Drives usFirstUnitNo: USHORT; // erste vergebene Laufwerksnummer / first Driver Letter End; Begin (* uQueryCDRom *) /************************************ * Vorbelegungen * * initial assignments ************************************/ ulCDRomCount := 0; ulFirstCDRomDiskNo := 0; ulParams := 0; /************************************ * die beiden Devices abarbeiten * * accessing both Devices ************************************/ For cCurDriver := '1' To '2' Do Begin /************************************ * Device oeffnen * * open Device ************************************/ cszDriverName[6] := cCurDriver; If (DosOpen(cszDriverName, // Devicename hDevHandle, // Handle ulAction, // Aktionscode 0, // Dateigr”áe FILE_NORMAL, // Attribute: read/write OPEN_ACTION_OPEN_IF_EXISTS, // OpenFlag: ”ffnen, wenn vorhanden OPEN_FLAGS_FAIL_ON_ERROR Or // Modus: Fehlermeldung per Returncode OPEN_SHARE_DENYNONE Or // keine Einschr„nkungen fr Dritte OPEN_ACCESS_READONLY, // nur lesender Zugriff NIL)=NO_ERROR) Then // keine EA Begin /************************************ * IOCtl-Funktion aufrufen * * Call to IOCtl ************************************/ If (DosDevIOCtl(hDevHandle, // Handle / Handle $82, // Kategorie / Category $60, // Funktion / Function NIL, // keine Parameterliste / No Parameterlist 0, // Laenge Parameterliste / Length of Parameterlist ulParams, // Groesse der gelieferten Parameterdaten // / Number of Bytes for Parameterdata rCDInfo, // Puffer fuer gelieferte Daten // / Buffer for returned Data SizeOf(rCDInfo), // Groesse des Datenpuffers // / Size of Databuffer ulData)=NO_ERROR) Then // Groesse der gelieferten Daten // / Number of Bytes for returned Data Begin ulCDRomCount := rCDInfo.usCDRomCount; ulFirstCDRomDiskNo := Succ(rCDInfo.usFirstUnitNo); End; DosClose(hDevHandle); End; End; (* For *) End; (* uQueryCDRom *) Function GetLocalDriveType( DriveNumber: longint ): TDriveType; var IOCtlParameters: Word; rc: APIRET; ParameterLength: longWord; DataLength: longword; DeviceData: TDeviceParameters; Fixed: boolean; FirstCDDrive: ULONG; NumCDDrives: ULONG; begin TWord( IOCtlParameters ).LoByte := 0; // BPB of physical Device TWord( IOCtlParameters ).HiByte := DriveNumber - 1; // drive number, zero base ParameterLength := SizeOf( IOCtlParameters ); // input length of parameters DataLength := 0; // input length of data (none) rc := DosDevIOCTL( HFILE(-1), // Open Device (not a file) IOCTL_DISK, // Category DSK_GETDEVICEPARAMS, // Function IOCtlParameters, // Parameters SizeOf( IOCtlParameters ), // (max) size of parameters ParameterLength, // parameters length DeviceData, // results SizeOf( DeviceData ), // (max) size of data block DataLength ); // data block length Fixed := ( DeviceData.Attributes and 1 ) > 0; // bit 0 indicates fixed (1) or removable (0) if not Fixed then begin result := dtRemovable; QueryCDRoms( FirstCDDrive, NumCDDrives ); if ( DriveNumber >= FirstCDDrive ) and ( DriveNumber < FirstCDDrive + NumCDDrives ) then result := dtCD; exit; end; result := dtHard; end; // Takes a one-based drive number Function GetDriveType( DriveNumber: longint ): TDriveType; var szDrive: CString; FSData: array[ 0..sizeof( FSQBuffer) + 3*_MAX_PATH ] of char; pBuffer: PFSQBUFFER2; FSDataLength: ULONG; rc: APIRET; begin assert( DriveNumber >= 1 ); assert( DriveNumber <= 26 ); if ( DriveNumber >=1 ) and ( DriveNumber <= 2 ) then begin if IsFloppyDrive( DriveNumber ) then begin result := dtFloppy; exit; end; result := dtNone; // don't let OS/2 try a fake B: drive exit; end; DosErrorAPI( FERR_DISABLEHARDERR ); szDrive := DriveNumberToDriveLetter( DriveNumber ) + ':'; FSDataLength := sizeof( FSData ); pBuffer := Addr( FSData ); rc := _DosQueryFSAttach( szDrive, 0, // ignored FSAIL_QUERYNAME, pBuffer, FSDataLength ); if rc = 0 then begin case pBuffer^.iType of FSAT_REMOTEDRV: result := dtNetwork; FSAT_LOCALDRV: // Figure out what kind of local drive it is... result := GetLocalDriveType( DriveNumber ); else begin // should never happen result := dtNone; exit; end; end; end else if rc = ERROR_NOT_READY then begin // No media? // Have a look for a local disk anyway. result := GetLocalDriveType( DriveNumber ); end else begin result := dtNone; end; DosErrorAPI( FERR_ENABLEHARDERR ); end; const DEVLEN = 8; CNLEN = 15; // Computer name length UNCLEN = (CNLEN+2); // UNC computer name length NNLEN = 12; // 8.3 Net name length (share name length) RMLEN = (UNCLEN+1+NNLEN); // Maximum remote name length type use_info_0 = record ui0_local: cstring[ DEVLEN ]; // note this is of size DEVLEN + 1 ui0_pad_1: char; ui0_remote: pchar; space: array[ 0..RMLEN ] of char; // remote path is written to somewhere in here end; use_info_1 = record ui0_local: cstring[ DEVLEN ]; ui0_pad_1: char; ui0_remote: pchar; // address of a buffer to hold remote path ui1_password: pchar; // ui1_status: USHORT; ui1_asg_type: SHORT; ui1_refcount: USHORT; ui1_usecount: USHORT; space: array[ 0..RMLEN ] of char; // remote path is written to somewhere in here end; TNet32UseGetInfo = Function( pszServer: pchar; pszUseName: pchar; // e.g. drive x: ulLevel: ULONG; pbBuffer: pointer; // pointer to output buffer ulBuffer: ULONG; // size of output in buffer Var pulTotalAvail: ULONG ) : word; CDecl; Var Net32UseGetInfo: TNet32UseGetInfo; hNetAPI32DLL: HMODULE; TriedLoading: boolean; // 129 Net32UseGetInfo Function GetNetworkDriveRemotePath( DriveNumber: longint ): string; var ErrorName: array[ 0..255 ] of char; dummy: cstring; rc: word; UseName: array[ 0..255 ] of char; UseInfo: use_info_0; pUseInfo: pointer; TotalBytesNeeded: ULONG; RemotePath: array[ 0..255 ] of char; Dummy2: array[ 0..4096 ] of char; // try to fix stack probs begin Result := ''; if not TriedLoading then begin TriedLoading := true; rc := DosLoadModule( ErrorName, sizeof( ErrorName ), 'NETAPI32', hNetAPI32DLL ); if rc = NO_ERROR then begin // NetAPI32.DLL loaded OK rc := DosQueryProcAddr( hNetAPI32DLL, 129, dummy, pointer( Net32UseGetInfo ) ); if rc <> 0 then Net32UseGetInfo := nil; end; end; if Assigned( Net32UseGetInfo ) then begin UseName[ 0 ] := DriveNumberToDriveLetter( DriveNumber ); UseName[ 1 ] := ':'; UseName[ 2 ] := #0; RemotePath[ 0 ] := #0; // UseInfo.ui0_remote := Addr( RemotePath ); pUseInfo := Addr( UseInfo ); rc := Net32UseGetInfo( nil, // server - always nil Addr( UseName ), 0, // info level 0 pUseInfo, sizeof( UseInfo ), TotalBytesNeeded ); if rc = 0 then Result := StrPas( UseInfo.ui0_remote ); end; end; Initialization End.