views:

797

answers:

2

I have a native C++ application that, for the time being simply needs to send its command line string and current mouse cursor coordinates to a WPF application. The message is sent and received just fine, but I cannot convert the IntPtr instance in C# to a struct.

When I try to do so, the application will either crash without exception or the line of code that converts it is skipped and the next message in the loop is received. This probably means there's a native exception occurring, but I don't know why.

Here's the C++ program. For the time being I'm ignoring the command line string and using fake cursor coordinates just to make sure things work.

#include "stdafx.h"
#include "StackProxy.h"
#include "string"

typedef std::basic_string<WCHAR, std::char_traits<WCHAR>> wstring;

struct StackRecord
{
    //wchar_t CommandLine[128];
    //LPTSTR CommandLine;
    //wstring CommandLine;
    __int32 CursorX;
    __int32 CursorY;
};

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    COPYDATASTRUCT data;
    ZeroMemory(&data, sizeof(COPYDATASTRUCT));

    StackRecord* record = new StackRecord();

    wstring cmdLine(lpCmdLine);
    //record.CommandLine = cmdLine;
    record->CursorX = 5;
    record->CursorY = 16;
    data.dwData = 12;
    data.cbData = sizeof(StackRecord);
    data.lpData = record;

    HWND target = FindWindow(NULL, _T("Window1"));

    if(target != NULL)
    {
     SendMessage(target, WM_COPYDATA, (WPARAM)(HWND) target, (LPARAM)(LPVOID) &data);
    }
    return 0;
}

And here is the part of the WPF application that receives the message. The second line inside the IF statement is skipped over, if the whole thing doesn't just crash.

 public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
 {
  if (msg == Interop.WM_COPYDATA)
  {
   var data = (Interop.CopyDataStruct)Marshal.PtrToStructure(lParam, typeof(Interop.CopyDataStruct));
   var record = (Interop.StackRecord)Marshal.PtrToStructure(data.lpData, typeof(Interop.StackRecord));
   MessageBox.Show(String.Format("X: {0}, Y: {1}", record.CursorX, record.CursorY));
  }
  return IntPtr.Zero;
 }

And here are the C# definitions for the structs. I have toyed endlessly with marshalling attributes and gotten nowhere.

internal static class Interop
{
 public static readonly int WM_COPYDATA = 0x4A;

 [StructLayout(LayoutKind.Sequential, Pack = 1)]
 public struct CopyDataStruct
 {
  public IntPtr dwData;
  public int cbData;
  public IntPtr lpData;
 }

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
 public struct StackRecord
 {
  //[MarshalAs(UnmanagedType.ByValTStr)]
  //public String CommandLine;
  public Int32 CursorX;
  public Int32 CursorY;
 }
}

Any ideas?

+2  A: 
tyranid
"namely the Pack = 1 will cause the COPYDATASTRUCT to become misaligned and reading from the pointer is likely to end in pain."Ah, so obvious now. I am used to reading structs from files where Pack = 1 makes sense. The C++ application is 32-bit, but I have a 64-bit system and the .NET code is JITted to 64-bit code. Removing Pack = 1 fixed it. Thanks."Looking at your commented code passing a LPWSTR or wstring is going to cause serious issues"I thought it might be. I also tried passing a fixed wchar_t string as you did without success, but that was due to the above issue.
Matt Olenik
+1  A: 

Hi, I've build a couple of applications (with VC++ and VC#, respectively), addressing the "boiled-down" variant of the problem (i.e. inability to get that struct), they seem to work flawelessly, so it may really be something with your setup, as tyranid says.

Anyway, here's the code (it must be enough to just paste it into newly created WIN32 APPLICATION (for VC++) and WINDOWS FORMS APPLICATION for C# to run and test):

StackProxy.cpp

#include "stdafx.h"
#include "StackProxy.h"
#include <string>


struct StackRecord {
    __int32 CursorX;
    __int32 CursorY;
};


int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
    StackRecord record;

    record.CursorX = 5;
    record.CursorY = 16;

    COPYDATASTRUCT data;

    data.dwData = 12;
    data.cbData = sizeof(StackRecord);
    data.lpData = &record;

    HWND target = FindWindow(NULL, _T("Window1"));

    if(target != NULL)
        SendMessage(target, WM_COPYDATA, (WPARAM)(HWND) target, (LPARAM)(LPVOID) &data);

    return 0;
}

Form1.cs

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public struct COPYDATASTRUCT
        {
            public System.Int32 dwData;
            public System.Int32 cbData;
            public System.IntPtr lpData;
        }

        int WM_COPYDATA = 0x4A;

        [StructLayout(LayoutKind.Sequential)]
        public struct StackRecord
        {
            public Int32 CursorX;
            public Int32 CursorY;
        }

        public Form1()
        {
            InitializeComponent();
            Text = "Window1";
        }

        protected override void WndProc(ref Message msg)
        {
            if (msg.Msg == WM_COPYDATA) {
                COPYDATASTRUCT cp = (COPYDATASTRUCT)Marshal.PtrToStructure(msg.LParam, typeof(COPYDATASTRUCT));
                StackRecord record = (StackRecord)Marshal.PtrToStructure(cp.lpData, typeof(StackRecord));
                MessageBox.Show(String.Format("X: {0}, Y: {1}, Data: {2}", record.CursorX, record.CursorY, cp.dwData));
            } 
            base.WndProc(ref msg);
        }
    }
}

Hope this helps.

P.S. I've not got much knowledge of C# and (especially) interop (having interest primarily in C++ programming), but seeing no one answer [a few hours ago] just thought it would be a nice challenge to try this problem. Not to mention the bounty :)

*amn it, i'm late:))

mlvljr
Ah, thanks anyway!
Matt Olenik