Доступ к нативным объектам через 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