【问题标题】:File not found when loading dll from vb6从 vb6 加载 dll 时找不到文件
【发布时间】:2018-03-28 23:54:58
【问题描述】:

我在 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 找到的。没有失败的加载,但没有加载 Intel Fortran dll(ProcMon 跟踪似乎在那之前停止了)。

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

该 dll 使用 Fortran Composer XE 2011 编译。

谁能提供帮助?

【问题讨论】:

    标签: vb6 interop dll


    【解决方案1】:

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

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

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

    1. 找到并加载 DLL,运行 DllMain 方法(如果存在)。
    2. 在 DLL 中找到该过程。
    3. 调用过程。

    错误可能发生在任何这些阶段。 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
    

    【讨论】:

      【解决方案2】:

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

      做:

      MsgBox "当前目录为" & CurDir

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

      【讨论】:

      • 正如我所解释的,dll 已加载(我可以通过 ProcMon 看到),但是随后它被神秘地卸载并且 vb6 返回错误。
      【解决方案3】:

      我解决此问题的标准首选方法是拆分 ProcMon(或 XP 上的 FileMon)。设置过滤器,以便您可以看到它在哪里搜索文件。它可能正在其他地方寻找文件或寻找不同的文件名。

      【讨论】:

        【解决方案4】:

        Private Declare Sub 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>
        

        Apartment 线程模型在应用程序启动时加载所有模块。使用类只会在类被实例化时“加载”dll。还可以使调用代码更整洁,而不会混淆 Windows API 调用:(即 modMain):

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

        【讨论】:

          【解决方案5】:

          我尝试了@roomaroo 的回答,但它没有给我足够具体的信息。使用Dependency Walker 帮助我解决了这个问题。根据@bnadolson,还必须 chdir

          【讨论】:

            猜你喜欢
            • 2015-02-07
            • 1970-01-01
            • 2016-09-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-07-17
            • 1970-01-01
            相关资源
            最近更新 更多