2011-05-31 60 views
4

我想將一些C++代碼移植到C#中,而我需要做的其中一件事是使用PostMessage將字節數組傳遞給另一個進程的窗口。我試圖得到源代碼給其它程序,所以我可以看到正是它的期望,但在此期間,這裏是原來的C++代碼如下所示:PostMessage的字節數組封送

unsigned long result[5] = {0}; 
//Put some data in the array 
unsigned int res = result[0]; 
Text winName = "window name"; 
HWND hWnd = FindWindow(winName.getConstPtr(), NULL); 
BOOL result = PostMessage(hWnd, WM_COMMAND, 10, res); 

而且這是我現在有:

[DllImport("User32.dll", SetLastError = true, EntryPoint = "FindWindow")] 
public static extern IntPtr FindWindow(String lpClassName, String lpWindowName); 

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

[StructLayout(LayoutKind.Sequential)] 
public struct COPYDATASTRUCT 
{ 
    public int dwData; 
    public int cbData; 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)] 
    public byte[] lpData; 
} 

public const int WM_COPYDATA = 0x4A; 

public static int sendWindowsByteMessage(IntPtr hWnd, int wParam, byte[] data) 
{ 
    int result = 0; 

    if (hWnd != IntPtr.Zero) 
    { 
     int len = data.Length; 
     COPYDATASTRUCT cds; 
     cds.dwData = wParam; 
     cds.lpData = data; 
     cds.cbData = len; 
     result = SendMessage(hWnd, WM_COPYDATA, wParam, ref cds); 
    } 

    return result; 
} 

//*****// 

IntPtr hWnd = MessageHelper.FindWindow(null, windowName); 
if (hWnd != IntPtr.Zero) 
{ 
    int result = MessageHelper.sendWindowsByteMessage(hWnd, wParam, lParam); 
    if (result == 0) 
    { 
     int errCode = Marshal.GetLastWin32Error(); 
    } 
} 

注意,我不得不使用C#中使用C++ PostMessageSendMessage切換。

所以現在發生的是,我得到的結果和errCode都是0,我認爲這意味着消息沒有處理 - 實際上看着另一個應用程序,我沒有看到預期的響應。我已經驗證了hWnd != IntPtr.Zero,所以我認爲這條消息被髮布到正確的窗口,但是消息數據是錯誤的。任何想法我做錯了什麼?

更新

我仍然沒有在評論中嘗試建議後任何運氣。這就是我目前有:

[DllImport("User32.dll", SetLastError = true)] 
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); 

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

public struct BYTEARRDATA 
{ 
    public byte[] data; 
} 

public static IntPtr IntPtrAlloc<T>(T param) 
{ 
    IntPtr retval = Marshal.AllocHGlobal(Marshal.SizeOf(param)); 
    Marshal.StructureToPtr(param, retval, false); 
    return (retval); 
} 

public static void IntPtrFree(IntPtr preAllocated) 
{ 
    //Ignores errors if preAllocated is IntPtr.Zero! 
    if (IntPtr.Zero != preAllocated) 
    { 
     Marshal.FreeHGlobal(preAllocated); 
     preAllocated = IntPtr.Zero; 
    } 
} 

BYTEARRDATA d; 
d.data = data; 
IntPtr buffer = IntPtrAlloc(d); 

COPYDATASTRUCT cds; 
cds.dwData = new IntPtr(wParam); 
cds.lpData = buffer; 
cds.cbData = Marshal.SizeOf(d); 

IntPtr copyDataBuff = IntPtrAlloc(cds); 
IntPtr r = SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, copyDataBuff); 
if (r != IntPtr.Zero) 
{ 
    result = r.ToInt32(); 
} 

IntPtrFree(copyDataBuff); 
copyDataBuff = IntPtr.Zero; 
IntPtrFree(buffer); 
buffer = IntPtr.Zero; 

這是一個64位進程試圖聯繫一個32位的過程,所以有可能是一些有,但我不知道是什麼。

+0

爲什麼你必須從'PostMessage'切換到'SendMessage'? – 2011-05-31 14:48:55

+0

看看我的[上一個問題]這個答案(http://stackoverflow.com/questions/6131636/send-byte-from-c-via-win32-sendmessage/6131886#6131886) - 長話短說,PostMessage是異步的,SendMessage是同步的,並且呼叫需要是同步的,以便數據對於呼叫的長度有效。 – 2011-05-31 18:30:58

+0

嘗試將數據從64位進程傳遞到32位進程將會是一個問題。你在'copyDataBuff'中傳遞的指針是64位,但是你將它傳遞給一個32位的進程,它不知道如何處理它。如果兩個進程都是32位,這個代碼是否工作? – 2011-05-31 20:07:57

回答

7

問題是COPYDATASTRUCT應該包含一個指針作爲最後一個成員,並且您傳遞了整個數組。

看看例子上pinvoke.net:http://www.pinvoke.net/default.aspx/Structures/COPYDATASTRUCT.html

意見後更多信息:

根據這些定義:

const int WM_COPYDATA = 0x004A; 

[StructLayout(LayoutKind.Sequential)] 
struct COPYDATASTRUCT 
{ 
    public IntPtr dwData; 
    public int cbData; 
    public IntPtr lpData; 
} 
[DllImport("User32.dll", SetLastError = true, EntryPoint = "FindWindow")] 
public static extern IntPtr FindWindow(String lpClassName, String lpWindowName); 

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

我可以創建兩個.NET程序,測試WM_COPYDATA。下面是接收器的窗口過程:使用SendMessage

protected override void WndProc(ref Message m) 
{ 
    switch (m.Msg) 
    { 
     case WM_COPYDATA: 
      label3.Text = "WM_COPYDATA received!"; 
      COPYDATASTRUCT cds = (COPYDATASTRUCT)Marshal.PtrToStructure(m.LParam, typeof(COPYDATASTRUCT)); 
      byte[] buff = new byte[cds.cbData]; 
      Marshal.Copy(cds.lpData, buff, 0, cds.cbData); 
      string msg = Encoding.ASCII.GetString(buff, 0, cds.cbData); 
      label4.Text = msg; 
      m.Result = (IntPtr)1234; 
      return; 
    } 
    base.WndProc(ref m); 
} 

並調用它的代碼:

Console.WriteLine("{0} bit process.", (IntPtr.Size == 4) ? "32" : "64"); 
Console.Write("Press ENTER to run test."); 
Console.ReadLine(); 
IntPtr hwnd = FindWindow(null, "JimsForm"); 
Console.WriteLine("hwnd = {0:X}", hwnd.ToInt64()); 
var cds = new COPYDATASTRUCT(); 
byte[] buff = Encoding.ASCII.GetBytes(TestMessage); 
cds.dwData = (IntPtr)42; 
cds.lpData = Marshal.AllocHGlobal(buff.Length); 
Marshal.Copy(buff, 0, cds.lpData, buff.Length); 
cds.cbData = buff.Length; 
var ret = SendMessage(hwnd, WM_COPYDATA, 0, ref cds); 
Console.WriteLine("Return value is {0}", ret); 
Marshal.FreeHGlobal(cds.lpData); 

可正常工作時,發送者和接收者是32個進程,當他們64位進程。 如果兩個進程的「位」不匹配,它將無法工作

有幾個原因爲什麼這不適用於32/64或64/32。假設你的64位程序想把這個消息發送給一個32位的程序。 64位程序傳遞的lParam值將爲8個字節。但是32位程序只能看到它的4個字節。所以該程序將不知道從哪裏獲取數據!

即使有效,COPYDATASTRUCT結構的大小也是不同的。在32位程序中,它包含兩個指針和一個DWORD,總大小爲12個字節。在64位程序中,COPYDATASTRUCT的長度爲20個字節:每個8個字節的兩個指針和一個4字節的長度值。

你也有類似的問題。

我嚴重懷疑你會得到WM_COPYDATA爲32/64或64/32工作。

+0

這是一個很好的例子 - 我仍然得到0作爲結果,雖然(見我的最新代碼的更新)。我做了什麼明顯錯誤的事情? – 2011-05-31 19:50:31

+0

@Matt:查看我的其他信息。我認爲你不能完成這項工作。 – 2011-05-31 23:46:35

+1

Windows負責將數據複製到其他進程的地址空間,並將指針存儲到本地緩衝區。所以每個進程都會使用正確大小的指針來供自己使用。目標進程不會收到發件人傳遞的COPYDATASTRUCT! (並且指向發件人地址空間的指針無論如何都是無用的) – 2011-06-01 02:43:50

0

難道是32位和64位的問題?

嘗試將COPYDATASTRUCT的dwData成員設置爲IntPtr而不是int。

對相關的問題,請參見本主題:

http://www.vistax64.com/net-general/156538-apparent-marshalling-related-problem-x64-but-works-x86.html

見COPYDATASTRUCT的原始定義:

http://msdn.microsoft.com/en-us/library/ms649010(VS.85).aspx

這裏的ULONG_PTR的在x64上的含義是:

http://msdn.microsoft.com/en-us/library/aa384255(VS.85).aspx

要存儲64位指針值,請使用ULONG_PTR。使用32位編譯器編譯時,ULONG_PTR值爲32位,使用64位編譯器編譯時爲64位。

+0

這是一個64位的進程試圖調用一個32位的,但傳遞dwData作爲一個IntPtr似乎沒有幫助(這並不意味着這不是問題)。 – 2011-05-31 19:49:13

0

在你的IntPtrAlloc函數中,SizeOf(param)給你什麼?我認爲這將是數組引用的大小,而不是數組內容的大小。所以Windows會將.NET數組引用複製到另一個進程中,這是完全沒有意義的。

固定陣列,並使用Marshal.UnsafeAddrOfPinnedArrayElement來獲得lpData的正確值。

2

這將適用於32位發送器到64位接收器,64位發送器到32位接收器。也可以從32到32,64到64.你甚至不需要聲明COPYDATASTRUCT。非常簡單:

const int WM_COPYDATA = 0x004A; 

    [DllImport("user32.dll", SetLastError = true)] 
    static extern IntPtr FindWindow(String lpClassName, String lpWindowName); 

    [DllImport("user32.dll", SetLastError = true)] 
    static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); 

    static IntPtr SendMessage(IntPtr hWnd, byte[] array, int startIndex, int length) 
    { 
     IntPtr ptr = Marshal.AllocHGlobal(IntPtr.Size * 3 + length); 
     Marshal.WriteIntPtr(ptr, 0, IntPtr.Zero); 
     Marshal.WriteIntPtr(ptr, IntPtr.Size, (IntPtr)length); 
     IntPtr dataPtr = new IntPtr(ptr.ToInt64() + IntPtr.Size * 3); 
     Marshal.WriteIntPtr(ptr, IntPtr.Size * 2, dataPtr); 
     Marshal.Copy(array, startIndex, dataPtr, length); 
     IntPtr result = SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, ptr); 
     Marshal.FreeHGlobal(ptr); 
     return result; 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     IntPtr hWnd = FindWindow(null, "Target Window Tittle"); 
     byte[] data = System.Text.Encoding.ASCII.GetBytes("this is the sample text"); 
     SendMessage(hWnd, data, 0, data.Length); 
    } 

    protected override void WndProc(ref Message m) 
    { 
     switch (m.Msg) 
     { 
      case WM_COPYDATA: 
       byte[] b = new Byte[Marshal.ReadInt32(m.LParam, IntPtr.Size)]; 
       IntPtr dataPtr = Marshal.ReadIntPtr(m.LParam, IntPtr.Size * 2); 
       Marshal.Copy(dataPtr, b, 0, b.Length); 
       string str = System.Text.Encoding.ASCII.GetString(b); 
       MessageBox.Show(str); 
       // m.Result = put result here; 
       return; 
     } 
     base.WndProc(ref m); 
    }