procedure TForm1.Button1Click(Sender: TObject);
var h,hButton1:HWND;
begin
 WinExec('mstsc',1);
 Sleep(1000);
 h:=FindWindow(0,'远程桌面连接');
 if h=0 then exit;
 hButton1:=CreateWindow('Button','取数据',WS_VISIBLE or WS_CHILD or BS_PUSHLIKE or BS_TEXT,0,0,74,24,h,0,0,nil);
end;
上面的代码所实现的功能是打开一个“远程桌面连接”,然后在该窗体上添加一个按扭。我的问题是:如何给这个按扭加上单击事件?例如单击时弹出一句 ShowMessage('你好!') ?我目前可以用鼠标钩子做到,我的方法是在钩住鼠标抬起事件,抬起时判断鼠标位置所在的句柄是不是我创建的Button句柄。
可能还会有其它方法。我希望求到的答案不是用鼠标构子(但可以用其它类型钩子)。当然,直接在自己的窗体上建一个按扭写好事件后,再设置按扭的父句柄,这应该是最简单的方法。但这个是有缺陷的(单击按扭时会使原窗体“远程桌面连接”失去焦点)。
etomahawk一直在消息处理上让我比较佩服,所以该贴向他提问了。但大家也可以回答,如果答案有多种,我会给贴子加分,每种都可以得100分。

解决方案 »

  1.   

    Button1.OnClick := MyButtonClick;procedure TForm1.MyButtonClick(Sender: TObject);
    begin
    //
    end;
      

  2.   

    unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure ButtonClick(Sender: TObject);
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;var
      Form1: TForm1;implementation{$R *.dfm}procedure TForm1.ButtonClick(Sender: TObject);
    begin
       showmessage('hello');
    end;procedure TForm1.FormCreate(Sender: TObject);
    begin
    button1.OnClick:=buttonclick;
    end;end.
      

  3.   

    谢谢“小和”的回答,我在提交4楼贴子的时候还没有看到你的回复。不过你的答案跟2楼的一样,不可用。这个问题的答案可能是用到某消息。不能出现  Button1: TButton;这样的代码。注意我的按扭是怎么创建的?不是用TButton来实现,所以根本不能用里面的方法。
      

  4.   

    实现一个提示框是可以的,但没什么大意义,关键是你需要的处理是否能用这种方式达到。unit Unit23;
    interface
    uses   Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,dialogs,StdCtrls;
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);   
        procedure MyClick(sender: TObject);
      private
      public
      end;
    var  Form1: TForm1;
    implementation
      {$R *.dfm}
    procedure TForm1.Button1Click(Sender: TObject);
    var f: TForm; h: hwnd;
    begin
      WinExec('mstsc',1);
      Sleep(1000);
      h:=FindWindow(0,'远程桌面连接');
      if h=0 then exit;
      F := Tform.Create(self);
      f.Tag := self.Handle ;
      f.Width := 74;
      f.Height := 24;
      f.BorderStyle := bsNone;
      with TButton.Create(F) do
      begin
        Caption := '取数据';
        left := 0;
        Top := 0 ;
        Width := 74;
        Height :=24;
        Parent := F;
        OnClick := MyClick;
      end;
      f.ParentWindow := h;
      f.Show;
    end;procedure TForm1.MyClick(sender: TObject);
    var H: HWND;  //远程连接窗口的handle
    begin
      H := 0;
      if (sender <> nil) and (sender is TButton) and (TButton(sender).Parent.ParentWindow > 0)  then
        H := (TButton(sender).Parent).ParentWindow;   //获取远程连接窗口的handle
      Windows.MessageBox(h, '新按钮点击', '', MB_OK);
      //以下可以处理H所指向的远程连接窗口相关事情
    end;end.
      

  5.   

    procedure TForm1.MyClick(sender: TObject);
    var H: HWND;  //远程连接窗口的handle
    begin
      H := 0;
      if (sender <> nil) and (sender is TButton) and (TButton(sender).Parent.ParentWindow > 0)  then
        H := (TButton(sender).Parent).ParentWindow;   //获取远程连接窗口的handle
      Windows.MessageBox(h, '新按钮点击', '', MB_OK);
      //以下可以处理H所指向的远程连接窗口相关事情
      SendMessage(h, WM_LBUTTONDOWN, 0, 0);
      SendMessage(h, WM_LBUTTONUP, 0, 0);
      SendMessage(h, WM_LBUTTONDOWN, 0, 0);
      SendMessage(h, WM_LBUTTONUP, 0, 0);
    end;
    模拟按下没有成功?再有,如果先把远程连接程序关闭了,再关闭本程序,就会报错?
      

  6.   


    var
      oldButton1WinProc: Pointer;
      h, hButton1: HWND;function NewButton1WinProc(hWnd, Msg, wParam, lParam: Integer): Integer; stdcall;
    begin
      Result := CallWindowProc(oldButton1WinProc, hWnd, Msg, wParam, lParam);
      case Msg of
        WM_LBUTTONDOWN:
          MessageBox(h,'你好!','提示',MB_OK);
      end;
    end;procedure TForm1.Button1Click(Sender: TObject);
    begin
      WinExec('mstsc', 1);
      Sleep(1000);
      h := FindWindow(0, '远程桌面连接');
      if h = 0 then exit;
      hButton1 := CreateWindow('Button', '取数据', WS_VISIBLE or WS_CHILD or BS_PUSHLIKE or BS_TEXT, 0, 0, 74, 24, h, 0, GetModuleHandle(nil), nil);
      //获取nButton1的WinProc,保存
      oldButton1WinProc := Pointer(GetWindowLong(hButton1, GWL_WNDPROC));
      //设置nButton1的WinProc为NewButton1WinProc
      SetWindowLong(hButton1, GWL_WNDPROC, Integer(@NewButton1WinProc));
    end;
      

  7.   

    刚看到,写了一个,测试一下。LS的,记住按钮是创建到其他程序的窗体上的,是不同进程的,你的代码只会产生一个异常。CSDN怎么这样啊,上传我找了半天才找到,好不容易上传了,用看不到上传了的文件。
    到我上传的资源里去看看吧。利用了消息HOOK, 由于是全局HOOK,使用了一个dll。
    主单元:
      procedure InstallHook; stdcall; external 'Hook.dll';procedure TForm1.Button1Click(Sender: TObject);
    begin
      InstallHook;
    end;DLL 工程:
    library Hook;uses
      SysUtils,
      Windows,
      Messages,
      Classes;var
      g_hBtn : HWND  = 0;
      g_hHook: HHook = 0;
      g_hWin : HWND  = 0;{$R *.res}function MsgFilter(nCode: Integer; wPar: WPARAM; lPar: LPARAM): LResult; stdcall;
    begin
      if (PCWPRETSTRUCT(lPar)^.message = WM_COMMAND) then
      begin
        // 在这里验证是否为我们自己创建的按钮被按下了
        if (GetWindowLong(PCWPRETSTRUCT(lPar)^.lParam, GWL_USERDATA) =
            PCWPRETSTRUCT(lPar)^.lParam) then
        begin
          // 这里写你的按钮消息处理过程代码
          MessageBox(0, '按钮按下!', '信息提示', MB_OK + MB_ICONINFORMATION);
        end;
      end;  Result:= CallNextHookEx(g_hHook, nCode, wPar, lPar);
    end;procedure InstallHook; stdcall;
    begin
      WinExec('mstsc',1);
      Sleep(1000);  // 我用的是英文版的操作系统,所以这里进行了修改
      g_hWin:=FindWindow(nil, 'Remote Desktop Connection');//'远程桌面连接');
      if g_hWin = 0 then exit;  // 创建一个按钮
      g_hBtn:=CreateWindow('Button','取数据',
                           WS_VISIBLE or WS_CHILD or BS_PUSHLIKE or BS_TEXT,
                           0, 0, 74, 24, g_hWin, 0, 0, nil);  // 把自己的句柄保存到用户数据中,以便收到WM_COMMAND消息后进行验证
      // 至于这里为什么要保存起来,自己考虑考虑
      SetWindowLong(g_hBtn, GWL_USERDATA, Integer(g_hBtn));  g_hHook:= SetWindowsHookEx(WH_CALLWNDPROCRET, @MsgFilter,
                                 HInstance,
                                 GetWindowThreadProcessId(g_hWin));  // 检查钩子是否成功挂接
      if g_hHook = 0 then
      begin
        MessageBox(0, PChar(Format('Hook failed: %d', [GetLastError])),
                  'ERROR', MB_OK);
      end;
    end;exports
      InstallHook;begin
    end.
      

  8.   

    这个贴子可以结贴了。hotzhu 的代码也是我在开贴提问时所说的一个思路,自己在自己窗体上搞,然后设置父句柄即可。虽然hotzhu的代码避免了单击按扭时会使原窗体“远程桌面连接”失去焦点,但却有另一个致使的缺点:如果先把远程连接程序关闭了,再关闭本程序,就会报错。   本贴只有100分,计划给你40分。   另说一下:解决你这个问题最简单的办法是不要动态创建窗体,而是直接设置自己的程序主窗体的父句柄。etomahawk 的代码我没有去测试,因为需要额外加一个DLL文件支持程序运行,这一点有点不爽。即使能实现,这个代码我也是不能用的。但这个技术应该是得到认可。etomahawk在15楼又对自己的答案进行否定,我觉得没有必要这样认真,你的程序如果有缺点说出来就可以了,没有必要有一点缺点就全盘否认。     本贴只有100分,计划给你60分。  另说一下:应该也可以实现无DLL钩子的吧? 
    ideation_shang 的答案暂时没有发现什么缺点,是最接近我要的答案,但本贴只有100分,已经分给上面两位,所以只能再加分,因此他将得100分。
    我知道各位朋友并不是在乎分的人,但我做为散分人,明白分虽然没有什么实际价值,但却是对答案认可以的一种直接表现形式。而许多时候,人其实就活在“面子”上,如果我的给分有异议可以提出,否则我就结贴了。
      

  9.   

    SetWindowLong(hButton1, GWL_WNDPROC, longint(@ButtonProc));
      

  10.   

    to etomahawk:
    你说子类化会发生错误,应该是由于h,hButton1,oldButton1WinProc三个变量作用域的在Button1Click内而导致.表示歉意.
      

  11.   

     另说一下:解决你这个问题最简单的办法是不要动态创建窗体,而是直接设置自己的程序主窗体的父句柄。楼主:
    按你的说法,我做了,没有解决呀?
    unit Unit1; interface
    uses   Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,dialogs,StdCtrls,
      ExtCtrls;
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);   
        procedure MyClick(sender: TObject);
      private
      public
      end;
    var  Form1: TForm1;
    implementation
      {$R *.dfm}
    procedure TForm1.Button1Click(Sender: TObject);
    var  h: hwnd;
    begin
       WinExec('mstsc',1);
      Sleep(1000);
      h:=FindWindow(nil,'远程桌面连接');
      if h=0 then exit;
      //F := Tform.Create(nil);
      self.BorderStyle := bsNone;
      with Button1 do
      begin
        Caption := '取数据';
        left := 0;
        Top := 0 ;
        Width := 74;
        Height :=24;
        OnClick := MyClick;
      end;
      self.ParentWindow := h;
      self.Tag := h;
      self.Left:=0;
      self.Top:=0;
      self.Width := 74;
      self.Height := 24;end;procedure TForm1.MyClick(sender: TObject);
    var H: HWND;  //远程连接窗口的handle
    begin
      H := 0;
      if (sender <> nil) and (sender is TButton) and (TButton(sender).Parent.ParentWindow > 0)  then
        H := (TButton(sender).Parent).ParentWindow;   //获取远程连接窗口的handle
      MessageBox(h, '新按钮点击', '', MB_OK);
      //以下可以处理H所指向的远程连接窗口相关事情
    end;end.现在倒是可以先关远程窗口了,不过,form1只能结束进程才能关.是我没明白你的意思,还是我明白了.
      

  12.   

    我是说如果是私有变量,由于作用域的问题,肯定有问题.
    比如下面的代码procedure TForm1.Button1Click(Sender: TObject);
    var
      h, hButton1: HWND;
      oldButton1WinProc: Pointer;
      function NewButton1WinProc(hWnd, Msg, wParam, lParam: Integer): Integer; stdcall;
      begin
        Result := CallWindowProc(oldButton1WinProc, hWnd, Msg, wParam, lParam);
        case Msg of
          WM_LBUTTONDOWN:
            MessageBox(h, '你好!', '提示', MB_OK);
        end;
      end;
    begin
      WinExec('mstsc', 1);
      Sleep(1000);
      h := FindWindow(0, '远程桌面连接');
      if h = 0 then exit;
      hButton1 := CreateWindow('Button', '取数据', WS_VISIBLE or WS_CHILD or BS_PUSHLIKE or BS_TEXT, 0, 0, 74, 24, h, 0, GetModuleHandle(nil), nil);
      //获取nButton1的WinProc,保存
      oldButton1WinProc := Pointer(GetWindowLong(hButton1, GWL_WNDPROC));
      //设置nButton1的WinProc为NewButton1WinProc
      SetWindowLong(hButton1, GWL_WNDPROC, Integer(@NewButton1WinProc));
    end;