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.