INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Log In

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips Forums!
  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!

*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.

Jobs

How To

Turbo Pascal Compatibility: DOS by Glenn9999
Posted: 24 Nov 08

A DOS unit for Delphi, which should give some TP compatibility, which hopefully all should work, save the issues.  Again if anything is not implemented, either it wasn't a good idea, or I didn't know how (I included empty function prototypes and the relevant data types).

The big standing issue: NTFS drives seem to not store short file names for everything, which means you can not get compatible SFNs for all files in the system.  In that case, you will get LFNs returned, which may or may not be what your program is ready for (I defined the SearchRec record to have a String type instead of String[12] like the original).  Double-check your program if you use FindFirst/FindNext to see if this won't be a problem.

Hope this helps someone, if not by its direct use, to see how to do something from the DOS unit in Delphi/Win32.

CODE

unit dos;
  { unit for DOS functions in Delphi - coded by Glenn9999 under Delphi 3.  Used helps from
    the Internet for Turbo Pascal references and the Free Pascal sources. }
  interface
    uses sysutils, windows;
    const
      { file attribute constants }
      ReadOnly = faReadOnly;
      Hidden = faHidden;
      SysFile = faSysFile;
      VolumeID =  faVolumeID;
      Directory = faDirectory;
      Archive = faArchive;
      AnyFile = faAnyFile;
    type
      Int64 = Comp;      { comment out if you have Int64 type }

      { data types and records that were defined in the DOS unit }
      PathStr = String[79];
      DirStr = String[67];
      NameStr = String[8];
      ExtStr = String[4];
      ComStr = string[128];

      { used for PackTime and UnPackTime }
      DateTime = record
         Year, Month, Day, Hour, Min, Sec: Word;
      end;

      { searchrec type.  Changed in certain respects to ease functionality
        in Windows, since the exact record format shouldn't matter too much
        as long the record is not accessed directly, as opposed to access by
        the record type definition.  Also, NTFS file systems do not necessarily
        store and return short file names, so you may get LFNs out of FindFirst
        if run against such systems.
         - check your TP program before you try using FindFirst }
      SearchRec = record
         Attr: Byte;                 { attribute of file returned }
         Time: Longint;              { packed timestamp }
         Size: Longint;              { size of file }
         Name: string;               { name of file (short name if available }
         { variables following are necessary for continued functionality
           of findfirst/FindNext }
         FindHandle: THandle;        { saved search handle }
         ExcludeAttr: Integer;       { saved attribute parm }
         Path: PathStr;              { saved path parm }
      end;

      Registers = record { for the do-nothing calls }
        case Integer of
           0: (AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags: Word);
           1: (AL, AH, BL, BH, CL, CH, DL, DH: Byte);
        end;

    var
      doserror: integer;
      DosExitCode: DWord;

    { these two functions are not in the DOS unit, but are here if needed,
      otherwise the regular functions as defined in the DOS unit will only
      report a maximum of 2GB. }
    function  DiskSizeEx(Drive: Byte): Int64;  // addl functions added
    function  DiskFreeEx(Drive: Byte): Int64;  // these work for > 2GB

    function  fexpand(filename: string): string;
    procedure GetFAttr(var f; var attr: word);
    procedure SetFAttr(var F; Attr: Word);
    procedure FSplit(Path: PathStr; var Dir: DirStr;
                       var Name: NameStr; var Ext: ExtStr);
    procedure PackTime(var T: DateTime; var P: Longint);
    procedure UnpackTime(P: Longint; var T: DateTime);
    procedure FindFirst(Path: PathStr; Attr: Word; var F: SearchRec);
    procedure FindNext(var F: SearchRec);
    function  DiskFree(Drive: Byte): Longint;
    function  DiskSize(Drive: Byte): Longint;
    function  DosVersion: Word;
    function  FSearch(Path: PathStr; DirList: String): PathStr;
    procedure GetFTime(var F; var Time: Longint);
    procedure SetFTime(var F; Time: Longint);
    procedure GetDate(var Year,Month,Day, DayOfWeek: Word);
    procedure GetTime(var Hour,Minute,Second,Sec100: Word);
    procedure SetDate(Year,Month,Day: Word);
    procedure SetTime(Hour,Minute,Second,Sec100: Word);
    function  EnvCount: Integer;
    function  EnvStr(Index: Integer): String;
    function  GetEnv(EnvVar: String): String;
    procedure Exec(Path: PathStr; ComLine: ComStr);

    { do nothing functions - generally incompatible for Windows, included
      here both for documentation and to not break compilation of programs
      that might not otherwise work. }
    procedure GetVerify(var Verify: Boolean);
    procedure Intr(IntNo: Byte; var Regs: Registers);
    procedure Keep(ExitCode: Word);
    procedure MsDos(var Regs: Registers);
    procedure GetCBreak(var Break: Boolean);
    procedure SetCBreak(Break: Boolean);
    procedure GetIntVec(IntNo: Byte; var Vector: Pointer);
    procedure SetIntVec(IntNo: Byte; Vector: Pointer);
    procedure SetVerify(Verify: Boolean);
    procedure SwapVectors;

  { crossover DOS unit, allows some functions to go through sysutils unit }

  implementation
    uses messages;

  { ***********************************************************************
    Service functions for the other functions listed in the interface unit
   *********************************************************************** }
     function GetShortName(sLongName: string): string;
       begin
         Result := sLongName;
       end;

     function getvolname(input: string): string;
     { returns Volume Name of the drive that is inputted
       adapted from http://www.delphicorner.f9.co.uk/articles/wapi2.htm
       MAX_PATH is a dword defined to be 260 }
       var
         nVNameSer: PDWORD;
         pVolName: PChar;
         FSSysFlags, maxCmpLen: DWord;
         pFSBuf: PChar;
       begin
         GetMem(pVolName, MAX_PATH);
         GetMem(pFSBuf, MAX_PATH);
         GetMem(nVNameSer, MAX_PATH);
         GetVolumeInformation(PChar(input), pVolName, MAX_PATH,
                         nVNameSer, maxCmpLen, FSSysFlags, pFSBuf,
                         MAX_PATH);
         GetVolName := String(pVolName);
         FreeMem(pVolName, MAX_PATH);
         FreeMem(pFSBuf, MAX_PATH);
         FreeMem(nVNameSer, MAX_PATH);
       end;

     function DiskFreeEx(Drive: Byte): Int64;
       { redone DiskFree function which reports amount free on a disk > 2GB
         - original from Delphi 3 sources, changed to increase size of
         variable returned.  Can be called if real size is necessary. }
       var
         RootPath: array[0..4] of Char;
         RootPtr: PChar;
         SectorsPerCluster, BytesPerSector, FreeClusters, TotalClusters: Integer;
         OutInt64: Int64;
       begin
         RootPtr := nil;
         if Drive > 0 then
           begin
             StrCopy(RootPath, 'A:\');
             RootPath[0] := Char(Drive + $40);
             RootPtr := RootPath;
           end;
         if GetDiskFreeSpace(RootPtr, SectorsPerCluster, BytesPerSector,
           FreeClusters, TotalClusters) then
           begin
             OutInt64 := SectorsPerCluster;
             Result := OutInt64 * BytesPerSector * FreeClusters;
           end
         else
           Result := -1;
       end;

     function DiskSizeEx(Drive: Byte): Int64;
       { redone DiskSize function which reports size of disk > 2GB
         - original from Delphi 3 sources, changed to increase size of
         variable returned.  Can be called if real size is necessary. }
       var
         RootPath: array[0..4] of Char;
         RootPtr: PChar;
         SectorsPerCluster, BytesPerSector, FreeClusters, TotalClusters: Integer;
         OutInt64: Int64;
       begin
         RootPtr := nil;
         if Drive > 0 then
           begin
             StrCopy(RootPath, 'A:\');
             RootPath[0] := Char(Drive + $40);
             RootPtr := RootPath;
           end;
         if GetDiskFreeSpace(RootPtr, SectorsPerCluster, BytesPerSector,
           FreeClusters, TotalClusters) then
           begin
             OutInt64 := SectorsPerCluster;
             Result := OutInt64 * BytesPerSector * TotalClusters;
           end
         else
           Result := -1;
       end;

     function ProcessAMsg: Boolean;
       { service function for ProcessMessage }
       var
         Msg: TMsg;
         msg_proc: boolean;
       begin
         msg_proc := False;
         if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
           begin
             msg_proc := True;
             if Msg.Message <> WM_QUIT then
               begin
                 TranslateMessage(Msg);
                 DispatchMessage(Msg);
               end;
           end;
         ProcessAMsg := msg_proc;
       end;

     procedure ProcessMessage;
      { this should be an equivalent to TApplication.ProcessMessages }
       begin
         while ProcessAMsg do;
       end;

     function os_is_nt: Boolean;
       { returns whether the OS is NT based or not }
       var
         osvinfo: TOsVersionInfo;
       begin
        { get windows version }
         osvinfo.dwOSVersionInfoSize := Sizeof(osvinfo);
         GetVersionEx(osvinfo);
         os_is_nt := (osvinfo.dwPlatformId = VER_PLATFORM_WIN32_NT);
       end;

     function NTSetPrivilege(sMachine, sPrivilege: string;
                             bEnabled: Boolean): Boolean;
     { set privilege on remote computer.  Define sMachine to be null if you want
       local machine.   Modified from something on SwissCenter. }
       var
         hToken: THandle;
         TokenPriv: TTokenPrivileges;
         PrevTokenPriv: TTokenPrivileges;
         ReturnLength: DWord;
       begin
      // Only for Windows NT/2000/XP and later.
         if not (os_is_nt) then
           begin
             Result := true;
             Exit;
           end;

      // obtain the processes token
         if OpenProcessToken(GetCurrentProcess(),
            TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then
           begin
             try
             // Get the locally unique identifier (LUID) .
              if LookupPrivilegeValue(PChar(sMachine), PChar(sPrivilege),
                 TokenPriv.Privileges[0].Luid) then
                begin
                  TokenPriv.PrivilegeCount := 1; // one privilege to set

                  case bEnabled of
                    True: TokenPriv.Privileges[0].Attributes  := SE_PRIVILEGE_ENABLED;
                    False: TokenPriv.Privileges[0].Attributes := 0;
                  end;

                  ReturnLength := 0; // replaces a var parameter
                  PrevTokenPriv := TokenPriv;

                // enable or disable the privilege

                  AdjustTokenPrivileges(hToken, False, TokenPriv,
                           SizeOf(PrevTokenPriv), PrevTokenPriv, ReturnLength);
                end;
             finally
               CloseHandle(hToken);
             end;
           end;
       // test the return value of AdjustTokenPrivileges.
         Result := GetLastError = ERROR_SUCCESS;
         if not Result then
           raise Exception.Create(SysErrorMessage(GetLastError));
       end;

{ ***********************************************************************
    Dos Compatibility Functions listed after this point
   *********************************************************************** }

     function fexpand(filename: string): string;
       { returns the fully qualified path for a file }
       var
         test: string;
         test_len: integer;
       begin
         // we need a string for ExpandFileName if there is not one
         if filename = '' then filename := '*.*';
         test := GetShortName(ExpandFileName(Filename));
         { check for trailing . - we want the path as TP returns,
           not the base directory identifier as well }
         test_len := length(test);
         if test[test_len] = '.' then
           begin
             test := copy(test, 1, test_len-1);
             dec(test_len, 1);
           end;
         // ExpandFileName returns *.* on the path.  The TP FExpand did not.
         if pos('\*.*', test) > 0 then
           test := copy(test, 1, test_len-4);
    //   if test[test_len] = '\' then
    //      test := test + '*.*';
         fexpand := test;
       end;

     procedure GetFAttr(var f; var attr: word);
       { gets the file attribute for a file variable }
       begin
         doserror := 0;
         attr := FileGetAttr(TFileRec(f).Name);
         if attr = -1 then
           doserror := GetLastError;
       end;

     procedure SetFAttr(var F; Attr: Word);
       { sets the file attribute for a file variable }
       begin
         doserror := 0;
         doserror := FileSetAttr(TFileRec(f).Name, attr);
       end;

     procedure FSplit(Path: PathStr; var Dir: DirStr;
                  var Name: NameStr; var Ext: ExtStr);
       { splits a full file path into the directory, name, and extension }
       var
         filename: string;
       begin
         Path := GetShortName(Path);
         Dir := ExtractFilePath(Path);
         filename := ExtractFileName(Path);
         Name := copy(filename, 1, pos('.', filename)-1);
         Ext := ExtractFileExt(Path);
       end;

     procedure PackTime(var T: DateTime; var P: Longint);
       { datetime type to packed DOS Timestamp }
       var
         MSec: Word;
         DateTime: TDateTime;
       begin
         MSec := 0;
         With T Do
           begin
             DateTime := EncodeDate(Year, Month, Day) +
                         EncodeTime(Hour, Min, Sec, MSec);
           end;
         P := DateTimeToFileDate(DateTime);
       end;

     procedure UnpackTime(P: Longint; var T: DateTime);
       { Packed DOS Timestamp to datetime type }
       var
         MSec: Word;
         MyDateTime: TDateTime;
       begin
         MyDateTime := FileDateToDateTime(p);
         With T Do
           begin
             DecodeDate(MyDateTime, Year, Month, Day);
             DecodeTime(MyDateTime, Hour, Min, Sec, MSec);
           end;
       end;

     procedure move_sr(F: TSearchRec; var O: SearchRec);
       { moves information from TSearchRec to SearchRec }
       begin
         if F.FindData.cAlternateFileName <> '' then
           O.Name := F.FindData.cAlternateFileName
         else
           O.Name := F.Name;
         O.Size := F.Size;
         O.Attr := F.Attr;
         O.Time := F.Time;
         O.FindHandle := F.FindHandle;
         O.excludeattr := F.ExcludeAttr;
       end;

     procedure FindFirst(Path: PathStr; Attr: Word; var F: SearchRec);
       { revised FindFirst.  TP returned volume attribute, so we must
         handle that first, and return it first }
       var
         tempsr: TSearchRec;
         return_volid: boolean;
         expf: string;
       begin
         DosError := 0;
         { determine whether to return VolumeID }
         return_volid := false;
         if (Attr and VolumeID) = VolumeID then
           begin
             expf := ExpandFileName(Path);
             if Copy(expf, 2, 5) = ':\*.*' then
               return_volid := true;
           end;
         { handle volume ID if it is called for }
         if return_volid then
           begin
             F.name := GetVolName(expf[1] + ':\');
             if F.name <> '' then   { if there is a volumeID to return }
               begin
                 if Length(F.Name) > 8 then  // format in the way DOS does
                   F.Name := Copy(F.name, 1, 8) + '.' + Copy(F.Name, 9, 20);
                 F.ExcludeAttr := Attr;                   { save attr }
                 F.Attr := VolumeID;                      { indicate VolumeID attr }
                 F.FindHandle := INVALID_HANDLE_VALUE;    { have not opened FindFirst }
                 F.Path := Path;                          { store path }
               end
             else
               if Attr = VolumeID then  { if we are supposed to only return VolumeID}
                 DosError := 18;
           end
         else
           begin   { not supposed to return VolumeID }
             DosError := SysUtils.FindFirst(Path, Attr, tempsr);
             Move_SR(tempsr, F);
           end;
       end;

     procedure FindNext(var F: SearchRec);
       { revised FindNext }
       var
         tempsr: TSearchRec;
       begin
         { check if FindFirst actually called - i.e. first call was for
           VolumeID }
         if F.FindHandle = INVALID_HANDLE_VALUE then
           begin
             DosError := SysUtils.FindFirst(F.Path, F.ExcludeAttr, tempsr);
             F.Path := '';
             Move_SR(tempsr, F);
           end
         else
           begin
             tempsr.FindHandle := F.FindHandle;
             tempsr.ExcludeAttr := F.ExcludeAttr;
             DosError := SysUtils.FindNext(tempsr);
             Move_SR(tempsr, F);
             if DosError <> 0 then
               SysUtils.FindClose(tempsr);
           end;
       end;

     function DiskFree(Drive: Byte): Longint;
       { TP DOS unit compatible function.  Calls the working Diskfree function
         and then returns a maximum of 2GB. }
       var
         DF: Int64;
       begin
         DF := DiskFreeEx(Drive);
         if DF > MAXLONGINT then
           Result := MAXLONGINT
         else
           Result := Trunc(DF);
       end;

     function DiskSize(Drive: Byte): Longint;
       { TP DOS unit compatible function.  Calls the working DiskSize function
         and then returns a maximum of 2GB. }
       var
         DS: Int64;
       begin
         DS := DiskSizeEx(Drive);
         if DS > MAXLONGINT then
           Result := MAXLONGINT
         else
           Result := Trunc(DS);
       end;

     function DosVersion: Word;
       { uses Win32 version in same format as expected in DOS unit,
         lo byte = major hi byte = minor.  Values come from sysutils unit }
       begin
         DosVersion := (Win32MinorVersion shl 8) + Win32MajorVersion;
       end;

     function  FSearch(Path: PathStr; DirList: String): PathStr;
       { searches for the Path in the Directory List given }
       begin
         FSearch := GetShortName(FileSearch(Path, DirList));
       end;

     procedure GetFTime(var F; var Time: Longint);
       { return file time.  Takes file id and packed time }
       begin
         doserror := 0;
         Time := FileGetDate(TFileRec(F).Handle);
         if Time = -1 then
           doserror := GetLastError;
       end;

     procedure SetFTime(var F; Time: Longint);
       { Set file time.  Takes file id and packed time }
       begin
         doserror := 0;
         FileSetDate(TFileRec(f).Handle, time);
         doserror := GetLastError;
       end;

     function EnvCount: Integer;
       { returns the number of environment strings.  Is resource-intensive,
         be careful in calling this function }
       var
         Env1, Env2: PChar;
         envi_count: integer;
       begin
         envi_count := 0;
         Env1 := GetEnvironmentStrings;
         Env2 := Env1;
         if Env2 <> nil then
           repeat
             inc(Env2, StrLen(Env2) + 1);
             inc(envi_count);
           until Env2^ = #0;
         FreeEnvironmentStrings(Env1);
         EnvCount := envi_count;
       end;

     function EnvStr(Index: Integer): String;
       { returns an environment string with specific index.
         Is resource-intensive, be careful in calling this function }
       var
         Env1, Env2: PChar;
         envi_count: integer;
       begin
         envi_count := 1;
         Env1 := GetEnvironmentStrings;
         Env2 := Env1;
         if Env2 <> nil then
            while (envi_count <> index) and (Env2^ <> #0) do
              begin
                inc(Env2, StrLen(Env2) + 1);
                inc(envi_count);
              end;
         EnvStr := StrPas(Env2);
         FreeEnvironmentStrings(Env1);
       end;

     function GetEnv(EnvVar: String): String;
       { gets an environment string with a specific name }
       var
         PathName: PChar;
         Buffer: array[0..255] of char;
       begin
         PathName := PChar(EnvVar);
         GetEnvironmentVariable(PathName, @Buffer, Sizeof(Buffer));
         GetEnv := String(Buffer);
       end;

     procedure Exec(Path: PathStr; ComLine: ComStr);
       { executes a program, and waits for completion }
       var
         StartInfo  : TStartupInfo;
         ProcInfo   : TProcessInformation;
         CreateOK   : Boolean;
         ErrorCode  : DWord;
         AppDone    : DWord;
       begin
         ErrorCode := 0;
         FillChar(StartInfo,SizeOf(TStartupInfo),#0);
         FillChar(ProcInfo,SizeOf(TProcessInformation),#0);
         StartInfo.cb := SizeOf(TStartupInfo);

         CreateOK := Windows.CreateProcess(nil,
                PChar(String(Path) + ' ' + String(ComLine)),
                nil, nil, False,
                CREATE_NEW_PROCESS_GROUP+IDLE_PRIORITY_CLASS+SYNCHRONIZE,
                nil, nil, StartInfo, ProcInfo);
         WaitForInputIdle(ProcInfo.hProcess, INFINITE);
         if CreateOK then
           repeat
             AppDone := WaitForSingleObject(ProcInfo.hProcess, 10);
             ProcessMessage;
           until AppDone <> WAIT_TIMEOUT;
         CloseHandle(ProcInfo.hProcess);
         CloseHandle(ProcInfo.hThread);
         GetExitCodeProcess(ProcInfo.hProcess, ErrorCode);
         DosExitCode := GetLastError;
       end;

    procedure GetDate(var Year,Month,Day,DayOfWeek: Word);
      { returns the system date }
      var
        MySystemTime: TSystemTime;
      begin
        GetLocalTime(MySystemTime);
        with MySystemTime do
          begin
            Year := wYear;
            Month := wMonth;
            Day := wDay;
            DayOfWeek := wDayOfWeek;
          end;
      end;

    procedure GetTime(var Hour,Minute,Second,Sec100: Word);
      { returns the system time }
      var
        MySystemTime: TSystemTime;
      begin
        GetLocalTime(MySystemTime);
        with MySystemTime do
          begin
            Hour := wHour;
            Minute := wMinute;
            Second := wSecond;
            Sec100 := wMilliseconds;
          end;
      end;

    procedure SetDate(Year,Month,Day: Word);
      { sets the system date }
      const
        SE_SYSTEMTIME_NAME = 'SeSystemtimePrivilege';
      var
        MySystemTime: TSystemTime;
      begin
        GetLocalTime(MySystemTime);
        NTSetPrivilege('',SE_SYSTEMTIME_NAME, true);
        with mysystemtime do
          begin
            wYear := Year;
            wMonth := Month;
            wDay := Day;
          end;
        SetLocalTime(MySystemTime);
        NTSetPrivilege('',SE_SYSTEMTIME_NAME, false);
      end;

    procedure SetTime(Hour,Minute,Second,Sec100: Word);
      { sets the system time }
      const
        SE_SYSTEMTIME_NAME = 'SeSystemtimePrivilege';
      var
        MySystemTime: TSystemTime;
      begin
        GetLocalTime(MySystemTime);
        NTSetPrivilege('',SE_SYSTEMTIME_NAME, true);
        with mysystemtime do
          begin
            wHour := Hour;
            wMinute := Minute;
            wSecond := Second;
            wMilliseconds := Sec100;
          end;
        SetLocalTime(MySystemTime);
        NTSetPrivilege('', SE_SYSTEMTIME_NAME, false);
      end;

{ ***********************************************************************
  do nothing functions follow.  These are things that were in the DOS unit,
  but do not have any applicability to Windows (or were not implemented yet
  in this unit for some reason) -  they are included more for
  compatibility than functionality
  *********************************************************************** }

    procedure GetVerify(var Verify: Boolean);
      { do nothing function}
      begin
      end;

    procedure Intr(IntNo: Byte; var Regs: Registers);
      { do nothing function}
      begin
      end;

    procedure Keep(ExitCode: Word);
      { do nothing function}
      begin
      end;

    procedure MsDos(var Regs: Registers);
      { do nothing function}
      begin
      end;

    procedure GetCBreak(var Break: Boolean);
      { do nothing function}
      begin
      end;

    procedure SetCBreak(Break: Boolean);
      { do nothing function}
      begin
      end;

    procedure GetIntVec(IntNo: Byte; var Vector: Pointer);
      { do nothing function}
      begin
      end;

    procedure SetIntVec(IntNo: Byte; Vector: Pointer);
      { do nothing function}
      begin
      end;

    procedure SetVerify(Verify: Boolean);
      { do nothing function}
      begin
      end;

    procedure SwapVectors;
      { do nothing function}
      begin
      end;

  end.

Back to Embarcadero: Delphi FAQ Index
Back to Embarcadero: Delphi Forum

My Archive

Resources

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close