Этот вопрос будет плохо поставлен, но я вращаю свои колеса в разное время, и я не уверен, как выразить это лучше.
Мне нужно написать DLL, используя C, который вызывается из VB6. Я использую Visual Studio Express 2013. Я знаю... VB6 является древним, и я думаю, что менеджеры кода теперь убеждены, что они должны его мусор. Но в то же время это нужно сделать.
Для начала я пытаюсь написать DLL с помощью одной функции, которая ничего не делает, кроме как распечатать сообщение, и это отлично работает при вызове DLL с помощью VB.NET.
Вот что у меня для TinyDll.h.
#ifdef TINYDLL_EXPORTS
#define TINYDLL_API __declspec(dllexport)
#else
#define TINYDLL_API __declspec(dllimport)
#endif
extern TINYDLL_API void __stdcall testdll();
И здесь TinyDll.cpp
#include "stdafx.h"
#include "TinyDll.h"
#include <stdexcept>
using namespace std;
void __stdcall testdll()
{
printf("Got into the dll.\n");
}
Кстати, я пробовал это с и без __stdcall. Я также попытался использовать файл.def, чтобы отменить имя в DLL, но теперь мне не ясно, что должно работать. Примеры, которые я нашел, показывают, что это должно работать
LIBRARY TinyDll
Exports
testdll
но это не так. Имя в dll все еще находится в искаженном виде: testdll @@YGXXZ.
Мой тестовый код довольно тривиален, и он отлично работает с VB.NET, но он не будет работать с VB6. Проблема связана с тем, что процесс VB6 не может найти dll и/или найти функции внутри него. Для записи здесь используется код VB6 для тестирования
Declare Sub testdll Lib "TinyDll.dll" Alias "?testdll@@YGXXZ" ()
Sub Main()
testdll()
End Sub
Во-первых, я исправлю, что нет необходимости называть regsvr32 или regasm? DLL не является COM-объектом - VB6 вызывает мой код C, а не наоборот. Когда мы пытаемся выполнить любую из этих двух вещей, либо "точка входа DllRegisterServer не найдена", либо "указанный модуль не может быть найден".
В довершение всего, среда разработки VB6 слишком старая, чтобы работать на моей машине с Windows 7, а человек, пытающийся протестировать мою DLL, находится в другом состоянии и является строго программистом VB.
Я читал все, что мог найти, по поиску в Google, поэтому я надеюсь, что кто-то узнает о веб-сайте, в котором четко излагаются факты или показывает рабочий пример.
Как это часто бывает с таким вопросом (т.е. Одним из них является использование жесткой старой среды, используемой для того, чтобы делать вещи, для которых это никогда не предназначалось), теперь это работает, но неясно, почему именно.
Мы считаем, что без полного пути к dll VB6 не может его найти. Если мы объявим функцию с
Declare Function TestTinyDLL Lib "e:\full\path\down\to\TinyDll.dll" Alias "_testdll@0"
то вызов работает, но он не работает, если полный путь не указан, независимо от того, где находится dll. Мы также обнаружили, что мы можем избежать предоставления полного пути путем "инициализации" dll следующим образом (благодаря: Файл не найден при загрузке dll из vb6 для этой подсказки):
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
Почему это работает, это тайна, но это так. Надеюсь, это поможет кому-то еще столкнуться со старым кодом VB6!
extern "C"
чтобы подавить это?