从 Microsoft Visual Studio C++ 调用 Go 代码的方法有吗?

3
有没有办法从Microsoft Visual Studio C++中调用Go代码?
请不要随意关闭我的问题。
这个链接如何使用Visual Studio编译器构建带有cgo的库?没有回答我的问题。它没有给出可行的解决方案。
目前似乎只能使用mingw gcc来编译cgo。https://github.com/golang/go/issues/49080#issuecomment-947865866 所以,目前看来这个问题是无法解决的。
因为mingw c头文件与Windows SDK并不完全相同,总会有一些代码无法正常工作。
这就像一个“存在”或“不存在”的问题。
如果你想从C++调用Go代码,请使用mingw gcc来编译所有的代码。
你不能用mingw编译cgo然后在Visual Studio C++中使用它。(也许可以,但总会有一些代码无法正常工作)
我找到的唯一方法是使用mingw gcc编译cgo,如此链接https://gist.github.com/geraldstanje/4624ac47eb3dec5b8bbe12d4714ed330
但是这种方法有一个问题。
如果您将.a库与普通Visual C ++代码链接,则会失败,因为.a库包含Visual C ++不知道的符号。
clang -w *.cpp v2pn.a
LINK : warning LNK4217: symbol '__acrt_iob_func' defined in 'libucrt.lib(_file.obj)' is imported by 'v2pn.a(000005.o)' in function '_cgo_preinit_init'
LINK : warning LNK4286: symbol '__acrt_iob_func' defined in 'libucrt.lib(_file.obj)' is imported by 'v2pn.a(000006.o)'
LINK : warning LNK4217: symbol '_errno' defined in 'libucrt.lib(errno.obj)' is imported by 'v2pn.a(000005.o)' in function '_cgo_beginthread'
v2pn.a(000005.o) : error LNK2019: unresolved external symbol __imp__beginthread referenced in function _cgo_beginthread
v2pn.a(000005.o) : error LNK2019: unresolved external symbol __mingw_vfprintf referenced in function fprintf
v2pn.a(000006.o) : error LNK2001: unresolved external symbol __mingw_vfprintf
a.exe : fatal error LNK1120: 2 unresolved externals
clang: error: linker command failed with exit code 1120 (use -v to see invocation)

如果你用mingw gcc将.a库与visual c++代码链接起来,
嗯,会有更多的问题。因为mingw头文件与Windows SDK头文件并不完全同步,会出现很多错误。
就像这个例子一样。
gcc.exe *.cpp v2pn.a
v2pn.cpp: In function 'void set_dns_by_guid(PCHAR)':
v2pn.cpp:48:5: error: 'DNS_INTERFACE_SETTINGS' was not declared in this scope
   48 |     DNS_INTERFACE_SETTINGS settings = {DNS_INTERFACE_SETTINGS_VERSION1};
      |     ^~~~~~~~~~~~~~~~~~~~~~
v2pn.cpp:49:5: error: 'settings' was not declared in this scope
   49 |     settings.Flags = 0x0002;
      |     ^~~~~~~~
v2pn.cpp:51:17: error: 'SetInterfaceDnsSettings' was not declared in this scope
   51 |     DWORD ret = SetInterfaceDnsSettings(interfaceGUID, &settings);
      |                 ^~~~~~~~~~~~~~~~~~~~~~~

这是因为mingw的netioapi.h头文件与Windows SDK的netioapi.h头文件不同。
而Windows SDK的netioapi.h头文件包含了DNS_INTERFACE_SETTINGS声明,但mingw的netioapi.h头文件没有。

你找到解决办法了吗?对于像我这样需要支持一些C代码但又不喜欢用C编程的人来说,从C到Go的互操作性可能非常棒。但是在Windows上,我似乎经常遇到各种问题。 - undefined
2个回答

1
我终于成功使用MSVC构建了静态库和共享库。
首先,你需要Mingw-w64来进行go build。我建议使用WinLibs端口。我已经测试过了GCC 13.2.0(带有MCF线程)+ MinGW-w64 11.0.1(UCRT)- release 2。检查你的$env:PATH,确保路径中没有其他的gcc.exewhere.exe gcc
然后,你可以使用go build来构建你的go代码。
要启用多线程支持,请使用/MD标志和cl.exe。这就是为什么你会得到错误unresolved external symbol __imp__beginthread
如果你使用WinLibs的Mingw-w64,你会看到错误unresolved external symbol vfprintf而不是__mingw_vfprintf。这是因为你需要库legacy_stdio_definitions.lib
但是cgo仍然无法成功初始化。你还需要一些魔法代码。
#ifdef _MSC_VER #ifdef __cplusplus extern "C" { #endif
__pragma(comment(lib, "legacy_stdio_definitions.lib"));
void _rt0_amd64_windows_lib();
__pragma(section(".CRT$XCU", read)); __declspec(allocate(".CRT$XCU")) void (*init_lib)() = _rt0_amd64_windows_lib;
__pragma(comment(linker, "/include:init_lib"));
#ifdef __cplusplus } #endif #endif
将这段代码添加到你的C/C++项目中(如果代码是纯C,请不要忘记删除extern "C")。特别感谢ks75vl。说真的,我不知道这段代码是做什么的。但它确实有效。
现在你可以成功使用MSVC构建你的C/C++项目了。
对于动态链接库(.dll),你还需要一个.def文件来导出你的函数。例如:
EXPORTS _rt0_amd64_windows_lib MyFunction
以及一个用于编译dll的简单C文件:
#include "mylib.h"
__pragma(comment(lib, "legacy_stdio_definitions.lib"));
详细的命令请参考示例仓库。我在readme中写了所有的命令,还有用C、C++、Objective-C和Swift编写的简单示例代码。是的,它也可以在macOS上使用Clang。只需查看readme和src/cgo文件夹即可。

1
就像你所说的,cgo不支持链接MSVC C++库。即使你尝试暴露类似C的API并尝试链接,很可能会遇到名称修饰问题。一个可行的解决方案是在需要暴露的C++ API周围编写一个C包装器,并使用正确的extern前缀。这将解决C++名称修饰问题,而cgo不区分C库是由哪个编译器编译的。
举个例子, API.cpp
class CppApi {
  int Run(int arg1);
}

CAPI.h

#ifdef API_EXPORTS
#define RUN_API __declspec(dllexport)
#else
#define RUN_API __declspec(dllimport)
#endif

#ifdef __cplusplus
extern "C" {
#endif
  RUN_API void* GetAPI();
  RUN_API int Run(void* obj, int arg1);
#ifdef __cplusplus
}
#endif

CAPI.cpp

void* GetAPI() {
  return (void*)(new CppApi());
}
int Run(void* obj, int arg1) {
  CppApi cppObj = CppApi*)obj;
  return cppObj->Run(arg1);
}

请注意,这个解决方案只适用于愿意暴露纯C函数而没有任何C++类型或任何Microsoft类型的情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接