有没有办法找到要在 GetProcAddress 中使用的 C++ 错位名称?

Posted

技术标签:

【中文标题】有没有办法找到要在 GetProcAddress 中使用的 C++ 错位名称?【英文标题】:Is there a way to find the C++ mangled name to use in GetProcAddress? 【发布时间】:2013-04-15 13:42:05 【问题描述】:

GetProcAddress 与 C++ 一起使用的常见“解决方案”是“extern”C”,但这会破坏重载。名称修改允许多个函数共存,只要它们的签名不同。但是有没有办法找到GetProcAddress 的这些错位名称?

【问题讨论】:

【参考方案1】:

VC++ 编译器知道它自己的名称修饰方案,那么为什么不使用它呢?在template<typename T> T GetProcAddress(HMODULE h, const char* name) 内部,宏__FUNCDNAME__ 包含GetProcAddress 的错位名称。这包括T 部分。因此,在GetProcAddress<void(*)(int) 中,我们有一个名称为void(*)(int) 的子字符串。由此,我们可以简单地推导出void foo(int); 的错位名称

此代码依赖于 VC++ 宏 __FUNCDNAME__对于 MinGW,您需要将其基于 __PRETTY_FUNCTION__

FARPROC GetProcAddress_CppImpl(HMODULE h, const char* name, std::string const& Signature)

    // The signature of T appears twice in the signature of T GetProcAddress<T>(HMODULE, const char*) 
    size_t len = Signature.find("@@YA");
    std::string templateParam = Signature.substr(0, len);
    std::string returnType    = Signature.substr(len+4);
    returnType.resize(templateParam.size()); // Strip off our own arguments (HMODULE and const char*)
    assert(templateParam == returnType); 
    // templateParam and returnType are _pointers_ to functions (P6), so adjust to function type (Y)
    std::string funName = "?" + std::string(name) + "@@Y" + templateParam.substr(2);
    return ::GetProcAddress(h, funName.c_str());


template <typename T>
T GetProcAddress(HMODULE h, const char* name)

    // Get our own signature. We use `const char* name` to keep it simple.
    std::string Signature = __FUNCDNAME__ + 18; // Ignore prefix "??$GetProcAddress@"
    return reinterpret_cast<T>(GetProcAddress_CppImpl(h, name, Signature));


// Showing the result

struct Dummy  ;

__declspec(dllexport) void foo( const char* s)

    std::cout << s;


__declspec(dllexport) void foo( int i, Dummy )

    std::cout << "Overloaded foo(), got " << i << std::endl;


__declspec(dllexport) void foo( std::string const& s )

    std::cout << "Overloaded foo(), got " << s << std::endl;


__declspec(dllexport) int foo( std::map<std::string, double> volatile& )

    std::cout << "Overloaded foo(), complex type\n";
    return 42;


int main()

    HMODULE h = GetModuleHandleW(0);
    foo("Hello, ");
    auto pFoo1 = GetProcAddress<void (*)( const char*)>(h, "foo");
    // This templated version of GetProcAddress is typesafe: You can't pass 
    // a float to pFoo1. That is a compile-time error.
    pFoo1(" world\n");
    auto pFoo2 = GetProcAddress<void (*)( int, Dummy )>(h, "foo");
    pFoo2(42, Dummy()); // Again, typesafe.
    auto pFoo3 = GetProcAddress<void (*)( std::string const& )>(h, "foo");
    pFoo3("std::string overload\n");
    auto pFoo4 = GetProcAddress<int (*)( std::map<std::string, double> volatile& )>(h, "foo");
    // pFoo4 != NULL, this overload exists.
    auto pFoo5 = GetProcAddress<void (*)( float )>(h, "foo");
    // pFoo5==NULL - no such overload.

【讨论】:

请注意,__PRETTY_FUNCTION__ 不会这样工作,因为它没有被破坏,它看起来像 T GetProcAddress(HMODULE, const char*) [with T = blahblah]。我不确定在 GCC 下是否存在任何诸如损坏的函数名称宏之类的东西(不这么认为)。 @Damon:你说得对,我误读了文档。确实没有这个宏,&lt;cxxabi.h&gt; header 只提供了 de-mangle 功能。 @MSalters 很有趣,但它不适用于我尝试过的函数,因为 dll 中的损坏名称中有反向引用,但在您的函数中却没有;因为该函数在命名空间中,并且返回同一命名空间中的类的对象;在返回类型中,命名空间名称被替换为 @1 ... 备案:dll中的名称:?myFunction@sfw@@YAAAVEStore@1@XZ 由模板化的GetProcAddress推导出的名称:?myFunction@sfw@@YAAAVEStore@sfw@@XZ @F4:奇怪,__FUNCDNAME__ 里面的 GetProcAddress&lt;T&gt; 是什么?我曾期望它具有相同的 @1 反向引用。【参考方案2】:

使用dumpbin /exports 'file.dll' 获取所有符号的修饰/未修饰名称。

【讨论】:

【参考方案3】:

仅使用GetProcAddress 是不可能做到的。但是,一种方法是枚举该特定模块的所有导出函数,并进行模式匹配以查找所有损坏的名称。

更具体地说,请参阅this answer here。您需要做的唯一更改是将TRUE 传递给MappedAsImage 参数,并将GetModuleHandle 的返回值作为Base 参数传递给ImageDirectoryEntryToData 函数调用。

void EnumerateExportedFunctions(HMODULE hModule, vector<string>& slListOfDllFunctions)

    DWORD *dNameRVAs(0);
    _IMAGE_EXPORT_DIRECTORY *ImageExportDirectory;
    unsigned long cDirSize;
    _LOADED_IMAGE LoadedImage;
    string sName;
    slListOfDllFunctions.clear();

    ImageExportDirectory = (_IMAGE_EXPORT_DIRECTORY*)
        ImageDirectoryEntryToData(hModule,
        TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &cDirSize);
    if (ImageExportDirectory != NULL)
    
        dNameRVAs = (DWORD *)ImageRvaToVa(LoadedImage.FileHeader, 
            LoadedImage.MappedAddress,
        ImageExportDirectory->AddressOfNames, NULL);
        for(size_t i = 0; i < ImageExportDirectory->NumberOfNames; i++)
        
            sName = (char *)ImageRvaToVa(LoadedImage.FileHeader, 
                    LoadedImage.MappedAddress,
                   dNameRVAs[i], NULL);
         slListOfDllFunctions.push_back(sName);
        
    

【讨论】:

【参考方案4】:

我无法完全理解为什么您会想要/需要 MSalters' solution 的 constexpr 版本,但它就是这样,完成了命名空间修改。用作

using F = int(double*);
constexpr auto f = mangled::name<F>([] return "foo::bar::frobnicate"; );
constexpr const char* cstr = f.data();

其中F 是函数签名,foo::bar::frobnicate 是函数的(可能是限定的)名称。

#include<string_view>
#include<array>

namespace mangled

    namespace detail
    
        template<typename F>
        inline constexpr std::string_view suffix()
        
            auto str = std::string_view(__FUNCDNAME__);
            return str.substr(14, str.size() - 87);
        
        
        template<typename L>
        struct constexpr_string
        
            constexpr constexpr_string(L) 
            static constexpr std::string_view data = L();
        ;

        template<typename Name>
        inline constexpr int qualifiers()
        
            int i = -2, count = -1;
            while(i != std::string_view::npos)
            
                i = Name::data.find("::", i + 2);
                count++;
            
            return count;
        

        template<typename Name>
        inline constexpr auto split()
        
            std::array<std::string_view, qualifiers<Name>() + 1> arr = ;
            int prev = -2;
            for(int i = arr.size() - 1; i > 0; i--)
            
                int cur = Name::data.find("::", prev + 2);
                arr[i] = Name::data.substr(prev + 2, cur - prev - 2);
                prev = cur;
            
            arr[0] = Name::data.substr(prev + 2);
            return arr;
        

        template<typename F, typename Name>
        struct name_builder
        
            static constexpr auto suf = detail::suffix<F>();
            static constexpr auto toks = split<Name>();
            static constexpr auto len = Name::data.size() + suf.size() - toks.size() + 6;
            static constexpr auto str = [] 
                std::array<char, len> arr = ;
                arr[0] = '?';
                int i = 1;
                for(int t = 0; t < toks.size(); t++)
                
                    if(t > 0)
                        arr[i++] = '@';
                    for(auto c : toks[t])
                        arr[i++] = c;
                

                arr[i++] = '@';
                arr[i++] = '@';
                arr[i++] = 'Y';

                for(auto c : suf)
                    arr[i++] = c;
                
                return arr;
            ();
        ;
    

    template<typename F, typename LambdaString>
    inline constexpr std::string_view name(LambdaString)
    
        using Cs = detail::constexpr_string<LambdaString>;
        using N = detail::name_builder<F, Cs>;
        return N::str.data(), N::len;
    

【讨论】:

以上是关于有没有办法找到要在 GetProcAddress 中使用的 C++ 错位名称?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中转换 GetProcAddress 返回的指针

Mingw,XAudio2和GetProcAddress失败

为啥传统的 GetProcAddress 到 std::function 工作不简单

GetProcAddress 使用注意事项

选择要在 Jenkins 中构建的分支

delphi中调用.OCX怎样进行的?万分感谢!