views:

1176

answers:

3

I am intercepting Win32 API calls a native dll or exe is doing from C# using some kind of hooking. In this particular case I am interested in DrawText() in user32.dll. It is declared like this in Win32 API:

INT WINAPI DrawTextW(HDC hdc, LPCWSTR str, INT count, LPRECT rect, UINT flags)

The LPRECT struct has the following signature (also in Win32 API):

typedef struct tagRECT { 
    LONG left;
    LONG top;
    LONG right;
    LONG bottom;
} RECT LPRECT;

LONG is a typedef for 32bit integers on 32bit systems (don't know about 64bit systems, it is irrelevant at this point because I am on 32bit Windows). To be able to access the members of this struct I declared it in my C# code...

[StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct RECT
    {
        public Int32 left;
        public Int32 top;
        public Int32 right;
        public Int32 bottom;
    }

... and wrote the signature of P/Invoke using this RECT struct:

[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
static extern IntPtr DrawText(IntPtr HDC, String str, Int32 count, ref RECT rect, UInt32 flags, IntPtr dtp);

Since structs are value types in C# as opposed to being reference types like in C/C++, the ref modifier is necessary here.

However when I use rect.top rect.left etc, they almost always return 0. I know for a fact that this is incorrect. But after googling countless hours and trying a lot of different things, I couldn't make this simple stuff work.

Things I've tried:

  • Using different primitives for RECT members (int, long, short, UInt32...). Actually it is kinda obvious that this is not a type problem because in any case I should see some garbled numbers, not 0.
  • Removing ref modifier. This is also stupid (desperate times, desperate measures) because rect.left correctly returns the pointer to rect instead of its value.
  • Tried unsafe code blocks. Didn't work but I may have made a mistake in the implementation (I don't remember what I've done). Besides this approach is generally reserved for tricky pointer situations in COM and Win32, it is overkill for my case anyway.
  • Tried adding [MarshallAs] before the members of RECT. Made no difference.
  • Played around with Pack values. No difference.

I am fairly sure that I'm missing something very easy and straightforward but I have no idea what it is...

Any help is appreciated. Thank you.

+1  A: 

I notice you say you have tried [MarshallAs] but have you tried [MarshalAs(UnmanagedType.Struct)]?

Werg38
+1  A: 

Part of the problem is the use of String where a StringBuilder should be used.

Try this signature (Generated with PInvoke Interop Assistant)


[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct tagRECT {

    /// LONG->int
    public int left;

    /// LONG->int
    public int top;

    /// LONG->int
    public int right;

    /// LONG->int
    public int bottom;
}

public partial class NativeMethods {

    /// Return Type: int
    ///hdc: HDC->HDC__*
    ///lpchText: LPCWSTR->WCHAR*
    ///cchText: int
    ///lprc: LPRECT->tagRECT*
    ///format: UINT->unsigned int
    [System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint="DrawTextW")]
public static extern  int DrawTextW([System.Runtime.InteropServices.InAttribute()] System.IntPtr hdc, [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)] System.Text.StringBuilder lpchText, int cchText, ref tagRECT lprc, uint format) ;

}
JaredPar