首页 > 系统相关 >[转]C# 获得窗体句柄并发送消息(利用windows API可在不同进程中获取)

[转]C# 获得窗体句柄并发送消息(利用windows API可在不同进程中获取)

时间:2023-03-07 15:34:57浏览次数:50  
标签:IntPtr windows 句柄 System C# 窗体 using public

 C#使用Windows API获取窗口句柄控制其他程序窗口

编写程序模拟鼠标和键盘操作可以方便的实现你需要的功能,而不需要对方程序为你开放接口。比如,操作飞信定时发送短信等。我之前开发过飞信耗子,用的是对飞信协议进行抓包,然后分析协议,进而模拟协议的执行,开发出了客户端,与移动服务器进行通信,但是这有一些缺点。如果移动的服务器对接口进行变更,我所编写的客户端也要进行相应的升级。如果服务器的协议进行了更改,甚至个人编写的这种第三方客户端需要重写。而我个人也没有这个时间和精力,或者说没有足够的利益支撑我继续去重构飞信耗子。因此,这款还算优秀的软件,现在就束之高阁了,我自己也觉得遗憾。上周,某项目验收,需要修改界面,但是零时找不到源码了。我在两三个小时内要解决这个问题,时间紧迫。我突然想起室友以前做过模拟鼠标键盘去发送飞信消息的小程序。于是我赶紧电话咨询了一下。然后掌握了这个技巧,按时解决了问题。我觉得这个技巧还是很有用的,现总结如下:

首先,引入如下三个API接口:

 
  1. [DllImport("user32.dll")]
  2. public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
  3.  
  4. [DllImport("User32.dll", EntryPoint = "SendMessage")]
  5. private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, string lParam);
  6.  
  7. [DllImport("User32.dll ")]
  8. public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr childe, string strclass, string FrmText);

第一个与第三个是用于查找窗口句柄的,凡运行于Windows上的窗口,都具有句柄。窗口上的文本框,按钮之类的,也有其句柄(可看作子窗口句柄)。这些句柄的类型可以通过

Spy++进行查询。比如C语言编写的程序中,文本框的句柄类型一般为“EDIT”,C#写的程序则不是,可以具体去查。第二个接口则是用于向窗口发送各种消息,比如向文本框发送

字符串,或者向按钮发送按下与弹起的消息等。详细解释如下:

 
  1. IntPtr hwnd = FindWindow(null, "无标题 - 记事本");     //不同进程也可使用此API查找其他进程下的窗体

这是用于查找操作系统中打开的窗口中标题名为无标题 - 记事本的窗口。第一个参数是此窗口的类型。这两个参数知道一

个即可,另一个可以填null。但是如果是用窗口类型查找,则可能只能得到其中的一个窗口。因此通过标题进行查找是非常方便的。

 
  1. IntPtr htextbox = FindWindowEx(hwnd, IntPtr.Zero, "EDIT", null);

这个函数用于获得窗口中子窗口的句柄,子窗口指的其实就是窗口中的各种控件。第一个参数是父窗口的句柄,第二个参数指示获得的是同一类型中的第几个子窗口。填

IntPtr.Zero则表示获得第一个子窗口。第三个参数表示你需要找的子窗口的类型,第四个参数一般为null。如果一个窗口中有两个文本框,那么可以用如下操作获得第二个文本框

的句柄。

 
  1. IntPtr htextbox = FindWindowEx(hwnd, IntPtr.Zero, "EDIT", null);
  2. IntPtr htextbox2 = FindWindowEx(hwnd, htextbox, "EDIT", null);//填上次获得的句柄,可以得到下一个的句柄。

这里只是先将第二个参数填为IntPtr.Zero,获取第一个EDIT类型的文本框,然后第二次调用时,再将第二参数填为第一个文本框的句柄,那么执行返回的就是下一个文本框的句柄

了。因此htextbox2得到的就是第二文本框的句柄。
在可以自由获得各种窗口及其上控件的句柄后,我们就可以向其发送各种消息进行鼠标和键盘的模拟了。比如:

 
  1. SendMessage(htextbox, WM_SETTEXT, IntPtr.Zero, name);

这句是为文本框填写相应的字符串name。

 
  1. IntPtr hbutton = FindWindowEx(hwnd, IntPtr.Zero, "BUTTON", null);
  2. SendMessage(hbutton, WM_LBUTTONDOWN, IntPtr.Zero, null);
  3. SendMessage(hbutton, WM_LBUTTONUP, IntPtr.Zero, null);

这三句是获得了窗口的一个button,然后发送按下,弹起消息给它,模拟了点击鼠标的动作。
SendMessage函数的第一个参数是窗口句柄,或者窗口中控件的句柄,第二个参数是消息的类型Flag,这些值是在API的一些头文件中定义好的。你要是在C#中用,就自己去定义他们,比如

 
  1. constint WM_SETTEXT =0x000C;
  2. constint WM_LBUTTONDOWN =0x0201;
  3. constint WM_LBUTTONUP =0x0202;
  4. constint WM_CLOSE =0x0010;

还有其他的类型Flag,可以参考上一篇Blog查询,也可以去查MSDN。第三个参数和第四个参数都是消息的具体内容。一般我们用的是最后一个参数。第三个参数填为IntPtr.Zero。

当然如果是鼠标的动作,那么最后一个参数就是null。

 
  1. SendMessage(htextbox, WM_SETTEXT, IntPtr.Zero, name);//填写文本框。
  2. SendMessage(hbutton, WM_LBUTTONDOWN, IntPtr.Zero, null);//鼠标按下按钮

//******************************

   在项目中有这样的需求,在主窗体隐藏时或者主进程运行时对其它窗体的控件或者事件进行控制,而且其它窗体是处于活动状态,而主窗体或者主进程是隐藏在后面的。这个时候使用句柄和消息来处理就比较好解决这些问题了,当然了也可以使用其它方法。比如将其它窗体在主窗体中申明并且定义,使之和主窗体一样一直存在于内存中,在各个窗体中申明公共方法,在主进程需要调用时直接调用即可,但是这样耗费了大量的系统资源。现在使用消息来解决这个问题。下面提供一个小程序,在主窗体中通过句柄和消息来控制子窗体中Label上文字变化和颜色,代码如下:

 

Windowns的API类

using System;
using System.Runtime.InteropServices;

namespace TestHwnd
{
    public class Win32API
    {
         [DllImport("user32.dll ", CharSet = CharSet.Unicode)]
        public static extern IntPtr PostMessage(IntPtr hwnd, int wMsg, string wParam, string lParam);

    }

}

 

主窗体程序(发送消息):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace TestHwnd
{
    public partial class Main : Form
    {

        //定义了一个子窗体的句柄
        public IntPtr hwndfrmTest;
        

        public Main()
        {
            InitializeComponent();
        }

     

        private void timer1_Tick(object sender, EventArgs e)
        {

            if(hwndfrmTest!=(IntPtr)0)
            {
                if(DateTime.Now.Second % 3 == 0)
                {
                    Win32API.PostMessage(hwndfrmTest, 0x60, "", "");
                }
                
                if(DateTime.Now.Second % 5 == 0)
                {
                    Win32API.PostMessage(hwndfrmTest, 0x61, "", "");
                }
                
            }
            
        }


        void Button2Click(object sender, EventArgs e)
        {
            frmTest frm=new frmTest();
            frm.Show(this);
        }
    }

子窗体程序(接收消息)

using System;
using System.Drawing;
using System.Windows.Forms;

namespace TestHwnd
{
     ///  <summary>
     ///  Description of frmTest.
     ///  </summary>
     public  partial  class frmTest : Form
    {
        Main main;
         public  frmTest()
        {
             //
             // The InitializeComponent() call is required for Windows Forms designer support.
             //
             InitializeComponent();
            
             //
             // TODO: Add constructor code after the InitializeComponent() call.
             //
        }
        
         void  FrmTest_Load( object sender, EventArgs e)
        {
            main = this.Owner as Main;

             //初始化该窗体的句柄

            main.hwndfrmTest =  this.Handle;
        }
        
         /// 重写窗体的消息处理函数DefWndProc,从中加入自己定义消息 MYMESSAGE 的检测的处理入口
         protected  override  void  DefWndProc( ref Message m)
        {
             switch (m.Msg)
            {
                 case  0x60:
                    {
                        label1.ForeColor=Color.Red;
                        label1.Text = DateTime.Now. ToString() +  "-" +  "测试成功。。。。,呵呵,变红了";
                    }
                     break;
                 case  0x61:
                    {
                        label1.ForeColor=Color.Blue;
                        label1.Text = DateTime.Now. ToString() +  "-" +  "测试成功。。。。,呵呵,变蓝了";
                    }
                     break;
                 default:
                     base. DefWndProc( ref m);
                     break;
            }
        }
        
        
        
         void  Button1Click( object sender, EventArgs e)
        {
                main.hwndfrmTest = (IntPtr) ( 0);
             this. Close();
        }
    }
}

//******************************************

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
 
namespace findWindowTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        // Find Window
        // 查找窗体
        // @para1: 窗体的类名 例如对话框类是"#32770"
        // @para2: 窗体的标题 例如打开记事本 标题是"无标题 - 记事本" 注意 - 号两侧的空格
        // return: 窗体的句柄
        [DllImport("User32.dll", EntryPoint = "FindWindow")]
        public static extern IntPtr FindWindow(string className, string windowName);
       
       
       
       
       
        // Find Window Ex
        // 查找窗体的子窗体
        // @para1: 父窗体的句柄 如果为null,则函数以桌面窗口为父窗口,查找桌面窗口的所有子窗口
        // @para2: 子窗体的句柄 如果为null,从@para1的直接子窗口的第一个开始查找
        // @para3: 子窗体的类名 为""表示所有类
        // @para4: 子窗体的标题 为""表示要查找的窗体无标题 如空白的textBox控件
        // return: 子窗体的句柄
        [DllImport("user32.dll", EntryPoint = "FindWindowEx")]
        private static extern IntPtr FindWindowEx(
            IntPtr hwndParent,
            IntPtr hwndChildAfter,
            string lpszClass,
            string lpszWindow);
 
        // SendMessage
        // 向窗体发送消息
        // @para1: 窗体句柄
        // @para2: 消息类型
        // @para3: 附加的消息信息
        // @para4: 附加的消息信息
        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        private static extern int SendMessage(
            IntPtr hWnd,
            int Msg,
            IntPtr wParam,
            string lParam);
 
        // 消息类型(部分)
        const int WM_GETTEXT     = 0x000D;  // 获得窗体文本 如获得对话框标题
        const int WM_SETTEXT     = 0x000C;  // 设置窗体文本 如设置文本框内容
        const int WM_CLICK       = 0x00F5;  // 发送点击消息如调用该窗体(按钮)的"button1_Click();"
 
        // 本程序针对指定的另一程序窗体因此声名了如下变量
        IntPtr Wnd  = new IntPtr(0);// 一卡通注册程序主窗体
        IntPtr sWnd = new IntPtr(0);// GroupBox控件 此为“一卡通注册程序”主窗体的子窗体
        IntPtr txt  = new IntPtr(0);// 文本框
        IntPtr btn1 = new IntPtr(0);// 查询按钮
        IntPtr btn2 = new IntPtr(0);// 注册按钮 这三个窗体又为“GroupBox控件”的子窗体
        //IntPtr popW = new IntPtr(0);// 弹出对话框
        //IntPtr popB = new IntPtr(0);// 弹出对话框确定按钮
 
        // 文件操作
        private String filename = string.Empty;
        private StreamReader reader = null;
 
        // 从“打开文件”对话框打开txt文件 同时获得需要的窗口句柄
        private void button2_Click(object sender, EventArgs e)
        {
            label2.Text = "";
            openFileDialog1.DefaultExt = "txt";
            openFileDialog1.Filter = "文本文件|*.txt";
            openFileDialog1.RestoreDirectory = true;
            openFileDialog1.FilterIndex = 1;
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                filename = openFileDialog1.FileName;
            }
 
            // 获得窗口句柄
            Wnd  = FindWindowEx((IntPtr)0, (IntPtr)0, null, "读者一卡通注册");// 一个注册程序的窗体
            sWnd = FindWindowEx(Wnd,  (IntPtr)0, null, "条件"); // 窗体上的一个GroupBox控件
            txt  = FindWindowEx(sWnd, (IntPtr)0, null, "");     // GroupBox内的textBox控件
            btn1 = FindWindowEx(sWnd, (IntPtr)0, null, "查询"); // GroupBox内的查询按钮
            btn2 = FindWindowEx(sWnd, (IntPtr)0, null, "注册"); // GroupBox内的注册按钮
        }
 
        // 重复地把文件内读取的行
        // 将该行发送给注册程序窗体上的文本框中
        // 并“点击”查询按钮和注册按钮
        // 直到文件读取完毕
        private void button3_Click(object sender, EventArgs e)
        {
            //计数
            int count = 0;
 
            //读取文件
            if (filename == string.Empty)
            {
                button2.Focus();
                return;
            }
 
            reader = new StreamReader(filename);
            if (reader.EndOfStream)
            {
                return;
            }
 
            string str = string.Empty;
 
            do
            {
                //读取学号 保存在变量str中
                str = reader.ReadLine();
 
                //设置学号
                SendMessage(txt, WM_SETTEXT, (IntPtr)0, str);
 
                //点击查询按钮
                SendMessage(btn1, WM_CLICK, (IntPtr)0, "");
 
                //点击注册按钮
                SendMessage(btn2, WM_CLICK, (IntPtr)0, "");
 
                count++;
            }
            while(!reader.EndOfStream);
 
            reader.Close();
            filename = string.Empty;
            label1.Text = "注册人数:";
            label2.Text = Convert.ToString(count);
        }
    }
}


//******************************

C#获取进程的主窗口句柄

通过调用Win32 API实现。

 

public class User32API
{
    private static Hashtable processWnd = null;

    public delegate bool WNDENUMPROC(IntPtr hwnd, uint lParam);

    static User32API()
    {
        if (processWnd == null)
        {
            processWnd = new Hashtable();
        }
    }

    [DllImport("user32.dll", EntryPoint = "EnumWindows", SetLastError = true)]
    public static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, uint lParam);

    [DllImport("user32.dll", EntryPoint = "GetParent", SetLastError = true)]
    public static extern IntPtr GetParent(IntPtr hWnd);

    [DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId")]
    public static extern uint GetWindowThreadProcessId(IntPtr hWnd, ref uint lpdwProcessId);

    [DllImport("user32.dll", EntryPoint = "IsWindow")]
    public static extern bool IsWindow(IntPtr hWnd);

    [DllImport("kernel32.dll", EntryPoint = "SetLastError")]
    public static extern void SetLastError(uint dwErrCode);

    public static IntPtr GetCurrentWindowHandle()
    {
        IntPtr ptrWnd = IntPtr.Zero;
        uint uiPid = (uint)Process.GetCurrentProcess().Id;  // 当前进程 ID
        object objWnd = processWnd[uiPid];

        if (objWnd != null)
        {
            ptrWnd = (IntPtr)objWnd;
            if (ptrWnd != IntPtr.Zero && IsWindow(ptrWnd))  // 从缓存中获取句柄
            {
                return ptrWnd;
            }
            else
            {
                ptrWnd = IntPtr.Zero;
            }
        }

        bool bResult = EnumWindows(new WNDENUMPROC(EnumWindowsProc), uiPid);
        // 枚举窗口返回 false 并且没有错误号时表明获取成功
        if (!bResult && Marshal.GetLastWin32Error() == 0)
        {
            objWnd = processWnd[uiPid];
            if (objWnd != null)
            {
                ptrWnd = (IntPtr)objWnd;
            }
        }

        return ptrWnd;
    }

    private static bool EnumWindowsProc(IntPtr hwnd, uint lParam)
    {
        uint uiPid = 0;

        if (GetParent(hwnd) == IntPtr.Zero)
        {
            GetWindowThreadProcessId(hwnd, ref uiPid);
            if (uiPid == lParam)    // 找到进程对应的主窗口句柄
            {
                processWnd[uiPid] = hwnd;   // 把句柄缓存起来
                SetLastError(0);    // 设置无错误
                return false;   // 返回 false 以终止枚举窗口
            }
        }

        return true;
    }
}

复制代码

 

调用User32API.GetCurrentWindowHandle()即可返回当前进程的主窗口句柄,如果获取失败则返回IntPtr.Zero。

--EOF--

 

2008年10月7日补充:微软实现的获取进程主窗口句柄代码


public class MyProcess
{
    private bool haveMainWindow = false;
    private IntPtr mainWindowHandle = IntPtr.Zero;
    private int processId = 0;

    private delegate bool EnumThreadWindowsCallback(IntPtr hWnd, IntPtr lParam);

    public IntPtr GetMainWindowHandle(int processId)
    {
        if (!this.haveMainWindow)
        {
            this.mainWindowHandle = IntPtr.Zero;
            this.processId = processId;
            EnumThreadWindowsCallback callback = new EnumThreadWindowsCallback(this.EnumWindowsCallback);
            EnumWindows(callback, IntPtr.Zero);
            GC.KeepAlive(callback);

            this.haveMainWindow = true;
        }
        return this.mainWindowHandle;
    }

    private bool EnumWindowsCallback(IntPtr handle, IntPtr extraParameter)
    {
        int num;
        GetWindowThreadProcessId(new HandleRef(this, handle), out num);
        if ((num == this.processId) && this.IsMainWindow(handle))
        {
            this.mainWindowHandle = handle;
            return false;
        }
        return true;
    }

    private bool IsMainWindow(IntPtr handle)
    {
        return (!(GetWindow(new HandleRef(this, handle), 4) != IntPtr.Zero) && IsWindowVisible(new HandleRef(this, handle)));
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool EnumWindows(EnumThreadWindowsCallback callback, IntPtr extraData);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int GetWindowThreadProcessId(HandleRef handle, out int processId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    public static extern IntPtr GetWindow(HandleRef hWnd, int uCmd);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool IsWindowVisible(HandleRef hWnd);
}

[转自]https://blog.csdn.net/u011555996/article/details/115529522

标签:IntPtr,windows,句柄,System,C#,窗体,using,public
From: https://www.cnblogs.com/castlewu/p/17188253.html

相关文章

  • Denoising Diffusion Implicit Models
    DenoisingDiffusionImplicitModels目录DenoisingDiffusionImplicitModels概Motivation代码SongJ.,MengC.andErmonS.Denoisingdiffusionimplicitmodels.......
  • common
    ReturnObject:定义返回结果publicclassReturnObject{privateStringcode;//处理成功或者失败的标记;1---成功;0----失败privateStringmessage;//成功或者失......
  • vba 过程(Sub、Function)
    过程是构成程序的一个模块,往往用来完成一个相对独立的功能。过程可以使程序更清晰、更具结构性。VBA具有四种过程:Sub过程、Function函数、Property属性过程和Event事件过......
  • CF1796E Colored Subgraphs
    个人思路:换根。从\(1\)开始DFS遍历。对于一个点,维护\(mx1_u=\min\limits_{v\inchild_u}mx1_v+1\),\(mx2_u\)为\(\min\limits_{v\inchild_u}mx2_v\)和\(m......
  • conda常用命令
    在使用python的时候,必然要接触到的就是包管理器了。python常用的包管理器一般有pip、conda、easy_install、poetry、pyenv。pip是官方退出的包管理器,可以下载、安装、升级和......
  • pandas处理Excel数据
    pandas数据转换成numpy数据DataFrame转换成ndarrayarray(data)series转换成ndarrayarr=series.as_matrix()print(type(data))#打印数据类型替换数据中的空值为0da......
  • 数组-leetcode-485
    ​​0️⃣python数据结构与算法学习路线​​学习内容:基本算法:枚举、排序、搜索、递归、分治、优先搜索、贪心、双指针、动态规划等…数据结构:字符串(string)、列表(list)、元......
  • 数组-Leetcode-697
    ​​0️⃣python数据结构与算法学习路线​​学习内容:基本算法:枚举、排序、搜索、递归、分治、优先搜索、贪心、双指针、动态规划等…数据结构:字符串(string)、列表(list)、元......
  • LeetCode题分类
    一.数组题目分类题目编号数组的遍历485、495、414、628统计数组中的元素645、697、448、442、41、274数组的改变、移动453、665、283二维数组及滚动数组118、119......
  • 动态规划-leetcode-494
    ​​0️⃣python数据结构与算法学习路线​​学习内容:基本算法:枚举、排序、搜索、递归、分治、优先搜索、贪心、双指针、动态规划等…数据结构:字符串(string)、列表(list)、元......