2009-12-11 13 views
7

J'ai une application C++ native qui, pour le moment, doit simplement envoyer sa chaîne de ligne de commande et les coordonnées du curseur de la souris en cours à une application WPF. Le message est envoyé et reçu très bien, mais je ne peux pas convertir l'instance IntPtr en C# en une structure.Envoyer une structure de C++ à WPF en utilisant WM_COPYDATA

Lorsque j'essaie de le faire, l'application se bloque sans exception ou la ligne de code qui la convertit est ignorée et le message suivant dans la boucle est reçu. Cela signifie probablement qu'il y a une exception native, mais je ne sais pas pourquoi.

Voici le programme C++. Pour le moment j'ignore la chaîne de la ligne de commande et j'utilise de fausses coordonnées du curseur juste pour m'assurer que les choses fonctionnent.

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

Et voici la partie de l'application WPF qui reçoit le message. La deuxième ligne à l'intérieur de l'instruction IF est ignorée si tout ne se bloque pas.

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

Et voici les définitions C# pour les structures. J'ai joué sans fin avec des attributs de marshalling et je n'ai obtenu nulle part.

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

Des idées?

+0

J'ai aussi essayé WndProc primordial dans une fenêtre Winforms, le comportement est le même. –

Répondre

7

Je ne suis pas sûr de ce que vous vous trompez nécessairement sans plus d'informations sur votre configuration. J'ai répliqué le code du mieux que je pouvais (en utilisant WndProc dans une application WPF, en envoyant à partir de ma propre application win32) et cela fonctionne très bien pour moi. Il y a quelques erreurs qui apparaîtront lorsque vous exécuterez des applications 64 bits, à savoir que le Pack = 1 entraînera un désalignement du COPYDATASTRUCT et que la lecture du pointeur risque de se terminer.

Il se bloque en passant juste les ints? Regarder votre code commenté en passant un LPWSTR ou un wstring va causer des problèmes sérieux, bien que cela ne devienne pas apparent jusqu'à ce que vous démariiez les données envoyées. Pour ce que cela vaut, ce sont des extraits de mon code qui semblent fonctionner pour moi, y compris la ligne de commande.

/* 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

"à savoir le Pack = 1 entraînera le désalignement COPYDATASTRUCT et la lecture du pointeur risque de se terminer dans la douleur." Ah, si évident maintenant. Je suis habitué à lire des structures à partir de fichiers où Pack = 1 a du sens. L'application C++ est de 32 bits, mais j'ai un système de 64 bits et le code .NET est JITTED au code de 64 bits. Retrait Pack = 1 l'a réparé. Merci. "Regarder votre code commenté en passant un LPWSTR ou wstring va causer de graves problèmes" Je pensais que ce pourrait être. J'ai également essayé de passer une chaîne wchar_t fixe comme vous l'avez fait sans succès, mais cela était dû au problème ci-dessus. –

3

J'ai construire deux applications (avec VC++ et VC#, respectivement), abordant la variante « descendante bouillie » du problème (à savoir l'impossibilité d'obtenir ce struct), ils semblent travailler flawelessly , donc il peut vraiment être quelque chose avec votre configuration, comme le dit tyranid.

Quoi qu'il en soit, voici le code (il doit être suffisant pour coller juste en nouveau WIN32 APPLICATION (pour VC++) et application Windows Forms pour C# pour exécuter et 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); 
     } 
    } 
} 

Espérons que cela aide.

P.S.Je n'ai pas beaucoup de connaissance de C# et (surtout) d'interop (ayant surtout de l'intérêt pour la programmation C++), mais voyant qu'il n'y a pas de réponse [il y a quelques heures] je pensais que ce serait un bon challenge. Sans parler de la prime :)

* amn, je suis en retard :))

+0

Ah, merci quand même! –

Questions connexes