我想写个程序控制其他程序的按钮,但是目标程序每次启动后系统给按钮分配的句柄都是不同的,所以保存句柄好像是行不通的。该怎么办?????

解决方案 »

  1.   

    你可以调用API试试
    但是比较麻烦
    先获得对象窗体句柄 FindWindow
    最得到要控制的按钮的句柄 GetChildWindows(这个我记不准了,总之你能得到按钮句柄)
    让然后再在自己的程序中调用PostMessage大体就是这个意思,我没对.NET程序测试过,应该也可以的,都是消息循环么。
      

  2.   

    楼主去看看C#提供的UIAutomation的类 里面就已经提供了相应的方法了
      

  3.   

    例子:using System.Runtime.InteropServices;
    [DllImport ( "user32.dll", EntryPoint = "FindWindow", SetLastError = true )]
    private static extern IntPtr FindWindow( string lpClassName, string lpWindowName );[DllImport ( "user32.dll", EntryPoint = "FindWindowEx", SetLastError = true )]
    private static extern IntPtr FindWindowEx( IntPtr hwndParent, uint hwndChildAfter, string lpszClass, string lpszWindow );[DllImport ( "user32.dll", EntryPoint = "SendMessage", SetLastError = true, CharSet = CharSet.Auto )]
    private static extern int SendMessage( IntPtr hwnd, uint wMsg, int wParam, int lParam );[DllImport ( "user32.dll", EntryPoint = "SetForegroundWindow", SetLastError = true )]
    private static extern void SetForegroundWindow( IntPtr hwnd );
    private void button1_Click( object sender, EventArgs e )
    {
        const uint BM_CLICK = 0xF5; //鼠标点击的消息,对于各种消息的数值,大家还是得去API手册    IntPtr hwndCalc = FindWindow ( null, "计算器" ); //查找计算器的句柄    if ( hwndCalc != IntPtr.Zero )
        {
        IntPtr hwndThree = FindWindowEx ( hwndCalc, 0, null, "3" ); //获取按钮3 的句柄    IntPtr hwndPlus = FindWindowEx ( hwndCalc, 0, null, "+" );  //获取按钮 + 的句柄
        IntPtr hwndTwo = FindWindowEx ( hwndCalc, 0, null, "2" );  //获取按钮2 的句柄
        IntPtr hwndEqual = FindWindowEx ( hwndCalc, 0, null, "=" ); //获取按钮= 的句柄
        SetForegroundWindow ( hwndCalc );    //将计算器设为当前活动窗口
        System.Threading.Thread.Sleep ( 2000 );   //暂停2秒让你看到效果
        SendMessage ( hwndThree, BM_CLICK, 0, 0 );
        System.Threading.Thread.Sleep ( 2000 );   //暂停2秒让你看到效果
        SendMessage ( hwndPlus, BM_CLICK, 0, 0 );
        System.Threading.Thread.Sleep ( 2000 );   //暂停2秒让你看到效果
        SendMessage ( hwndTwo, BM_CLICK, 0, 0 );
        System.Threading.Thread.Sleep ( 2000 );   //暂停2秒让你看到效果
        SendMessage ( hwndEqual, BM_CLICK, 0, 0 );    System.Threading.Thread.Sleep ( 2000 );
        MessageBox.Show ("你看到结果了吗?");
        }
        else
        {
        MessageBox.Show ("没有启动 [计算器]");
        }
    }  C# Readprocessmemory用法  C# GetWindowRect用法
    C# SendMessage用法程序编程 2010-11-28 10:01:54 阅读143 评论0   字号:大中小 订阅 
     函数功能:该函数将指定的消息发送到一个或多个窗口。此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回。该函数是应用程序和应用程序之间进行消息传递的主要手段之一。
        函数原型:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);    参数:    hWnd:其窗口程序将接收消息的窗口的句柄。如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。    Msg:指定被发送的消息。    wParam:指定附加的消息指定信息。    IParam:指定附加的消息指定信息。    返回值:返回值指定消息处理的结果,依赖于所发送的消息。    备注:需要用HWND_BROADCAST通信的应用程序应当使用函数RegisterWindowMessage来为应用程序间的通信取得一个唯一的消息。    如果指定的窗口是由调用线程创建的,则窗口程序立即作为子程序调用。如果指定的窗口是由不同线程创建的,则系统切换到该线程并调用恰当的窗口程序。线程间的消息只有在线程执行消息检索代码时才被处理。发送线程被阻塞直到接收线程处理完消息为止。C#中使用该函数首先导入命名空间:
    using System.Runtime.InteropServices; 然后写API引用部分的代码,放入 class 内部
    [DllImport("user32.dll", EntryPoint = "SendMessage")]
    private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam); 这个函数有四个参数,第一个是窗口句柄,窗口可以是任何类型的屏幕对象;第二个是用于区别其他消息的常量值;第三个通常是一个与消息有关的常量值,也可能是窗口或控件的句柄,第三个参数是可选参数,有的消息要,有的不需要,比如单击就不需要这个参数,
    别的消息,比如鼠标移动的可能需要在这里加上一些鼠标的参数;第四个通常是一个指向内存中数据的指针。在C#中消息需要定义成windows系统中的原始的16进制数字,比如 const int WM_Lbutton = 0x201; //定义了鼠标的左键点击消息。详细值在最后。例如:
    const int BM_CLICK = 0xF5;
    IntPtr maindHwnd = FindWindow(null, "QQ用户登录"); //获得QQ登陆框的句柄
    if (maindHwnd != IntPtr.Zero)
    {
        IntPtr childHwnd = FindWindowEx(maindHwnd, IntPtr.Zero, null, "登录");   //获得按钮的句柄
        if (childHwnd != IntPtr.Zero)
        {
            SendMessage(childHwnd, BM_CLICK, 0, 0);     //发送点击按钮的消息
        }
        else
        {
            MessageBox.Show("没有找到子窗口");
        }
    }
    else
    {
        MessageBox.Show("没有找到窗口");
    }  
      

  4.   

     IntPtr hwndThree = FindWindowEx ( hwndCalc, 0, null, "3" ); //获取按钮3 的句柄    IntPtr hwndPlus = FindWindowEx ( hwndCalc, 0, null, "+" );  //获取按钮 + 的句柄
        IntPtr hwndTwo = FindWindowEx ( hwndCalc, 0, null, "2" );  //获取按钮2 的句柄
        IntPtr hwndEqual = FindWindowEx ( hwndCalc, 0, null, "=" ); //获取按钮= 的句柄
    你这个是已经知道那个按钮叫"3"、"2",但有的窗体控件根本就不知道它叫什么。那怎么办?
      

  5.   


    你有目标程序吗?运行它,试着用spy++探测一下。
    要想操作他,就得了解他,必须的。
      

  6.   

    就拿千千静听来说吧  我想控制播放 暂停 
            IntPtr WinHandle = proc.MainWindowHandle;//取窗体句柄
            EnumChildWindow ecw = new EnumChildWindow(EnumChild);
            ApiFunction.EnumChildWindows(WinHandle, ecw, "");        //枚举窗体
            private bool EnumChild(IntPtr handle, string num)
            {
                StringBuilder Title = new StringBuilder();
                StringBuilder Type = new StringBuilder();            Title.Length = 100;
                Type.Length = 100;
                ApiFunction.GetWindowText(handle, Title, 100);//取Title
                ApiFunction.GetClassName(handle, Type, 100);//取类型 
                return true;
            }这是我自己写的枚举窗体控件。
    当枚举千千静听或酷狗时 跟本就获取不到title
      

  7.   

    这样就变成需要人为的去辨别按钮的功能,我是想第一次识别出各按钮功能后,生成配置文件下次就由程序自动识别了。但配置文件该保存什么呢?如果有title的按钮还好办,那没有title的按钮该怎么办?
      

  8.   

    千千静听我没控制过,我以前(很久很久了)控制过winamp(好像是这么拼的吧,也是播放MP3的)。你可以发送虚拟键,发快捷键。
      

  9.   

    我也很想知道答案。
    帮楼主顶吧!
    以下纯猜测想象:
    第一要得到控件的ID,因为控件是资源,它是编译时就已经存在的了。
    比如 #define IDC_DATA_TXT                    1001
    1001是在编译时确定的,不是运行时,所以它是固定的。
    第二得到控件所在窗体的句柄FindWindow得不到,你可以通过进程名,那个一定是固定的。
    第三hText = GetDlgItem(hMyWnd,IDC_DATA_TXT);得到句柄
    IDC_DATA_TXT是你第一得到的ID,hMyWnd是第二得到的窗体句柄。
    第四有了hText 你可发消息。我记得有写资源编辑器可以得到第一里提到的ID纯想象
      

  10.   

    对于句柄来说每次是不同的,但对于有些属性却是相同的,例如Text
      

  11.   


    用spy++探测,控件有类名的,相同的控件有相同的类名
    另外通过GetDlgCtrlID取得的id,在相同的千千静听版本中是绝对不会改变的,
    通常版本换了改变的可能性也很小
    你可以通过保留此ID,用来识别它们。
    通常用这种方法控制人家的窗口,你必须非常了解它才行
      

  12.   

    嗯  对啊  好像用这种方法控制其他程序的窗口不好用,例如我枚举酷狗的所有控件,结果出现的都是panel控件,根本就找不到button,还以为是自己枚举程序有问题,用UISpy.exe试下也一样。
    不知道有没有其他更好的办法。
      

  13.   

    嗯 对啊 好像用这种方法控制其他程序的窗口不好用,例如我枚举酷狗的所有控件,结果出现的都是panel控件,根本就找不到button,还以为是自己枚举程序有问题,用UISpy.exe试下也一样。
    不知道有没有其他更好的办法。
      

  14.   

    房主评价下我20楼的看法,有什么不妥。
    不太明白你的意思,我让你通过资源编辑器,取得ID,你怎么说到枚举上了。
    那就接着你的说。
    你得到panel是很正常的。
    人家做软件的时候一定方了panel,因为好布局么。
    在panel里放的才有可能是按钮,
    你第一此掉GetChildWindow得到的是panle
    你在用panel作为父窗体调用GetChildWindow就可以枚举到了啊。
    递归
      

  15.   

    不太明白你的意思,我让你通过资源编辑器,取得ID,你怎么说到枚举上了。
    那就接着你的说。
    你得到panel是很正常的。
    人家做软件的时候一定方了panel,因为好布局么。
    在panel里放的才有可能是按钮,
    你第一此掉GetChildWindow得到的是panle
    你在用panel作为父窗体调用GetChildWindow就可以枚举到了啊。
    递归
    我用的就是递归,但是就是找不到button。
    所以我觉得用这个办法控制窗体控件不通用。不过我又想到了另一个解决方案,那就是通过录制操作来控制,就跟按键精灵那样。
      

  16.   

    那对象窗口拉伸,缩放了还能行吗?
    没用过按键精灵。
    我比较期待hText = GetDlgItem(hMyWnd,IDC_DATA_TXT);
    呵呵
    你继续努力,我得下班了。
      

  17.   

    那对象窗口拉伸,缩放了还能行吗?
    没用过按键精灵。
    我比较期待hText = GetDlgItem(hMyWnd,IDC_DATA_TXT);
    呵呵
    你继续努力,我得下班了。这个我早料到了,那就是录制操作时记录窗体大小以及位置,要控制的时候按配置设置窗体的大小及位置即可。