C#向无窗口的进程发送消息_C#教程

注:本文适用.net2.0+的winform程序
一个winform程序,我希望它不能多开,那么在用户启动第二个实例的时候,作为第二个实例来说,大概可以有这么几种做法:

1.弹个窗告知用户【程序已运行】之类,用户点击弹窗后,退出自身

2.什么都不做,默默退出自身

3.让已运行的第一个实例把它的窗体显示出来,完了退出自身

显然第3种做法更地道,实现该效果的核心问题其实是:如何显示指定进程的窗口?

首先想到的是调用ShowWindow、SetForegroundWindow等API,配合使用可以将被遮挡、最小化的窗口前排显示出来,这也是很多涉及到这种案例的网文介绍的方法,此法的局限在于,目标进程的主窗口必须存在,准确说是要有有效的主窗口句柄,表现在访问Process.MainWindowHandle能得到一个非IntPtr.Zero的值,即有效的句柄;或者用spy类工具能看到该进程下有至少一个窗口;或者按alt+tab能将它的窗口切换出来。

那如果进程没窗口怎么办?先说一下什么情况下进程会没窗口,很简单,让Form.Visible=false(或者Form.Hide(),等价的)就行,此时窗体就消失了,既不可见,也没有对应的任务栏按钮,alt+tab也切不出来。当程序中的所有Form都Hide后,访问该进程的MainWindowHandle会得到IntPtr.Zero,这就是无窗口进程。那什么样的程序会这么干,太多了好吧,各种音乐播放器,杀软什么的,都允许【关闭/最小化到系统托盘】,在你点叉或者最小化后,窗体就会隐藏,只留一个图标在托盘区。由于这种进程的MainWindowHandle拿不到有效句柄,所以上面那些API是用不了的,只能另想办法。

回到问题【如何显示指定进程的窗口】,如果你的程序不允许关闭到托盘区,始终存在窗口的话(最小化也是存在),那你愉快的用ShowWindow、SetForegroundWindow等API就好,不用继续。但如果你的程序要像播放器杀软那样允许用户隐藏窗口的话,那还得继续折腾,此时问题变成【如何让无窗口的进程显示窗口】,我的思路是这样:既然目标进程没窗口,我没办法纯粹用外部手段操作到它的窗体,但因为程序是我自己写的,可不可以来个里应外合,办了这事。比如向它发一条特定消息,它在收到该消息后,心领神会,把自己的窗口显示出来~到时候荣华富贵享之sorry入戏了。这个思路主要涉及两个问题,怎么发和怎么收,至于收到后如何前排显示窗口之类,小case。

怎么发

SendMessage/PostMessage自然是指不上的,因为这俩货也是基于窗口的,其实我一度怀疑走消息这条路是否可行,这涉及到一个原理问题,就是如果消息一定是只能发送给窗口的话,那注定此路不通,只能考虑别的进程间通信方案。好在了解到PostThreadMessage这个API,解决了我的问题。该API是向指定线程发送消息(MSDN文档在此),这也说明在原理上,消息并非只可以发给窗口,还可以发给线程,至于还能不能发给别的什么东西就不知道了。先看一下发送语句:

void Main()
{
...
//向目标进程的主线程发送消息
PostThreadMessage(Process.GetProcessById(pid).Threads[0].Id, 0x80F0, IntPtr.Zero, IntPtr.Zero);
...
}
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
public static extern bool PostThreadMessage(int threadId, uint msg, IntPtr wParam, IntPtr lParam); 

API的第1个参数是目标线程的ID。注意两点:①此ID是系统全局的线程ID,并非Thread.ManagedThreadId这种“假”ID;②目标线程必须存在消息循环。winform的主线程往往就是UI线程,天然存在消息循环,所以无需考虑这个问题。第2个参数是要发送的消息ID。我们的目的是发一条收发双方约定的消息,所以这个消息要够特别,不能跟系统消息撞衫,所以范围最好介于0x8001~0xBFFF之间,这是系统留给应用程序自用的消息段(WM_APP)。后面俩参数我没用,你想让消息更特别一点,或想携带其它信息的话也可以用上。方法返回true/false分别代表发送成功/失败。

另外,目标进程也许有多个线程,其中哪个才是能收消息的主线程我没有科学的判断方法,大胆臆测就是Process.Threads集合中的第1项,这个猜测至今工作良好,不管它。若您有科学判断法,请告知~谢谢。

怎么收

由于消息是走线程过来的,所以别想着在主窗口的WndProc中去收,再说消息过来的时候,主窗口存不存在都是个问题。要用应用程序级别的消息筛选器来收,筛选器是个实现System.Windows.Forms.IMessageFilter接口的类(MSDN),该接口只需实现一个方法:bool PreFilterMessage(ref Message m),方法逻辑是,如果收到的消息m是你要处理并吃掉的,就返回true,其余消息则返回false放行。整个筛选器像这样:

class MsgFilter : IMessageFilter
{
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == 0x80F0)
{
DoSomething(); //显示窗口或其它事
return true;
}
return false;
}
}

事实上我收到消息后并不是直接做显示窗口相关的事,而是引发一个事件,主窗体注册该事件,在事件处理方法中再写显示窗口相关的代码。这是设计上的考量,与本文主旨无关,不多说。

筛选器写好后,还得把它添加到一个地方它才能工作,什么时候添加就什么时候才开始发挥作用,所以最好尽早添加,例如在main的开头。像这样:

void Main()
{
Application.AddMessageFilter(new MsgFilter());
...
}

至此,收发的问题解决。这实质上是一个进程间通信问题,所以其实任何进程通信手段都可以应用在本文的案例,走消息只是其中一种手段。

以上所述是小编给大家介绍的C#向无窗口的进程发送消息的相关知识,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索c#向窗口发送消息
向进程发送消息
c站、c语言、cf、ch、c罗,以便于您获取更多的相关知识。

时间: 2024-10-30 15:00:18

C#向无窗口的进程发送消息_C#教程的相关文章

C#简单读取主机上所有进程的方法_C#教程

本文实例讲述了C#简单读取主机上所有进程的方法.分享给大家供大家参考,具体如下: #region 取得windows的所有进程 public static string GetCourse() { System.Text.StringBuilder sb = new System.Text.StringBuilder(); string tempName = ""; int begpos, endpos; foreach (System.Diagnostics.Process this

模态窗口其实就是在当前窗口调用系统的消息循环,响应用户的操作,将相关的消息发送到对应的窗口(转)

  上周准备在公司内部转岗,面了3个部门windows客户端相关的工作,最终拿到3个Offer,主要涉及C++和Windows两大块内容,C++的题目基本都答上了,Windows一直都是我的弱项,在这里记录一下Windows相关的题目.有些答不上的问题就没列出来,还有些问题忘了,下面的答案有些大部分是我自己的理解,有些是直接从网上copy的,有问题大家可以讨论. 1:GetMessage和PeekMessage的区别? GetMessage:获取消息队列中的一个消息,存入MSG中,并从消息队列中

迷你飞信发送消息窗口是如何切换会话模式

在迷你飞信发送消息窗口,可以在消息模式和聊天模式间进行切换. 系统缺省为消息模式,点击"发送"按钮旁的下拉菜单,选择"切换到聊天模式"可以切换到聊天模式,方便用户与好友聊天. 在聊天模式下,点击"发送"按钮旁的下拉菜单,选择"切换到消息模式"可以切换到消息模式.

怎么样才能通过wifi给别人发送消息窗口啊

问题描述 怎么样才能通过wifi给别人发送消息窗口啊 最近有人链接我家wifi ,我觉得非常烦 所以问一下有谁知道有什么软件可以给链接我wifi的人发送一个窗口 就像这样的 主要是安卓 解决方案 要看你是什么wifi路由,如果是那种家庭傻瓜的话,没有办法,因为它们没有什么定制的可能.如果你的wifi路由是稍微高级一点的商业版,支持wifi网页认证,并且可以配置. 那么最简单的办法就是写一个网页,用alert()的方式弹出信息.这样出来的效果和你的截图一模一样. 解决方案二: 你是说流量劫持注入脚

webservice-centos环境下Webserviceclient发送消息无响应

问题描述 centos环境下Webserviceclient发送消息无响应 公司项目中有webservice 与SAP对接,在windows环境开发测试webservice都可以使用发送和接受数据,但是部署到centos服务器环境下就不行了,发送大量数数据SAPServer端就无响应,每次发送一条数据就可以,但是在windows环境下每次发送大量数据都没有问题,测试了好长时间在Centos环境下使用linux版的Myeclipse单独发送测试数据至SAPserver 也不行,报如下错误: com

showModalDialog网页对话框关闭时刷新父窗口时,弹出这样的提示框"请重新发送消息,无法刷新网页"

问题描述 showModalDialog网页对话框关闭时刷新父窗口时,弹出这样的提示框"请重新发送消息,无法刷新网页"在网上找了N种办法,就是没用,真伤脑筋.请教哪位大侠指教,项目好赶,如果问题解决马上给分. 解决方案 解决方案二:你是怎么刷的父页?是在父页用location.reload()刷吗?如果是这样的话,注意看一下子窗口的<head><Basetarget="_self"/>解决方案三:我里面已经写了<Basetarget=&

spy-给窗口发送消息,不能获得控件句柄

问题描述 给窗口发送消息,不能获得控件句柄 很多软件使用spy进行识别时,只能显示为一个整体,如果我想给这类窗口进行发送消息,实现给文本框输入值等操作时该怎么办? 解决方案 如果你只能看到窗体的句柄,那么说明这种程序中的控件其实根本不存在,是画出来的.这种程序叫做directui程序. 你只能模仿鼠标键盘操作. 解决方案二: 不是说所有的窗口都支持SPY++来抓取窗口,它一般是windows标准窗口才能获取控件,以及发送消息等. 而还有很多其他界面库等,根本不会处理消息机制.所以你也不能直接发送

同个局域网的两台计算机,win7系统已无信使服务,server2003如何向win7发送消息?

问题描述 同个局域网的两台计算机,win7系统已无信使服务,server2003如何向win7发送消息? 以前server和xp系统之间可以通过信使服务相互发送消息,可从vista后就取消了信使服务,那如何实现同一局域网内server2003和win7之间的消息发送呢? 解决方案 win7系统局域网中隐藏本地计算机 解决方案二: 只能用socket等程序了.或者tcp,udp等客户端,没有系统自带的服务

qq的在本地获取图片显示在下面的消息发送窗口,然后发送出去,显示在上面的对话框!怎么用C#实现?

问题描述 qq的在本地获取图片显示在下面的消息发送窗口,然后发送出去,显示在上面的对话框!怎么用C#实现?我的俩个上下对话框都是用richtexbox做的!