Search code examples
c++govisual-c++mingwcgo

Is there a way to call go code from Microsoft Visual Studio C++?


Is there a way to call go code from Microsoft Visual Studio C++?

Please Do Not close my question arbitrarily.

This link How to build a library with cgo using visual studio compiler? does not answer my question. It doesn't give a workable solution.

It seems cgo can only be compiled with mingw gcc at this time. https://github.com/golang/go/issues/49080#issuecomment-947865866

So, it seems this problem is unsolvable at this time.

Because mingw c header files are not completely the same as Windows SDK, there will always be some code that won't work.

This is like a "to be" or "not to be" problem.

If you want to call go code from c++, use mingw gcc to compile all your code.

you can't compile cgo with mingw and then use it in Visual Stdudio C++.(Maybe it will work, but there will always be some code that won't work)

All I can found is compile cgo with mingw gcc like this link https://gist.github.com/geraldstanje/4624ac47eb3dec5b8bbe12d4714ed330

But this method has a problem.

If you link the .a library with normal visual c++ code, it faied because .a library contains symbol that visual c++ doesn't know

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)

If you link the .a library with visual c++ code with mingw gcc,

Well, there will be much more problems. Because the mingw headers are not completely synchronous with windows SDK headers, there will be a lot errors.

Like this one

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);
      |                 ^~~~~~~~~~~~~~~~~~~~~~~

This is because the mingw netioapi.h header file is different from the Windows SDK netioapi.h.

And the Windows SDK netioapi.h header file contains the DNS_INTERFACE_SETTINGS declaration, but the mingw netioapi.h header file doesn't.


Solution

  • I finally successfully built both static and shared libraries with MSVC.

    1. First, you need Mingw-w64 for go build. I would recommend WinLibs port. I have tested with GCC 13.2.0 (with MCF threads) + MinGW-w64 11.0.1 (UCRT) - release 2. Check your $env:PATH to make sure there is no other gcc.exe in your path: where.exe gcc.

    2. Then you can use go build to build your go code.

    3. To enable multithread support, use /MD flag with cl.exe. That's why you got error unresolved external symbol __imp__beginthread.

      If you using WinLibs's Mingw-w64, you will see error unresolved external symbol vfprintf instead of __mingw_vfprintf. That's because you need the library legacy_stdio_definitions.lib.

      But cgo still unable to initialize successfully. You also need some magic code.

      #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
      

      Add this code to your C/C++ project (do not forget to remove extern "C" if the code is pure C). Special thanks to ks75vl. Seriously, I have no idea what this code does. But it does work.

    4. You can successfully build your C/C++ project with MSVC now.

    5. For dynamic-link library (.dll), you also need a .def file to export your functions. For example:

      EXPORTS
          _rt0_amd64_windows_lib
          MyFunction
      

      And a simple C file for compiling dll:

      #include "mylib.h"
      
      __pragma(comment(lib, "legacy_stdio_definitions.lib"));
      

    For detailed commands, here is a example repo. I have written all the commands in readme and pretty simple example code with C, C++, Objective-C, and Swift. Yes, it also works with Clang on macOS. Just check the readme and src/cgo folder.