在C#中使用自定义消息

  在C#中使用自定义消息非常简单,只需要下面几个简单的步骤就可以了:

  1、定义消息

  定义消息的方法与在VC中定义消息有一点点不同,比如在VC中申明一个自定义消息是这样的:

#define WM_TEST WM_USER + 101

  而在C#中消息需要定义成 Windows 系统中的原始的16进制数字,比如自定义消息

public const int USER = 0x0400;

  那么我们在VC中申明的自定义消息,在C#中就可以做对应的声明:

public const int WM_TEST = USER+101;

  2、发送消息

  消息发送是通过 Windows 提供的 API 函数 SendMessage 来实现的,它的原型定义:

  1. [DllImport(User32.dll,EntryPoint=SendMessage)]
  2. private static extern int SendMessage(
  3. IntPtr hWnd, // handle to destination window
  4. uint Msg, // message
  5. uint wParam, // first message parameter
  6. uint lParam // second message parameter
  7. );

  3、消息接收

  消息发出之后,在Form中如何接收呢?我们可以重载DefWinproc函数来接收消息。

  1. protected override void DefWndProc ( ref
  2. System.Windows.Forms.Message m )
  3. {
  4.  switch(m.Msg)
  5.  {
  6.   case Message.WM_TEST: //处理消息
  7.    break;
  8.   default:
  9.    base.DefWndProc(ref m);//调用基类函数处理非自定义消息。
  10.    break;
  11.   } 
  12. }

用C#调用Windows API和其它进程通信
                  关键字:
                  C#,API,FindWindow,FindWindowEx,SendMessage,进程,注册表

设计初衷:
                  公司为了便于网络管理,使用了IEEE
                  802.1X的网络访问控制,这样每次开机需要输入两次登录密码,于是我就研究了一下用C#来帮我输入第二此登录的密码

设计思想:
                  主要是通过调用Windows
                  API中的一些方法,主要使用的也就是FindWindow,FindWindowEx和SendMessage这三个函数,循环遍历当前的所有窗口,找到目标窗口和进程以后把保存在特定位置的用户名密码以及域信息自动填入输入框中,然后再触发一下button事件,最后程序本身退出。

环境:
                  在Windows 2000中文版 + sp4,VS.net 2003中文版下开发
                  在Windows 2000中文版下测试通过

程序截图:

具体设计这个Form的代码就略过不详细说了

为了使用Win32 API,需要先引入下面这个命名空间:
                  using System.Runtime.InteropServices;

另外还需要用到进程和注册表,所以还需要引入下面的两个命名空间:
                  using System.Threading;
                  using Microsoft.Win32;

下面的代码是用来添加对API的引用:

  1. Dll Import#region Dll Import
  2. [DllImport("User32.dll",EntryPoint="FindWindow")]
  3. private static extern IntPtr FindWindow(string lpClassName,
  4. string lpWindowName);
  5. [DllImport("user32.dll",EntryPoint="FindWindowEx")]
  6. private static extern IntPtr FindWindowEx(IntPtr
  7. hwndParent,
  8. IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
  9. [DllImport("User32.dll",EntryPoint="SendMessage")]
  10. private static extern int SendMessage(IntPtr hWnd,
  11. int Msg, IntPtr wParam, string lParam);
  12. #endregion

主要用到的就是这三个方法,具体的我这里就不详细介绍了,请参考MSDN。

需要用到的一些参数:
                     const int WM_GETTEXT = 0x000D;
                     const int WM_SETTEXT = 0x000C;
                     const int WM_CLICK = 0x00F5;

从名称上应该就可以了解这些参数具体的含义是什么了,而且这些参数都可以通过VS附带的工具Spy ++查到。

下面是整个程序的核心部分,查找窗体并对它进行操作:

  1. SearchWindow#region SearchWindow
  2. private int SearchWindow()
  3. {
  4. int retval = 0; //增加一个返回值用来判断操作是否成功
  5. //下面的这些参数都可以用Spy++查到
  6. string lpszParentClass = "#32770"; //整个窗口的类名
  7. string lpszParentWindow = "本地连接"; //窗口标题
  8. string lpszClass = "Edit"; //需要查找的子窗口的类名,也就是输入框
  9. string lpszClass_Submit = "Button"; //需要查找的Button的类名
  10. string lpszName_Submit = "确定"; //需要查找的Button的标题
  11. string text = "";
  12. IntPtr ParenthWnd = new IntPtr(0);
  13. IntPtr EdithWnd = new IntPtr(0);
  14. //查到窗体,得到整个窗体
  15. ParenthWnd =
  16. FindWindow(lpszParentClass,lpszParentWindow);
  17. //判断这个窗体是否有效
  18. if (!ParenthWnd.Equals(IntPtr.Zero))
  19. {
  20. //得到User Name这个子窗体,并设置其内容
  21. EdithWnd =
  22. FindWindowEx(ParenthWnd,EdithWnd,lpszClass,"");
  23. if (!EdithWnd.Equals(IntPtr.Zero))
  24. {
  25. text = this.tbUserName.Text.Trim();
  26. //调用SendMessage方法设置其内容
  27. SendMessage(EdithWnd, WM_SETTEXT, (IntPtr)0,
  28. text);
  29. retval ++;
  30. }
  31. //得到Password这个子窗体,并设置其内容
  32. EdithWnd =
  33. FindWindowEx(ParenthWnd,EdithWnd,lpszClass,"");
  34. if (!EdithWnd.Equals(IntPtr.Zero))
  35. {
  36. text = this.tbPassword.Text.Trim();
  37. SendMessage(EdithWnd, WM_SETTEXT, (IntPtr)0,
  38. text);
  39. retval ++;
  40. }
  41. //得到Domain这个子窗体,并设置其内容
  42. EdithWnd =
  43. FindWindowEx(ParenthWnd,EdithWnd,lpszClass,"");
  44. if (!EdithWnd.Equals(IntPtr.Zero))
  45. {
  46. text = this.tbDomain.Text.Trim();
  47. SendMessage(EdithWnd, WM_SETTEXT, (IntPtr)0,
  48. text);
  49. retval ++;
  50. }
  51. //得到Button这个子窗体,并触发它的Click事件
  52. EdithWnd = FindWindowEx(ParenthWnd,
  53. EdithWnd,lpszClass_Submit,lpszName_Submit);
  54. if (!EdithWnd.Equals(IntPtr.Zero))
  55. {
  56. SendMessage(EdithWnd,WM_CLICK,(IntPtr)0,"0");
  57. retval ++;
  58. }
  59. }
  60. return retval;
  61. }
  62. #endregion

这里有一点需要说明的是,当一个窗体下面有几个类名相同的子窗体时,也就是说如果有三个输入框,这三个输入框的类名都是Edit,查找结果是依次从上往下的,最开始我不知道该怎么办才能分出具体的每个不同的输入框,后来只能这样一个一个来查找来试一下,没想到居然是对的。(有别的办法么?)

上面的这段代码也只适用于中文版的操作系统,因为不同的操作系统下同一个窗体的名称都是不一样的,我这里也没有英文版的系统,所以也没办法进行测试。

为了免去每次都让用户手动输入的烦恼,我需要把这些信息都保存到一个特定的文件里面去,当用户在第一次运行这个程序的时候,只需要输入一次,点下
                  Save,先把这些信息保存到一个文件中,然后再把程序本身加载到系统启动项里去,这样下次开机的时候程序就可以自启动,然后从文件中读取信息完成以下的操作。

选择存放文件的路径:

  1. private string UserPro =
  2. System.Environment.GetEnvironmentVariable("USERPROFILE");
  3. private string PATH =
  4. System.Environment.GetEnvironmentVariable("USERPROFILE") + at
  5. "/Local Settings/AutoLog dot ini";
  6. 当用户点下Save按钮所触发的事件:
  7. Button Submit Click#region Button Submit Click
  8. private void btSubmit_Click(object sender, System.EventArgs
  9. e)
  10. {
  11. SaveData();
  12. }
  13. private void SaveData()
  14. {
  15. try
  16. {
  17. //Save Data
  18. FileInfo obj = new FileInfo(PATH);
  19. if(obj.Exists)
  20. obj.Delete();
  21. FileStream ofile = new
  22. FileStream(PATH,FileMode.Create);
  23. //Hidden the file
  24. File.SetAttributes(PATH,FileAttributes.Hidden);
  25. StreamWriter sw = new StreamWriter(ofile);
  26. //把用户名密码和域信息写入文件
  27. sw.WriteLine(this.tbUserName.Text);
  28. sw.WriteLine(this.tbPassword.Text);
  29. sw.WriteLine(this.tbDomain.Text);
  30. sw.Flush();
  31. sw.Close();
  32. ofile.Close();
  33. //把当前文件拷贝到指定位置,然后再添加到注册表的启动项里
  34. string opath = Application.StartupPath + at "/Login
  35. dot exe";
  36. string tpath = UserPro + at "/Local Settings/Login
  37. dot exe";
  38. if(File.Exists(tpath))
  39. File.Delete(tpath);
  40. File.Copy(opath,tpath);
  41. RegistryKey hklm = Registry.CurrentUser;
  42. RegistryKey run =
  43. hklm.CreateSubKey( at
  44. "SOFTWARE/Microsoft/Windows/CurrentVersion/Run");
  45. run dot SetValue("AutoLogin",tpath);
  46. //最后程序退出
  47. MessageBox.Show("OK","Information",
  48. MessageBoxButtons.OK,MessageBoxIcon.Information);
  49. Application.Exit();
  50. }
  51. catch(Exception ex)
  52. {
  53. MessageBox.Show(ex.ToString(),"Error",
  54. MessageBoxButtons.OK,MessageBoxIcon.Error);
  55. }
  56. }
  57. #endregion

这样的话,程序就可以从文件中读取已经存放好的信息来进行验证了。最后要做的就是,需要单独开一个进程来循环执行上面的SearchWindow这个方法,直到找到符合条件的窗口并成功验证为止,并且这个进程需要随程序的启动而启动。

我们可以在构造函数中添加一个名为LoadData的方法,然后在这个方法中进行具体的读文件信息和启动进程的操作。

当然,先定义好这个进程:
                  private Thread thread;

然后是LoadData这个方法:

  1. Load#region Load
  2. private void LoadData()
  3. {
  4. //Load Data
  5. FileStream ofile = new
  6. FileStream(PATH,FileMode.OpenOrCreate);
  7. StreamReader sr = new StreamReader(ofile);
  8. this.tbUserName.Text = sr.ReadLine();
  9. this.tbPassword.Text = sr.ReadLine();
  10. this.tbDomain.Text = sr.ReadLine();
  11. sr.Close();
  12. ofile.Close();
  13. //Thread Start
  14. thread = new Thread(new ThreadStart(Watch));
  15. thread.IsBackground = true;
  16. thread.Start();
  17. }
  18. private void Watch()
  19. {
  20. //循环查找这个窗口,直到成功为止
  21. while(true)
  22. {
  23. int i = this.SearchWindow();
  24. if(i == 4)
  25. break;
  26. }
  27. //程序退出并释放资源
  28. Application.Exit();
  29. this.Dispose();
  30. this.Close();
  31. }
  32. #endregion

好,到这里就介绍完毕了,当然还有很多需要改进的地方,比如说密码存在本机上应该用一种加密的方式来保存等等。我还是第一次接触用C#调用Windows
                  API这方面的东西,很多东西也都是从网上查资料才得到的,不足之处,恳请指出。

posted on 2005-01-16

字体:大 中 小

永久地址
                  http://www.shengfang.org/blog/p/20070507csharpusedefinemsg.php
                  引用地址 http://www.shengfang.org/blog/tb.php?tb_id=1191902922

2007年10月9日12:08星期二  [Info资料]

在C#中使用自定义消息相关推荐

  1. 如何在VB例程中接收自定义消息

    代码 如何在VB例程中接收自定义消息  您可以用API函数SetWindowLong指定处理消息的窗口过程(window procedure)为自定义的函数WindowProc,捕获消息ID为WM_U ...

  2. java sendmessage函数_vc中SendMessage自定义消息函数用法实例

    本文实例讲述了vc中SendMessage自定义消息函数用法,分享给大家供大家参考.具体如下: SendMessage的基本结构如下: SendMessage( HWND hWnd,  //消息传递的 ...

  3. python如何定义i_如何在Python中使用自定义消息引发相同的Exception?

    如何在Python中使用自定义消息引发相同的Exception? 我的代码中有这个ValueError块: try: do_something_that_might_raise_an_exceptio ...

  4. MFC中使用自定义消息 .

    HTML Tags and JavaScript tutorial MFC中使用自定义消息 MFC中使用自定义消息 消息映射.循环机制是Windows程序运行的基本方式.VC++ MFC 中有许多现成 ...

  5. 在MFC中如何自定义消息及相应事件(VC6.0及VS2005)

    在MFC中如何自定义消息及相应事件(VC6.0及VS2005) 2012-01-01 16:42:18|  分类: VS2005&VC++ |  标签:自定义消息  message   |字号 ...

  6. rocketmq 消息 自定义_rocketmq中的自定义消息头

    在springboot中使用rocketmq的客户端,有2种方式. 一是使用 org.apache.rocketmq的rocketmq-client 二是,在pom.xml中引用 org.apache ...

  7. java 自定义消息_Vc中自定义消息及其触发使用

    Vc中自定义消息及其触发使用 如何在VC++中加入自定义消息,我的做法: 这里我以一个对话框为例子,环境:VS 2008,新建一个工程,选择对话框:以编译器给出的对话框为蓝本,自己新建一个按钮如图: ...

  8. DELPHI中自定义消息的发送和接收

    DELPHI中的消息处理机制 Delphi是Borland公司提供的一种全新的WINDOWS编程开发工具.由于它采用了具有弹性的和可重用的面向对象Pascal(object-orientedpasca ...

  9. VC6升级到VC9,线程中自定义消息的升级方法

    在将VC6的工程升级到VC9时,碰到了一个问题,就是线程中的自定义消息总是提示如下错误: 无法从"LRESULT (__thiscall CAISDataRecvThread::* )(WP ...

最新文章

  1. centos7 安装 Mysql 5.7.28,详细完整教程
  2. ubuntu系统阅读CHM文档的最终解决方案
  3. 以色辨位的Farmer(洛谷P5832题题解,Java语言描述)
  4. Android开发:5-2、ListView、GridView、Spinner
  5. View Merge 在安全控制上的变化,是 BUG 还是增强 ?
  6. HTTP协议状态码详解(HTTP Status Code)(转)
  7. 用FlexGrid做开发,轻松处理百万级表格数据
  8. MOSS中集成各个子网站的数据到一个页面,做决策支持页面的首选: Web Capture
  9. Java从入门到精通 第10章 方法
  10. 蓝牙路由器系列产品:企业级Cassia E1000
  11. 计算机打印纸如何盖章,怎样使电脑制作的印章具有手动盖章效果
  12. 批量修改文件夹内文件格式
  13. IDEA的TODO的使用
  14. MySQL:ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA
  15. 富格林金业:原油天然气怎么掌控买卖点?
  16. 我的U盘终于中招啦:U盘快捷方式病毒
  17. 手机支架3d打印模型_新型高效率酶固定化3D打印支架
  18. 手把手带你爬取百度美女图片,Python练手项目!
  19. Asp.net网站iis设置起始页
  20. 新股前瞻|深耕IT界17年,伊登软件仅是个“中间商”

热门文章

  1. 指针02:指针所占内存空间
  2. 使用信号灯法,标志位解决测试生产者消费者问题(源码解析、建议收藏)
  3. git 小乌龟 TortoiseGit 记住账号密码
  4. Open3d之点云可视化
  5. SQL 常用语句大全
  6. (八)mybatis之映射器
  7. sqlalchemy学习
  8. Linux命令解释之vi
  9. 一年前我在知乎上提了个愚蠢的问题:如何入门 Linux ?
  10. Android反射修改view,Android 修改viewpage滑动速度的实现代码