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)。最简单的方式是直接发送一个整形随机数字,使 用这个数字在临时文件夹在建立一个临时文件,写入数据,程序的另一个实例接 收到消息后,读取这个文件获得数据,进行相应的处理,然后临时文件被删除, 从而实现了进程间的数据交换。

时间: 2024-08-30 09:10:36

C#实现自定义消息处理的相关文章

Delphi自定义消息应用例子

现在很多的应用程序都有这样一种功能,当用户选择最小化窗口时,窗口不是象平常那样最小化到任务栏上,而是"最小化"成一个任务栏图标.象FoxMail 3.0 NetVampire 3.0等都提供了这样的功能. 实现这样的功能实际上并不复杂,在窗口最小化时,窗口会发出WM_SYSCOMMAND消息,你只要需要截取Windows的WM_SYSCOMMAND消息,在窗口最小化时隐藏窗口并调用WindowsAPI函数Shell_NotifyIcon将定义的图标添加到任务栏上,Shell_Notif

c# WinForm 控件样式 _____

问题描述 c# WinForm 控件样式 _____ 自己用 WinForm 拖控件做出来的程序样式: Win7 系统样式: 请问 Win7 系统的控件样式是怎么做出来的? 以上面 ListView 为例.项选中的时候跟 Win7 系统的差好远. 我是想知道原理,有代码的话最好啦! 解决方案 static class NativeMethods { public const int WM_CREATE = 0x1; [DllImport("uxtheme.dll", CharSet =

MFC自定义消息

本文地址:http://blog.163.com/strive_only/blog/static/893801682010101911467765/ 消息机制是windows的典型运行机制,在MFC中有很多的消息如WM_BTN**等.但是在有些情况下我们需要自定义一些消息去做一些我们需要的功能,MFC的向导不能帮助我们做到这一点,我们可以通过添加相应的代码去完成这个功能. 添加自定义消息操作如下:1. 建立MFC工程,如基于对话框的应用程序,Test.2. 在资源中添加要处理的消息的值,即在CT

MFC对话框应用程序添加自定义消息

1. 定义自定义消息 [cpp] view plaincopy   /**   * \brief 消息测试   */   #define E6100_MSG_TEST          ( WM_USER + 1001 )   2. 声明自定义消息处理函数 [cpp] view plaincopy   /**   * \brief 写入数据操作完毕   */   afx_msg LRESULT OnTest( WPARAM wParam, LPARAM lParam );   3. 实现自定义消

Qt之输出控制

简述 在Qt项目开发过程中,往往需要对程序的一些信息进行控制,比如:打印日志.调试信息等,便于我们后期查找.跟踪及定位问题. 下面,我们来分享下常用的几种方式.   简述 示例代码 应用程序输出 控制台输出 重定向至文件   示例代码 我们简单地写一些测试代码,用qDebug输出一些基本信息. #include <QApplication> #include <QWidget> #include <qDebug> int main(int argc, char *arg

Qt之qInstallMessageHandler(输出详细日志)

简述 安装之前已定义的消息处理程序,返回一个指向前一个消息处理程序. 消息处理程序是一个函数,用于打印调试信息.警告信息.严重错误和致命的错误的消息.Qt库(debug模式)包含成百上千的警告信息打印,当发生内部错误时(通常是无效的函数参数).Qt在release模式下构建还包含这些警告,除非在编译时设置QT_NO_WARNING_OUTPUT和/或QT_NO_DEBUG_OUTPUT.如果你实现自己的消息处理程序,就可以完全控制这些消息. 缺省的消息处理程序向标准输出打印消息.如果是一个致命的

MFC自定义消息的实现方法_C 语言

一.概述: 消息机制是windows程序的典型运行机制,在MFC中有很多已经封装好了的消息,如WM_BTN**等.但是在有些特殊情况下我们需要自定义一些消息去完成一些我们所需要的功能,这时候MFC的向导不能帮助我们做到这一点.对此,我们可以通过添加相应的代码去完成这个功能. 二.实现方法: 添加自定义消息操作如下: 1. 建立MFC工程,如基于对话框的应用程序,Test. 2. 在资源中添加要处理的消息的值,即在CTestDlg.h中添加 如下代码. (因为很多MFC的消息是在WM_USER内的

Qt之qInstallMessageHandler(重定向至文件)

简述 在Qt之qInstallMessageHandler(输出详细日志)一节中,我们讲解了如何使用自定义消息处理程序输出调试信息,而且可以很直观.很方便的得到输出代码所在的文件.函数.行号等信息. 当软件发布的时候,通常都会采用日志输出功能,方便我们后期的跟踪.查找问题. 简述 自定义消息处理 效果 源码 更多参考 自定义消息处理 下面,我们来根据需要自定义消息处理程序.其中输出格式包含:输出信息.文件.函数.行号.日期时间.这样就很利于我们追踪了. 效果 源码 void myMessageO

Duilib界面解析1---简单的仿360窗口

最近开始研究Duilib,并逐个分析Duilib的demo例子,记录下来方便更多的人学习 .界面运行起来如下图所示,该Demo主要包含 BaseDialog.h和BaseDialog.cpp,以及界面布局文件的详细解释,每行注释已经添加. BaseDialog头文件源文件 以及布局文件如下,可自行下载Duilib 根据注释学习该例子 只针对新手~~~ #ifndef BaseDialog_H #define BaseDialog_H #include "UIlib.h" #includ