WinApi Miscellaneous

From TeamDeveloperWiki

Jump to: navigation, search

This page covers miscellaneous Windows API tips & tricks.

Contents


Image:Pointer.pngWhere to get WinAPI declarations, constant values and structure layouts

The common place to get info for WinAPI is Microsoft's MSDN library MSDN library. There you will find all info concerning WinAPI programming.

What is missing there are the actual values of WinAPI constants.
You might find them already defined in the WinApi32.apl which can be downloaded from the Wiki main page.

You can also use these very handy tools, named ApiViewer and ApiGuide.

Image:ApiViewer.jpg

With this tools you can search all WinApi constants.
You can download it here : ApiViewer and here ApiGuide.

The constants found there are in VB or C syntax. You will have to define the constants in TD by changing &H to 0x.

For instance : &H202 -> 0x202


Image:Pointer.pngHow to detect memory or GDI leaks

If you use Windows API functions to draw custom stuff on your GUI's for example it is very important to release or destroy created handles.
Without this, the resources can diminish up until severe (painting) errors occur.
An easy way to detect those memory leaks is using tools to monitor the amount of opened handles.

Great recommendations (free ones) are:
Process Explorer.
GDIView.


Image:Pointer.pngHow to determine a window exists

Having a window handle, use the WinApi function IsWindow to determine if it is present in the system and corresponds to a valid window.

This is the declaration

Library name: USER32.DLL
   Function: IsWindow
      Export Ordinal: 0
      Returns
         Boolean: BOOL
      Parameters
         Window Handle: HWND
   hWndMain = SalCreateWindow( frmMain, hWndNULL )
   ...
   If IsWindow( hWndMain )
      ! Window exists
   Else
      ! Does not exist 

Beware : this function checks if the window handle is valid, but it does not mean the window handle is the one you have created earlier. Window handles are reused in Windows, so the window handle could correspond to another window.


Image:Pointer.pngHow to fill strings with a specific character

To get high performance while filling a string with a specific character, use RtlFillMemory.
With large data, this function is significantly faster than any SAL implementation.

This is the declaration

Library name: KERNEL32.DLL
   Function: RtlFillMemory
      Export Ordinal: 0
      Returns
      Parameters
         Receive String: LPVOID
         Number: DWORD
         Number: BYTE

The next sample creates a very large string variable and is completely filled with spaces

   ! Create a string buffer size of 512K
   Call SalStrSetBufferLength( sData, 1024 * 512 )

   ! Now fill the string with spaces (ASCII value 32)
   Call RtlFillMemory( sData, 1024 * 512, 32 )


Image:Pointer.pngHow to disable Windows themes on your application

Applications build with Team Developer (prior to version 5.1) are not 100% compatible with the Windows themes (available from Win XP).
Some GUI objects are painted using the selected theme and some are not. This could look ugly and not very consistent.
In some cases it could be better to force the application to disable themes all the way.
Beware : only the special painting of objects are disabled, not the sizes and colors of the GUI objects.

First, use the following declaration of SetThemeAppProperties

Library name: UxTheme.dll
   Function: SetThemeAppProperties
      Export Ordinal: 0
      Returns
      Parameters
         Number: DWORD

Now call the function at the start of the application

   Application Actions
      On SAM_AppStartup
         Call SetThemeAppProperties( 0 )

This will disable all custom theme painting by Windows within the application.

Beware that this approach disables an application from running on OS < WinXP due to the absence of uxtheme.dll on these OS. Perhaps it is an option to have a faked uxtheme.dll to be installed with the application.

Another way is to determine which Windows OS the application is running on and based on the OS call the API function or not.


Image:Pointer.pngHow to format a number of bytes to a formatted string

The WinApi function StrFormatByteSize can be used to display
a formatted string presenting a number of bytes as bytes, kilobytes, megabytes and gigabytes.

       532 -> 532 bytes
      1340 -> 1.3KB
     23506 -> 23.5KB
   2400016 -> 2.4MB
2400000000 -> 2.4GB

This is the declaration

Library name: SHLSWAPI.DLL
   Function: StrFormatByteSizeA
      Export Ordinal: 0
      Returns
         String: LPSTR
      Parameters
         Number: DWORD
         Receive String: LPSTR
         Number: UINT

Here a sample to display a number of bytes

   Call SalStrSetBufferLength( sBuf, 1024 )
   Call StrFormatByteSizeA( 23506, sBuf, 1024 )
   !
   ! sBuf = "22,9 kB"

Here you can download a sample:


Image:Pointer.pngHow to format a number of milliseconds to a formatted string

The WinApi function StrFromTimeIntervalA can be used to display
a formatted string presenting a number of milliseconds as seconds, minutes and hours.
It will be formatted against the current locale. For instance English will be "hour", Dutch will be "uur".

   34000 -> 34 sec
   74000 -> 1 min 14 sec

This is the declaration

Library name: SHLSWAPI.DLL
   Function: StrFromTimeIntervalA
      Export Ordinal: 0
      Returns
         Number: INT
      Parameters
         Receive String: LPSTR
         Number: UINT
         Number: DWORD
         Number: INT

Here a sample to display a number of milliseconds

   Call SalStrSetBufferLength( sBuf, 1024 )
   Call StrFromTimeIntervalA( sBuf, 1024, 74000, 3 )

   !
   ! sBuf = "1 min 14 sec"

Here you can download a sample:


Image:Pointer.pngHow to get the current Windows user

The WinApi function GetUserName can be used to get the current Windows logon user.
(Implemented as GetUserNameW (Unicode) and GetUserNameA (ANSI)).

This is the declaration

Library name: ADVAPI32.DLL
   Function: GetUserNameA
      Export Ordinal: 0
      Returns
         Boolean: BOOL
      Parameters
         Receive String: LPSTR
         Receive Number: LPDWORD

Here a sample

Function: GetWindowsUser
   Returns
      String:
   Parameters
   Local variables
      String: sUser
      Number: nLen
   Actions
      ! Set nLen to the correct length (max size of the username + 1)
      Set nLen = 64
      Call SalStrSetBufferLength( sUser, nLen )
      Call GetUserNameA( sUser, nLen )
      Return sUser

Here you can download a sample:


Image:Pointer.pngUseful path functions

We all have done file and path handling in one way or another, putting backslashes to folders or stripping extensions for example.
Windows API gives us quite nice prebuild functionality, so that could save us reinventing the wheel in SAL.

Below a list of handy functions. They are all put together in two TD libraries for both ANSI and UNICODE.
(UNICODE version is for TD5.x and higher, the ANSI version is for pre TD 5.x versions).

You can download the library and a small test application from the sample vault.
Here -> WIKI_PathFunctions.zip

Click on the function names to get more information and requirements.

PathAddBackslash
Adds a backslash to the end of a path string when not present

This results in correct syntax for a path.
If the source path already has a trailing backslash, no backslash will be added.

INPUT:
   C:\dir_name\dir_name\file_name
OUTPUT:
   C:\dir_name\dir_name\file_name\
PathAddExtension Adds a file extension to a path string

If there is already a file extension present, no extension will be added.
If the pszPath points to a NULL string, the result will be the file extension only.
If pszExtension points to a NULL string, an ".exe" extension will be added.

INPUT:
   C:\dir_name\test
   .txt
OUTPUT:
   C:\dir_name\test.txt
PathAppend Appends one path to the end of another

This function automatically inserts a backslash between the two strings.
Only if one is not already present.

INPUT:
   name_1\name_2
   name_3
OUTPUT:
   name_1\name_2\name_3
PathCanonicalize Removes elements of a file path according to special strings inserted into that path

This function allows the user to specify what to remove from a path by inserting special character sequences into the path.
The ".." sequence indicates to remove the path part from the current position to the previous path part.
The "." sequence indicates to skip over the next path part to the following path part.
The root part of the path cannot be removed.

INPUT:
   A:\name_1\.\name_2\..\name_3
OUTPUT:
   A:\name_1\name_3
PathCommonPrefix Compares two paths to determine if they share a common prefix

A prefix is one of these types: "C:\\", ".", "..", "..\\".

INPUT:
   C:\win\desktop\temp.txt
   c:\win\tray\sample.txt
OUTPUT:
   C:\win 
PathCompactPathEx Truncates a path to fit within a certain number of characters by replacing path components with ellipses

The '/' separator will be used instead of '\' if the original string used it.
If source path points to a file name that is too long, instead of a path, the file name will be truncated to cchMax characters, including the ellipsis and the terminating NULL character.
For example, if the input file name is "My Filename" and cchMax is 10, PathCompactPathEx will return "My Fil...".

INPUT:
   "c:/program files/My SuperProgram/skins/sample.txt"
   Max length 30 characters
OUTPUT:
   "c:/program files/.../sample.txt" 
PathIsDirectoryEmpty Determines whether a specified path is an empty directory

"C:\" is considered a directory.

Obvious functionality  ;)
PathIsNetworkPath Determines whether a path string represents a network resource

PathIsNetworkPath interprets the following two types of paths as network paths.

  • Paths that begin with two backslash characters (\\) are interpreted as Universal Naming Convention (UNC) paths.
  • Paths that begin with a letter followed by a colon (:) are interpreted as a mounted network drive. However, PathIsNetworkPath cannot recognize a network drive mapped to a drive letter through the Microsoft MS-DOS SUBST command or the DefineDosDevice function.

Note The function does not verify that the specified network resource exists, is currently accessible, or that the user has sufficient permissions to access it.

INPUT:
   "c:\"
OUTPUT:
   FALSE -> no network drive 
INPUT:
   "\\Server\Folder"
OUTPUT:
   TRUE -> a network drive 
PathIsRelative Searches a path and determines if it is relative
INPUT:
   "c:\Test.txt"
OUTPUT:
   FALSE -> path is absolute 
INPUT:
   "Folder\Test.txt"
OUTPUT:
   TRUE -> path is relative 
PathIsRoot Parses a path to determine if it is a directory root

Returns TRUE for paths such as "\", "X:\", "\\server\share", or "\\server\".
Paths such as "..\path2" will return FALSE.

INPUT:
   "c:\"
OUTPUT:
   TRUE -> path contains a root
INPUT:
   "Folder\Test.txt"
OUTPUT:
   FALSE -> path does not contain a root
PathIsSameRoot Compares two paths to determine if they have a common root component

Returns TRUE if both strings have the same root component, or FALSE otherwise.

INPUT:
   C:\path1\one
   C:\path2\two
OUTPUT:
   TRUE -> These both have the same root part
PathIsUNC Determines if the string is a valid Universal Naming Convention (UNC) for a server and share path

Returns TRUE if the string is a valid UNC path, or FALSE otherwise.

INPUT:
   \\path1\path2
OUTPUT:
   TRUE -> is a valid UNC
INPUT:
   path1\path2
OUTPUT:
   FALSE -> is not a valid UNC
PathIsURL Tests a given string to determine if it conforms to a valid URL format

This function does not verify that the path points to an existing site—only that it has a valid URL format.

INPUT:
   http://tdwiki.daverabelink.net
OUTPUT:
   TRUE -> is a valid URL
INPUT:
   microsoft.com
OUTPUT:
   FALSE -> is not a valid URL
PathQuoteSpaces Searches a path for spaces. If spaces are found, the entire path is enclosed in quotation marks

TRUE if spaces were found; otherwise, FALSE.

INPUT:
   c:\program files\MyApp\test.txt
OUTPUT:
   "c:\program files\MyApp\test.txt"
PathRemoveArgs Removes any arguments from a given path

This function should not be used on generic command path templates (from users or the registry), but rather it should be used only on templates that the application knows to be well formed.

INPUT:
   MyApp.exe Arg1 Arg2 Arg3
OUTPUT:
   MyApp.exe
PathRemoveBackslash Removes the trailing backslash from a given path when present

Eh, the opposite of PathAddBackslash.

INPUT:
   c:\Folder1\Folder2\
OUTPUT:
   c:\Folder1\Folder2
PathRemoveExtension Removes the file extension from a path, if one is present
INPUT:
   c:\Test.txt
OUTPUT:
   c:\Test
PathRemoveFileSpec Removes the trailing file name and backslash from a path, if they are present
INPUT:
   c:\Folder1\Test.txt
OUTPUT:
   c:\Folder1
PathRenameExtension Replaces the extension of a file name with a new extension

If the file name does not contain an extension, the extension will be attached to the end of the string.

INPUT:
   c:\Folder1\Test.txt
   .doc
OUTPUT:
   c:\Folder1\Test.doc
PathStripPath Removes the path portion of a fully qualified path and file
INPUT:
   c:\Folder1\Test.txt
OUTPUT:
   Test.txt
INPUT:
   c:\Folder1\Folder2\
OUTPUT:
   Folder2\
PathUnquoteSpaces Removes quotes from the beginning and end of a path

Yes, the opposite of PathQuoteSpaces.

INPUT:
   "c:\program files\MyApp\test.txt"
OUTPUT:
   c:\program files\MyApp\test.txt


Image:Pointer.pngHow to get the elapsed time since the application was started

A gimmick, but a simple one.
The CRT function Clock returns the time since the process (application) was started.
The time is presented as clock-ticks, which is in milliseconds.

Declare this external function:

Library name: MSVCRT.dll
   Function: clock
      Export Ordinal: 0
      Returns
         Number: LONG
      Parameters

Here a sample to get the elapsed time

    Set nElapsedTime = clock( )

When running the application from IDE, the time returned by clock is the elapsed time since Team Developer IDE started.
When running the application as executable (EXE), the time returned is the elapsed time since the executable was started.

Here you can download a sample:


Image:Pointer.pngHow to use the Windows Timer (instead of SalTimer)

The good old SalTimerSet (SalTimerKill) function has one major drawback, it only supports timers up to 65535 milliseconds (65 seconds).
When you need larger timeframes, you can use the WinAPI functions TimerSet and KillTimer
The TimerSet function supports elapse times of 24.8 days (2.147.483.647 milliseconds).

The usage is nearly similar to the SalTimer functions.

Declare these external functions:

Library name: USER32.dll
Function: SetTimer
    Export Ordinal: 0
    Returns
        Number: UINT
    Parameters
        Window Handle: HWND
        Number: UINT
        Number: UINT
        Number: LPVOID
Function: KillTimer
    Export Ordinal: 0
    Returns
        Boolean: BOOL
    Parameters
        Window Handle: HWND
        Number: UINT

Also declare these constants

   Number: WM_TIMER                   = 0x0113
   Number: USER_TIMER_MAXIMUM         = 0x7FFFFFFF
   Number: USER_TIMER_MINIMUM         = 0x0000000A

Now trap the WM_Timer message:

   On WM_TIMER
      ! wParam holds the timer ID

To set a timer do this:

   ! Set timer ID 1 to 30 minutes
   Call SetTimer( hWndForm, 1, 1800000, NUMBER_Null )

To kill the timer:

   ! Kill timer ID 1
   Call KillTimer( hWndForm, 1 )


Here you can download a sample:

Personal tools