从vb6加载DLL时找不到文件

时间:2013-01-11 15:30:50

标签: vb6 interop dll

我在VB6中使用以下语法声明并调用dll函数:

'Declare the function
Private Declare Sub MYFUNC Lib "mylib.dll" ()

'Call the function
MYFUNC

调用该函数会导致错误File not found: mylib.dll。当应用程序从vb6 IDE或编译的可执行文件运行时,会发生这种情况。

dll在工作目录中,我已经检查过使用sysinternals中的ProcMon.exe查找它。没有失败的加载,但英特尔Fortran dll没有加载(ProcMon跟踪似乎在此之前停止)。

我也尝试在WinDbg.exe中运行该应用程序,奇怪的是,它的工作原理!这条线没有失败。 ProcMon跟踪显示在以这种方式运行程序时加载了Intel Fortran dll。

dll是使用Fortran Composer XE 2011编译的。

任何人都可以提供任何帮助吗?

5 个答案:

答案 0 :(得分:6)

加载DLL时,“找不到文件”通常会产生误导。这可能意味着DLL或它依赖的文件丢失了 - 但如果是这种情况,你会发现Process Monitor的问题。

通常,“找不到文件”消息实际上意味着找到了DLL,但在加载或调用方法时发生错误。

在DLL中调用过程实际上有三个步骤:

  1. 找到并加载DLL,运行DllMain方法(如果存在)。
  2. 在DLL中找到该过程。
  3. 致电程序。
  4. 错误可能发生在任何这些阶段。 VB6在幕后完成所有这些操作,因此您无法分辨出错误发生的位置。但是,您可以使用Windows API函数控制该过程。这应该告诉您错误发生的位置。您还可以设置断点并使用Process Monitor检查程序在每个点的行为,这可能会为您提供更多见解。

    下面的代码显示了如何使用Windows API调用DLL过程。要运行它,请将代码放入新模块,并将项目的启动对象设置为“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
    

答案 1 :(得分:4)

.dll必须位于当前“工作”目录中(或已注册),否则在运行时应用程序无法找到它。

执行:

MsgBox“当前目录是”& CURDIR

然后将其与您的预期进行比较。 .dll需要在该目录中。

答案 2 :(得分:1)

我对此问题的标准首选方法是打破ProcMon(或XP上的FileMon)。设置过滤器,以便您可以看到它正在搜索文件的确切位置。它可能正在其他地方查找文件或查找其他文件名。

答案 3 :(得分:0)

我尝试了@ roomaroo的回答,并没有给我足够的具体信息。使用Dependency Walker帮助我解决了问题。根据@bnadolson

,也必须使用chdir

答案 4 :(得分:0)

  

私人声明子MYFUNC Lib“mylib.dll”()

首先,您要声明一个Sub,而不是一个函数。 这些没有返回值:

(vb6) Sub() == (vc++) void Sub()
(vb6) Func() as string == (vc++) string Func()

您声明的路径是运行环境的本地路径。因此,当使用VB6.exe运行调试模式时,您需要将mylib.dll与VB6.exe放在同一目录中。

当您使用私有声明时,您可能需要考虑dll的包装类。这允许您将常见的dll访问组合在一起,但允许重用。然后使用类的方法来访问公开的函数。

因此,您可以使用上面提供的所有代码,将其复制到类

MyClass代码:

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>

公寓线程模型在应用程序启动时加载所有模块。使用类只会在实例化类时“加载”dll。也导致更整洁的调用代码而没有围绕Windows API调用的混淆:(即modMain):

Sub Main()
    Dim m_base As MyClass
    Set m_base = New MyClass
    MyClass.ClassMethod()
End Sub