1

我想做标题所说的。之前已经讨论过这个问题(How to create dll using gcc compiler/Mingw for visual basic?),但这些帖子已有 10 年历史。那时我正在毫无困难地编写 Excel/VBA 宏,但现在我想将它们更新到 64 位 Windows。从那时起,我也切换了编译器。gcc使用orgfortran调用的DLL 创建 DLL 时我没有遇到任何gcc问题Python。很难理解兼容性问题,因为你不知道一篇文章是什么时候写的,也不知道它在 64 位世界中是否仍然有效。你可能会因为我重新讨论这个老话题而责备我,但也许其他人也有类似的问题。我还找到了一篇很棒的文章(https://www.transmissionzero.co.uk/computing/building-dlls-with-mingw/)。我已尝试遵循这两种方法,但无法在 Excel 电子表格中进行任何操作。也许我犯了一个对某人来说很明显的错误。这是函数,code.c

__declspec(dllexport) int __stdcall sqint(int x){
   return x * x;
}

我使用以下方法构建 DLL:

gcc code.c -shared -o code.dll -s -Wl,--subsystem,windows,--kill-at,-Map=code.map

code.map 文件和 objdump 实用程序都显示了函数,例如这里有几行来自objdump -p code.dll

[Ordinal/Name Pointer] Table
    [   0] sqfloat
    [   1] sqint

哪里sqfloat是一个类似的双打代码。地图文件还显示了对库的引用,例如:

/usr/lib/gcc/x86_64-pc-cygwin/7.4.0/../../../../lib/libcygwin.a

接下来,我在 VBA 中声明它:

Declare PtrSafe Function sqint Lib "C:\Cwin\mix\code.dll" (ByVal x As Long) As Long

Visual Basic long 和 gcc int 都应该是 4 个字节。我应该可以直接调用该函数,但我尝试在调试期间将其放入另一个函数中。使用 VBA 调试器,我可以看到传递给函数的值,但除了可怕的#VALUE!. 如果您看到我做错了什么,我会很高兴收到您的来信。

2020 年 7 月 2 日更新:我再次尝试解决这个问题,但成功有限。我相信我创建的 DLL 不是 64 位的。我认为这将是默认设置。构建现在是这样的:

gcc code.c -Wa,-march=generic64 -shared -o code.dll -s -Wl,--subsystem,windows,--kill-at,-Map=code.map

如何测试 DLL的帖子之后,使用file code.dll给出:

code.dll: PE32+ executable (DLL) (GUI) x86-64 (stripped to external PDB), for MS Windows

sqfloat 函数现在返回正确的结果,但最终 Excel 会出错并关闭。sqint 函数返回 2*2 = 1。我尝试使用Integer而不是long,但没有变化。MSVBA 是否改变了 long 的定义?

所以目前的状态是我偶尔会得到一个结果,但是宏会杀死 Excel。

4

1 回答 1

1

我正在尝试创建一个 C 包装 DLL 来接口使用 CDECL 调用约定编写的商业 DLL,以便它可以与用 Excel VBA 编写的现有程序接口(因此使用 STDCALL 调用约定)。

我有完全相同的问题,可能是在尝试了非常相似的事情之后。我尝试过使用 mingw(gcc 和 g++,32 和 64 位)和 cygwin gcc 风格(gcc 和 g++ 32 和 64 位)。没运气。

我怀疑编译器或链接器存在问题,因为某些编译器组合生成的 DLL 无法被 VBA 识别(尽管它们可以被存根 exe 调用)抛出错误 53-DLL not found。其他组合会产生另一个错误(加载 DLL 的 48 错误),当然代码完全相同。

我正在使用带有 Office 64 位的 Windows 10 64 位。

这是 wrapper.c:

    #include "wrapper.h"
    #include <windows.h>
    #include <stdio.h>

    #ifdef cplusplus
    extern "C"
    {
    #endif

    PCNC_EXPORT void __stdcall WrFreeString(char* str)
    {
        //Memory
        return FreeString(str);
    }
    PCNC_EXPORT int __stdcall WrGetVer()
    {
        return GetVer();
    }
    PCNC_EXPORT char* __stdcall WrGetLibPath()
    {
        return GetLibPath();
    }

    // snip ___


    PCNC_EXPORT BOOL __stdcall DllMain(HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }

    #ifdef cplusplus
    }
    #endif

包装器.h:

    #include "PlanetCNCLib.h"
    #include "PlanetCNCLibTypeDef.h"

    #ifdef cplusplus
    extern "C"
    {
    #endif

    PCNC_EXPORT void __stdcall WrFreeString(char* str);
    PCNC_EXPORT int __stdcall WrGetVer();
    PCNC_EXPORT char* __stdcall WrGetLibPath();

    #ifdef cplusplus
    }
    #endif

来自 PlanetCNCLib.h:

    #ifndef __PlanetCNCLib_H__
    #define __PlanetCNCLib_H__


    #include "PlanetCNCLibTypeDef.h"


    #ifdef __cplusplus
    extern "C" {
    #endif


    #ifndef PCNC_EXPORT
    #   ifdef _WIN32
    #       define PCNC_EXPORT __declspec(dllexport)
    #   else
    #       define PCNC_EXPORT __attribute__((visibility("default"))) 
    #   endif
    #endif

    // etc

和Makefile:

    #CPP=/usr/bin/i686-w64-mingw32-g++.exe   # doesnt work (err 48 err loading DLL)
    #CPP=/usr/bin/x86_64-w64-mingw32-g++.exe # doesnt work (err 53 DLL not found)
    #CPP=/usr/bin/i686-pc-cygwin-g++         # doesnt work (err 48 err loading DLL) 
    #CPP=/usr/bin/x86_64-pc-cygwin-g++.exe   # doesnt work (err 53 DLL not found)
    #CPP=gcc                                 # doesnt even compile

    #LD=/usr/bin/i686-w64-mingw32-ld.exe
    #LD=/usr/bin/x86_64-w64-mingw32-ld.exe
    LD=$(CPP)

    LDFLAGS=-shared -L/usr/lib -L./lib/ -Wl,--add-stdcall-alias #--kill-at
    CFLAGS=-c

    all: PlanetCNCWrapper.dll

    wrapper.o:PlanetCNCLib.h PlanetCNCLibTypeDef.h wrapper.c
        $(CPP) -o $@ wrapper.c  $(CFLAGS)

    PlanetCNCWrapper.dll:wrapper.o
        $(LD) $(LDFLAGS) -o $@ $? -L./lib/ lib/PlanetCNCLib64.lib -lPlanetCNCLib64 

    clean:
        rm -f *.o *.e *~ .*~ *stackdump PlanetCNCWrapper.dll

其他有用的数据:

    Windows Version 1909 (OS Build 18363.778)
    System type: 64-bit operating system, x64-based processor
    Microsoft Excel for Office 365 MSO (16.0.11929.20708) 64 bits
    i686-w64-mingw32-g++ (GCC) 9.2.0
    x86_64-w64-mingw32-g++ (GCC) 9.2.0
    i686-pc-cygwin-g++ (GCC) 6.4.0
    x86_64-pc-cygwin-g++ (GCC) 9.3.0

忘了添加 --add-stdlib-alias 似乎没有做任何事情:符号在 DLL 中被修饰,不管这个标志的存在。

于 2020-04-26T19:37:03.390 回答