2011-07-21 147 views
4

我已經花了幾天(或更多)試圖讓這個工作。C#使用SendMessage,問題與WM_COPYDATA

手頭的應用程序是FTPRush,我知道有一個叫做rush_cmdline.exe的cmd行應用程序,它使用SendMessage發送請求到FTPRush

從調試rush_cmdline.exe我可以看到lParam,wParam,MessagehWnd

我的代碼如下所示(使用SendMessage函數,而不是SendMessageW):

[DllImport("User32.dll", EntryPoint = "FindWindow")] 
public static extern Int32 FindWindow(String lpClassName, String lpWindowName); 
[DllImport("USER32.DLL", EntryPoint= "SendMessage")] 
public static extern IntPtr SendMessage(int hWnd, int Msg, int wParam, IntPtr lParam); 

而且我已經嘗試了另一種規格也:

[DllImport("User32.dll", EntryPoint = "SendMessage")] 
public static extern int SendMessage(int hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam); 

手柄(hWnd)是沒有問題的,因爲這個工程:

int ftprush = FindWindow("TfmRush", null); 
ShowWindow(ftprush, 8); 

哪(我沒有粘貼dllimport,因爲它不是這裏很重要。讓我知道你是否希望看到它)把窗口放在前面。另外,我通過調試rush_cmdline.exe進行了檢查。所以手柄是一樣的。

兩次嘗試這兩個失敗(默默):

public const Int32 WM_COPYDATA = 0x4A; 
string msg = "RushApp.FTP.Login('backup','',0); "; 
// 1 
byte[] array = Encoding.UTF8.GetBytes((string)msg); 
int size = Marshal.SizeOf(array[0]) * array.Length + Marshal.SizeOf(array[0]); 
IntPtr ptr = Marshal.AllocHGlobal(size); 
Marshal.Copy(array, 0, ptr, array.Length); 
Marshal.WriteByte(ptr, size - 1, 0); 
SendMessage(ftprush, WM_COPYDATA, 0, ptr); 

// 2 
public struct COPYDATASTRUCT 
{ 
    public IntPtr dwData; 
    public int cbData; 
    [MarshalAs(UnmanagedType.LPStr)] 
    public string lpData; 
} 

COPYDATASTRUCT cds; 
cds.dwData = (IntPtr)100; 
cds.lpData = msg; 
cds.cbData = sarr.Length + 1; 
SendMessage(ftprush, WM_COPYDATA, 0, ref cds); 

我希望至少在第二解決方案正常工作,因爲它匹配得不錯這一點:perl example

任何啓示是極大的讚賞!

感謝,

  • 弗蘭克

UPDATE:

string msg = "RushApp.FTP.Login('backup','',0);\0"; 
var cds = new COPYDATASTRUCT 
{ 
      dwData = new IntPtr(3), 
      cbData = msg.Length + 1, 
      lpData = msg 
}; 
IntPtr ftprush = FindWindow("TfmRush", null); 
SendMessage(ftprush, WM_COPYDATA, IntPtr.Zero, ref cds); 
+0

你確定FTP Rush支持WM_COPYDATA嗎? FTPRush文檔對此消息有何評論?如果FTPRush不處理WM_COPYDATA,它將忽略該消息並且什麼都不做。 – shf301

+0

@ sh301:它確實支持WM_COPYDATA。這是perl例子中使用的,它是'rush_cmdline.exe'使用的。 – Frank

回答

7

我的定義有

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)] 
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, ref COPYDATASTRUCT lParam); 

public struct COPYDATASTRUCT { 
    public int cbData; 
    public IntPtr dwData; 
    [MarshalAs(UnmanagedType.LPStr)] public string lpData; 
} 

var cds = new Win32.COPYDATASTRUCT { 
              dwData = new IntPtr(3), 
              cbData = str.Length + 1, 
              lpData = str 
             }; 
Win32.SendMessage(ftprush, Win32.WM_COPYDATA, IntPtr.Zero, ref cds); 

當然,確保str是空值終止「 \ 0"

或者通過PInvoke.NET給出的定義是

[DllImport("user32.dll", CharSet = CharSet.Auto)] 
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, StringBuilder lParam); 
//If you use '[Out] StringBuilder', initialize the string builder with proper length first. 
+0

感謝您的回答,鮑勃。你的意思是'Win32.SendMessage(ftprush,Win32.WM_COPYDATA,IntPtr.Zero,ref cds);'(ref)?我已經更新了我的問題。我不得不添加ref來讓它編譯。或者我在這裏錯過了什麼? – Frank

+0

是的,我不是指ref,我犯了一個錯字! –

+0

我無法讓它與FTPRush一起工作,但是您的方法與我期望的方法一致,所以我認爲這是我的一個愚蠢的錯誤。接受答案。你有沒有可能嘗試一個SendMessage的例子? (哪個應用程序,哪個lParam) – Frank

1

在COPYDATASTRUCT參數的順序是非常重要的,而鮑勃淡水河谷的回答有他們在錯誤的順序。 http://msdn.microsoft.com/en-us/library/windows/desktop/ms649010(v=vs.85).aspx應該按以下順序:

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

我沒有得到的MarshalAs(UnmanagedType.LPStr)] public string lpData來工作的。我只是通過做自己的編組纔得到它的工作:

[StructLayout(LayoutKind.Sequential)] 
public struct COPYDATASTRUCT : IDisposable 
{ 
    public IntPtr dwData; 
    public int cbData; 
    public IntPtr lpData; 

    /// <summary> 
    /// Only dispose COPYDATASTRUCT if you were the one who allocated it 
    /// </summary> 
    public void Dispose() 
    { 
     if (lpData != IntPtr.Zero) 
     { 
      Marshal.FreeCoTaskMem(lpData); 
      lpData = IntPtr.Zero; 
      cbData = 0; 
     } 
    } 
    public string AsAnsiString { get { return Marshal.PtrToStringAnsi(lpData, cbData); } } 
    public string AsUnicodeString { get { return Marshal.PtrToStringUni(lpData); } } 
    public static COPYDATASTRUCT CreateForString(int dwData, string value, bool Unicode = false) 
    { 
     var result = new COPYDATASTRUCT(); 
     result.dwData = (IntPtr)dwData; 
     result.lpData = Unicode ? Marshal.StringToCoTaskMemUni(value) : Marshal.StringToCoTaskMemAnsi(value); 
     result.cbData = value.Length + 1; 
     return result; 
    } 
} 
1

在上面的2個答案之間我拼湊了一個工作示例。 Bryce Wagner的類可以工作,所以我添加了一個使用SendMessageTimeout發送數據的方法。這是一個靜態方法,所以你只需要調用它來發送數據。這不是我的工作,只是粘在一起,分享回來。

[StructLayout(LayoutKind.Sequential)] 
    public struct CopyData: IDisposable { 
     [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)] 
     static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, IntPtr wParam, ref CopyData target, 
               SendMessageTimeoutFlags fuFlags, uint uTimeout, out UIntPtr lpdwResult); 

     [Flags] 
     enum SendMessageTimeoutFlags: uint { 
      SMTO_NORMAL    = 0x0, 
      SMTO_BLOCK    = 0x1, 
      SMTO_ABORTIFHUNG  = 0x2, 
      SMTO_NOTIMEOUTIFNOTHUNG = 0x8 
     } 
     const uint WM_COPYDATA = 0x4A; 

     public IntPtr dwData; 
     public int cbData; 
     public IntPtr lpData; 

     public void Dispose() { 
      if (lpData != IntPtr.Zero) { 
       Marshal.FreeCoTaskMem(lpData); 
       lpData = IntPtr.Zero; 
       cbData = 0; 
      } 
     } 
     public string AsAnsiString { 
      get { return Marshal.PtrToStringAnsi(lpData, cbData); } 
     } 
     public string AsUnicodeString { 
      get { return Marshal.PtrToStringUni(lpData); } 
     } 
     public static CopyData CreateForString(int dwData, string value, bool Unicode = false) { 
      var result = new CopyData(); 
      result.dwData = (IntPtr) dwData; 
      result.lpData = Unicode ? Marshal.StringToCoTaskMemUni(value) : Marshal.StringToCoTaskMemAnsi(value); 
      result.cbData = value.Length + 1; 
      return result; 
     } 

     public static UIntPtr Send(IntPtr targetHandle, int dwData, string value, uint timeoutMs = 1000, bool Unicode = false) { 
      var cds = CopyData.CreateForString(dwData, value, Unicode); 
      UIntPtr result; 
      SendMessageTimeout(targetHandle, WM_COPYDATA, IntPtr.Zero, ref cds, SendMessageTimeoutFlags.SMTO_NORMAL, timeoutMs, out result); 
      cds.Dispose(); 
      return result; 
     } 
    } 

要使用它:

CopyData.Send(targetHandle, 1234, "This is a test"); 

使用的缺省爲1秒超時。