将非托管指针从钩子函数封送到托管对象,并使用托管包装类调用其成员函数

时间:2014-01-14 08:40:57

标签: c# c++ hook marshalling wrapper

我在C#中使用EasyHook挂钩了QtGui4.dll的非托管函数QPainter::drawTextItem(const QPointF &p, const QTextItem &ti);。我已将QPointFQTextItem类包装到托管结构中,并可通过编组来获取这些参数中的数据。但是,我想使用托管代码调用非托管QPainter类的成员函数,因此我尝试将QPainter类包装到托管类中。我可以访问非托管代码以进行测试,但在生产中无法访问它。我查看了非托管类QPainterQPaintDevice的内存布局。 QPainter类只是一个指针而QPaintDevice类只是两个指针(一个用于虚函数表)。我尝试按照 this blog 中的技术进行操作,但是当我调用包装的QPainter::device()方法只是为了查看是否可以获取指针时,我的目标(非托管,挂钩)应用程序崩溃了具有访问冲突错误(Access violation reading location 0x00000078)或参数异常错误(EEArgumentException at memory location 0x003786f4),具体取决于我的包装QPainter类是否使用顺序布局。我不希望有人纠正所有的源代码,但我希望有人能指出我在我的方法中犯的一些错误。顺便说一句,这是我第一次在Stack Overflow上发布一个问题,因为我通常会从一个或多个现有问题中找到问题的答案。我为这个长期问题道歉,但我试图尽可能地简化问题而不遗漏潜在的相关信息。在此先感谢您的帮助。

记忆转储:

class QPainter (size = 4):
  (0) d_ptr

class QPaintDevice (size = 8):
  (0) {vfptr}
  (4) painters

非托管代码(qpainter.h):

class QPainter
{
    QPainter();
    explicit QPainter(QPaintDevice *);
    ~QPainter();

    QPaintDevice *device() const; // trying to call this function

    void drawTextItem(const QPointF &p, const QTextItem &ti); // hooked function

    // class has additional functions, but left out for brevity
}

托管挂钩(hook.cs):

[DllImport("QtGui4.dll", CharSet = CharSet.Unicode, SetLastError = true,
    CallingConvention = CallingConvention.ThisCall,
    EntryPoint = "?drawTextItem@QPainter@@QAEXABVQPointF@@ABVQTextItem@@@Z")]
public static extern void QPainter_drawTextItem(IntPtr obj, IntPtr pt, IntPtr ti);

[UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Unicode,
    SetLastError = true)]
delegate void D_QPainter_drawTextItem(IntPtr obj, IntPtr pt, IntPtr ti);

static unsafe void QPainter_drawTextItem_Hooked(IntPtr obj, IntPtr pt, IntPtr ti)
{
    QTextItem qti = (QTextItem)Marshal.PtrToStructure(ti, typeof(QTextItem));
    QPointF qpt = (QPointF)Marshal.PtrToStructure(pt, typeof(QPointF));
    QPainter painter = (QPainter)Marshal.PtrToStructure(obj, typeof(QPainter));

    __QPaintDevice* pd = painter.device(); // ERROR

    QPainter_drawTextItem(obj, pt, ti);
}

托管包装器(wrap.cs):

[StructLayout(LayoutKind.Sequential, Size=8)]
public unsafe struct __QPaintDevice
{
    public IntPtr *vfptr;
    public IntPtr painters;
};

[StructLayout(LayoutKind.Sequential, Size=4)]
public unsafe struct __QPainter
{
    public IntPtr d_ptr;
};

// When the following line is included, I get this error:
//     Access violation reading location 0x00000078.
// When the following line is excluded, I get this error:
//     EEArgumentException at memory location 0x003786f4.

[StructLayout(LayoutKind.Sequential)] // is this useful?
public unsafe class QPainter : IDisposable
{
    private __QPainter* _this;
    private __QPaintDevice* _pd;

    [DllImport("QtGui4.dll", EntryPoint = "??0QPainter@@QAE@XZ",
        CallingConvention = CallingConvention.ThisCall)]
    private static extern int _QPainter_Constructor(__QPainter* ths);

    [DllImport("QtGui4.dll",
        EntryPoint = "??0QPainter@@QAE@PAVQPaintDevice@@@Z",
        CallingConvention = CallingConvention.ThisCall)]
    private static extern int _QPainter_Constructor(__QPainter* ths,
        __QPaintDevice* pd);

    [DllImport("QtGui4.dll", EntryPoint = "??1QPainter@@QAE@XZ",
        CallingConvention = CallingConvention.ThisCall)]
    private static extern int _QPainter_Destructor(__QPainter* ths);

    [DllImport("QtGui4.dll",
        EntryPoint = "?device@QPainter@@QBEPAVQPaintDevice@@XZ",
        CallingConvention = CallingConvention.ThisCall)]
    private static extern __QPaintDevice* _device(__QPainter* ths);

    public QPainter()
    {
        _this = (__QPainter*)Marshal.AllocHGlobal(sizeof(__QPainter));
        _QPainter_Constructor(_this);
    }

    public QPainter(__QPaintDevice* pd)
    {
        _this = (__QPainter*)Marshal.AllocHGlobal(sizeof(__QPainter));
        _pd = (__QPaintDevice*)Marshal.AllocHGlobal(sizeof(__QPaintDevice));
        _QPainter_Constructor(_this, pd);
    }

    public void Dispose()
    {
        _QPainter_Destructor(_this);
        Marshal.FreeHGlobal((IntPtr)_this);
        Marshal.FreeHGlobal((IntPtr)_pd);
        _this = null;
        _pd = null;
    }

    public __QPaintDevice* device()
    {
        return _device(_this);
    }
};

0 个答案:

没有答案