2009-12-11 14 views
7

Ho un'applicazione C++ nativa che, per il momento, deve semplicemente inviare la sua stringa di riga di comando e le coordinate del cursore del mouse corrente a un'applicazione WPF. Il messaggio viene inviato e ricevuto correttamente, ma non riesco a convertire l'istanza IntPtr in C# in una struttura.Invio di una struct da C++ a WPF utilizzando WM_COPYDATA

Quando provo a farlo, l'applicazione si arresta in modo anomalo senza eccezioni oppure la riga di codice che la converte viene saltata e viene ricevuto il messaggio successivo nel ciclo. Questo probabilmente significa che si sta verificando un'eccezione nativa, ma non so perché.

Ecco il programma C++. Per il momento sto ignorando la stringa della riga di comando e uso false coordinate del cursore solo per assicurarmi che le cose funzionino.

#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; 
} 

Ed ecco la parte dell'applicazione WPF che riceve il messaggio. La seconda riga all'interno dell'istruzione IF viene ignorata, se il tutto non si arresta.

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; 
    } 

E qui ci sono le definizioni C# per le strutture. Ho giocato all'infinito con attributi di marshalling e ottenuto da nessuna parte.

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; 
    } 
} 

Qualche idea?

+0

Ho anche provato l'override WndProc in una finestra WinForms, il comportamento è lo stesso. –

risposta

7

Non sono sicuro di cosa si stia sbagliando necessariamente senza ulteriori informazioni sulla configurazione. Ho replicato il codice al meglio (usando WndProc in un'app WPF, inviando dalla mia app win32) e funziona perfettamente per me. Ci sono alcuni errori che emergeranno definitivamente se si eseguono applicazioni a 64 bit, ovvero Pack = 1 farà sì che il COPYDATASTRUCT diventi disallineato e la lettura dal puntatore rischia di finire nel dolore.

Si sta schiantando passando solo gli ints? Guardando il tuo codice commentato che passa un LPWSTR o wstring causerà seri problemi, anche se questo non dovrebbe diventare evidente fino a quando non sarai unmarsale dei dati inviati.

Per quello che vale, questo è frammenti del mio codice che sembrano funzionare per me compreso il passaggio della riga di comando.

/* C++ code */ 
struct StackRecord 
{ 
    wchar_t cmdline[128]; 
    int CursorX; 
    int CursorY; 
}; 

void SendCopyData(HWND hFind) 
{ 
    COPYDATASTRUCT cp; 
    StackRecord record; 

    record.CursorX = 1; 
    record.CursorY = -1; 

    _tcscpy(record.cmdline, L"Hello World!"); 
    cp.cbData = sizeof(record); 
    cp.lpData = &record; 
    cp.dwData = 12; 
    SendMessage(hFind, WM_COPYDATA, NULL, (LPARAM)&cp); 
} 

/* C# code */ 
public static readonly int WM_COPYDATA = 0x4A; 

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

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 
public struct StackRecord 
{ 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)] 
    public string CommandLine; 
    public Int32 CursorX; 
    public Int32 CursorY; 
} 

protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 
{ 
    if (msg == WM_COPYDATA) 
    { 
     StackRecord record = new StackRecord(); 
     try 
     { 
      CopyDataStruct cp = (CopyDataStruct)Marshal.PtrToStructure(lParam, typeof(CopyDataStruct)); 
      if (cp.cbData == Marshal.SizeOf(record)) 
      { 
       record = (StackRecord)Marshal.PtrToStructure(cp.lpData, typeof(StackRecord)); 
      } 
     } 
     catch (Exception e) 
     { 
      System.Diagnostics.Debug.WriteLine(e.ToString()); 
     } 
     handled = true; 
    } 
    else 
    { 
     handled = false; 
    } 
    return IntPtr.Zero; 
} 
+0

"vale a dire che il pacchetto = 1 farà sì che il COPYDATASTRUCT diventi disallineato e la lettura dal puntatore rischia di finire nel dolore." Ah, così ovvio ora. Sono abituato a leggere le strutture da file in cui Pack = 1 ha senso. L'applicazione C++ è a 32 bit, ma ho un sistema a 64 bit e il codice .NET è JITted per il codice a 64 bit. Removing Pack = 1 risolto. Grazie. "Guardare il codice commentato che passa un LPWSTR o wstring causerà seri problemi" Ho pensato che potesse essere. Ho anche provato a passare una stringa fissa wchar_t come hai fatto senza successo, ma ciò era dovuto al problema precedente. –

3

Ho costruire un paio di applicazioni (con VC++ e VC#, rispettivamente), affrontando la variante "bollito-down" del problema (vale a dire l'incapacità di ottenere che struct), sembrano funzionare flawelessly , quindi potrebbe essere davvero qualcosa con il tuo setup, come dice tyranid.

Ad ogni modo, ecco il codice (che deve essere abbastanza per incollare solo nella nuova creazione Win32 (per VC++) e Applicazione Windows Form per C# per l'esecuzione e 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); 
     } 
    } 
} 

Spero che questo aiuti.

P.S.Non ho molta conoscenza di C# e (specialmente) dell'interop (avendo interesse soprattutto nella programmazione in C++), ma non vedendo nessuno rispondere [poche ore fa] ho pensato che sarebbe stata una bella sfida provare questo problema. Per non parlare della bontà :)

* AMN esso, sono in ritardo :))

+0

Ah, grazie comunque! –