如何将 C++ QString 参数转换为 C# 字符串

How to convert c++ QString parameter to c# string

我试图通过注入我的 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)。但是现在我在 Marshal.PtrToStringUni.

上得到一个 AccessViolationException
    [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;
            }
        }
    }

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

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

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

感谢有用的文章 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;
            }
        }
    }