Доступ к нативным объектам через 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 для исходного доступа к объекту (см. party-dlls">Вызов методов в сторонних 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;
};

СРР-файл:

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(). Помощь: Р

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

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

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

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

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

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


person Roman Holiday    schedule 03.03.2013    source источник
comment
Первое правило внедрения кода — вводить абсолютный минимум кода. Это делает C++/CLI очень плохим кандидатом, вам также нужно внедрить джиттер и CLR, чтобы заставить его работать. Шансы, что вы сможете понять, как это сделать, равны нулю. Даже если вы выясните это, вероятность того, что в процессе уже загружена CLR, и это неправильная версия, также высока. Не пытайтесь это сделать.   -  person Hans Passant    schedule 03.03.2013
comment
У нас дело о дуэльных экспертах!! См. ссылку на мой предыдущий вопрос и ответ Дэвида Хеффернана.   -  person Roman Holiday    schedule 04.03.2013
comment
Ваш предыдущий вопрос не касался внедрения кода в другой процесс.   -  person Hans Passant    schedule 04.03.2013


Ответы (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
person user1610015    schedule 03.03.2013
comment
1. Извините, я удалил несколько строк кода отладки перед публикацией - одна из проверенных GetLastError() возвращает ноль. 2. Я нигде не создаю экземпляр собственного объекта QXmlInputSource. Я создаю экземпляр класса-оболочки, который принимает указатель на уже существующий объект. Помните, что цель здесь - в основном отслеживать данные, передаваемые внутри приложения на основе Qt. Если я создаю свой собственный объект, он просто будет пустым... 3. и 4. Сегодня я поиграю с ними. - person Roman Holiday; 04.03.2013
comment
Я уже застрял: P Фрагменты кода, которые вы опубликовали, похоже, предполагают, что у меня есть доступ к классам QString, QChar и QXmlInputSource как к родным классам. Но я не знаю! Заголовочные и исходные файлы, которые я опубликовал, являются просто обертками, которые принимают указатель void, указывающий на адрес нативного объекта. QtXml4.dll — это сторонняя библиотека, и у меня нет файла lib для импорта. Среду Qt я пока вообще не трогал, но не исключено, что мне удалось получить файл lib.. - person Roman Holiday; 04.03.2013
comment
Да, вы можете получить файл .lib для Qt. Тогда вы можете избежать всего, что связано с GetProcAddress и указателями функций, и просто вызывать методы файла .lib даже для объектов, которые были созданы в целевом исполняемом файле (но вы должны убедиться, что версии библиотек Qt совпадают). В любом случае, я бы рассмотрел комментарий Ханса Пассана, прежде чем идти дальше по этому маршруту. Я просто не знаком с внедрением DLL. - person user1610015; 04.03.2013