Creating a Windows DLL With Visual Basic



Comments



Description

Creating a Windows DLL with Visual Basicby Ron Petrusha 04/26/2005 As the first rapid application development language, Visual Basic attracted attention for its elegant graphical interface and overall ease of use, which allowed a relatively inexperienced programmer to accomplish in minutes what often took days for advanced programmers using languages like C and C++. As a result, Visual Basic drew millions of new programmers, many of whom might never have even considered programming had it not been for the language's simplicity. Because of this simplicity, and because Visual Basic was attracting a following that the proponents of other languages could only dream of, non-Visual Basic programmers (who were really green with envy) counterattacked by pointing to the inexperience of most Visual Basic programmers and to the problems that stem from Visual Basic's design goal of shielding the developer from the complexities of the underlying operating system. To bolster their contention that Visual Basic is underpowered and underdeveloped, critics liked to point to the many things "real" programmers do that Visual Basic programmers cannot. Perhaps the most common limitation that critics continually point to is Visual Basic's inability to create a standard Windows dynamic link library (DLL). Certainly it's true that out of the box, Visual Basic doesn't allow you to create a Windows DLL in the same way that you can create other project types, like a Standard EXE or an ActiveX DLL. In this article, we'll go exploring to see how Visual Basic generates its executables. In the process, we'll discover that with a little bit of extra work, we can in fact create Windows DLLs with Visual Basic. What Is a Windows Dynamic Link Library? A dynamic link library (DLL) is a library of functions and procedures that can be called from an application or another DLL. Using a library in this way has two major functions:  It permits the sharing of code. The same DLL can be used by many other DLLs and applications. The Win32 API, for instance, is implemented as a series of Windows DLLs. Moreover, as long as multiple processes load the same DLL at the same base address, they can share the code in the DLL. In other words, a single DLL in memory can be accessed by multiple processes.  It allows for component-based and modular development, which makes development and the upgrade process easier. Ordinarily, when a static library is used in application development, the library's modules must be linked into the finished application. With dynamic linking, the modules reside in a separate DLL file that is loaded dynamically, either when the application loads or when its member functions are needed. A dynamic link library may include internal functions, which can be called only from within the DLL. Its main purpose, however, is to provide exported functions-- that is, functions that reside in a module of the DLL and can be called from other DLLs and applications. Frequently, a definition (.def) file is used in C/C++ projects to list a DLL's exports. A DLL also includes an optional entry point, which is called when a process or thread loads or unloads the DLL. Windows calls this entry point when a process loads and unloads the DLL. It also calls the entry point when the process creates or terminates a thread. That allows the DLL to perform any per-process and per- application initialization and cleanup. The syntax of this entry point, which must use the standard-call calling convention (used by default in Visual Basic), is: Public Function DllMain(hinstDLL As Long, fdwReason As Long, lpwReserved As Long) As Boolean Its parameters are: hInstDLL, a Long containing the instance handle of the DLL. This is the same as the DLL's module handle. fdwReason, a constant indicating why the entry point has been called. Possible values are:  DLL_PROCESS_ATTACH (1) A process is loading the DLL. Any per-process initialization should be performed.  DLL_THREAD_ATTACH (2) The process is spawning a new thread. Any per-thread initialization should be performed.  DLL_THREAD_DETACH (3) A thread is exiting. Any per-thread cleanup should be performed.  DLL_PROCESS_DETACH (0) A process is detaching the DLL, or the process is exiting. Any per-process cleanup should be performed.  lpvReserved A Long that provides more information about DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH. (It is unused if fdwReason is DLL_THREAD_ATTACH or DLL_THREAD_DETACH.) If fdwReason isDLL_PROCESS_ATTACH, it is Nothing for libraries loaded dynamically using functions likeLoadLibrary and GetProcAddress, and it is not Nothing for libraries loaded statically by providing library stubs at compile time. If fdwReason is DLL_PROCESS_DETACH, it is Nothing if the call has resulted from a call to the Win32 FreeLibrary function, and it is not Nothing if the entry point is called during process termination. The return value of the function is meaningful only if fdwReason is DLL_PROCESS_ATTACH. If initialization succeeds, the function should return True; otherwise, it should return False. Note that because the function is an entry point called by Windows, the values of the arguments passed to the function are provided by Windows. Also, the entry point is not called when a thread is terminated using the Win32TerminateThread function, nor is it called when a process is terminated using the Win32TerminateProcess function. The DLL Code In attempting to develop a Windows DLL, we'll create a very simple library of math functions. The following is the DLL's code, which we'll store in a code module (a .bas file) named MathLib: Option Explicit Public Const DLL_PROCESS_DETACH = 0 Public Const DLL_PROCESS_ATTACH = 1 Public Const DLL_THREAD_ATTACH = 2 Public Const DLL_THREAD_DETACH = 3 Public Function DllMain(hInst As Long, fdwReason As Long, lpvReserved As Long) As Boolean Select Case fdwReason Case DLL_PROCESS_DETACH ' No per-process cleanup needed Case DLL_PROCESS_ATTACH DllMain = True Case DLL_THREAD_ATTACH ' No per-thread initialization needed Case DLL_THREAD_DETACH ' No per-thread cleanup needed End Select End Function Public Function Increment(var As Integer) As Integer If Not IsNumeric(var) Then Err.Raise 5 Increment = var + 1 End Function Public Function Decrement(var As Integer) As Integer If Not IsNumeric(var) Then Err.Raise 5 Decrement = var - 1 End Function Public Function Square(var As Long) As Long If Not IsNumeric(var) Then Err.Raise 5 Square = var ^ 2 End Function Several characteristics about the code are worth mentioning. The first is that although it includes a DllMainprocedure, no per-process or per-thread initialization needs to be performed. So DllMain simply returnsTrue if it is called with the fdwReason argument set to DLL_PROCESS_ATTACH. Second, the point of providing a Windows DLL is to allow other languages to call it. To ensure interoperability, we want to confine ourselves to language features that the Win32 API supports, so that our DLL can be called from as many development environments and platforms as possible. We could have made each of our three math functions more flexible, for example, by defining both the incoming argument and the return value as Variants. That would have allowed the function to determine the data type it should interpret the incoming data as, in addition to the data type it should return. But the Variant is a data type defined by COM, Microsoft's Component Object Model, and is not a data type Win32 API recognizes. So instead, the code uses standard Win32 API data types. We'll also need a test program to tell us whether our Windows DLL is working properly. For that purpose, we can create a Standard EXE project with one form and one code module. The code module simply consists of the Declare statements that define the functions found in the DLL: Public Declare Function Increment Lib "MathLib.dll" (var As Integer) As Integer Public Declare Function Decrement Lib "MathLib.dll" (var As Integer) As Integer Public Declare Function Square Lib "MathLib.dll" (var As Long) As Long Rather than simply specifying the name of the DLL in the Lib clause, you also should add the full path to the directory that contains the DLL. The form's code performs the calls to the DLL functions: Option Explicit Dim incr As Integer Dim decr As Integer Dim sqr As Long Private Sub cmdDecrement_Click() decr = Increment(decr) cmdDecrement.Caption = "x = " & CStr(decr) End Sub Private Sub cmdIncrement_Click() incr = Increment(incr) cmdIncrement.Caption = "x = " & CStr(incr) End Sub Private Sub cmdSquare_Click() sqr = Square(srr) cmdSquare.Caption = "x = " & CStr(sqr) End Sub Private Sub Form_Load() incr = 1 decr = 100 sqr = 2 End Sub Title Make a standard DLL Description This example shows how to make a standard DLL in Visual Basic 6. Keywords DLL, ActiveX DLL Categories ActiveX, Windows This examples builds a standard DLL that you can call by using the normal DLL calling conventions. For full details, see the articleCreating a Windows DLL with Visual Basic. Thanks to Luke Emmet for pointing this article out. The basic staps are: 1. Hack the linking process. 1. Make an executable program to call the linker. Reomve the default Form1 and create the following Sub Main. Public Sub Main() Dim SpecialLink As Boolean, fCPL As Boolean, fResource _ As Boolean Dim intPos As Integer Dim strCmd As String Dim strPath As String Dim strFileContents As String Dim strDefFile As String, strResFile As String Dim oFS As New Scripting.FileSystemObject Dim fld As Folder Dim fil As File Dim ts As TextStream, tsDef As TextStream strCmd = Command Set ts = oFS.CreateTextFile(App.Path & "\lnklog.txt") ts.WriteLine "Beginning execution at " & Date & " " & _ Time() ts.WriteBlankLines 1 ts.WriteLine "Command line arguments to LINK call:" ts.WriteBlankLines 1 ts.WriteLine " " & strCmd ts.WriteBlankLines 2 ' Determine if .DEF file exists ' ' Extract path from first .obj argument intPos = InStr(1, strCmd, ".OBJ", vbTextCompare) strPath = Mid(strCmd, 2, intPos + 2) intPos = InStrRev(strPath, "\") strPath = Left(strPath, intPos - 1) ' Open folder Set fld = oFS.GetFolder(strPath) ' Get files in folder For Each fil In fld.Files If UCase(oFS.GetExtensionName(fil)) = "DEF" Then strDefFile = fil SpecialLink = True End If If UCase(oFS.GetExtensionName(fil)) = "RES" Then strResFile = fil fResource = True End If If SpecialLink And fResource Then Exit For Next ' Change command line arguments if flag set If SpecialLink Then ' Determine contents of .DEF file Set tsDef = oFS.OpenTextFile(strDefFile) strFileContents = tsDef.ReadAll If InStr(1, strFileContents, "CplApplet", _ vbTextCompare) > 0 Then fCPL = True End If ' Add module definition before /DLL switch intPos = InStr(1, strCmd, "/DLL", vbTextCompare) If intPos > 0 Then strCmd = Left(strCmd, intPos - 1) & _ " /DEF:" & Chr(34) & strDefFile & Chr(34) & _ " " & _ Mid(strCmd, intPos) End If ' Include .RES file if one exists If fResource Then intPos = InStr(1, strCmd, "/ENTRY", vbTextCompare) strCmd = Left(strCmd, intPos - 1) & Chr(34) & _ strResFile & _ Chr(34) & " " & Mid(strCmd, intPos) End If ' If Control Panel applet, change "DLL" extension to ' "CPL" If fCPL Then strCmd = Replace(strCmd, ".dll", ".cpl", 1, , _ vbTextCompare) End If ' Write linker options to output file ts.WriteLine "Command line arguments after " & _ "modification:" ts.WriteBlankLines 1 ts.WriteLine " " & strCmd ts.WriteBlankLines 2 End If ts.WriteLine "Calling LINK.EXE 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 This program does roughly the same thing that Visual Basic does when it creates a DLL except it adds the /DEF flag to the command.  Compile the executable.  Rename the normal Visual Basic linker from Link.exe to LinkLnk.exe. On my system, it's at C:\Program Files\Microsoft Visual Studio\VB98.  Copy the executable program that you compiled into this directory and name it Link.exe. When Visual Basic links the DLL, it calls this program, which calls the renamed LinkLnk.exe program, adding the new /DEF parameter.  Export the DLL's routines. 1. Create a file named .def where is the name of the DLL. In this example, the DLL is named Fibonacci.dll so this file is called Fibonacci.def. 2. Add code to this file similar to the following: NAME MathLib LIBRARY MathMod DESCRIPTION "Add-on Library of Mathematical Routines" EXPORTS DllMain @1 Fibo @2 This tells the linker about the main entry point DllMain and this example's function Fibo, both of which are created shortly.  Build the DLL. 1. Create a new ActiveX DLL project. 2. Leave the default Class1 class alone. You will not use it but Visual Basic needs it to it has something to compile into the ActiveX DLL. 3. Add a code module and insert this code: Public Const DLL_PROCESS_DETACH = 0 Public Const DLL_PROCESS_ATTACH = 1 Public Const DLL_THREAD_ATTACH = 2 Public Const DLL_THREAD_DETACH = 3 Public Function DllMain(hInst As Long, fdwReason As Long, _ lpvReserved As Long) As Boolean Select Case fdwReason Case DLL_PROCESS_DETACH ' No per-process cleanup needed Case DLL_PROCESS_ATTACH DllMain = True Case DLL_THREAD_ATTACH ' No per-thread initialization needed Case DLL_THREAD_DETACH ' No per-thread cleanup needed End Select End Function ' Return a Fibonacci number. Public Function Fibo(ByVal N As Integer) As Long If N <= 1 Then Fibo = 1 Else Fibo = Fibo(N - 1) + Fibo(N - 2) End If End Function DllMain is the DLL entry point. Fibo is a function that the DLL is exporting.  Compile the DLL. This should invoke the new Link.exe you built. If you look in that program's directory, you should see the log file it generates.  Build a test program to call the DLL. 1. Make a standard Visual Basic EXE. 2. Declare the routine exported by the DLL as in the following code: Private Declare Function Fibo Lib _ "C:\WebSite\HowToSrc\a2\Fibonacci.dll" (ByVal N As _ Integer) As Long Insert the path to the DLL on your computer.  Run the program. That should do it. Watch out for typos. If the .DEF file doesn't spell the function's name correctly, the DLL won't compile and the error messages are not very good. See the article mentioned at the beginning for more detail and some information about how the original author figured all this out
Copyright © 2024 DOKUMEN.SITE Inc.