程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> C#實現自定義消息處理

C#實現自定義消息處理

編輯:關於C#

眾所周知,委托和事件機制是C#應用程序的一個很重要的方面。

Microsoft 的 BCL 類庫對Windows的控件進行了幾乎全面的封裝,使應用程序 開發人員甚至不用了解消息循環就能寫出相樣的程序。

然而,甚至Windows UI編程到了 WPF 時代,消息機制仍然占據著舉足輕重的 作用。可以這麼說,沒有消息循環就沒有Windows。(當然WPF很大程度上是對D3D 的封裝,它本身並不是基於Win32的消息循環, 但是Micorsoft仍然在WPF中提供了 對Win32消息機制的支持)。

BCL2.0 的System.Windows.Forms 命名空間的控件則完全是對 Win32 控件的 進一步封裝,因此C#2.0本身也是基於Win32消息循環機制的,在編寫C#桌面應用 時,在一些地方,甚至還必須借助於消息,否則可能無法實現一些功能,或者很 難實現。比如Aplication類提供了對添加 AddMessageFilter 的支持,Control類 放出了幾個類似於 ProcessCmdKey 的虛方法,另外,Control類還把 WndProc 實 現為虛擬的,以便控件開發人員能夠方便地重寫。

廢話不多說了,考慮這樣一種情況:一個應用程序關聯一種文件類型,雙擊這 樣類型的文件,則使用該應用程序打開這個文檔。這很容易,實現,程序把文件 (可能是多個)當作命令行參數讀取,現在假設這是一個多文檔應用程序,如果 應用程序正在運行,那麼一般希望用這個已經存在的程序來打開這些文件,而不 是重新啟用一個新的程序實例。這在Windows應用中很常見,比如用Visual Studio 打開程序源文件,用IE7打開另外一個html文檔(當然這個可以配置是啟 用新實例還是用已有實例打開它)等。假設這個軟件是用Win32或者VC開發的,那 很容易實現,查找已經存在的實例(進程),如果找到,則進一步查找它的主窗 口,並向其發送消息,傳送文件列表,讓它去處理,當前例程則結束。主窗口則 要處理這個自定義的消息,但是如果使用的是C#,你可能犯傻了,怎麼辦呢?

實際上,上述方法在C#應用程序中仍然適用,你仍然可以在C#中自定義消息。 下面演示這一設想。假設最後生成的可執行程序叫 “MyApplication.exe”。

1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.Runtime.InteropServices;
5using System.IO;
6using System.Reflection;
7using System.Windows.Forms;
8
9namespace MyApplication {
10  internal static class Program {
11    [STAThread]
12    private static void Main(string[] args) {
13      Application.EnableVisualStyles();
14      Application.SetCompatibleTextRenderingDefault (false);
15
16      List<string> fileList = new List<string>();
17      foreach (string filename in args) {
18        if (File.Exists(filename)) {
19          fileList.Add(filename);
20        }
21      }
22
23      if (! SingleInstanceHelper.OpenFilesInPreviousInstance(fileList.ToArray())) {
24        Application.Run(new MyWindow());
25      }
26    }
27  }
28
29  public class MyWindow : Form {
30    protected override void WndProc(ref Message msg) {
31      if (!SingleInstanceHelper.PreFilterMessage(ref msg, this)) {
32        base.WndProc(ref msg);
33      }
34    }
35  }
36
37  internal static class SingleInstanceHelper {
38    private static readonly int CUSTOM_MESSAGE = 0X400 + 2;
39    private static readonly int RESULT_FILES_HANDLED = 2;
40
41    [DllImport("user32.dll")]
42    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
43
44    [DllImport("user32.dll")]
45    private static extern IntPtr SetForegroundWindow (IntPtr hWnd);
46
47    public static bool OpenFilesInPreviousInstance (string[] fileList) {
48      int currentProcessId = Process.GetCurrentProcess ().Id;
49      string currentFile = Assembly.GetEntryAssembly ().Location;
50      int number = new Random().Next();
51      string fileName = Path.Combine(Path.GetTempPath (), "sd" + number + ".tmp");
52      try {
53        File.WriteAllLines(fileName, fileList);
54        List<IntPtr> alternatives = new List<IntPtr>();
55        foreach (Process p in Process.GetProcessesByName("MyApplication")) {
56          if (p.Id == currentProcessId) continue;
57
58          if (string.Equals(currentFile, p.MainModule.FileName, StringComparison.CurrentCultureIgnoreCase)) {
59            IntPtr hWnd = p.MainWindowHandle;
60            if (hWnd != IntPtr.Zero) {
61              long result = SendMessage(hWnd, CUSTOM_MESSAGE, new IntPtr(number), IntPtr.Zero).ToInt64();
62              if (result == RESULT_FILES_HANDLED) {
63                return true;
64              }
65            }
66          }
67        }
68        return false;
69      } finally {
70        File.Delete(fileName);
71      }
72    }
73
74    internal static bool PreFilterMessage(ref Message m, Form mainForm) {
75      if (m.Msg != CUSTOM_MESSAGE)
76        return false;
77
78      long fileNumber = m.WParam.ToInt64();
79      m.Result = new IntPtr(RESULT_FILES_HANDLED);
80      try {
81        MethodInvoker invoker = delegate {
82          SetForegroundWindow(mainForm.Handle) ;
83        };
84        mainForm.Invoke(invoker);
85        string tempFileName = Path.Combine (Path.GetTempPath(), "sd" + fileNumber + ".tmp");
86        foreach (string file in File.ReadAllLines (tempFileName)) {
87          invoker = delegate {
88            // Open the file
89          };
90          mainForm.Invoke(invoker);
91        }
92      } catch (Exception) {
93        return false;
94      }
95
96      return true;
97    }
98  }
99}

該示例程序通過檢查進程只允許應用程序運行一個實例,然後自定義消息,通 過 SendMessage 向程序的另一個實例發送消息,由於牽涉到進程間發送數據,如 果直接發送則必須使用類型的 Marshal, 而Marshal過的自定義類型在C#中是不允 許傳遞的(除非使用了Hook)。最簡單的方式是直接發送一個整形隨機數字,使 用這個數字在臨時文件夾在建立一個臨時文件,寫入數據,程序的另一個實例接 收到消息後,讀取這個文件獲得數據,進行相應的處理,然後臨時文件被刪除, 從而實現了進程間的數據交換。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved