Доступ к нативным объектам через C++/CLI из C#!

Я написал приложение на C#, которое внедряет DLL в сторонний исполняемый файл (который, как оказалось, был собран с использованием инфраструктуры Qt). Эта DLL-библиотека использует EasyHook для перехвата вызовов ряда определенных функций. Когда мой внедренный код вызывается, я пытаюсь проверить некоторые объекты, которые являются параметрами этих функций.

Например, я перехватил вызов, сделанный для разбора некоторого XML:

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

В моем коде C# у меня есть подпись PInvoke, чтобы соответствовать этому:

static extern bool XmlParse(IntPtr Reader, IntPtr Source)

Я хотел бы вызвать функцию "data()", которая является членом класса "Source". То есть QXmlSimpleReader анализирует необработанный XML из QXmlInputSource, но перед этим я пытаюсь проверить необработанный XML с помощью этой функции "data()".

По совету одного из экспертов здесь я попытался использовать 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";
}

Приложение на основе Qt падает, когда я пытаюсь вызвать указатель на функцию fpData(). Помогите:P

Некоторая дополнительная информация, которая может или не может помочь:

  • Я успешно вызывал функции для "более простых" объектов, таких как QString.count() и QString.data(), используя ту же методологию. (QString, кажется, просто облегченная оболочка для стандартной строки Unicode).

  • В файле QtXml4.dll, который содержит интересующие меня функции XML, на самом деле есть ДВА метода parse(); один, где источник является константой, а другой источник - константой *. Я понятия не имею, должен ли я использовать один или другой. Я не думаю, что мои подписи изменятся в любом случае.

  • Пока я пытался поиграться, я попытался разыменовать IntPtr в коде C# и передать его в C++:

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

Если я распечатываю значения этих двух IntPtrs, Source имеет значение около 3,5 МБ, а DerefSrc имеет значение 1,6 ГБ, что примерно соответствует адресу QtXml4.dll в памяти. Я предполагаю, что 3.5Mb является относительным смещением, в то время как DerefSrc явно является абсолютным эталоном. Стоит ли конвертировать DerefSrc в относительный адрес и вместо этого передавать его в C++...?

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
Другие вопросы по тегам