1. Technology

Your suggestion is on its way!

An email with a link to:

http://delphi.about.com/od/windowsshellapi/l/aa102003c.htm

was emailed to:

Thanks for sharing About.com with others!

Your first raw API Delphi program
Page 3: More complex example: System Info
 Win prizes by sharing code!
Do you have some Delphi code you want to share? Are you interested in winning a prize for your work?
Delphi Programming Quickies Contest
 More of this Feature
• Page 1: Your first Delphi application without the Forms unit
• Page 2: Message Boxes, Binary combine with the "OR" operator, and the PChar type.
 Join the Discussion
Post your views and comments to this chapter of the free "raw API programming" Delphi Course
Discuss!
 Related Resources
• A Guide to raw API programming.TOC

Once, that you know how to code a simple windowless Delphi application with no user interaction, and how to call an API message box, you are ready for your second application...

System Info Program
Now, let's get some user input. An easy way to get a small amount user input is with Message Boxes. In this System Info program a message box will ask about creating a folder and create a folder on the C:\ drive if the user picks the "YES" button. Another message box will ask about creating a "System Info.txt" file, and this "System Info.txt" file will have a lot of system information written to it using the methods of the first program. This application shows how to get information about the computer like the windows version, screen resolution, and the work area rectangle, so later when you do window creation you can change them for system values like screen resolution.

Getting info from the OS
The following program will show several ways to get character strings from Windows OS functions, see the GetWindowsDirectory(), GetCurrentDirectory() and GetComputerName() functions in the code below. When getting a PChar from windows, you will have to deal with making sure your variables that receive null-terminated strings have enough memory for this string. There will be more information about the PChar Variable type and API pointers, in the future chapters, Using PChar in API functions, and Pointers.
Comments have been added to the code to help you, but you must read the Win32 API Help for each of these functions to see the other options or info that is available from them. The following functions should be helpful to every Windows coder:

  • procedure GetLocalTime(var lpSystemTime: TSystemTime);
  • function GetWindowsDirectory(lpBuffer: PAnsiChar; uSize: Cardinal): Cardinal;
  • function GetSystemDirectory(lpBuffer: PChar; uSize: Cadinal): Cardinal;
  • function GetComputerName(lpBuffer: PChar; var nSize: Cardinal): Boolean;
  • function GetUserName(lpBuffer: PChar; var nSize: Cardinal): Boolean;
  • function GetSystemMetrics(nIndex: Integer): Integer;
  • function SystemParametersInfo(uiAction, uiParam: Cardinal; pvParam: Pointer; fWinIni: Cardinal): Boolean;
  • function GetVersionEx(var lpVersionInformation: TOSVersionInfo): Boolean;
  • function ExpandEnvironmentStrings(lpSrc: PChar; lpDst: PChar; nSize: Cardinal): Cardinal;
  • procedure GlobalMemoryStatus(var lpBuffer: TMemoryStatus);
  • function ShellExecute(hWnd: HWND; Operation, FileName, Parameters, Directory: PChar; ShowCmd: Integer): HINST;

    Compile and run the code below. The pictures show the program "results":

    A Message Box

    Program's output in NOtepad

    program Project1;
    {
    This Program gets System Info to a text file and 
    opens that file in the default Text edit program. 
    There is no GUI created and no message loop is used. 
    User interaction is done with 2 Message Boxes
    }
    
    uses
      Windows, ShellApi;
      {ShellApi is added to use ShellExecute}
    
    {$R *.RES}
    
    var
     WinDir: Array[0..4095] of Char;
     {WinDir: Array[0..65535] of Char;}
     {
     Getting PChar strings from the Windows OS requires
     the recieving var to have memory for that string.
     An array of Char does not change it's memory use like
     a PChar or String var. Using a Larger array of Char
     will make sure you have enough space for the string.
     I use Array[0..4095] alot, 4 Kb will be more than
     is needed most of the time, If you need it for a
     Multiline Edit then Array[0..65535] will cover the
     max size of it's text buffer
     }
    
     WindowsDir, SystemDir, CurrentDir :String;
     ComName, UserName: String;
     Transfer: PChar;
     SystemTime1: TSystemTime;
     ScreenWidth, ScreenHeight, WinBorder: Integer;
     CharSize: Cardinal;
     GotMouse, ShowSound: Boolean;
     GotVersion: Boolean = False;
     WorkRect: TRect;
     OSVersionInfo1: TOSVersionInfo;
    
    const
     TxtFile = 'C:\Info Folder\System Info.txt';
    
    function Int2Str(Number : Int64) : String;
    var Minus : Boolean;
    begin
     {
     SysUtils is not in the Uses clause so I can 
     not use IntToStr() so I have to 
     define an Int2Str function here
     }
       Result := '';
       if Number = 0 then
          Result := '0';
       Minus := Number < 0;
       if Minus then
          Number := -Number;
       while Number > 0 do
       begin
          Result := Char((Number mod 10) + Integer('0')) + Result;
          Number := Number div 10;
       end;
       if Minus then
          Result := '-' + Result;
    end;
    
    
    procedure MakeTextFile;
    var
      File1: TextFile;
      MemStatus: TMemoryStatus;
    begin
     AssignFile(File1, TxtFile);
     {$I-}
     Rewrite(File1);
     {$I+}
     if IOResult = 0 then
     begin
       WriteLn(File1,' the Time is '+Int2Str(SystemTime1.wHour)+':'
        +Int2Str(SystemTime1.wMinute));
       WriteLn(File1,'           This File is '+TxtFile);
       WriteLn(File1,'This Program is '+ParamStr(0));
       WriteLn(File1,'Windows Folder is '+WindowsDir);
       WriteLn(File1,'Windows System Folder is '+WinDir {SystemDir});
       {WinDir, Array of Char can be used as a String}
       WriteLn(File1,'Current Folder is '+CurrentDir);
       WriteLn(File1,'Computer Name is '+ComName);
       WriteLn(File1,'User Name is '+UserName);
    
       {ExpandEnvironmentStrings is used to get DOS environment info}
      if ExpandEnvironmentStrings('%PATH%',WinDir,512) <> 0 then
        WriteLn(File1,'Path is - '+WinDir);
      if ExpandEnvironmentStrings('%TMP%',WinDir,MAX_PATH) <> 0 then
        WriteLn(File1,'TMP is - '+WinDir);
    
      if GotMouse then
        WriteLn(File1,'Mouse is Present')
      else 
        WriteLn(File1,'No Mouse is Present');
      if ShowSound then
        WriteLn(File1,'Sounds are made Visible')
      else 
        WriteLn(File1,'Sounds are Not made visible');
      WriteLn(File1,'Screen Work Area Rectangle Top is '+Int2Str(WorkRect.Top)+
        ' Left is '+Int2Str(WorkRect.Left)+' Bottom is '+Int2Str(WorkRect.Bottom)
        +' Right is '+Int2Str(WorkRect.Right));
      WriteLn(File1,'Windows Sizing Border multiplier is  '+Int2Str(WinBorder));
      WriteLn(File1,'Screen Width is '+Int2Str(ScreenWidth)+' Screen Height is '
        +Int2Str(ScreenHeight));
    
      MemStatus.dwLength := SizeOf(MemStatus);
      {TMemoryStatus has a Size variable 
      so you have to intialize it}
      GlobalMemoryStatus(MemStatus);
      {GlobalMemoryStatus gets memory and page file info}
      WriteLn(File1,'Tolal System Memory is '+Int2Str(MemStatus.dwTotalPhys)
        +' bytes -  Total Page File is '+Int2Str(MemStatus.dwTotalPageFile)
        +' Memory Load is '+Int2Str(MemStatus.dwMemoryLoad)+'%');
    
      if GotVersion then
      begin
        WriteLn(File1,'Windows Major Version is '
        +Int2Str(OSVersionInfo1.dwMajorVersion)
        +' Minor Version is '+Int2Str(OSVersionInfo1.dwMinorVersion)
        +' CSDversion is'+OSVersionInfo1.szCSDVersion);
        if OSVersionInfo1.dwPlatformId = VER_PLATFORM_WIN32_NT then
        WriteLn(File1,'This is a windows NT system');
      end;
     end;
    {$I-}
    CloseFile(File1);
    {$I+}
    if IOResult = 0 then
     ShellExecute(0, 'open', TxtFile, nil, nil, SW_SHOWNORMAL);
     {
     ShellExecute is included here because it is a very useful function.
     Here it displays the "System Info.txt" so I do not have to make a GUI
     with a Multiline Edit control to show it. It can also be set to do the same
     with NotePad.exe instead of the default, like this
     ShellExecute(0, 'open', 'Notepad.exe', TxtFile, nil, SW_SHOWNORMAL);
     }
    end;
    
    
    begin  // MAIN program begin
     GetLocalTime(SystemTime1);
     {GetLocalTime will find out the Date and Time}
    
     {when geting a PChar from windows OS, you will have to deal
     with getting and setting the amount of memory needed
     for the PChar, Array of Char, or String. The following
     examples will show different methods of getting strings}
    
     GetWindowsDirectory(WinDir, MAX_PATH);
     {WinDir is a Fixed length array of Char, the array can be
     larger than the amount of charaters needed, but not smaller.
     See the various array lengths in the var clause above.
     MAX_PATH is a const used for the Maximum number of charaters
     allowed in a path string}
    
     WindowsDir := String(WinDir);
    
     GetSystemDirectory(WinDir,GetSystemDirectory(WinDir,1)+1);
     {the result of GetSystemDirectory(WinDir,1) is the length of
     the PChar string for the System Directory, add 1 for the
     null charater at the end of a PChar string}
     
     SystemDir := String(WinDir);
    
     SetLength(CurrentDir,GetCurrentDirectory(1,nil));
     {You can use a pascal String to get the PChar string if you
     SeLength( ) to the amount of characters needed and then
     typecast the String to PChar}
     
     GetCurrentDirectory(Length(CurrentDir)+1,PChar(CurrentDir));
    
     CharSize := MAX_COMPUTERNAME_LENGTH + 1;
     {the computerName has a max length, given in
     MAX_COMPUTERNAME_LENGTH, so you can use this to set the memory
     needed for the PChar string, it's OK if it's more than is needed}
    
     GetMem(Transfer, CharSize);
     {allocate memory to the Transfer variable
     Always use FreeMem( ) after a GetMem}
    
     GetComputerName(Transfer, CharSize);
     ComName := String(Transfer);
     FreeMem(Transfer);
    
     GetUserName(nil,CharSize);
     {GetUserName(nil,CharSize) gets the length of the
     UserName string into the CharSize var}
     
     SetLength(UserName,CharSize);
     GetUserName(@UserName[1],CharSize);
     {the address of the first Charater of the String
     @UserName[1] is used instead of a PChar typecast}
    
    
     ScreenWidth := GetSystemMetrics(SM_CXSCREEN);
     {GetSystemMetrics( ) can get you alot of useful info
     see API help for the List of parameters}
     
     ScreenHeight := GetSystemMetrics(SM_CYSCREEN);
     GotMouse := Boolean(GetSystemMetrics(SM_MOUSEPRESENT));
    
     SystemParametersInfo(SPI_GETSHOWSOUNDS,0,@ShowSound,0);
     {SystemParametersInfo( ) can get and set some System settings}
     SystemParametersInfo(SPI_GETWORKAREA,0,@WorkRect,0);
     SystemParametersInfo(SPI_GETBORDER,0,@WinBorder,0);
    
     OSVersionInfo1.dwOSVersionInfoSize := SizeOf(OSVersionInfo1);
     {many Windows Record structures have a Size variable which
     must be filled BEFORE the address of the Record is passed
     to Windows}
     
     if GetVersionEx(OSVersionInfo1) then
       GotVersion := True;
     {GetVersionEx will provide you with Windows version info
     for the computer that is running this program
     Notice that the variable OSVersionInfo1 does not need a @, like
     the @WorkRect and @WinBorder used in the SystemParametersInfo,
     there is only one Type of variable allowed in GetVersionEx,
     but in SystemParametersInfo the variable can be a TRect, a Boolean
     an Integer or other Type so a Pointer Type is used}
    
    
     {All user input is from Message Boxs, and the Result 
     (ID_YES) will determine what is done look in the API 
     help for more MessageBox button and icon options}
     
     if MessageBox(0,PChar('Today is '+Int2Str(SystemTime1.wMonth)+'/'
         +Int2Str(SystemTime1.wDay)+'/'
         +Int2Str(SystemTime1.wYear)+#10
         +'Do you want to create the Folder "Info Folder"?'
         +#10+'To place a System Info.txt file with System Information'),
         'Make Folder ?',  MB_YESNO or MB_ICONQUESTION or MB_DEFBUTTON2 
         or MB_SETFOREGROUND) = ID_YES then
      begin
        if CreateDirectory('C:\Info Folder',nil) then
        begin
          {in the MessageBox function there is the hWnd, 
          handle of owner window, which is the window handle to return focus 
          to, after the message box. You would set your Main Window handle 
          here, but there is no Main Window in this App so I set it to 0 instead.}
    
        if MessageBox(0,'Do you want to create the File "System Info.txt"?',
                'Make File ?', MB_OKCANCEL or MB_ICONQUESTION) = ID_OK then
          MakeTextFile;
        end 
        else
        MessageBox(0,'Could not create a Folder',
        ' ERROR, folder was not created', MB_OK or MB_ICONERROR)
      end;
    end.
    

    The message boxes show up and get some user input, but there are no calls for "CreateWindow" and there is no Message loop in this app. Windows system creates the message boxes and handles the messages, also they are modal to your app, so execution stops while the message boxes are shown. Notice that the text file creation has been moved to a procedure above the "begin" for the main program. SysUtils is not in the uses clause so the function Int2Str has been added. The SysUtils unit will add about 22 Kb to your App's file size because it adds many Exception message strings and other initialization data and functions.

    There were several API functions which returned a pointer to a null-terminated string, like GetUserName(@UserName[1],CharSize), and you were shown several ways to get a variable to have the memory needed to receive the string. The windows API functions use pointers to send and receive information. When working with the "normal" Delphi VCL Forms units you may not be aware of the type checking and pointers used by the compiler.

    Since API functions use pointers like the PChar so much, you might look at the Delphi Help for "Overview of pointers", "Using pointers, arrays, and string constants" and "Pointer types". Other Delphi Help topics you might look at are "Working with null-terminated strings" and "Mixing Pascal strings and null-terminated strings". There will also be some information about PChar and Pointers the following chapters.

    To the next chapter: A Guide to raw API programming
    If you need any kind of help at this point, please post to the Delphi Programming Forum where all the questions are answered and beginners are treated as experts.

    First page > Your first Delphi application without the Forms unit > Page 1, 2, 3

    A guide to developing Delphi programs in raw Windows API: Next Chapter >>
    >> Creating an API GUI Windows program with message loop

  • ©2014 About.com. All rights reserved.