2014年2月27日 星期四

[C#/win32 API] SendMessage() 與 PostMessage()

網路上比較少SendMessage()跟PostMessage資料

先給SendMessage範例




SendMessage會等到接收端check後才會繼續執行,而PostMessage則是丟入訊息queue,繼續做。

以下範例都要先加入 using System.Runtime.InteropServices;

SendMessage A端(控制端):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Runtime.InteropServices;
namespace TestA
{
    public partial class Form1 : Form
    {
        [DllImport("user32.dll")]
        private static extern int SendMessage(IntPtr hwnd, uint wMsg, int wParam, IntPtr lParam);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern uint RegisterWindowMessage(string lpString);
        uint MSG_SHOW = RegisterWindowMessage("Show Message");

        [DllImport("user32.dll")]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
     

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click_1(object sender, EventArgs e)
        {
            IntPtr form_name = FindWindow(null, "要控制的視窗元件名稱(text,不是name)");//找B的IntPtr 用來代表指標或控制代碼

            if (form_name != IntPtr.Zero)
            {
                try
                {
                    int iNum = 9527;
                    SendMessage(form_name, MSG_SHOW, iNum, IntPtr.Zero);
                }
                catch (Exception)
                {
                }
            }
        }
    }
}

SendMessage B端(接收端):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace TestB
{
    public partial class TestB_1 : Form
    {
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern uint RegisterWindowMessage(string lpString);
        uint MSG_SHOW = RegisterWindowMessage("Show Message");
        public TestB_1()
        {
            InitializeComponent();
        }
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == MSG_SHOW)
            {
                label1.Text = (string)m.WParam.ToString();
            }
            base.WndProc(ref m);
        }
    }
}

PostMessage A端(呼叫端):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace postmessager
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        [DllImport("user32.dll", EntryPoint = "FindWindow", CharSet = CharSet.Auto)]
        private extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
 
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int PostMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
 
        private void post()
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            IntPtr ptr = FindWindow(null, "post messager B");
            if (ptr != IntPtr.Zero)
            {
                int msg = 9527;
                PostMessage(ptr, msg, IntPtr.Zero, IntPtr.Zero);
            }
        }
    }
}

PostMessage B端(接收端):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace PostmessagerB
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == 9527)//m.Msg == MSG_SHOW
            {
                //label1.Text = (string)m.WParam.ToString();
                MessageBox.Show("9527");
            }
            base.WndProc(ref m);
        }
    }
}

至於為何可以使用SendMessage(),PostMessage(),FindWindow(),RegisterWindowMessage(),都是要先

import user32.dll (也就是[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] 後面為參數)

然後接收端使用WndProc (WindowProcess,我個人理解縮寫函意)去接收。

大部分程式碼也是參考網路上的,但大家都抄來抄去,而且我有修改程式碼,所以就不註明出處了(因為也不知道原始是誰的),當然我的版本是比較精簡化。

End

15 則留言:

  1. 請問PostMessage接收端有需要宣告win32api嗎?
    我在VS2008下編譯好像會不給過
    錯誤 1 必須是類別、委派、列舉、介面或結構
    protected override void WndProc(ref Message m)

    回覆刪除
  2. 謝謝版大詳細的熱心分享!!
    不知道您是否可以分享PeekMessage 與 GetMessage這兩個函數在C#上的使用呢?
    (這兩個函數可以查到的資料更少Q_Q)
    再次感謝!!

    回覆刪除
    回覆
    1. 目前還沒有寫到關於Peek & Get這部份耶!

      但這篇我想您可以參考看看
      http://blog.xuite.net/grimmslaw/78/55733626-GetMessage+PeekMessage+WaitMessage%E5%87%BD%E6%95%B8%E7%9A%84%E7%94%A8%E6%B3%95

      希望對您有幫助!

      刪除
    2. 謝謝您提供的資訊! 我會好好研究@@
      另外想請問版大!
      假設我用PostMessage要如何傳遞結構(structure)?
      我是參考 http://msdn.microsoft.com/zh-tw/library/4ca6d5z7(v=vs.110).aspx
      想法:
      (A端)
      struct AStudent
      {
      public string name;
      public int number;
      }

      private void button1_Click(object sender, EventArgs e)
      {
      IntPtr ptr = FindWindow(null, "PostMessage-Get");

      AStudent ATom;
      ATom.name = "Tom";
      ATom.number = 1;
      // Initialize unmanged memory to hold the struct.
      IntPtr pnt = Marshal.AllocHGlobal(Marshal.SizeOf(ATom));
      Marshal.StructureToPtr(ATom, pnt, false);

      if (ptr != IntPtr.Zero)
      {
      int msg = 9527;
      PostMessage(ptr, msg, IntPtr.Zero, pnt);
      }

      }

      B端
      struct BStudent
      {
      public string name;
      public int number;
      }

      protected override void WndProc(ref Message m) //WndProc():Processes Windows messages.
      {
      if (m.Msg == 9527)//m.Msg == MSG_SHOW
      {
      label1.Text = Convert.ToString(m.Msg);
      BStudent BTom;
      BTom = (BStudent)Marshal.PtrToStructure(m.LParam, typeof(BStudent));
      label3.Text = BTom.name;
      }
      base.WndProc(ref m);
      }

      但在傳資料時(A端按下button), B端的
      BTom = (BStudent)Marshal.PtrToStructure(m.LParam, typeof(BStudent));
      此行出現錯誤, 不知為什麼>_<

      想請問版主我這樣寫哪裡出了問題 還是根本不該這樣寫Orz
      謝謝!!

      刪除
    3. 可能要麻煩您有完整的錯誤訊息會比較好研究,

      如果說是要傳結構,我覺得用Serialize/Deserialize也很不錯~

      因為WndProc()會不停的一直找尋是否有message進來,如果只是單純要把

      東西傳到另一支程式,可以試試看用開檔讀檔的方式(前面幾篇有寫到序列化)

      刪除
    4. 另外可以設中斷點(F9)在那一行,看看傳入的型態是甚麼?

      不過沒有完整的錯誤訊息可能debug稍稍困難

      刪除
    5. 嗯!謝謝回覆!
      我直接把出問題的敘述貼到imgur這個照片分享空間@@
      http://imgur.com/0gXjJId
      希望版主能幫我看一下>_<
      另外, 我也有去讀版主建議我看您之前寫的"序列化"並測試過基本功能OK!
      也是個非常好的結構傳遞方式!!
      但還是希望能搞清楚用PostMessage傳資料到底是哪裡出了問題Orz
      謝謝!!

      刪除
    6. 其實這問題我也是第一次看到,但我想....會不會是需要加unsafe?

      一同學習囉!

      刪除
    7. 謝謝建議! 但我用unsafe試過也不太行QQ
      請問如果要傳結構的話 您會怎麼寫阿?
      感恩!!

      刪除
    8. 我剛好有寫到 要兩支程式傳結構的部分,但不曉得是否為你需要的?

      不論如何,講一下我的想法,首先是先建立一個類別(可序列化)

      1.A程式先把要的資料(Student,PhoneNumber,Name)放入structure

      2.把Structure利用Stream方式+Serialize寫入檔案(我是用.dat,反正別人開不了就是了)

      3.在另一支程式定期去收(利用timer)或是利用postmessage通知說 我寫入囉~(這邊很隨意,就是有通知就好了)然後Deserialize 然後就可在另一支程式上有這樣完整的Structure了:)

      刪除
    9. 嗯!! 瞭解了 謝謝!
      我也用您教的序列化方式來傳struct吧!
      感謝您這麼有耐心的替我解惑T_T

      刪除
    10. 不會~ 這是我想很久,但最容易的方式了(其他方法也是可以 但是都很複雜)

      記得要用try catch以免同時開檔(機率很小 我試過) 會有error

      刪除
    11. 恩恩!! 謝謝提醒^____^

      刪除