-4

重複の可能性:
Unity の C++ プラグイン「EntryPointNotFoundExeption」

C++ の個々の関数で extern "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 を逆コンパイルして、関数名の EntryPoints を確認する必要があります。私はこれをする必要はありません。

私はextern "C"で少し熱心になったことを知っています...

4

1 に答える 1

3

更新: @peechykeen がコメントで指摘したように、.def を使用する場合は、マングルされた名前を直接変更できます。名前を変更するのではなく、エクスポートされた名前を非表示にする方がはるかに便利ですが、元の回答を残しています。

元の答え:

これを行う 1 つの方法は、エクスポートされた名前を序数の後ろに「隠す」ことです。そのためには、.def ファイルを定義し、その EXPORTS セクションに非表示にするすべてのエクスポート名を入れる必要があります。たとえば、Boost シリアライゼーションによってエクスポートされた関数に序数 1 を割り当てるには、次のようにします。

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

すべての機能についても同様です。現在、これを手動で行うのは面倒で、エラーが発生しやすくなっています。また、エクスポートされたインターフェイスの一部を変更するたびに、リンク エラーが発生します。それを半自動化するために、Dependency Walkerと Perl スクリプトを使用します。それはこのように動作します:

  1. .def ファイルでは、EXPORTS セクションにマーカーを配置します。

    EXPORTS
    ;BEGIN_RENAMING_TAG
    ;END_RENAMING_TAG
    
  2. バイナリを Dependency Walker にロードし、エクスポートされた関数ウィンドウに移動し、エクスポートされたすべての関数を選択してクリップボードにコピーします。

  3. コピーしたテキストを .def ファイルの BEGIN/END タグの間に貼り付けます。
  4. .def ファイルで次の Perl スクリプトを実行します。

    #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 スクリプトにデマングリング アルゴリズムを実装して、正確な名前を付け、オーバーロード (同じ名前、異なる引数) に注意し、enum 名の定数を維持する必要があります。

序数の背後にある関数にアクセスするには、使用しますGetProcAddressが、序数を LPCSTR にキャストします。詳細については、関数のドキュメントを参照してください。

于 2012-07-25T15:29:56.407 に答える