The common "solution" to use GetProcAddress
with C++ is "extern "C", but that breaks overloading. Name mangling allows multiple functions to co-exist, as long as their signature differs. But is there a way to find these mangled names for GetProcAddress
?
Since C is a programming language that does not support name function overloading, it does no name mangling.
Name mangling is the encoding of function and variable names into unique names so that linkers can separate common names in the language. Type names may also be mangled. Name mangling is commonly used to facilitate the overloading feature and visibility within different scopes.
GetProcAddress verifies that the specified ordinal is in the range 1 through the highest ordinal value exported in the . def file. The function then uses the ordinal as an index to read the function's address from a function table.
It isn't specified by the standard, and has certainly changed between versions of the same compiler in my experience, though it has to be deterministic over some fixed set of circumstances, because otherwise there would be no way to link two separately compiled modules.
The VC++ compiler knows its own name mangling scheme, so why not use that? Inside template<typename T> T GetProcAddress(HMODULE h, const char* name)
, the macro __FUNCDNAME__
contains the mangled name of GetProcAddress
. That includes the T
part. So, inside GetProcAddress<void(*)(int)
, we have a substring with the mangled name of void(*)(int)
. From that, we can trivially derive the mangled name of void foo(int);
This code relies on the VC++ macro __FUNCDNAME__
. For MinGW you'd need to base this on .__PRETTY_FUNCTION__
instead
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.
}
Use dumpbin /exports 'file.dll'
to get the decorated / undecorated name of all the symbols.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With