You are on page 1of 5

Creating the Windows DLL

So, after examining an ActiveX DLL's export table, intercepting Visual Basic's call to the
compiler, intercepting Visual Basic's call to the linker, and comparing the arguments passed to
the linker with those required by a C/C++ compiler to generate a Windows DLL, we've finally
identified why we aren't able to successfully create a Windows DLL with Visual Basic. And
fortunately, we can work around that restriction. We should be able to create a standard
Windows DLL if we do the following:
1. Create a .def file for our project. We can specify our exported functions in the .def file in
several ways, but it's best to keep it simple:
2. NAME MathLib
3. LIBRARY MathMod
4. DESCRIPTION "Add-on Library of Mathematical Routines"
5. EXPORTS DllMain @1
6. Increment @2
7. Decrement @3
8. Square @4
The NAME statement defines the name of the DLL. The LIBRARY statement must either
precede the list of exported functions or appear on the same line as the first function. The
.def file should also list the ordinal position of each exported function preceded by an @
symbol.
9. Decide how we want to intercept the call to the linker. Two major techniques are
available to do this:
o Patching the Import Address Table (IAT), which requires that we build a Visual
Basic add-in that modifies the IAT in order to intercept particular calls by Visual
Basic to the Win32 API. Although it's certainly the most elegant method, its
complexity makes it a worthy subject for a separate article.
o Building a proxy linker that intercepts the call to the real linker, modifies the
command-line arguments to be passed to the linker, and then calls the linker with
the correct command-line arguments. This is the approach we used to discover
what arguments Visual Basic was passing to the compiler and linker, and it's the
approach we'll adopt to create a Windows DLL.
In building our proxy linker, we want a sufficiently flexible design so that we can
generate other kinds of files, if need be.
10. Modify the arguments to the linker to add the /DEF switch along with the path and
filename of our .def file. To do this, you must create a Visual Basic Standard EXE
project, add a reference to the Microsoft Scripting Runtime Library, remove the form
from the project, and add a code module. The source code for the proxy linker is as
follows:
11.
12. Option Explicit
13.
14. Public Sub Main()
15.
16. Dim SpecialLink As Boolean, fCPL As Boolean, fResource As Boolean
17. Dim intPos As Integer
18. Dim strCmd As String
19. Dim strPath As String
20. Dim strFileContents As String
21. Dim strDefFile As String, strResFile As String
22. Dim oFS As New Scripting.FileSystemObject
23. Dim fld As Folder
24. Dim fil As File
25. Dim ts As TextStream, tsDef As TextStream
26.
27. strCmd = Command
28.
29. Set ts = oFS.CreateTextFile(App.Path & "\lnklog.txt")
30.
31. ts.WriteLine "Beginning execution at " & Date & " " & Time()
32. ts.WriteBlankLines 1
33. ts.WriteLine "Command line arguments to LINK call:"
34. ts.WriteBlankLines 1
35. ts.WriteLine " " & strCmd
36. ts.WriteBlankLines 2
37.
38. ' Determine if .DEF file exists
39. '
40. ' Extract path from first .obj argument
41. intPos = InStr(1, strCmd, ".OBJ", vbTextCompare)
42. strPath = Mid(strCmd, 2, intPos + 2)
43. intPos = InStrRev(strPath, "\")
44. strPath = Left(strPath, intPos - 1)
45. ' Open folder
46. Set fld = oFS.GetFolder(strPath)
47.
48. ' Get files in folder
49. For Each fil In fld.Files
50. If UCase(oFS.GetExtensionName(fil)) = "DEF" Then
51. strDefFile = fil
52. SpecialLink = True
53. End If
54. If UCase(oFS.GetExtensionName(fil)) = "RES" Then
55. strResFile = fil
56. fResource = True
57. End If
58. If SpecialLink And fResource Then Exit For
59. Next
60.
61. ' Change command line arguments if flag set
62. If SpecialLink Then
63. ' Determine contents of .DEF file
64. Set tsDef = oFS.OpenTextFile(strDefFile)
65. strFileContents = tsDef.ReadAll
66. If InStr(1, strFileContents, "CplApplet", vbTextCompare) > 0
Then
67. fCPL = True
68. End If
69.
70. ' Add module definition before /DLL switch
71. intPos = InStr(1, strCmd, "/DLL", vbTextCompare)
72. If intPos > 0 Then
73. strCmd = Left(strCmd, intPos - 1) & _
74. " /DEF:" & Chr(34) & strDefFile & Chr(34) & " " & _
75. Mid(strCmd, intPos)
76. End If
77. ' Include .RES file if one exists
78. If fResource Then
79. intPos = InStr(1, strCmd, "/ENTRY", vbTextCompare)
80. strCmd = Left(strCmd, intPos - 1) & Chr(34) & strResFile & _
81. Chr(34) & " " & Mid(strCmd, intPos)
82. End If
83.
84. ' If Control Panel applet, change "DLL" extension to "CPL"
85. If fCPL Then
86. strCmd = Replace(strCmd, ".dll", ".cpl", 1, , vbTextCompare)
87. End If
88.
89. ' Write linker options to output file
90. ts.WriteLine "Command line arguments after modification:"
91. ts.WriteBlankLines 1
92. ts.WriteLine " " & strCmd
93. ts.WriteBlankLines 2
94. End If
95.
96. ts.WriteLine "Calling LINK.EXE linker"
97. Shell "linklnk.exe " & strCmd
98. If Err.Number <> 0 Then
99. ts.WriteLine "Error in calling linker..."
100. Err.Clear
101. End If
102.
103. ts.WriteBlankLines 1
104. ts.WriteLine "Returned from linker call"
105. ts.Close
106. End Sub
This proxy linker modifies only the command-line arguments passed to the linker if a .def
file is present in the directory that contains the Visual Basic project; otherwise it simply
passes the command-line arguments on to the linker unchanged. If a .def file is present, it
adds a /DEF switch to the command line. It also determines whether any resource files are
to be added to the linked file list. Finally, it examines the export table to determine if a
function named CplApplet is present; if it is, it changes the output file's extension from
.dll to .cpl.
107. To install the proxy linker, rename the original Visual Basic linker LinkLnk.exe,
copy the proxy linker to the Visual Basic directory, and name it Link.exe.
Once we create our proxy linker, we can reload our MathLib project and compile it into a DLL
by selecting the Make MathLib.exe option from the File menu.

Testing the DLL
Once we create our Windows DLL, the final step is to test it to make sure that it works. To do
this, create a new Standard EXE project (let's call it MathLibTest) and add a code module. To
make sure that code in our project can access the functions exported by the DLL, we use the
standard Visual Basic Declare statement. We declare our three exported math routines in the
code module as follows:

Option Explicit

Public Declare Function Increment Lib "C:\VBProjects\MathLib\mathlib.dll" ( _
value As Integer) As Integer

Public Declare Function Decrement Lib "C:\VBProjects\MathLib\mathlib.dll" ( _
value As Integer) As Integer

Public Declare Function Square Lib "C:\VBProjects\MathLib\mathlib.dll" ( _
value As Long) As Long
We can then use the following code in the form module to call the routines in the DLL:

Option Explicit

Private Sub cmdDecrement_Click()
txtDecrement.Text = Decrement(CInt(txtDecrement.Text))
End Sub

Private Sub cmdIncrement_Click()
txtIncrement.Text = Increment(CInt(txtIncrement.Text))
End Sub

Private Sub cmdSquare_Click()
txtSquare.Text = Square(CLng(txtSquare.Text))
End Sub

Private Sub Form_Load()
txtIncrement.Text = 0
txtDecrement.Text = 100
txtSquare.Text = 2
End Sub
When we call each of the MathLib functions, the application window might appear as it does in
Figure 2, confirming that the calls to the MathLib routines work as expected.

Figure 2: Testing calls to MathLib.dll

You might also like