还得请教你。
帖子:http://topic.csdn.net/u/20081021/12/932eb86e-ec10-46f7-9d43-8f37e2aeea1d.html
里面,你教我拦截WM_WINDOWPOSCHANGING消息,然后在窗口子类化里丢弃SWP_SHOWWINDOW,确实能在窗口显示出来之前隐藏窗口。
但有点后续问题。因为我的目标窗口是一个VB程序,上面有个文本框有个.setfocus事件。但由于窗口还没显示出来,那个文本框也还不可见,所以执行.setfocus的时候那个VB程序就出错了(VB里面不允许对不可见的控件执行.setfocus)。无奈之下,我改成拦截WM_WINDOWPOSCHANGED消息,但这样一来,好像出现WM_WINDOWPOSCHANGED消息的时候,窗口就已经显示出来了,所以你教我的那个子类化里面丢弃SWP_SHOWWINDOW消息的办法隐藏不了窗口了。
我改成:
  if msg = WM_WINDOWPOSCHANGED then//原来是WM_WINDOWPOSCHANGING
  begin
    wndPos := PWindowPos(lp);
    if (wndPos^.flags and SWP_SHOWWINDOW) <> 0 then
    begin
      wndPos^.flags := (wndPos^.flags and (not SWP_SHOWWINDOW));
    end;
  end;
也还是不行,窗口没有隐藏。
我要是直接用showwindow(hwnd,sw_hide);的话,那个窗口总是先显示一下然后才隐藏,效果不好。
能帮我再想想还有什么好办法吗?谢谢了

解决方案 »

  1.   

    试一下把窗口重绘的消息先拦截掉,然后InvalidateRect,之后再ShowWindows(hwdn,sw_hide)
      

  2.   


    我拦截到消息后,为了判断是否是我要的窗口,还要做挺多数据准备操作,甚至还有数据库操作,如果拦截消息重绘消息的话,就太频繁了,因为发出重绘消息太多了,太多操作都会引发重绘消息,这样系统运行很可能受影响其实我开始想过拦截WM_WINDOWPOSCHANGING和WM_WINDOWPOSCHANGED消息,发现目标窗口的话而且是WM_WINDOWPOSCHANGING消息的话,就用原来dandycheung教我方法,先隐藏;发现目标窗口而且还是WM_WINDOWPOSCHANGED消息的话,再执行我填单的操作。
    这个想法是基于对于一个窗口显示过程来说,WM_WINDOWPOSCHANGING消息会比WM_WINDOWPOSCHANGED先出现的想法。但这样即使可行,我也有点不甘心,因为判断目标窗口的操作在两次拦截中都要执行一次,效率不高,相当于比原来低了一半。而这样的代价仅仅就是为了实现一个漂亮的显示效果,有点不划算。想知道是不是还有更好的办法。
      

  3.   

    呵呵,原来还是这个老问题啊。从小的名字也能看出来,...ING 是进行时,...ED 是完成时,所以后者发生的时候窗口已经显示出来。对于 VB 的程序,由于我没有太多研究过,所以不敢保证能给你一个绝对没有副作用的解决办法,所以只能给你一些建议,你自己去实践。1、你可以在 WM_WINDOWPOSCHANGING 里把窗口的坐标调整到屏幕之外
    2、你可以把窗口的大小设置为 0 宽 0 高
    3、你还可以用 SetWindowRgn 或者 SetLayeredWindowAttributes 这类的函数把目标窗口弄得即使处于可见状态用户也看不见我能想象的副作用是,由于这些方法都是让用户看不到事实上处于已经显示出来的状态的窗口,所以有可能会对当前的活动窗口的激活状态带来一些闪烁(因为后续你肯定还会隐藏它)。除非特别仔细或者挑剔的用户,这个副作用倒是也影响不大。
      

  4.   

    你同时处理 WM_WINDOWPOSCHANGING 和 WM_WINDOWPOSCHANGED 的想法原本就很好,如果我没有记错的话,你对这两个消息的处理并不是在钩子里,而是在子类化窗口过程里。这就是说,不会有两次判断目标窗口的情况,而且你要做的事情不管放在哪个消息处理里都要做,根本不存在所谓的效率折半问题。
      

  5.   

    楼主的问题其实就是主窗口还没显示出来,那个文本框也不可见,调用出错。
    其实在禁用窗体的显示事件的时候,你可以判断句柄的父类名,也就是说如果在你的窗体上控件的显示,你放过,如果是窗体本身,就不放过。如果楼主不会写这部分代码,那么只需要你给我提供需要钩的这个VB做的EXE文件,我来帮你续写后面的代码。另外,3楼朋友让次写的那个不让任务管理器出现任意进程的代码没有效果,后来我在QQ上跟你说话你没有反应。
      

  6.   

    其实你以前贴子的那个问题,我也认真的研究过。比如说钩住记事本,我发现
       if CompareText(clsName, 'Notepad') = 0 then 
    关键是这句,使钩住的事件激发成了唯一。如果去掉这句,那么记事本中有多少个控件,就会激发多少次Show事件。而你在激发的每一个事件中只需要判断父类名是不是Notepad即可,如果是,就放掉,如果不是,再判断它是不是Notepad本身,如果是本身,就取消它的Show。
      

  7.   


    我是在钩子里处理的,子类化过程仅仅做了丢弃消息的事。
    我的想法是:
    if (winStruct.message = WM_WINDOWPOSCHANGING) or (winStruct.message = WM_WINDOWPOSCHANGED) then
    begin
    1、判断是否目标窗口
    2、如果是目标窗口
    2.1 如果是WM_WINDOWPOSCHANGING的话,子类化,实现隐藏效果
    2.2 否则,进行我预期的填单操作
    end;
      

  8.   

    我在7楼已经表述清楚了。不需要第二次判断。就是一次过滤成功。你可能是没有明白一个窗体创建需要激发多次Show事件,因为窗体上的每个单一控件也是做为句柄来处理的,因此也有Show事件。
      

  9.   


    按照之前的帖子讨论的办法,我肯定拦截的只是目标窗口的那次show。因为我要拿到窗口的句柄做事情。出现的多次show应该是窗体上每个子窗体(或者称为控件)的show,而不是窗体本身的show
      

  10.   

    假设窗口上有4个文本框,T1,T2,T3,T4,那我应该是拦截到5次WM_WINDOWPOSCHANGING吧?其中有一个是窗口本身的
    不知道窗口本身的那个WM_WINDOWPOSCHANGING消息相对于4个文本框来说,是最先出现,还是最后出现呢?
      

  11.   

    哦,还真不知道窗口上的那些控件会不会触发WM_WINDOWPOSCHANGING消息。好像有点胡说的意味,哈
      

  12.   

    你说的对。窗体出现在T1,T2,T3,T4之前还是之后这个不好说。但肯定是可以实现你要的功能。如果你说效率问题,按照之前的帖子讨论的办法,我肯定拦截的只是目标窗口的那次show,这个说的对,我在 7楼已经说了“  if CompareText(clsName, 'Notepad') = 0 then 关键是这句,使钩住的事件激发成了唯一”,注意最后两个字是“唯一”。但“唯一”只能说明你钩住的是窗体显示事件,但在钩住该事件之前,以已经激发该事件N次了,只不过你过滤掉(exit)了。而你在过滤之前加上判断不就Ok了?根本没有影响到效率。
      

  13.   

    “而你在过滤之前加上判断不就Ok了?根本没有影响到效率。”
    你的意思是过滤之前,先判断是不是目标窗口?还是判断有没有SWP_SHOWWINDOW?
    如果你的意思是前者,那这个判断不知道要执行多少次
    如果是后者,那我总不能所有的show都给去掉SWP_SHOWWINDOW吧。
      

  14.   

    先定义:var cap:array[0..100] of char;
    在这句之前 if CompareText(clsName, 'Notepad') = 0 then 
    加上GetClassName(GetParent(句柄),cap,99);
    if lstrcmpi(cap,'notepad')=0 then
      begin
        Result := CallNextHookEx(hhook, nCode, wParam, lParam);
        exit;
      end;
    即可。
    如果你的文本框不是在窗体上,而是在Panel上,那么还要再取一级父句柄。具体情况具体分析,你不发EXE给我,我就只能回答你这么多了。
      

  15.   

    我在2楼就说了,我为了判断是否是我要的窗口,还要做挺多数据准备操作,甚至还有数据库操作。这句话,就是对应你的lstrcmpi(cap,'notepad')=0这个判断。
    你明白了吗?我不想让这个判断频繁发生。
    你想象的太简单。
      

  16.   

    if CompareText(clsName, 'Notepad') = 0 then 
    发生了几次,我加上去的代码也发生了几次。总之,你给EXE我马上就可以做出来。如果你想自己搞,那算了。等高人来给你提供更好的算法吧。
      

  17.   

    我开始想过拦截WM_WINDOWPOSCHANGING和WM_WINDOWPOSCHANGED消息,发现目标窗口的话而且是WM_WINDOWPOSCHANGING消息的话,就用原来dandycheung教我方法,先隐藏;发现目标窗口而且还是WM_WINDOWPOSCHANGED消息的话,再执行我填单的操作。 
    这个想法是基于对于一个窗口显示过程来说,WM_WINDOWPOSCHANGING消息会比WM_WINDOWPOSCHANGED先出现的想法。刚才测试了一下这样做,发现不行。好像是我拦截WM_WINDOWPOSCHANGING并在子类化里丢弃SWP_SHOWWINDOW之后,这个窗口就不会再出现WM_WINDOWPOSCHANGED消息了。