当前位置:
首页 > 编程开发 > Objective-C编程 >
-
C#实现自定义消息处理
制作者:剑锋冷月 单位:无忧统计网,www.51stat.net
众所周知,委托和事件机制是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)。最简单的方式是直接发送一个整形随机数字,使用这个数字在临时文件夹在建立一个临时文件,写入数据,程序的另一个实例
接收到消息后,读取这个文件获得数据,进行相应的处理,然后临时文件被删除,从而实现了进程间的数据交换。
进一步查找它的主窗口,并向其发送消息,传送文件列表,让它去处理,当前例程则结束。主窗口则要处理
这个自定义的消息,但是如果使用的是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)。最简单的方式是直接发送一个整形随机数字,使用这个数字在临时文件夹在建立一个临时文件,写入数据,程序的另一个实例
接收到消息后,读取这个文件获得数据,进行相应的处理,然后临时文件被删除,从而实现了进程间的数据交换。
众所周知,委托和事件机制是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)。最简单的方式是直接发送一个整形随机数字,使用这个数字在临时文件夹在建立一个临时文件,写入数据,程序的另一个实例
接收到消息后,读取这个文件获得数据,进行相应的处理,然后临时文件被删除,从而实现了进程间的数据交换。
进一步查找它的主窗口,并向其发送消息,传送文件列表,让它去处理,当前例程则结束。主窗口则要处理
这个自定义的消息,但是如果使用的是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)。最简单的方式是直接发送一个整形随机数字,使用这个数字在临时文件夹在建立一个临时文件,写入数据,程序的另一个实例
接收到消息后,读取这个文件获得数据,进行相应的处理,然后临时文件被删除,从而实现了进程间的数据交换。
最新更新
Objective-C语法之代码块(block)的使用
VB.NET eBook
Add-in and Automation Development In VB.NET 2003 (F
Add-in and Automation Development In VB.NET 2003 (8
Add-in and Automation Development in VB.NET 2003 (6
Add-in and Automation Development In VB.NET 2003 (5
AddIn Automation Development In VB.NET 2003 (4)
AddIn And Automation Development In VB.NET 2003 (2)
Addin and Automation Development In VB.NET 2003 (3)
AddIn And Automation Development In VB.NET 2003 (1)
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
SQL Server -- 解决存储过程传入参数作为s
武装你的WEBAPI-OData入门
武装你的WEBAPI-OData便捷查询
武装你的WEBAPI-OData分页查询
武装你的WEBAPI-OData资源更新Delta
5. 武装你的WEBAPI-OData使用Endpoint 05-09
武装你的WEBAPI-OData之API版本管理
武装你的WEBAPI-OData常见问题
武装你的WEBAPI-OData聚合查询
OData WebAPI实践-OData与EDM
OData WebAPI实践-Non-EDM模式