有没有办法找到要在 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:你说得对,我误读了文档。确实没有这个宏,<cxxabi.h>
header 只提供了 de-mangle 功能。
@MSalters 很有趣,但它不适用于我尝试过的函数,因为 dll 中的损坏名称中有反向引用,但在您的函数中却没有;因为该函数在命名空间中,并且返回同一命名空间中的类的对象;在返回类型中,命名空间名称被替换为 @1 ...
备案:dll中的名称:?myFunction@sfw@@YAAAVEStore@1@XZ 由模板化的GetProcAddress推导出的名称:?myFunction@sfw@@YAAAVEStore@sfw@@XZ
@F4:奇怪,__FUNCDNAME__
里面的 GetProcAddress<T>
是什么?我曾期望它具有相同的 @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失败