Domanda

Possibile Duplicare:
C++ plugin per l'Unità “EntryPointNotFoundExeption”

Ho capito come evitare l'alterazione dei nomi con extern "C" alle singole funzioni in c++, ma c'è un modo per evitare che durante l'esportazione di funzioni membro?

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

Ora, quando ho voglia utilizzare queste funzioni in Unità, ho bisogno di decompilare il dll per capire qual è il mio EntryPoints sono per i nomi di funzione.Io non voglio avere a che fare questo.

So che ho avuto un po ' troppo zelante con extern "C"...

È stato utile?

Soluzione

AGGIORNAMENTO:Come @peechykeen sottolineato nei commenti, se avete intenzione di utilizzare .def poi direttamente, è possibile rinominare i nomi alterati.Sto per lasciare l'originale risposta anche se è molto più utile per nascondere esportato nomi piuttosto la ridenominazione di loro.

Originale risposta:

Un modo per farlo è quello di "nascondere" il esportato nomi dietro gli ordinali.Per questo è necessario definire un .def file e poi nelle ESPORTAZIONI sezione mettere tutti i esportato i nomi che si desidera nascondere.Ad esempio, per assegnare ordinale 1 per una funzione esportata da un Boost di serializzazione in questo modo:

EXPORTS
??0?$oserializer@Vportable_binary_oarchive@@U?$pair@$$CBHH@std@@@detail@archive@boost@@QEAA@XZ  @1  NONAME

E così via per tutte le funzioni.Ora, fare questo manualmente è sia noioso e soggetto a errori.E si ottenere gli errori di collegamento ogni volta che si modifica una qualsiasi parte del esportato interfaccia.Semi-automatizzare, io uso Dependency Walker e uno script Perl.Funziona in questo modo:

  1. In .def file di inserire i marcatori delle ESPORTAZIONI sezione:

    EXPORTS
    ;BEGIN_RENAMING_TAG
    ;END_RENAMING_TAG
    
  2. Caricare il binario di Dependency Walker, vai a funzioni esportate finestra, selezionare tutte le funzioni esportate e copiarli negli appunti.

  3. Passato il testo copiato tra inizio/FINE tags .file def.
  4. Eseguire il seguente script in Perl sul .file 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. Le voci nel vostro .def file ora sono tutti in <entry> @<ordinal> NONAME formato.

Ora, io non uso questi numeri ordinali per accedere alle funzioni, ho solo rinominarli come bug me per esporre centinaia di funzioni esportate, che non ho bisogno (Boost di serializzazione che io uso a sua volta utilizza dllexport per forza il collegamento delle sue funzioni).Così si avrebbe bisogno di fare di più in Perl script e dire esportare un enum con funzione di nomi e numeri ordinali.Se si vuole fare questo diritto sarebbe necessario implementare un demangling algoritmo in script in Perl che permette di preciso i nomi, prendersi cura di sovraccarico (stesso nome, diverso args) e mantenere il enum nome costanti.

Per accedere alle funzioni dietro ordinali, si utilizza GetProcAddress ma il cast del ordinale per LPCSTR.Vedere la documentazione per la funzione per ulteriori dettagli.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top