I am declaring and calling a dll function using the following syntax in VB6:
'Declare the function
Private Declare Sub MYFUNC Lib "mylib.dll" ()
'Call the function
MYFUNC
Calling the function results in the error File not found: mylib.dll
. This happens when the application is run from the vb6 IDE or from a compiled executable.
The dll is in the working directory, and I have checked that it is found using ProcMon.exe from sysinternals. There are no failed loads, but the Intel Fortran dlls are not loaded (the ProcMon trace seems to stop before then).
I have also tried running the application in WinDbg.exe, and weirdly, it works! There are no failures on this line. The ProcMon trace shows that the Intel Fortran dlls are loaded when the program is run in this way.
The dll is compiled with Fortran Composer XE 2011.
Can anyone offer any help?
When loading DLLs, "file not found" can often be misleading. It may mean that the DLL or a file it depends on is missing - but if that was the case you would have spotted the problem with Process Monitor.
Often, the "file not found" message actually means that the DLL was found, but an error occured when loading it or calling the method.
There are actually three steps to calling a procedure in a DLL:
Errors can happen at any of these stages. VB6 does all this behind the scenes so you can't tell where the error is happening. However, you can take control of the process using Windows API functions. This should tell you where the error is happening. You can alse set breakpoints and use Process Monitor to examine your program's behaviour at each point which may give you more insights.
The code below shows how you can call a DLL procedure using the Windows API. To run it, put the code into a new module, and set the startup object for your project to "Sub Main".
Option Explicit
' Windows API method declarations
Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long
Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Function CallWindowProc Lib "user32" Alias _
"CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, _
ByVal Msg As Any, ByVal wParam As Any, ByVal lParam As Any) _
As Long
Private Declare Function FormatMessage Lib "kernel32" Alias _
"FormatMessageA" (ByVal dwFlags As Long, lpSource As Long, _
ByVal dwMessageId As Long, ByVal dwLanguageId As Long, _
ByVal lpBuffer As String, ByVal nSize As Long, Arguments As Any) _
As Long
Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000
Const MyFunc As String = "MYFUNC"
Const MyDll As String = "mylib.dll"
Sub Main()
' Locate and load the DLL. This will run the DllMain method, if present
Dim dllHandle As Long
dllHandle = LoadLibrary(MyDll)
If dllHandle = 0 Then
MsgBox "Error loading DLL" & vbCrLf & ErrorText(Err.LastDllError)
Exit Sub
End If
' Find the procedure you want to call
Dim procAddress As Long
procAddress = GetProcAddress(dllHandle, MyFunc)
If procAddress = 0 Then
MsgBox "Error getting procedure address" & vbCrLf & ErrorText(Err.LastDllError)
Exit Sub
End If
' Finally, call the procedure
CallWindowProc procAddress, 0&, "Dummy message", ByVal 0&, ByVal 0&
End Sub
' Gets the error message for a Windows error code
Private Function ErrorText(errorCode As Long) As String
Dim errorMessage As String
Dim result As Long
errorMessage = Space$(256)
result = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0&, errorCode, 0&, errorMessage, Len(errorMessage), 0&)
If result > 0 Then
ErrorText = Left$(errorMessage, result)
Else
ErrorText = "Unknown error"
End If
End Function
The .dll must be in the current "working" directory (or registered), otherwise at run-time the application can't find it.
Do:
MsgBox "The current directory is " & CurDir
And then compare that with what you were expecting. The .dll would need to be in that directory.
My standard first go-to approach to this issue is to break out ProcMon (or FileMon on XP). Setup the filters so that you can see where exactly it's searching for the file. It is possible that it's looking for the file elsewhere or for a different file name.
Private Declare Sub MYFUNC Lib "mylib.dll" ()
Firstly you are declaring a Sub, not a function. These don't have return values:
(vb6) Sub() == (vc++) void Sub()
(vb6) Func() as string == (vc++) string Func()
The path you have declared is local to the running environment. Thus when running is debug mode using VB6.exe, you'll need to have mylib.dll in the same directory as VB6.exe.
As you are using private declare, you might want to consider a wrapper class for your dll. This allows you to group common dll access together but allowing for reuse. Then methods of the class are used to access the exposed function.
So you can use all the code provided above, copy it into a class
MyClass code:
Option Explicit
'Private Declare Sub MYFUNC Lib "mylib.dll" ()
'<all code above Main()>
Private Sub Class_Initialize()
'initialise objects
End Sub
Private Sub Class_Terminate()
'Set anyObj = Nothing
End Sub
Public Sub ClassMethod()
On Error Goto errClassMethod
'Perhaps look at refactoring the use of msgbox
'<code body from Main() given above>
exit sub
errClassMethod:
'handle any errors
End Sub
'<all code below main>
Apartment threading model loads ALL modules when the application is started. Using a class will only "load" the dll when the class is instantiated. Also results in neater calling code without the surrounding obfuscation of windows API calls: (ie. modMain):
Sub Main()
Dim m_base As MyClass
Set m_base = New MyClass
MyClass.ClassMethod()
End Sub
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With