Monday, June 21, 2010

How to work with APIs or DLLs in Excel VBA ?

An API is exactly as its acronym suggests, an interface. Just like the interface between your screen and yourself is the keyboard or mouse, so the underlying code just translates your actions to the screen....everything between these 2 is handled by Library files that call other functions to perform actions. You use API while programing in VBA, you just don't realise it...the VBA just acts like a translator in converting your code to the appropriate Op-codes so that the underlying library files can perform some action/function.

The plus side of using API's means that the libraries can be updated independently of the application, and many applications can share a single DLL. The down side is that if you get it wrong you may not receive an error and/or your system will become unstable or crash causing you to reboot.

Always good practice to constantly save when working with APIs. Also can be difficult to debug your program, in fact you can't debug API calls in VBA.

In VBA before you can call a function in a DLL, you must provide VBA with information about where that function is and how to call it. There are two ways to do this:

1) by setting a reference to the DLL's type library (.tlb)

2) by using a Declare statement in a module as above.

The Declare keyword alerts VBA that you want to include the definition for a DLL function in your project. A Declare statement in a standard module can be public or private, depending on whether you want the API function to be available to only a single module or to the entire project. For class modules, a Declare statement must be private so .........................

The Public Function >

Public Declare Function sndPlaySound Lib "winmm.dll" _
Alias "sndPlaySoundA" ( _
ByVal lpszSoundName As String, _
ByVal uFlags As Long) As Long

In most cases you will see API calls with No explicit path as you would use the core window library files which are located in your system dir.

If a procedure does not return a value, write the declare as a Sub:


Declare Sub publicname Lib "libname" [Alias "alias"] [([[ByVal] variable [As type] [,[ByVal] variable [As type]]...])]

As per Std Sub Procedure set-up, DLL procedures declared in standard modules are public by default and can be called from anywhere in your application. DLL procedures declared in any other type of module are private to that module, and you must identify them as such by preceding the declaration with the Private keyword. Procedure names are case-sensitive in 32-bit versions of Visual Basic. In previous, 16-bit versions, procedure names were not case-sensitive.

Lets look @ the above Function;


Specifying the Library

The Lib clause in the Declare statement tells Visual Basic where to find the .dll file that contains the procedure. When you're referencing one of the core Windows libraries;

User32 [user interface functions],

Kernel32 [operating system kernel functions],

GDI32 [graphics device interface functions], and

shell32.dll [Windows shell functions],

you don't need to include the file name extension:


Declare Function RegisterClass Lib "user32" Alias "RegisterClass" (Class As WNDCLASS) As Long

For other DLLs, the Lib clause is a file specification that can include a path:

Declare Function lzCopy Lib "c:\windows\lzexpand.dll"

_(ByVal S As

_ Integer, ByVal D As Integer) As Long

If you do not specify a path for libname, Visual Basic will search for the file in the following order:

1. Directory containing the .exe file

2. Current directory

3. Windows 32-bit system directory \Windows\System32

4. Windows directory \Windows

5. Path environment variable (See here to get these)

In most cases you will see API calls with No explicit path as you would use the core window library files which are located in your system dir. So in the above example we are Looking @ the winmm.dll

sndPlaySound = The name by which Visual Basic will refer to the function everywhere else in your code. You can make this any name you want (as long as you use the proper Alias clause), but it's safest to make this the Actual name of the function in the Windows API.


The Alias clause in the declare statement is needed to specify the correct character set. Windows API functions that contain strings actually exist in two formats: ANSI and Unicode.

It is the A (ANSI) @ end that signifies this in "sndPlaySoundA"

The Unicode version ends with the letter W (WCHAR type). So if the function has two entry points then you would expect to see the Alias used.

ANSI or Unicode

The 32-bit versions of Windows (Windows 95+ and Windows NT) support two separate character sets: ANSI, in which each character takes 1 byte of storage and there are only 256 different possible characters; and Unicode, in which each character takes up 2 bytes of storage and there are 65,536 different possible characters ie. 16-bit Unicode character. VBA, Access' programming language, supports Unicode only. In general, this works in your favour, allowing support for many languages, including those that use character sets different from the English ones. In the Windows header files, therefore, you'll get both ANSI and Unicode versions of each function that contains a string. Have a look for winmm.dll and right click to select Quickview, you should get something like this;

Export table

Ordinal | Entry Point | Name

0004 00012d0 CloseDriver

0005 0008db3 DefDriverProc

etc....listing all the entry points or LP Long pointers to the various functions with in the library.

Now for the Arguments and Types;

(ByVal lpszSoundName As String, ByVal uFlags As Long) As Long

lpszSoundName = a string that specifies the sound to play. This must be the Fullpathname. If this parameter is NULL, any currently playing waveform sound is stopped. ie To stop a currently playing sound then send the VBA Const VBNullString and NOT "" !!

ByVal = when a value is passed ByVal, the actual value is passed directly to the function, the exception to this is string variables, but that is another story, and when passed ByRef, the address of the value is passed, so you may see something like LPWORD which is actually a Long Pointer to a memory address.

As String = value passed is a string = the full path name to the sound file

ByVal uFlags As Long

The function is expecting some Flags to be declared here as the data type Long (Look up data Type for Long) = Long (long integer) variables are stored as signed 32-bit (4-byte) numbers ranging in value from -2,147,483,648 to 2,147,483,647. The type-declaration character for Long is the ampersand (&).

As for the Public Constants

Public Const SND_SYNC = &H0

Public Const SND_ASYNC = &H1

Public Const SND_NODEFAULT = &H2

Public Const SND_MEMORY = &H4

Public Const SND_LOOP = &H8

Public Const SND_NOSTOP = &H10

What does &H0, &H1, &H2, &H4, &H8, &H10 mean?? They are Hexadecimal values (Base 16)

The function will recognize various Constants or flags that tell it how/what to do eg.

Flags for playing the sound;


The sound is played asynchronously and PlaySound returns immediately after beginning the sound. To terminate an asynchronously played waveform sound, call PlaySound with lpszSoundName set to VBNullString.


The sound plays repeatedly until PlaySound is called again with the lpszSoundName parameter set to VBNullString. You must also specify the SND_ASYNC flag to indicate an asynchronous sound event.


No default sound event is used. If the sound cannot be found, PlaySound returns silently without playing the default sound.


The specified sound event will yield to another sound event that is already playing. If a sound cannot be played because the resource needed to generate that sound is busy playing another sound,the function immediately returns FALSE without playing the requested sound. If this flag is not specified, PlaySound attempts to stop the currently playing sound so that the device can be used to play the new sound.


If the driver is busy, then return immediately without playing the sound.


Synchronous playback of a sound event. PlaySound returns after the sound event completes.

The values; &H0, &H1, &H2, &H4, &H8, &H10 are the Long Hex values of these constants eg.

&[Long] H(Hex) 0 or &H0 = 0 Decimal

&[Long] H(Hex) 10 or &H10 = 16 Decimal

No comments:

Post a Comment