The ActiveX DLL Project Type
Let's begin by creating an ActiveX DLL project and seeing what happens if we try to call it as if it were a standard Windows DLL. When you create an ActiveX DLL project, Visual Basic automatically adds a class module (a .cls file) to it. You can rename this if you want, but don't include any code. Instead, add a code module (a .bas file) to the project, add the DLL's code, and then compile the DLL. When you run the DLL test application, the error message dialog shown in Figure 1 appears. The error message indicates that although the DLL was found, the specific called function (
Increment) was not.
The most likely cause of this error is that the function is not actually exported by the DLL. We can use the DumpBin utility to examine a DLL's exports by using the syntax
Figure 1. Error when accessing an ActiveX DLL as a Windows DLL
Dumpbin <path and name of dll> /exports
If we run DumpBin using this syntax, we see the following output:
Microsoft (R) COFF Binary File Dumper Version 6.00.8447 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. Dump of file mathlib.dll File Type: DLL Section contains the following exports for MathLib.dll 0 characteristics 41B9E52C time date stamp Fri Dec 10 10:04:28 2004 0.00 version 1 ordinal base 4 number of functions 4 number of names ordinal hint RVA name 1 0 0000192E DllCanUnloadNow 2 1 00001902 DllGetClassObject 3 2 00001918 DllRegisterServer 4 3 000018EC DllUnregisterServer Summary 1000 .data 1000 .reloc 1000 .rsrc 1000 .text
Our DLL exports four functions, all of which are utility functions that support COM. Clearly we need to export
DllMain and our three math functions. But how? Visual Basic does not appear to allow you to export DLL functions from ActiveX DLLs, thus effectively preventing you from using Visual Basic to create a standard Windows DLL.
This difficulty, however, is not insurmountable. When we select the File -> Make <filename>.dll menu option to create an ActiveX DLL, it appears that Visual Basic is seamlessly taking our source code and outputting an ActiveX DLL. But if we examine the subdirectory in which Visual Basic was installed, it appears that the process is not quite so seamless. Along with VB6.EXE, the Visual Basic executable that defines the Visual Basic environment, we can also find C2.EXE and LINK.EXE, which are a compiler and a linker, respectively. Their presence in this directory suggests that VB6.EXE itself does not handle the generation of a DLL file, but that at some point in the compilation process, it calls these programs.
We can find out how Visual Basic is using the compiler and linker more precisely by renaming them and creating wrapper executables named C2 and LINK that in turn call the real compiler and linker. The following is the source code for a new version of a console-mode C2.EXE that calls the "real" C2 compiler, which we've renamed C2comp.exe:
Public Sub Main() On Error Resume Next Dim strCmd As String, strPath As String Dim oFS As New Scripting.FileSystemObject Dim ts As TextStream strCmd = Command strPath = App.Path Set ts = oFS.CreateTextFile(strPath & "\c2log.txt") ts.WriteLine "Beginning execution at " & Date & " " & Time() ts.WriteBlankLines 1 ts.WriteLine "Command line parameters to c2 call:" ts.WriteLine " " & strCmd ts.WriteBlankLines 1 ts.WriteLine "Calling C2 compiler" Shell "c2comp.exe " & strCmd If Err.Number <> 0 Then ts.WriteLine "Error in calling C2 compiler..." End If ts.WriteBlankLines 1 ts.WriteLine "Returned from c2 compiler call" ts.Close End Sub
The process of compiling an ActiveX DLL produces the following output in our log file:
Beginning execution at 12/10/2004 12:44:22 PM Command line parameters to c2 call: -il "C:\DOCUME~1\Ron\LOCALS~1\Temp\VB277103" -f "C:\VB Projects\ MathLib\MathMod.bas" -W 3 -Gy -G5 -Gs4096 -dos -Zl -Fo"C:\ VB Projects\MathLib\MathMod.OBJ" -QIfdiv -ML -basic Calling C2 compiler Returned from c2 compiler call
These are fairly standard command-line arguments to produce object files that in turn are supplied to the linker. That means that to determine how to produce a Windows DLL, we'll have to intercept the call to the linker so that we can see what arguments Visual Basic passes to it. The following code does that:
Public Sub Main() On Error Resume Next Dim strCmd As String, strPath As String Dim oFS As New Scripting.FileSystemObject Dim ts As TextStream strCmd = Command strPath = App.Path Set ts = oFS.CreateTextFile(strPath & "\lnklog.txt") ts.WriteLine "Beginning execution at " & Date & " " & Time() ts.WriteBlankLines 1 ts.WriteLine "Command line parameters to LINK call:" ts.WriteLine " " & strCmd ts.WriteBlankLines 1 ts.WriteLine "Calling LINK linker" Shell "linklnk.exe " & strCmd If Err.Number <> 0 Then ts.WriteLine "Error in calling linker..." Err.Clear End If ts.WriteBlankLines 1 ts.WriteLine "Returned from linker call" ts.Close End Sub
It requires that we rename the linker LinkLnk.exe and name our link wrapper Link.exe.
When we attempt to compile an ActiveX DLL project, our linker log file contains the following output:
Beginning execution at 12/11/2004 12:44:33 PM Command line parameters to LINK call: "C:\Program Files\Microsoft Visual Studio\VB98\Class1.OBJ" "C:\Program Files\Microsoft Visual Studio\VB98\Project1.OBJ" "C:\Program Files\Microsoft Visual Studio\VB98\VBAEXE6.LIB" /ENTRY:__vbaS /OUT:"C:\Program Files\Microsoft Visual Studio\VB98\Project1.dll" /BASE:0x11000000 /SUBSYSTEM:WINDOWS,4.0 /VERSION:1.0 /DLL /INCREMENTAL:NO /OPT:REF /MERGE:.rdata=.text /IGNORE:4078 Calling LINK linker Returned from linker call
If we compare these command-line arguments with the syntax required to link the object files for a DLL using either C or C++, an omission becomes immediately apparent. Although the
/DLL switch is supplied to create a standard DLL, there is no
/DEF switch to define a module definition (.def) file that lists the functions exported by the DLL. (If we were programming in C or C++, we could use statements within our code to define our exports. Visual Basic doesn't support this, however, making the .def file the sole means of defining a library's exports.) Moreover, if we examine the files generated for an ActiveX DLL project by the Visual Basic environment, we'll also find that Visual Basic itself has not generated a .def file.