从C#通过C ++ / CLI访问本机对象!

时间:2013-03-03 10:52:29

标签: c# qt c++-cli pinvoke easyhook

我编写了一个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从QXmlInputSource解析原始XML,但在此之前,我试图通过这个“data()”函数检查原始XML。

根据其中一位专家的建议,我尝试使用C ++ / CLI本地访问该对象(请参阅Calling methods in third-party DLLs)。我在C ++中构造了一个包装器对象,它接受来自C#代码的IntPtr:

部首:

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

一些可能有帮助或可能没有帮助的其他信息:

  • 我使用相同的方法成功调用了“simpler”对象上的函数,例如QString.count()和QString.data()。 (QString似乎只是标准unicode字符串的轻量级包装器。)

  • 在包含我感兴趣的XML函数的QtXml4.dll文件中,实际上有两个parse()方法;一个是Source是const&和另一个,Source是一个const *。我不知道我是否应该使用其中一个。我认为我的签名在任何情况下都不会改变。

  • 当我试图玩游戏时,我尝试在C#代码中取消引用IntPtr并将其传递给C ++:

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

如果我打印出这两个IntPtrs的值,Source的值大约为3.5Mb,而DerefSrc的值为1.6Gb - 大致与内存中QtXml4.dll的地址相匹配。我的猜测是3.5Mb是相对偏移,而DerefSrc显然是绝对参考。是否值得将DerefSrc转换为相对地址并将其传递给C ++而不是......?

1 个答案:

答案 0 :(得分: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