x64 DLL export function names

前端 未结 4 1091
小鲜肉
小鲜肉 2021-02-01 10:00

I am trying to port a 32-bit dll (and application) to 64-bit and I have managed to build it without errors. When trying to load it with my 64-bit application I noticed that the

4条回答
  •  臣服心动
    2021-02-01 10:27

    As you can tell, in 64-bit Windows names are not decorated.

    In 32-bit __cdecl and __stdcall symbols, the symbol name is prepended by an underscore. The trailing '@8' in the exported name for the 32-bit version of your example function is the number of bytes in the parameter list. It is there because you specified __stdcall. If you use the __cdecl calling convention (the default for C/C++ code), you won't get that. If you use __cdecl, it makes it much easier to wrap GetProcAddress() with something like:

    #if _WIN64
    #define DecorateSymbolName(s)   s
    #else
    #define DecorateSymbolName(s)   "_" ## s
    #endif
    

    then just call with

    pfnConnect   = GetProcAddress(hDLL, DecorateSymbolName("Connect"));
    pfnOtherFunc = GetProcAddress(hDLL, DecorateSymbolName("OtherFunc"));
    

    or something similar (error checking omitted in example). To do this, remember to declare your exported functions as:

    __declspec(dllexport) long __cdecl Connect(char * name, long size);
    __declspec(dllexport) long __cdecl OtherFunc(int someValue);
    

    In addition to being easier to maintain, if during development the signature of an exported function changes, you don't have to screw around with your #define wrappers.

    Downside: if during development the number of bytes in a given function's parameter list changes, it will not be caught by the application importing the function because the changing the signature will not change the name. Personally, I don't think this is an issue because the 64-bit build would blow up under the same circumstances anyway as the names are not decorated. You just have to make sure your application is using the right version of the DLL.

    If the user of the DLL is using C++, you can wrap things in a better way using C++ capabilities (wrap the entire explicitly-loaded library in a wrapper class, e.g.):

    class MyDLLWrapper {
    public:
      MyDLLWrapper(const std::string& moduleName);  // load library here
      ~MyDLLWrapper();                              // free library here
    
      FARPROC WINAPI getProcAddress(const std::string& symbolName) const {
        return ::GetProcAddress(m_hModule, decorateSymbolName(symbolName));
      }
      // etc., etc.
    private:
      HMODULE m_hModule;
      // etc.
      // ...
    };
    

    There's actually a lot more you can do with a wrapper class like this, it's just an example.

    On edit: since OP mentioned using PInvoke in the comments - if anyone decides to do this, do not forget to add CallingConvention = CallingConvention.Cdecl in the [DllImport] declaration when using PInvoke. __cdecl might be the default for unmanaged C/C++, but is not the default for managed code.

提交回复
热议问题