简体   繁体   中英

Calling C dll from VB6, where the dll is written using Visual Studio 2013

This question is going to be poorly-posed, but I'm spinning my wheels big-time, and I'm not sure how to express it better.

I need to write a DLL, using C, which is to be called from VB6. I'm using Visual Studio Express 2013. I know...VB6 is ancient, and I think the managers of the code are now convinced they should junk it. But in the meantime, this needs to be done.

To start, I am trying to write a DLL with a single function that does nothing but print a message, and that works fine when calling the DLL using VB.NET.

Here's what I have for TinyDll.h.

#ifdef TINYDLL_EXPORTS
#define TINYDLL_API __declspec(dllexport)
#else
#define TINYDLL_API __declspec(dllimport)
#endif

extern TINYDLL_API void __stdcall testdll();

And here's TinyDll.cpp

#include "stdafx.h"
#include "TinyDll.h"
#include <stdexcept>

using namespace std;

void __stdcall testdll()
{
  printf("Got into the dll.\n");
}

Incidentally, I've tried this with and without __stdcall. I also tried using a .def file to de-mangle the name in the dll, but it's not clear to me now that should work. The examples I've found indicate that this should work

LIBRARY TinyDll
Exports
  testdll

but it does not. The name in the dll is still in mangled form: ?testdll@@YGXXZ.

My test code is fairly trivial, and it works perfectly with VB.NET, but it won't work with VB6. The problem has something to do with the VB6 process not being able to find the dll and/or find the functions inside of it. For the record, here's the VB6 code being used to test

Declare Sub testdll Lib "TinyDll.dll" Alias "?testdll@@YGXXZ" ()

Sub Main()
  testdll()
End Sub    

First, am I correct that it is not necessary to call regsvr32 or regasm? The dll is not a COM object -- VB6 is calling my C code, not the other way around. When we try to do either of these two things, either "the entry-point DllRegisterServer was not found" or "The specified module could not be found."

To top it off, the VB6 development environment is too old to run on my Windows 7 machine and the person who is trying to test my DLL is in another state and is strictly a VB programmer.

I've read everything I can find by googling, so I'm hoping that someone knows of a website that clearly lays out the facts or shows a working example.

As is so often the case with this kind of question (ie, one involving a crusty old environment being used to do stuff it was never meant for), it now works, but it's not clear exactly why.

We think that without a full path to the dll, VB6 can't find it. If we declare the function with

Declare Function TestTinyDLL Lib "e:\full\path\down\to\TinyDll.dll" Alias "_testdll@0"

then the call works, but it doesn't work if the full path isn't given, no matter where the dll resides. We also found that we can avoid providing the full path by "initializing" the dll as follows (thanks to: File not found when loading dll from vb6 for this clue):

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

Declare Function BogusCall Lib "otherdll.dll" Alias "_BogusCall@0" () As Integer

Declare Function TestTinyDLL Lib "TinyDLL.dll" Alias "_testdll@0" () As Integer

Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000


Sub Main()

    InitializeDLL App.Path & "\" & "otherdll.dll", "_BogusCall@0"

    InitializeDLL App.Path & "\" & "TinyDLL.dll", "_testdll@0"

    Dim Version As String

    Version = "TinyDLLTestProgram" & vbCrLf & vbCrLf

    Version = Version & "BogusCall = " & CStr(BogusCall ()) & vbCrLf

    Version = Version & "TestTinyDLL= " & CStr(TestTinyDLL()) & vbCrLf

    Form1.txtOutput.Text = CStr(Version)

End Sub

Sub InitializeDLL(myDLL As String, myFunc As String)

    ' 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

Why this works is a mystery, but it does. I hope this helps anyone else faced with old VB6 code!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM