I'm trying to hook the QPainter::drawText function in another application by injecting my DLL and detour the QPainter::drawText function to my own application. I'm doing this because the other application does not expose a usable API and I want to do some basic statistical analysis on the data I'm getting.
All is working fine: I see the QPainter::drawText functions being called, but I'm unable to convert the QString parameter to anything useful. All I get is two characters when I Marshall the QString parameter as LPWStr.
I'm no C++ superhero, so I'm a bit lost. I think I'm looking at some pointer or reference because of the two characters I get for each call, but I'm not sure. After a few nights trying to make sense of it I'm close to the point of giving up.
I've demangled the QPainter::drawText function (found using Dependency Walker: ?drawText@QPainter@@QAEXABVQRect@@HABVQString@@PAV2@@Z) with https://demangler.com/ and it comes up with this function declaration:
public: void __thiscall QPainter::drawText(class QRect const &,int,class QString const &,class QRect *)
I've converted this into the following DllImport (I substituted the QRect and Qstring classes to IntPtr, because I have no idea how to convert them to 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);
This is what I have so far:
Detouring 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);
        }
    }
Create QPainter::drawText detour
            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 });
Update 2016-1-31
Thus far I have found this (see https://github.com/mono/cxxi/blob/master/examples/qt/src/QString.cs). But now I'm getting an AccessViolationException on the 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;
            }
        }
    }
 
                        
Thanks to the helpful article https://woboq.com/blog/qstringliteral.html, which explains the QString data structure in Qt5 I've managed to get the hook working: