问题描述
我在 Go 中创建了这个带有导出函数的 dll,当我使用 rundll32 或从带有 loadlbraryA 的 c++ 可执行文件调用它时,该函数工作正常。但是当我从 C++ dll 调用时出现错误,我试图读取 GetLastError()
并且它说 cannot find the module you are calling
。我尝试了所有可能的方法来提供 loadlibraryA 的完整路径,但发生了同样的错误,并且 Go DLL 的句柄每次都为空。此外,我检查了 Go DLL,并且测试函数正确导出。
- 来自 rundll32
- 来自 c++ main() 和 loadlibrary
并且在 test() 中不起作用:
- 来自 c++ DllMain() 和 loadlibrary
- 从 c++ DllMain() 和从 kernel32.dll 直接调用的 loadlibrary
- 来自 c++ DllMain() 和在 DllMain() 中创建的 ThreadFunction
文件夹结构
ProjectFolder
-- main.go
-- dllmain.h
-- dllmain.go
构建
go build -buildmode=c-shared -o my-go-library.dll main.go
调用示例dll
rundll32 my-go-library.dll Test
工作来源
main.go:
package main
import "C"
import (
"syscall"
"time"
"unsafe"
)
//export Test
func test() {
MessageBoxPlain("TestMsgBox","Test")
main()
}
//export OnProcessAttach
func OnProcessAttach() {
MessageBoxPlain("OnAttachMsgBox","OnAttach")
test()
}
// MessageBox of Win32 API.
func MessageBox(hwnd uintptr,caption,title string,flags uint) int {
ret,_,_ := syscall.NewLazyDLL("user32.dll").NewProc("MessageBoxW").Call(
uintptr(hwnd),uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))),uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title))),uintptr(flags))
return int(ret)
}
// MessageBoxPlain of Win32 API.
func MessageBoxPlain(title,caption string) int {
const (
NULL = 0
MB_OK = 0
)
return MessageBox(NULL,title,MB_OK)
}
func main() {}
dllmain.h:
#include <windows.h>
void OnProcessAttach(HINSTANCE,DWORD,LPVOID);
typedef struct {
HINSTANCE hinstDLL; // handle to DLL module
DWORD fdwReason; // reason for calling function // reserved
LPVOID lpReserved; // reserved
} MyThreadParams;
DWORD WINAPI MyThreadFunction(LPVOID lpParam) {
MyThreadParams params = *((MyThreadParams*)lpParam);
OnProcessAttach(params.hinstDLL,params.fdwReason,params.lpReserved);
free(lpParam);
return 0;
}
BOOL WINAPI DllMain(
HINSTANCE _hinstDLL,// handle to DLL module
DWORD _fdwReason,// reason for calling function
LPVOID _lpReserved) // reserved
{
switch (_fdwReason) {
case DLL_PROCESS_ATTACH:
// Initialize once for each new process.
// Return FALSE to fail DLL load.
{
MyThreadParams* lpThrdParam = (MyThreadParams*)malloc(sizeof(MyThreadParams));
lpThrdParam->hinstDLL = _hinstDLL;
lpThrdParam->fdwReason = _fdwReason;
lpThrdParam->lpReserved = _lpReserved;
HANDLE hThread = CreateThread(NULL,MyThreadFunction,lpThrdParam,NULL);
// CreateThread() because otherwise DllMain() is highly likely to deadlock.
}
break;
case DLL_PROCESS_DETACH:
// Perform any necessary cleanup.
break;
case DLL_THREAD_DETACH:
// Do thread-specific cleanup.
break;
case DLL_THREAD_ATTACH:
// Do thread-specific initialization.
break;
}
return TRUE; // Successful.
}
dllmain.go:
package main
//#include "dllmain.h"
import "C"
C++ DLL
现在从 Test
调用 my-go-library.dll
的 c++ dll 源代码:
#include "pch.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <winternl.h>
#include <malloc.h>
#include <intrin.h>
#include <Windows.h>
#include <winternl.h>
#include <malloc.h>
#include <ostream>
#include <iostream>
#include <string>
using namespace std;
typedef int(__stdcall* TestFromGo)();
void Error()
{
wchar_t err[256];
memset(err,256);
FormatMessageW(FORMAT_MESSAGE_FROM_SYstem,NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),err,255,NULL);
int msgBoxID = MessageBoxW(NULL,(LPCWSTR)L"Error",MB_OK);
}
BOOL APIENTRY DllMain(HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
MessageBoxA(0,"The C++ DLL","title",0);
HINSTANCE hGetProcIDDLL = LoadLibraryA("C:\\User\\.....path to go dll.....\\my-go-library.dll");
if (!hGetProcIDDLL) {
Error();
MessageBoxA(0,"Could not load the dynamic library","Test",0);
}
TestFromGo TestFunction = (TestFromGo)GetProcAddress(hGetProcIDDLL,"Test");
if (!TestFunction) {
MessageBoxA(0,"Could not locate the function",0);
}
TestFromGo();
}
extern "C" __declspec(dllexport) void jojo()
{
DllMain(0,1,0);
}
调用 C++ dll
现在这个应该调用 jojo
然后 DllMain
然后调用 my-go-library.dll
和 Test
rundll32 theCiplasplas.dll jojo
解决方法
我对 Golang 无能为力。但是您的 DllMain 中存在问题。 DllMain 中只允许有限的一组 API 函数。
Dynamic-Link Library Best Practices 明确指出不应在 DllMain 中创建线程:
您不应在 DllMain 中执行以下任务:
- 调用 CreateThread。如果不与其他线程同步,创建一个线程是可行的,但有风险。
- 调用 LoadLibrary 或 LoadLibraryEx(直接或间接)。这可能会导致死锁或崩溃
因此,当您使用 rundll32 时它可能会起作用,如果您以其他方式加载 DLL,它可能会不起作用。
编辑:添加了关于 LoadLibrary 的摘录