Искажение имени в экспортированной функции-члене c++ в C# (Unity) [дублировать]

StackOverflow https://stackoverflow.com//questions/11652585

Вопрос

Возможный дубликат:
Плагин 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.Это работает следующим образом:

  1. В файле .def разместите маркеры в разделе ЭКСПОРТА:

    EXPORTS
    ;BEGIN_RENAMING_TAG
    ;END_RENAMING_TAG
    
  2. Загрузите двоичный файл в Dependency Walker, перейдите в окно экспортированных функций, выберите все экспортированные функции и скопируйте их в буфер обмена.

  3. Вставьте скопированный текст между тегами BEGIN/END в файле .def.
  4. Запустите следующий 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;
    
  5. Все записи в вашем файле .def теперь находятся в <entry> @<ordinal> NONAME формат.

Теперь я не использую эти порядковые номера для доступа к функциям, я просто переименовываю их, поскольку мне мешает предоставлять сотни экспортированных функций, которые мне не нужны (ускоренная сериализация, которую я использую, в свою очередь, использует dllexport чтобы принудительно связать его функции).Таким образом, вам нужно было бы сделать больше в Perl-скрипте и, скажем, экспортировать перечисление с именами функций и порядковыми номерами.Если вы хотите сделать это правильно, вам нужно было бы реализовать алгоритм разборки в скрипте Perl, который давал бы вам точные имена, заботился бы о перегрузке (одно и то же имя, разные аргументы) и сохранял бы константы имен перечислений.

Чтобы получить доступ к функциям, стоящим за ординалами, вы используете GetProcAddress но приведите порядковый номер к LPCSTR.Более подробную информацию смотрите в документации к этой функции.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top