WinApi Miscellaneous

From Team Developer SqlWindows Wiki
Revision as of 14:00, 9 January 2019 by DaveRabelink (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Miscellaneous Windows API & external functions


Contents


Pointer2.png Where to get WinAPI declarations, constant values and structure layouts Pointer.png

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.


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


Pointer2.png How to prevent "Not responding" state on "heavy load" functions Pointer.png

The operating system defines an application hang as a UI thread that has not processed messages for at least 5 seconds. Obvious bugs cause some hangs, for example, a thread waiting for an event that is never signaled, and two threads each holding a lock and trying to acquire the others. You can fix those bugs without too much effort. However, many hangs are not so clear. Yes, the UI thread is not retrieving messages - but it is equally busy doing other 'important' work and will eventually come back to processing messages.

  However, the user perceives this as a bug. The design should match the user's expectations. 

If the application's design leads to an unresponsive application, the design will have to change. Finally, and this is important, unresponsiveness cannot be fixed like a code bug; it requires upfront work during the design phase. Trying to retrofit an application's existing code base to make the UI more responsive is often too expensive. The following design guidelines might help.

  • Make UI responsiveness a top-level requirement; the user should always feel in control of your application
  • Ensure that users can cancel operations that take longer than one second to complete and/or that operations can complete in the background; provide appropriate progress UI if
  • Queue long-running or blocking operations as background tasks (this requires a well-thought out messaging mechanism to inform the UI thread when work has been completed)

Keep the code for UI threads simple; remove as many blocking API calls as possible

... but if application still going into "Not responding state" there is an option to switch off that unwanted behaviour with user32.dll function DisableProcessWindowsGhosting() which has no return value and also not any parameter.

(external function) Library name: USER32.DLL ThreadSafe: No Function: DisableProcessWindowsGhosting

        Library name: USER32.DLL 
                  ThreadSafe: No 
Function: DisableProcessWindowsGhosting
        Description: function disables the window ghosting feature for the calling GUI process. 
                     Window ghosting is a Windows Manager feature that lets the user minimize, move, or close the main window of an  application that is not responding.
                    (http://msdn.microsoft.com/en-us/library/windows/desktop/ms648415(v=vs.85).aspx)
        Export Ordinal: 0
        Returns
        Parameters



More source: http://msdn.microsoft.com/en-us/library/windows/desktop/dd744765(v=vs.85).aspx

BUT BE AWARE!!!: There is no function to enable window ghosting in a process once it has been disabled. Once ghosting has been disabled in a process, it remains disabled for the duration of the process. What that means: changed (unwanted)behaviour in GUI - doing so it may seriously mess up the user interface, it depends which messages are used for coding/messaging ... But its worth to try at least if "Not Responding" is issue...

Riho Kukk, Tresoor Tarkvara OÜ, Estonia

Pointer2.png How to detect memory or GDI leaks Pointer.png

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:


Pointer2.png How to determine a window exists Pointer.png

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.


Pointer2.png How to fill strings with a specific character Pointer.png

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 )


Pointer2.png How to disable Windows themes on your application Pointer.png

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.


Here you can download a sample:
Down.png ForceThemesOff.zip


Pointer2.png How to format a number of bytes to a formatted string Pointer.png

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


ByteFormat.png


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:
Down.png WIKI_ShowBytes.zip


Pointer2.png How to format a number of milliseconds to a formatted string Pointer.png

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


TimeFormat.png


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:
Down.png WIKI_ShowTimeInterval.zip


Pointer2.png How to get the current Windows user Pointer.png

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
   Function: GetUserNameW
      Export Ordinal: 0
      Returns
         Boolean: BOOL
      Parameters
         Receive String: LPWSTR
         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:
Down.png WIKI_GetWindowsUser.zip


Pointer2.png Useful path functions Pointer.png

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 pre-build 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.
Down.png 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:
   https://wiki.tdcommunity.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


Pointer2.png How to get the elapsed time since the application was started Pointer.png

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:
Down.png WIKI_ApplicationRunningTime.zip


Pointer2.png How to use the Windows Timer (instead of SalTimer) Pointer.png

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:
Down.png WIKI_Timer.zip


Pointer2.png Special folders Pointer.png

To get the path to a specific special folder you can use the shell function SHGetSpecialFolderPath.


Examples of special folders are:

CSIDL_SYSTEM -> C:\Windows\system32
CSIDL_PROGRAM_FILES -> C:\Program Files (x86)
CSIDL_MY_DOCUMENTS -> C:\Users\Dave en Esther\Documents

etc


Declare this external function:
(A = ANSI, W=UNICODE)

Library name: shell32.dll
    Function: SHGetSpecialFolderPathA
        Description:
        Export Ordinal: 0
        Returns
            Boolean: BOOL
        Parameters
            Window Handle: HWND
            Receive String: LPSTR
            Number: INT
            Boolean: BOOL
    Function: SHGetSpecialFolderPathW
        Description:
        Export Ordinal: 0
        Returns
            Boolean: BOOL
        Parameters
            Window Handle: HWND
            Receive String: LPWSTR
            Number: INT
            Boolean: BOOL


The sample contains a wrapper for this WinApi function.

    String PALGetSpecialFolder( pnCSIDL, pbIncTrailingBackslash )


Here a sample to get the Windows Fonts folder:

    Set dfFolder = PALGetSpecialFolder( CSIDL_FONTS, FALSE )
    !
    ! Output here is : C:\Windows\Fonts


Here you can download the sample:
(It contains ANSI and UNICODE versions)
Down.png WIKI_SpecialFolders.zip


Pointer2.png Detect system idle time Pointer.png

If your application must react on the fact that the system is not used for a specific time interval, you can use the
user32 function GetLastInputInfo.


Declare the external function:

Library name: user32.dl
    Function: GetLastInputInfo
        Description:
        Export Ordinal: 0
        Returns
            Boolean: BOOL
        Parameters
            structPointer
                 Number: UINT
                 Receive Number: DWORD


It returns the tick-count of the last input event (in msecs).


Calculate the difference between the currrent tick count and the one from the function above.
This is the idle time and depending on the value you can logoff or do other automated tasks.


Here you can download a sample:
Down.png WIKI_SystemIdleDetect.zip


Pointer2.png Convert (binary) buffer to formatted string: CryptBinaryToString Pointer.png

A string variable in TD can contain "normal" strings or any binary data (eg images, file contents etc).
Using the WinAPI function CryptBinaryToString you can
format the contents of a string variable to readable text depending on the used flags.
For instance, this function can format the data to be viewed as most HEX viewers show data:


0000    4c 69 6e 65 20 31 0d 0a  4c 69 6e 65 20 32 0d 0a   Line 1..Line 2..
0010    4c 69 6e 65 20 33 0d 0a  54 61 62 31 09 54 61 62   Line 3..Tab1.Tab
0020    32 09 54 61 62 33 0d 0a  41 6e 64 20 73 6f 6d 65   2.Tab3..And some
0030    20 6d 6f 72 65 20 74 65  78 74 20 68 65 72 65 20    more text here
0040    21 21 21 21 00                                     !!!!.


Here the HEX values of the bytes are displayed in blocks and also the ASCII representation of those bytes.
The function also is able to convert data to other encodings like base64 in several flavors.
Below the same data but now formatted to BASE64 HEADER:


-----BEGIN CERTIFICATE-----
TABpAG4AZQAgADEADQAKAEwAaQBuAGUAIAAyAA0ACgBMAGkAbgBlACAAMwANAAoA
VABhAGIAMQAJAFQAYQBiADIACQBUAGEAYgAzAA0ACgBBAG4AZAAgAHMAbwBtAGUA
IABtAG8AcgBlACAAdABlAHgAdAAgAGgAZQByAGUAIAAhACEAIQAhAAAA
-----END CERTIFICATE-----


This could come in handy to create your own HEX viewer or to inspect any string byte contents.


A TD wrapper function is created which calls CryptBinaryToString, named PALCryptBinaryToString.

bOk = PALCryptBinaryToString( psBuffer, pnFlags, rpsString )
Converts a buffer to a formatted string.

Parameters     Type            Description
psBuffer       String          Buffer to convert
pnFlags        Number          Formatting flags, see CRYPT_STRING constants
rpsString      Receive string  OUTPUT: Formatted string

Return value
bOk = TRUE if the function succeeds. Otherwise it returns FALSE


This function is placed in a separate library to be used in your own projects.
This library also contains all WinAPI flag constants to be used in PALCryptBinaryToString.


CryptBinaryToString.png


Here you can download a sample to show how to use PALCryptBinaryToString.
Both ANSI and UNICODE versions are supplied.
Down.png WIKI_CryptBinaryToString.zip


Pointer2.png Advanced file operations: copy, move, rename and delete Pointer.png

Team Developer offers several file functions, for instance to copy or delete files.
When you need more advanced features, have a look at the WinAPI function SHFileOperation.


Some of the features are:


  • Copy complete folders and subfolders with their files to the destination folder
  • Delete folders and subfolders even when they contain files
  • Rename multiple files in a single operation
  • Move multiple files
  • Show confirmation dialogs (eg to overwrite) or abort
  • Use the recycle bin when deleting files
  • For all operations, specify Standard MS-DOS wildcard characters
  • Automatically rename files when collisions are detected


Though most of these operations can be created using the standard Sal and Vis functions, this API function does it out of the box.


A nice sample to show how to use this API function can be found in the sample vault:
Down.png SHFileOperation.zip


SHFileOperationSample.png


Pointer2.png Check existence of DLL functions at runtime Pointer.png

When building applications using external functions from Windows API or other third party DLL's, the system you are building on obviously contains
the DLL's and DLL functions. If not, you are not able to compile your application in the first place.
But what about the system your application is running on, at customer sites?


For instance, an application using new Windows 7 API functions on a Windows 7 development environment, but you are not sure
on which Windows OS your application will be used by the customer.
Or you use a third party DLL, which comes in several versions and you are not certain a specific version is installed on the deployment system.


In those cases where at runtime the application calls a non existent exported function, you will get this error message:


ExternalFunctionNotFoundError.png


After this message, the application closes.


To prevent this, you can check at runtime if a particular API function is present on the running system.
Basically, it is getting a handle to the DLL and then getting the proc address of the function using it's name.


These external API functions are used to do this:

   GetModuleHandle (ANSI or UNICODE version)
   LoadLibraryEx (ANSI or UNICODE version)
   GetProcAddress
   FreeLibrary


There are two situations:

  • The DLL is already loaded by your application. Most commonly used WinAPI dll's are.
  • The DLL is not yet loaded. You application did not call any DLL function from it


In the first case, the DLL is already loaded, the actions are the easiest.
You first get the handle to the DLL by:

   Set nHandle = GetModuleHandleA( "kernel32.dll" )


With this handle you can query the functions:

   Set nProcAddress = GetProcAddress( nHandle, "GetSystemDefaultUILanguage" )


When nProcAddress is not NULL, the functionname was found.
Beware that the functionname is case sensitive. You must supply the exact case to get the function address.


The other situation, the DLL is not yet loaded by the application. For instance when a third party DLL is not yet called.
Here, you need to load the DLL explicitly to get the handle. After loading and checking the function, you should free the dll afterwards:

   Set nHandle = LoadLibraryExA( "MyCustom.dll", NUMBER_Null, DONT_RESOLVE_DLL_REFERENCES )
   ...
   ! Check the functions
   ...
   Call FreeLibrary( nHandle )


The LoadLibraryEx function loads the dll from the search PATH. If you specify a full path, only that path is used to search the dll.
The DONT_RESOLVE_DLL_REFERENCES parameter will load the DLL without loading linked DLL's and does not initialise the DLL.


If you are not sure if a DLL is already loaded, combine the GetModuleHandle and LoadLibraryEx.


A ready to use function is present in the sample: PALCheckDLLFunction


This function will first try to get the handle from the requested DLL and if that fails tries to load it.
You can use this function as-is in your own projects to check the existence of DLL functions, like:

   If PALCheckDLLFunction( "MyCustom.dll", "MyFunction1" ) > 0
      Call MyFunction1( )
   Else
      ! Show error message or perform alternative task


Here you can download the sample which contains PALCheckDLLFunction for both ANSI and UNICODE TD versions:
Down.png WIKI_CheckDLLFunctionExists.zip


Pointer2.png External datatypes and byte sizes Pointer.png

The several datatypes used in external functions have a predefined size.
The next table shows the commonly used types and their sizes in bytes.


Type Size (bytes)
BYTE, CHAR 1
ATOM, WORD, SHORT, USHORT, WCHAR 2
BOOL, DWORD, HANDLE, HWND, INT, LONG, LPARAM, UINT, ULONG, WPARAM 4
LPATOM, LPBOOL, LPBYTE, LPCHAR, LPDOUBLE, LPDWORD, LPFLOAT, LPHANDLE, LPHWND, LPINT, LPLPARAM, LPLONG, LPLPVOID, LPSHORT, LPSTR, LPUINT, LPULONG, LPUSHORT, LPWORD, LPVOID, LPWPARAM, LPWCHAR, LPWSTR 4
DOUBLE, FLOAT 8


Pointer2.png File version info: get TD version including build number Pointer.png


File version details from an executable or DLL can be shown manually in the file properties dialog


FileVersionInfoDialog.png


The version information can also be retrieved programmatically.
This is especially useful in these situations:

  • Need to check if required versions of particular dlls or executables are installed on the system
  • Depending on the versions found, your application might need to execute different functionality
  • From your application, a need to display the installed versions for debugging or logging purposes
  • Display or log the TD runtime version including build numbers


Using Windows API, the version information strings can be retrieved with these functions:


  • GetFileVersionInfoA / GetFileVersionInfoW
  • GetFileVersionInfoSizeA / GetFileVersionInfoSizeW
  • VerQueryValueA / VerQueryValueW


The sample provided here shows how to implement it in TD.
It also offers a global function to retrieve the TD runtime version including the build number.


FileVersionInfoSample.png


The archive contains both the ANSI and UNICODE version.

Here you can download the sample:
Down.png WIKI_FileVersionInfo.zip


Pointer2.png External datatype reference and CStructEx library Pointer.png


When implementing external function definitions, it is important to adopt the correct datatypes for return values and parameters.
The datatype definition instructs TD to handle the data according to the intended bit/byte sizes and sign.
Incorrect definitions can lead to datatype casting which transforms/cast the data in unexpected ways.


ExternalFuncDef.png


The general rule is that you try to match the datatype definition exactly to the intended definition.
For example, when a parameter expects an INT datatype, the TD definition should be set to Number: INT, obviously.


Though you might come into doubt what to choose when the datatype is not explicitly one of the base datatypes, like INT or LONG.
For example, Windows API functions could define other types, which are aliases or placeholders for the really underlying datatypes.
Look at HBITMAP, which is a Windows API datatype. Team Developer does not have the option to set HBITMAP for exported functions.


Reading the documentation of the function should indicate which base type it represents.
Even then, in most cases it is searching all kinds of docs to find out which TD datatype to take.


Personally, I still struggle to get the correct datatype, which takes time and in some cases, I accidentally use the wrong one.


Therefore, I decided to make it a bit easier. I tried to get all base types and related Windows API datatypes into a list for future reference.
The list offers this info per datatype:


  • The TD datatype(s) to use
  • The size in bits/bytes of the type
  • The maximum value range of the type (e.g. 0..255)
  • The sign (+/-)


ExternalFuncRef.png


(Some types are only present in TD70 (32 and 64 bit). This TD version has support for 64bit types.)


The way to use it is: look up the API datatype in the list and determine which TD external function datatype to use.
In addition, which CStruct functions to use to read/write the datatypes from/to buffers.


Here you can access the external function datatype reference (PDF and Excel format):
Down.png External datatype reference


Extended CStruct functions (CStructEx library)


Developers in TD are able to access (memory) buffers to read or write data directly.
This is done using the CStruct library (cstructl.apl) which contains several functions for a limited set of API datatypes.
For instance, using CStructGetWord, we can read a WORD value from a buffer. Using CStructPutWord we can write a WORD value to a buffer.


However, looking closely at the CStruct library, it is striking that not all base datatypes have corresponding Get and Put functions.


For instance, CStructGetWord, is defined to get only UNSIGNED values. This means that it only supports types having a maximum value range of 0 to 65535.
A SIGNED value has a different maximum range, in this case -32768 to 32767.


Most of the CStruct functions have this limitation. They are designed to handle UNSIGNED values only.
When an UNSIGNED value is handled using these functions, the values will be truncated at certain values and result in strange values.


To help out with these issues, I created an extension to the CStruct library, called CStructEx library.
It offers many more functions for a wide range of different datatypes.
All extended functions have the function name CStructEx... or CStructExPtr...
The Ptr functions are using memory pointers (number) instead of string buffers.


The API reference list shows which CStruct function to use for the particular datatypes.


The CStructEx library consist of 3 libraries:

  • Library to use in ANSI TD versions
  • Library to use in UNICODE TD versions
  • Library to use in TD70 and up (both 32 and 64 bit versions).


Here you can download the CStructEx library:
Down.png CStructEx library archive



Pointer2.png Windows Speech API (SAPI): Text to Speech / Speech to Text Pointer.png


The Speech Application Programming Interface or SAPI is an API developed by Microsoft to allow the use of speech recognition and speech synthesis within Windows applications.
In general the Speech API is a freely re-distributable component which can be shipped with any Windows application that wishes to use speech technology.


Simply said, using this API you are able to convert spoken text (eg audio from a microphone) to text within your application, called Speech to Text.
Also, giving text to the engine creates spoken audio (computer voice) which is played through the speaker or saved to a file, called Text to Speech.


A sample has been created in Team Developer to show how to use the API.
Two versions are present: one for TD versions up to TD6.3 and one starting from TD 7.0.
The sample shows both Speech to Text and Text to Speech implementations.


Here you can download the sample:
Down.png WIKI_SpeechAPI.zip


Also a short YouTube video to show the usage of the sample:
Video.png Speech API demo