使用BDS2007的时候常常会出现闪屏结束后任务栏上的对应的按钮不消失的情况,白白占据了一个任务栏格子,唯一的办法就是重复启动它,运气好就不会出了。所以我决定解决掉它。

首先观察一下这个闪屏是怎么做才不会在任务栏显示的。
运行BDS,用Spy看下窗口样式,发现是WS_POPUP,且没有WS_EX_TOOLWINDOW。看起来是用隐藏父窗口的做法来实现的,如果隐藏父窗口失败,就可能会显示出来。我们就直接为他加上WS_EX_TOOLWINDOW样式,使它永远不显示就行了。开工

载入BDS.exe,bp SetWindowLongA。运行后观察堆栈,直到看到SplashScreen的窗口。断在这里:

代码:
2015ED8D    E8 5645FEFF     CALL vcl100.@Controls@TWinControl@GetHandle$qqrv
2015ED92    50              PUSH EAX
2015ED93    FF15 B4E11E20   CALL DWORD PTR DS:[@Forms@SetLayeredWindowAttributes]                ; user32.SetLayeredWindowAttributes
2015ED99    EB 2C           JMP SHORT vcl100.2015EDC7
2015ED9B    81E6 FFFFF7FF   AND ESI,FFF7FFFF
2015EDA1    56              PUSH ESI
2015EDA2    6A EC           PUSH -14
2015EDA4    8BC3            MOV EAX,EBX
2015EDA6    E8 3D45FEFF     CALL vcl100.@Controls@TWinControl@GetHandle$qqrv
2015EDAB    50              PUSH EAX
2015EDAC    E8 FB3DF8FF     CALL <JMP.&user32.SetWindowLongA>     <--这里
2015EDB1    68 85040000     PUSH 485
2015EDB6    6A 00           PUSH 0
2015EDB8    6A 00           PUSH 0
2015EDBA    8BC3            MOV EAX,EBX
2015EDBC    E8 2745FEFF     CALL vcl100.@Controls@TWinControl@GetHandle$qqrv
2015EDC1    50              PUSH EAX
2015EDC2    E8 CD3CF8FF     CALL <JMP.&user32.RedrawWindow>
2015EDC7    5E              POP ESI
2015EDC8    5B              POP EBX
2015EDC9    C3              RETN
断在了vcl100.bpl运行库里面。bds.exe是依靠于运行库执行的,窗口样式也是运行库执行的,如果改运行库的话,就会导致所有窗口的处理都受到影响。
顺着堆栈往回找,可以找到bds实际调用的是vcl100.@Forms@TCustomForm@SetVisible$qqro,就是显示窗口。经过调试,这个地方只会处理闪屏窗口,正好可以拿来patch,我们在显示之前修改窗口的风格就可以了。vcl100.@Forms@TCustomForm@SetVisible$qqro>的参数是FormID之类的东西,不是实际可用的句柄,需要通过vcl100.@Controls@TWinControl@GetHandle$qqrv函数转换为实际窗口句柄,这个API已经被导入了,可以直接调用。不幸的是我们需要的SetWindowLongA却没有导入,直接动态载入好了,准备完成,可以动手了。

用topo添加一段空白空间到43CA2A,从原来调用vcl100.@Forms@TCustomForm@SetVisible$qqro跳转过去。
代码:
0040626B    A1 2C404200     MOV EAX,DWORD PTR DS:[42402C]
00406270  - E9 B5670300     JMP bds.0043CA2A
00406275    A1 2C404200     MOV EAX,DWORD PTR DS:[42402C]
代码:
0043CA2A   .  60            PUSHAD                       //保存寄存器
0043CA2B   .  90            NOP                      
0043CA2C   .  E8 E359FCFF   CALL <JMP.&vcl100.@Controls@TWinControl@GetHandle$qqrv>                 //获得实际窗口句柄。fastcall协定,不需要平衡堆栈
0043CA31   .  50            PUSH EAX
0043CA32   .  68 5ECA4300   PUSH bds.0043CA5E                                                    ; /pModule = "user32.dll"
0043CA37   .  E8 D448FCFF   CALL <JMP.&kernel32.GetModuleHandleA>                                ; \GetModuleHandleA
0043CA3C   .  68 6ACA4300   PUSH bds.0043CA6A                                                    ; /ProcNameOrOrdinal = "SetWindowLongA"
0043CA41   .  50            PUSH EAX                                                             ; |hModule
0043CA42   .  E8 3149FCFF   CALL <JMP.&kernel32.GetProcAddress>                                  ; \GetProcAddress
0043CA47   .  5A            POP EDX
0043CA48   .  68 80000100   PUSH 10080                //偷懒直接赋值,规范些应该先GetWindowLongA,然后or 80后赋值。
0043CA4D   .  6A EC         PUSH -14            //GWL_EXSTYLE
0043CA4F   .  52            PUSH EDX              //窗口句柄
0043CA50   .  FFD0          CALL EAX             //SetWindowLongA
0043CA52   .  61            POPAD                   //恢复寄存器
0043CA53   .  E8 E44FFCFF   CALL <JMP.&vcl100.@Forms@TCustomForm@SetVisible$qqro>         //原代码
0043CA58   .- E9 1898FCFF   JMP bds.00406275        //返回
0043CA5D      00            DB 00
0043CA5E   .  75 73 65 72 3>ASCII "user32.dll",0
0043CA69   .  00            ASCII 0
0043CA6A   .  53 65 74 57 6>ASCII "SetWindowLongA",0
复制到文件,保存,幻影窗口的问题解决了