如何将c ++ QString参数转换为c#string

时间:2016-01-31 09:02:35

标签: c# qt easyhook

我正在尝试通过注入我的DLL并将QPainter::drawText函数绕道到我自己的应用程序来将QPainter::drawText函数挂钩到另一个应用程序中。我这样做是因为其他应用程序没有公开可用的API,我想对我得到的数据做一些基本的统计分析。

一切正常:我看到QPainter::drawText函数被调用,但我无法将QString参数转换为有用的参数。当我将QString参数设置为LPWStr时,我得到的只有两个字符。

我不是C ++超级英雄,所以我有点失落。我想我正在看一些指针或引用,因为我为每次调用得到两个字符,但我不确定。几个晚上试图理解它之后,我已经接近放弃了。

我已经使用https://demangler.com/QPainter::drawText函数(使用Dependency Walker:?drawText@QPainter@@QAEXABVQRect@@HABVQString@@PAV2@@Z找到)解除了它,并且它出现了这个函数声明:

public: void __thiscall QPainter::drawText(class QRect const &,int,class QString const &,class QRect *)

我已将此转换为以下DllImport(我将QRectQstring类替换为IntPtr,因为我不知道如何将它们转换为C#)。

    [DllImport("Qt5Gui.dll", SetLastError = true,  CharSet = CharSet.Unicode, CallingConvention = CallingConvention.ThisCall, EntryPoint = "?drawText@QPainter@@QAEXABVQRect@@HABVQString@@PAV2@@Z")]
    public static extern void QPainter_drawText(IntPtr obj, IntPtr p1, int p2, IntPtr p3, IntPtr p4);

这是我到目前为止所做的:

绕道Qt QPainter :: drawText

    LocalHook QPainter_drawTextHook;

    [DllImport("Qt5Gui.dll", SetLastError = true,  CharSet = CharSet.Unicode, CallingConvention = CallingConvention.ThisCall, EntryPoint = "?drawText@QPainter@@QAEXABVQRect@@HABVQString@@PAV2@@Z")]
    public static extern void QPainter_drawText(IntPtr obj, IntPtr p1, int p2, IntPtr p3, IntPtr p4);

    [UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Unicode, SetLastError = true)]
    delegate void TQPainter_drawText(IntPtr obj, IntPtr p1, int p2, IntPtr p3, IntPtr p4);

    static void QPainter_drawText_Hooked(IntPtr obj, IntPtr p1, int p2, IntPtr p3, IntPtr p4)
    {
        var qs3 = (QString)Marshal.PtrToStructure(p3, typeof(QString));

        try
        {
            ((Main)HookRuntimeInfo.Callback).Interface.GotQPainter_drawText(qs3.ToString());
            QPainter_drawText(obj, p1, p2, p3, p4);
        }
        catch (Exception ex)
        {
            ((Main)HookRuntimeInfo.Callback).Interface.ErrorHandler(ex);
        }
    }

创建QPainter :: drawText绕行

            QPainter_drawTextHook = LocalHook.Create(
                                        LocalHook.GetProcAddress("Qt5Gui.dll", "?drawText@QPainter@@QAEXABVQRect@@HABVQString@@PAV2@@Z"),
                                        new TQPainter_drawText(QPainter_drawText_Hooked), 
                                        this);

            QPainter_drawTextHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });

更新2016-1-31 到目前为止,我已经找到了这个(见https://github.com/mono/cxxi/blob/master/examples/qt/src/QString.cs)。但现在我在AccessViolationException上获得Marshal.PtrToStringUni

    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct QString
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct Data
        {
            public int @ref;
            public int alloc, size;
            public IntPtr data;
            public ushort clean;
            public ushort simpletext;
            public ushort righttoleft;
            public ushort asciiCache;
            public ushort capacity;
            public ushort reserved;
            public IntPtr array;
        }

        public Data* d;
        #endregion

        public override string ToString()
        {
            try
            {
                return Marshal.PtrToStringUni(d->array, d->alloc * 2);
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }
    }

2 个答案:

答案 0 :(得分:0)

QString是复杂类型。 您可以尝试导入QString::FromUtf16以创建QString并将IntPtr传递给您的函数

string str = "Test";
var p2 = new IntPtr(QString.Utf16(str,str.Length));

编辑:此外,您可以尝试使用https://github.com/ddobrev/QtSharp

答案 1 :(得分:0)

感谢有用的文章https://woboq.com/blog/qstringliteral.html,它解释了Qt5中的QString数据结构,我设法让钩子工作:

    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct QString
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct QStringData
        {
            public int @ref;
            public int size;
            public uint alloc;
            public uint capacityReserved;
            public fixed byte data[128];
        }

        public QStringData* data;

        public override string ToString()
        {
            try
            {
                var bytes = new byte[data->size * 2];
                Marshal.Copy((IntPtr)data->data, bytes, 0, data->size * 2);
                return Encoding.Unicode.GetString(bytes);
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }
    }