Article ID: 106553 - View products that this article applies to.
This article was previously published under Q106553
This article outlines how to use DLLs with Visual Basic. It covers the following issues:
1.0 What Is a DLL?DLLs (Dynamic Link Libraries) are an important aspect of Windows. A DLL contains functions that your executable program can call during execution. In other words, a DLL is a library of functions that your program can link with dynamically.
A link can be static or dynamic. Static links don't change. All the address information needed by your program to access the library function is fixed when the executable file is created and remains unchanged during execution.
Dynamic links are created as needed. When your program needs a function that is not in the executable file, Windows loads the dynamic link library (the DLL), making all of its functions available to your application. At that time, Windows resolves the address of each function and dynamically links it to your application.
All Custom controls used in Visual Basic are DLLs. The only difference is that they require special handling in terms of messages received from Visual Basic.
1.1 Why Use DLLs?Here are four reasons why you might want to use a DLL:
1.2 Anatomy of a DLLEvery DLL must contain a LibMain function and should contain a Windows Exit Procedure (WEP) in addition to the exported functions that can be called by an executable program.
1.3 DLL Memory Management IssuesUse the large memory model.
C stores all variables defined as static or global (defined outside of a function) in the program's heap space, and C stores all other variables on the stack.
In the small and medium model, all pointers are near by default. This means that the data is accessed by 16-bit offsets to either the data segment (DS) register, or the stack segment (SS) register. Unfortunately, the compiler has no way of knowing whether the offset is from the DS or the SS. In most programs this would not be a problem because the DS and SS point to the same segment. A DLL, however, is a special case.
A DLL has its own data segment but shares its stack with the calling program. This means that the DS and the SS do not point to the same location. The easiest solution to this problem is to build the DLL in the large memory model where all variables are referenced by a 32-bit value.
Why Allocate Memory Dynamically?Allocating memory dynamically is a Windows-friendly technique. Declaring large arrays of data takes up space in either your program's stack, which is limited to 64K, or you program's Data Segment, which wastes disk space and Windows memory. It is better to ask Windows for the memory when you need it, and then free it when you have finished.
Allocating MemoryIn Windows, you can dynamically allocate two types of memory, local and global. Local memory is limited to 64K, and in the case of a DLL, local memory is shared with the program that called the DLL. Global memory is all of the memory available to Windows after it has loaded.
Local memory is allocated and managed using the LocalAlloc, LocalLock LocalUnlock, and LocalFree functions -- as in this example:
It is faster to allocate local memory than it is to allocate global memory. But allocations from the local heap are limited to 64K, which must be shared amongst all programs that are calling the DLL. It is best to use local memory when small, short lived blocks of memory are required.
Global memory is allocated and managed using the GlobalAlloc, GlobalLock GlobalUnlock, and GlobalFree functions -- as in this example:
The GlobalAlloc function allocates memory in multiples of 4K.
If you want to share memory allocated in the DLL with other programs, you should allocate it using the GMEM_SHARED flag. If you want to share the memory through DDE, you must allocate it by using the GMEM_DDESHARE flag.
Be Careful When Storing Data in Static VariablesIf you try to store data in a DLL using global or static variables, don't be surprised if these values have changed when you next call your DLL. The data stored in this way will be common to all applications that access this DLL. No matter how many applications use a DLL, there is only one instance of the DLL. The best way to get around this is to return structures from the DLL and pass them in again when they are needed.
File HandlesIt is not possible to share file handles between applications or DLLs. Each application has its own file-handle table. For two applications to use the same file using a DLL, they must both open the file individually.
1.4 Building a DLL Using Visual C++Here are the steps necessary to build a DLL using Visual C++:
1.5 Example C DLLThe following DLL contains the GetDiskInfo function, which can be called from Visual Basic. It will return the disk space available, the current drive name and the volume name.
Use the following DISKINFO.DEF file in Visual C++:
LIBRARY diskinfoNOTE: The LIBRARY name in the .DEF file must be the same as the DLL file name, or else Visual Basic will give you "Error in loading DLL." For example, create the file DISKINFO.DLL using the LIBRARY DISKINFO statement in the .DEF file above.
DESCRIPTION 'GetDiskInfo Can be called from Visual Basic'
EXETYPE WINDOWS 3.1
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE SINGLE
2.0 Calling DLLs from Visual BasicIn Visual Basic, all functions, including DLL functions, that you want to call must first be declared by using the Declare statement. You can declare your functions in the declarations section of a Form or a Module. If you declare a DLL procedure or function in a Form, it is private to that Form. To make it public, you must declare it in a Module. The following is an example Declare statement:
You must enter the entire Declare statement as one, single line. This particular Declare statement declares the user-defined procedure GETDISKINFO located in user-created DISKINFO.DLL file.
Once you declare the function, you can call and use the function just as you would call and use a Visual Basic function.
2.1 DLL ParametersBecause DLLs are typically written in C, DLLs can use a wide variety of parameters not directly supported by Visual Basic. As a result, when passing parameters, the programmer has to find the appropriate data type to pass.
Passing Arguments by Value or by ReferenceBy default, Visual Basic passes all arguments by reference. (When passing by reference, Visual Basic supplies a 32-bit far address.) However, many DLL functions expect an argument to be passed by value. This can be achieved by placing the ByVal keyword in front of the argument declaration.
The following sections show you how to convert parameters to Visual Basic.
8- to 16-Bit Numeric ParametersPass 8- to 16-bit numeric parameters (int, short, unsigned int, unsigned short, BOOL, and WORD) as Integer.
32-bit Numeric ParametersPass 32-bit numeric parameters (long, unsigned long, and DWORD) as LONG.
Object HandlesAll handles are unique 16-bit integer values associated with a Window and are passed by value, so pass these parameters as Integer.
StringsStrings include the LPSTR and LPBYTE data types (pointer to characters or pointer to unsigned characters). Pass these parameters as (ByVal paramname As String). To pass Visual Basic strings directly, pass them as (param As String).
For additional information on passing strings between Visual Basic and a C DLL, please see the following article in the Microsoft Knowledge Base:
118643NOTE: Visual Basic strings require special handling, so don't pass strings directly unless the DLL explicitly requires it.
(http://support.microsoft.com/kb/118643/EN-US/ )How to Pass a String or String Arrays Between VB and a C DLL
Pointers to Numeric ValuesPass pointers to numeric values by simply not using the ByVal keyword.
StructuresIf the Visual Basic user-defined type matches the structure expected by the DLL, the structure can be passed by reference.
NOTE: Structures cannot be passed by value.
Pointers to ArraysPass the first element of the array by reference.
Pointers to functionsVisual Basic does not support callback functions, so DLL functions that have pointers to functions cannot be used with Visual Basic.
Null PointersIf a DLL expects a Null pointer, pass it as (ByVal paramname As Any). You can use 0& as the value of paramname when calling the DLL.
2.2 TroubleshootingBelow are solutions to some problems you may encounter.
System Resources Keep Getting Lower After the DLL Is CalledIf your DLL is using GDI objects, you must remember to free them after using them. This may not be obvious in Visual Basic, but when using the Windows SDK (software development kit) if you create a GDI object (for example, CreateBrushIndirect), you must delete it by using DeleteObject later on.
Bad DLL Calling Convention ErrorThis error is often caused by incorrectly omitting or including the ByVal keyword from the Declare statement. This error can also be caused if the wrong parameters are passed.
Error in Loading DLLThis error occurs when you call a dynamic-link library procedure and the file specified in the procedure's Declare statement cannot be loaded. You can use the Microsoft Windows API function LoadLibrary to find out more specific information about why a DLL fails to load.
General Protection (GP) FaultGP faults occur when your program writes to a block of memory that doesn't belong to it. The two most likely reasons for this are:
2.3 Example Visual Basic Calling ProgramThere are two parts to calling a DLL in a Visual Basic program. First you declare the function, and then you use it in event code.
Here is an example of a Declare statement. The Declare statement should be put in a module or in a form's General Declarations section.
Specify ByVal statements exactly as shown, or else a GP fault may occur.
Once the function is declared, you can use it in event code. The following example uses a function from the DLL in the Command1 Click event code:
Article ID: 106553 - Last Review: July 15, 2004 - Revision: 2.1