1

DLLをサードパーティの実行可能ファイル(Qtフレームワークを使用して構築されたもの)に挿入するC#アプリケーションを作成しました。このDLLは、EasyHookを使用して、いくつかの特定の関数への呼び出しをインターセプトします。挿入されたコードが呼び出されると、これらの関数のパラメーターであるオブジェクトのいくつかを検査しようとします。

たとえば、XMLを解析するために行われた呼び出しをインターセプトしました。

virtual bool __thiscall QXmlSimpleReader::parse(class QXmlInputSource const &)

私のC#コードには、これに一致するPInvoke署名があります。

static extern bool XmlParse(IntPtr Reader, IntPtr Source)

「Source」クラスのメンバーである「data()」関数を呼び出したいと思います。つまり、QXmlSimpleReaderはQXmlInputSourceから生のXMLを解析しますが、その前に、この「data()」関数を介して生のXMLを検査しようとしています。

ここの専門家の1人のアドバイスで、私はC ++ / CLIを使用してオブジェクトにネイティブにアクセスしようとしました(サードパーティのDLLのメソッドの呼び出しを参照)。C#コードからIntPtrを受け入れるC++のラッパーオブジェクトを作成しました。

ヘッダ:

public ref class QXmlInputSource
{
public:
    // Constructor must be called with C# IntPtr
    QXmlInputSource(IntPtr Ptr);

    bool LoadQt(void);
    bool UnloadQt(void);

    String^ Data();

private:
    // Pointer to the native Qt object
    void * Native;
    HINSTANCE DllHandle;

    // SIGNATURE: virtual QString QXmlInputSource::data() const
    typedef void * (__thiscall *QXmlInputSource_Data)(void *);
    QXmlInputSource_Data fpData;
};

CPPファイル:

QXmlInputSource::QXmlInputSource(IntPtr Ptr)
{
    LoadQt();
    Native = Ptr.ToPointer();
}

bool QXmlInputSource::LoadQt(void)
{
    FARPROC Addr;

    /* get handle to dll */
    std::wstring LibName = QtPath + QtXml;
    DllHandle = LoadLibrary(LibName.c_str()); 

    /* get pointer to the function in the dll*/ 
    Addr = GetProcAddress(HMODULE (DllHandle), "?data@QXmlInputSource@@UBE?AVQString@@XZ"); 
    fpData = QXmlInputSource_Data(Addr);

    return true;
}

bool QXmlInputSource::UnloadQt()
{
    /* Release the Dll */ 
    FreeLibrary(DllHandle);
    return true;
}

String^ QXmlInputSource::Data()
{
    void* Ptr = fpData(Native);
    return "EPIC FAIL";
}

fpData()関数ポインタを呼び出そうとすると、Qtベースのアプリケーションがクラッシュします。ヘルプ:P

役立つ場合と役に立たない場合があるいくつかの追加情報:

  • 同じ方法を使用して、QString.count()やQString.data()などの「より単純な」オブジェクトの関数を正常に呼び出しました。(QStringは、標準のUnicode文字列の単なる軽量ラッパーのようです)。

  • 私が興味を持っているXML関数を含むQtXml4.dllファイルには、実際には2つのparse()メソッドがあります。1つはSourceがconst&であり、もう1つはSourceがconst*です。どちらを使うべきかわかりません。いずれにしても私の署名は変わらないと思います。

  • 遊んでいる間、C#コードでIntPtrを逆参照して、C++に渡してみました。

    IntPtr DerefSrc =(IntPtr)Marshal.PtrToStructure(Source、typeof(IntPtr));

これら2つのIntPtrの値を出力すると、Sourceの値は約3.5Mbですが、DerefSrcの値は1.6Gbです。これは、メモリ内のQtXml4.dllのアドレスとほぼ一致します。私の推測では、3.5Mbは相対オフセットですが、DerefSrcは明らかに絶対参照です。DerefSrcを相対アドレスに変換し、代わりにC ++に渡すことは一撃の価値があります...?

4

1 に答える 1

1

いくつかの問題があります。

1.: LoadLibrary と GetProcAddress の戻り値をチェックしていません。QXmlInputSource::data は DLL でエクスポートされたメソッドでもありますか? そうでない場合、GetProcAddress は明らかに失敗します。

2.: QXmlInputSource クラスをどのようにインスタンス化しますか? C#コードでそれをやろうとしているように見えるのでお願いします.これは正しく行うのが難しいです(クラスに必要なサイズを知り、そのサイズの適切に整列されたメモリチャンクを割り当て、コンストラクタをそれ)。

3.: 関数ポインタの呼び出し方が間違っています。適切な型のメソッド ポインターを宣言する必要があります。

FARPROC fp = ::GetProcAddress(...);

typedef QString (QXmlInputSource::*DataMethod)();
DataMethod mpData = reinterpret_cast<DataMethod>(fp);

QXmlInputSource source;
QString data = (source.*mpData)();

4.: QXmlInputSource::data のドキュメントを見ると、QString が返されることがわかります。これはポインターとはまったく異なります (現在扱っているように)。これを System.String に変換するには、次のようなコードが必要です。

QString s1 = (QChar*)L"example string";
String^ s2 = gcnew String((wchar_t*)s1.data()); // calls the String::String(wchar_t*) constructor overload
于 2013-03-03T13:03:37.870 に答える