6

我在 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 编译。

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

4

5 回答 5

7

加载 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
于 2013-01-22T09:25:10.560 回答
4

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

做:

MsgBox "当前目录是" & CurDir

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

于 2013-01-13T06:11:26.103 回答
1

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

于 2013-01-12T00:49:38.047 回答
1

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 访问分组在一起,但允许重用。然后使用类的方法来访问暴露的函数。

所以你可以使用上面提供的所有代码,将它复制到一个类中

我的类代码:

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
于 2018-03-28T15:16:31.503 回答
0

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

于 2014-04-09T14:30:12.727 回答