Искажение имени в экспортированной функции-члене c++ в C# (Unity) [дублировать]
-
11-12-2019 - |
Вопрос
Возможный дубликат:
Плагин C++ для Unity “Entrypoint notfoundexeption”
Я понимаю, как предотвратить искажение имен с помощью extern "C" для отдельных функций в c++, но есть ли какой-либо способ предотвратить это при экспорте функций-членов?
WMIWrapper.cpp
namespace WMIWrapper
{
extern "C" {
WMIWrapper::WMIWrapper()
{
_locator = NULL;
_service = NULL;
_monitors = NULL;
}
WMIWrapper::~WMIWrapper()
{
if(_service != NULL)
_service->Release();
if(_locator != NULL)
_locator->Release();
}
void WMIWrapper::CreateCOM(wchar_t* err, int errLength)
{
wstringstream ERRStream (wstringstream::in | wstringstream::out);
HRESULT hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(FAILED(hRes))
{
ERRStream << "Unable to launch COM: 0x" << std::hex << hRes << endl;
}
hRes = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0);
if(FAILED(hRes))
{
ERRStream << "Unable to set security level for COM: " << std::hex << hRes << endl;
}
if(FAILED(hRes = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_ALL, IID_PPV_ARGS(&_locator))))
{
ERRStream << "Unable to create a WbemLocator: " << std::hex << hRes << endl;
}
if(ERRStream != NULL)
wcscpy_s(err, errLength, ERRStream.str().c_str());
}
void WMIWrapper::CreateService(wchar_t* err, int errLength)
{
wstringstream ERRStream (wstringstream::in | wstringstream::out);
HRESULT hRes;
if(_locator == NULL || FAILED(hRes = _locator->ConnectServer(L"root\\CIMV2", NULL, NULL, NULL, WBEM_FLAG_CONNECT_USE_MAX_WAIT, NULL, NULL, &_service)))
{
ERRStream << "Unable to connect to \"CIMV2\": " << std::hex << hRes << endl;
}
if(ERRStream != NULL)
wcscpy_s(err, errLength, ERRStream.str().c_str());
}
void WMIWrapper::GetMonitors(wchar_t* err, int errLength)
{
HRESULT hRes;
wstringstream ssMonitorDescription;
if(_locator == NULL
|| _service == NULL
|| FAILED(hRes = _service->ExecQuery(L"WQL", L"SELECT * FROM Win32_DesktopMonitor", WBEM_FLAG_FORWARD_ONLY, NULL, &_monitors)))
{
ssMonitorDescription << "Unable to retrieve desktop monitors: " << std::hex << hRes << endl;
wcscpy_s(err, errLength, ssMonitorDescription.str().c_str());
return;
}
IWbemClassObject* clsObj = NULL;
int numElems;
while((hRes = _monitors->Next(WBEM_INFINITE, 1, &clsObj, (ULONG*)&numElems)) != WBEM_S_FALSE)
{
if(FAILED(hRes))
break;
VARIANT vRet;
VariantInit(&vRet);
if(SUCCEEDED(clsObj->Get(L"Description", 0, &vRet, NULL, NULL)) && vRet.vt == VT_BSTR)
{
ssMonitorDescription << "Description: " << vRet.bstrVal << endl;
VariantClear(&vRet);
}
}
clsObj->Release();
wcscpy_s(err, errLength, ssMonitorDescription.str().c_str());
}
void WMIWrapper::HelloWorld(wchar_t* testString, int length)
{
wstring hello = L"Hello World";
wcscpy_s(testString, length, hello.c_str());
}
}
}
WMIWrapper.h
#ifndef _WMIWRAPPER_H_
#define _WMIWRAPPER_H_
#include <Windows.h>
#include <sstream>
#include <iostream>
#include <WbemCli.h>
using std::endl;
using std::wstring;
using std::wstringstream;
#pragma comment(lib, "wbemuuid.lib")
namespace WMIWrapper
{
extern "C" {
class WMIWrapper
{
public:
WMIWrapper();
~WMIWrapper();
__declspec(dllexport) void CreateCOM(wchar_t*, int);
__declspec(dllexport) void CreateService(wchar_t*, int);
__declspec(dllexport) void GetMonitors(wchar_t*, int);
__declspec(dllexport) void HelloWorld(wchar_t*, int);
private:
IWbemLocator* _locator;
IWbemServices* _service;
IEnumWbemClassObject* _monitors;
};
}
}
#endif
Теперь, когда я хочу использовать эти функции в Unity, мне нужно декомпилировать библиотеку dll, чтобы узнать, каковы мои точки входа для имен функций.Я не хочу, чтобы мне приходилось это делать.
Я знаю, что немного переусердствовал с внешней буквой "С"...
Решение
ОБНОВЛЕНИЕ:Как отметил @peechykeen в комментариях, если вы собираетесь использовать .def, тогда вы можете напрямую переименовать свои искаженные имена.Я оставляю оригинальный ответ, хотя он гораздо полезнее для скрытия экспортированных имен, а не для их переименования.
Оригинальный ответ:
Один из способов сделать это - "скрыть" экспортируемые имена за порядковыми номерами.Для этого вам нужно будет определить файл .def, а затем в его разделе ЭКСПОРТА поместить все экспортируемые имена, которые вы хотите скрыть.Например, чтобы присвоить порядковый номер 1 функции, экспортируемой с помощью ускоренной сериализации, вы могли бы сделать это:
EXPORTS
??0?$oserializer@Vportable_binary_oarchive@@U?$pair@$$CBHH@std@@@detail@archive@boost@@QEAA@XZ @1 NONAME
И так далее для всех функций.Теперь делать это вручную одновременно утомительно и чревато ошибками.И вы будете получать ошибки ссылок каждый раз, когда будете изменять какую-либо часть экспортированного интерфейса.Чтобы полуавтоматизировать это, я использую Зависимый ходок и скрипт на Perl.Это работает следующим образом:
В файле .def разместите маркеры в разделе ЭКСПОРТА:
EXPORTS ;BEGIN_RENAMING_TAG ;END_RENAMING_TAG
Загрузите двоичный файл в Dependency Walker, перейдите в окно экспортированных функций, выберите все экспортированные функции и скопируйте их в буфер обмена.
- Вставьте скопированный текст между тегами BEGIN/END в файле .def.
Запустите следующий Perl-скрипт в файле .def:
#perl -w print $ARGC; die "ERROR: Provide name of one DEF file to process\n" if @ARGV != 1; my $renaming = 0; my $counter = 1; my $fileName = $ARGV[0]; my @lines; open(FILE, $fileName) or die $!; while(<FILE>) { if(/;END_RENAMING_TAG/) { $renaming = 0; } if($renaming == 1) { chomp; my $line = $_."\t@".$counter."\tNONAME"; push(@lines, $line); ++$counter; } else { chomp; push(@lines, $_); } if(/;BEGIN_RENAMING_TAG/) { $renaming = 1; } } close FILE; open(FILE, ">$fileName") or die $!; print FILE join("\n", @lines); close FILE;
Все записи в вашем файле .def теперь находятся в
<entry> @<ordinal> NONAME
формат.
Теперь я не использую эти порядковые номера для доступа к функциям, я просто переименовываю их, поскольку мне мешает предоставлять сотни экспортированных функций, которые мне не нужны (ускоренная сериализация, которую я использую, в свою очередь, использует dllexport
чтобы принудительно связать его функции).Таким образом, вам нужно было бы сделать больше в Perl-скрипте и, скажем, экспортировать перечисление с именами функций и порядковыми номерами.Если вы хотите сделать это правильно, вам нужно было бы реализовать алгоритм разборки в скрипте Perl, который давал бы вам точные имена, заботился бы о перегрузке (одно и то же имя, разные аргументы) и сохранял бы константы имен перечислений.
Чтобы получить доступ к функциям, стоящим за ординалами, вы используете GetProcAddress
но приведите порядковый номер к LPCSTR.Более подробную информацию смотрите в документации к этой функции.