简体   繁体   English

从 C++ Z06416233FE5EC4C5933122E4AB248AF1 调用 Go dll

[英]Calling a Go dll from a C++ dll

I have created this dll in Go with an exported function which works fine while I'm calling it with rundll32 or from a c++ executable with the loadlbraryA. I have created this dll in Go with an exported function which works fine while I'm calling it with rundll32 or from a c++ executable with the loadlbraryA. But I get an error when calling from c++ dll which I tried to read GetLastError() And it says cannot find the module you are calling .但是从 c++ dll 调用时出现错误,我试图读取GetLastError()它说cannot find the module you are calling I tried every possible way to give the full path to loadlibraryA but the same error happens and the handle to Go DLL is null every time.我尝试了所有可能的方法来提供 loadlibraryA 的完整路径,但同样的错误发生了,并且 Go DLL 的句柄每次都是 null。 Also, I examined the Go DLL, and the Test function is exported properly.此外,我检查了 Go DLL,测试 function 正确导出。

In short, It works in calling Test():简而言之,它在调用 Test() 时起作用:

  • From rundll32从rundll32
  • From c++ main() and loadlibrary从 c++ main() 和 loadlibrary

And doesn't work in Test():并且在 Test() 中不起作用:

  • From c++ DllMain() and loadlibrary从 c++ DllMain() 和 loadlibrary
  • From c++ DllMain() and loadlibrary directly called from kernel32.dll从 c++ DllMain() 和 loadlibrary 直接从 kernel32.dll 调用
  • From c++ DllMain() and a ThreadFunction created in DllMain()来自 c++ DllMain() 和在 DllMain() 中创建的 ThreadFunction

Folder structure文件夹结构

ProjectFolder
-- main.go
-- dllmain.h
-- dllmain.go

Build with构建

go build -buildmode=c-shared -o my-go-library.dll main.go

Call example dll调用示例 dll

rundll32 my-go-library.dll Test

Working source工作源

main.go: 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, caption, title, MB_OK)
}

func main() {}

dllmain.h: 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, 0, MyThreadFunction, lpThrdParam, 0, 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: dllmain.go:

package main

//#include "dllmain.h"
import "C"

C++ DLL C++ DLL

Now the c++ dll source which calls Test from my-go-library.dll :现在 c++ dll 源代码从my-go-library.dll调用Test

#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, 0, 256);
    FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err, 255, NULL);
    int msgboxID = MessageBoxW(NULL,err,(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", "Test", 0);
    }

    TestFromGo();
}

extern "C" __declspec(dllexport) void jojo()
{
    DllMain(0, 1, 0);
}

Calling the c++ dll调用 c++ dll

Now this one supposed to call jojo then DllMain then calling my-go-library.dll and Test现在这个应该调用jojo然后DllMain然后调用my-go-library.dllTest

rundll32 theCiplasplas.dll jojo

I can't help with Golang.我对 Golang 无能为力。 But there are problems in your DllMain.但是您的 DllMain 中存在问题。 There are only a limited set of API functions allowed in DllMain . DllMain中仅允许一组有限的 API 函数。

The Dynamic-Link Library Best Practices explicitly states that you should not create a thread in DllMain: 动态链接库最佳实践明确指出您不应在 DllMain 中创建线程:

You should never perform the following tasks from within DllMain:永远不要在 DllMain 中执行以下任务:

  • Call CreateThread.调用 CreateThread。 Creating a thread can work if you do not synchronize with other threads, but it is risky.如果你不与其他线程同步,创建一个线程可以工作,但它是有风险的。
  • Call LoadLibrary or LoadLibraryEx (either directly or indirectly).调用 LoadLibrary 或 LoadLibraryEx(直接或间接)。 This can cause a deadlock or a crash这可能会导致死锁或崩溃

So it might work when you use rundll32, it might not work if you load your DLL in another way.因此,当您使用 rundll32 时它可能会起作用,如果您以另一种方式加载 DLL,它可能会不起作用。

Edit : Added excerpt about LoadLibrary编辑:添加了关于 LoadLibrary 的摘录

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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