1

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 ?

Eugene Gluhotorenko
  • 3,094
  • 2
  • 33
  • 52

2 Answers2

3

"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

Community
  • 1
  • 1
Viktor Latypov
  • 14,289
  • 3
  • 40
  • 55
  • Попробовал с Int32 - то же самое. А чтобы проверить printf("%d\n", sizeof(CommunicationInfo)) нету компилятора под рукой. в любом случае, зная реальный размер CommunicationInfo, как я смогу задать его через .net ? – Eugene Gluhotorenko May 20 '12 at 22:08
  • The the "[MarshalAs(UnmanagedType.LPWStr)] public StringBuilder srcModuleName;" also makes a trouble. Try searching the SO for the answer of how to marshal the string pointers. StringBuilder won't help. You have to allocate the unmanaged memory for each of the fields. – Viktor Latypov May 20 '12 at 22:10
  • 1
    http://stackoverflow.com/questions/1223690/pinvoke-error-when-marshalling-struct-with-a-string-in-it – Viktor Latypov May 20 '12 at 22:13
  • Yes, on Windows, `long` is 32 bits in both C and C++ for both 32 and 64 bit processes. – David Heffernan May 21 '12 at 09:44
  • Just pointed out the problem to the OP. Of course, long is 32-bit on Windows. The list of addressing/int modes is here http://www.unix.org/version2/whatsnew/lp64_wp.html – Viktor Latypov May 21 '12 at 09:46
1

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);
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490