从 C++ dll 调用 Go dll

问题描述

我在 Go 中创建了这个带有导出函数的 dll,当我使用 rundll32 或从带有 loadlbraryA 的 c++ 可执行文件调用它时,该函数工作正常。但是当我从 C++ dll 调用时出现错误,我试图读取 GetLastError() 并且它说 cannot find the module you are calling。我尝试了所有可能的方法来提供 loadlibraryA 的完整路径,但发生了同样的错误,并且 Go DLL 的句柄每次都为空。此外,我检查了 Go DLL,并且测试函数正确导出。

简而言之,它在调用 test() 时起作用:

  • 来自 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.dllTest

rundll32 theCiplasplas.dll jojo

解决方法

我对 Golang 无能为力。但是您的 DllMain 中存在问题。 DllMain 中只允许有限的一组 API 函数。

Dynamic-Link Library Best Practices 明确指出不应在 DllMain 中创建线程:

您不应在 DllMain 中执行以下任务:

  • 调用 CreateThread。如果不与其他线程同步,创建一个线程是可行的,但有风险。
  • 调用 LoadLibrary 或 LoadLibraryEx(直接或间接)。这可能会导致死锁或崩溃

因此,当您使用 rundll32 时它可能会起作用,如果您以其他方式加载 DLL,它可能会不起作用。

编辑:添加了关于 LoadLibrary 的摘录

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...