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

本文关键字:转换 字符串 参数 QString c++ | 更新日期: 2023-09-27 18:32:40

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

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

我不是C++超级英雄,所以我有点迷茫。我想我正在查看一些指针或引用,因为我每次调用都会得到两个字符,但我不确定。经过几个晚上试图理解它,我接近放弃的地步。

我已经用 https://demangler.com/拆除了QPainter::drawText函数(使用依赖沃克:?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::d rawText

    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::d原始文本绕行

            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.PtrToStringUniAccessViolationException.

    [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;
            }
        }
    }

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

QString 是复杂类型。您可以尝试导入用于创建 QString 的QString::FromUtf16并将 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;
            }
        }
    }