Eugene Gluhotorenko
Eugene Gluhotorenko

Reputation: 3164

WinAPI SendMessage from .NET

I have an example of winapi code:

struct CommunicationInfo {
    long internalMsg;
    const TCHAR * srcModuleName;
    void * info; 
  };

...

const TCHAR* szText = _T("Hello from my plugin!\n(test message)");
    CommunicationInfo ci = { 0x0401, cszMyPlugin, (void *) szText };
    ::SendMessage( hNppWnd, 0x111, (WPARAM) _T("NppExec.dll"), (LPARAM) &ci );

I want make the same call from .net and i wrote such wrapper:

[StructLayout(LayoutKind.Sequential)]
    public struct CommunicationInfo
    {
        public Int64 internalMsg;
        [MarshalAs(UnmanagedType.LPWStr)]
        public StringBuilder srcModuleName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public StringBuilder data;
    };

...

[DllImport("user32")]
public static extern IntPtr SendMessage(IntPtr hWnd, 
    NppMsg Msg, IntPtr wParam, 
    [MarshalAs(UnmanagedType.Struct)] CommunicationInfo communicationInfo);

...

SendMessage(hNppWnd, 0x111, 
    Marshal.StringToHGlobalUni("NppExec.dll"), 
    new CommunicationInfo 
    { 
        data = new StringBuilder("test test"),
        internalMsg = 0x0401,
        srcModuleName = new StringBuilder("ModuleName")
    });

But this code doesn't work. Where did I make a mistake ?

Upvotes: 1

Views: 1187

Answers (2)

David Heffernan
David Heffernan

Reputation: 612963

As Viktor points out, C/C++ long is 32 bits in size so needs to be matched with C# int. On top of that, the passing of the struct is not handled correctly. In addition the call to StringToHGlobalUni leaks since you never call FreeHGlobal.

I'd probably handle the marshalling something like this:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct CommunicationInfo
{
    public int internalMsg;
    public string srcModuleName;
    public string data;
};
....
[DllImport("user32")]
public static extern IntPtr SendMessage(
    IntPtr hWnd, 
    uint Msg, 
    [MarshalAs(UnmanagedType.LPWStr)] string wParam, 
    ref CommunicationInfo communicationInfo
);
....
CommunicationInfo communicationInfo = new CommunicationInfo 
{ 
    internalMsg = 0x0401,
    srcModuleName = "ModuleName",
    data = "test test"
};
SendMessage(hNppWnd, 0x111, "NppExec.dll", ref communicationInfo);

Upvotes: 1

Viktor Latypov
Viktor Latypov

Reputation: 14467

"long" field in CommunicationInfo struct is 32-bit in WinAPI, I believe. So try defining "internalMsg" as System.Int32 in C#

To be sure, try calling printf("%d\n", sizeof(CommunicationInfo)) in C/C++ to know the actual size. If it is (4 + 4 + 4) on a 32-bit system, then the C# struct must also be of 12 byte size.

The "char*" pointer must also be the pointer to unmanaged memory, so the StringBuilder just won't do.

See this PInvoke error when marshalling struct with a string in it for the marshalling sample

Upvotes: 3

Related Questions