Skip to content

Instantly share code, notes, and snippets.

@KnIfER
Last active March 7, 2026 08:29
Show Gist options
  • Select an option

  • Save KnIfER/e40b265ec7eaefa154831d5b6c8fa0ea to your computer and use it in GitHub Desktop.

Select an option

Save KnIfER/e40b265ec7eaefa154831d5b6c8fa0ea to your computer and use it in GitHub Desktop.
windows 解决任意窗口启动闪白(white flash on start)的终极方案

黑暗模式下,最难忍受启动白屏: chrome,edge,vscode,github desktop,这些浏览器/electron应用……

终极方案 : 启动时注入 dll ,劫持 createWindow, 或将窗口隐藏,或将白色设置为透明色,或显示一个黑色半透明遮罩。

Edge

劫持 createWindow,将白色设置为透明色即可。

dllmain.cpp :

void SetWindowTransparentColorKey(HWND hWnd)
{
    SetWindowLongPtrW(hWnd, GWL_EXSTYLE, GetWindowLongPtrW(hWnd, GWL_EXSTYLE) &~WS_EX_TRANSPARENT| WS_EX_LAYERED);
    auto TPKey = RGB(255, 255, 255);
    SetLayeredWindowAttributes(hWnd, TPKey, 255, LWA_COLORKEY);
}

...
MH_CreateHook(&CreateWindowExW, &MyCreateWindowExW, reinterpret_cast<void**>(&fpCreateWindowExW));
MH_EnableHook(&CreateWindowExW);

injector.cpp :

// Injector.cpp : This file contains the 'main' function. Program execution begins and ends there.
#include <Windows.h>
//#include <detours.h>
#include <string>
#include <cstdlib>   // 包含 system() 函数
int wmain(int argc, wchar_t* argv[]) {
    std::string command = "I:\\Softwares\\detours\\withdll.exe -d C:\\base\\hook\\popup-resizer\\x64\\Release\\popup-resizer.dll  \"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe\" ";
    int exit_code = std::system(command.data());
    return 0;
}

效果: https://www.youtube.com/watch?v=JrMxuaE3KfE

同样方法,chrome 效果差一些。

tablacus explorer

tablacus 默认是延时,显示窗口,我不大喜欢,因为这让我觉得是卡顿。

用以上方法即可,不用劫持,修改源代码即可,效果不错的。

vscode

最难忍vscode : 启动慢,白屏久,甚至需要移开视线,看看风景。

但有时避之不及,直接呼我一脸,白屏太难受……

使用以上方法几乎无效。我尝试hook CreateProcess ,劫持所有子进程,也还是无效。

只好用“笨办法”,也就是终极方案:显示半透明浮窗,遮住白屏即可。

半透明浮窗 用 ahk 写,作者是deekseep,豆包太笨了:

#SingleInstance, Force
SetWorkingDir, %A_ScriptDir%
#NoTrayIcon 

ScreenWidth := A_ScreenWidth
ScreenHeight := A_ScreenHeight
ScreenHeight -= 235  ; 修改:避免遮挡任务栏。我的任务栏是双层的。

Gui +LastFound +AlwaysOnTop -Caption +ToolWindow  
Gui Color, Black                                   
; Gui Show, Maximize 
Gui Show, x0 y0 w%ScreenWidth% h%ScreenHeight%, BlackWindow 

WinSet, Transparent, 150

Sleep 2000

ExitApp

Esc::ExitApp

然后是注入,和edege一样,无需真正的注入器。此例只需启动浮窗后,再启动原应用。

需要字写先导程序,然后将其填入注册表,作为 Code.exe 的调试器。

injector.cpp

// Injector.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <Windows.h>
//#include <detours.h> // 无需真正的注入器

#include <string>


#include <cstdlib>   // 包含 system() 函数

int wmain(int argc, wchar_t* argv[]) {
    // Check if we have at least the original executable path
    //if (argc < 2) {
    //    // Handle error: no target specified
    //    return 1;
    //}
    //MessageBoxA(NULL, "123", "?", MB_OK);
    ::ShowWindow(::GetConsoleWindow(), SW_HIDE);

    // Reconstruct the command line for the original process.
    // Start with the path to the target executable (argv[1]).
    // A more robust solution would combine all remaining arguments.

    wchar_t commandLine[32768];
    //if (argc >= 2) {
    //    wcscpy_s(commandLine, argv[1]);
    //    for (int i = 2; i < argc; ++i) {
    //        wcscat_s(commandLine, L" ");
    //        wcscat_s(commandLine, argv[i]);
    //    }
    //}

    //MessageBoxW(NULL, (LPWSTR)commandLine, L"123", MB_OK);

    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;

    //wcscpy_s(commandLine, L"D:\\Code\\WODPlayer\\bin\\WODPlayer.exe");
    wcscpy_s(commandLine, L"C:\\vscode\\Code.exe");
    //wcscpy_s(commandLine, L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe");
    //wcscat_s(commandLine, L" --disable-gpu");
    //wcscat_s(commandLine, L" --enable-features=RemoveRedirectionBitmap --RemoveRedirectionBitmap");
    //wcscat_s(commandLine, L" --enable-force-dark");
    //wcscat_s(commandLine, L" --force-dark");

    // 显示遮罩
    std::system("start R:\\test_g.ahk");


    DWORD createFlags = CREATE_NEW_CONSOLE |          // 可选:创建新控制台
        CREATE_BREAKAWAY_FROM_JOB |  // 脱离作业对象,绕过IFEO
        DEBUG_ONLY_THIS_PROCESS;     // 仅调试当前进程,不触发递归 DEBUG_PROCESS


    BOOL created = CreateProcessW(
        nullptr,           // No application name (use command line)
        commandLine,       // Full command line for the target
        nullptr, nullptr,  // Process/thread security attributes
        FALSE,             // Handle inheritance
        0,     // **** CRITICAL FLAG ****
        nullptr, nullptr,  // Environment/current directory
        &si, &pi
    );
    if (!created) {
        MessageBoxW(NULL, (LPWSTR)commandLine, L"!created", MB_OK);
        // Handle error
        return 1;
    }

    //CloseHandle(pi.hProcess);

    //WCHAR szMsg[512] = { 0 };
    //wsprintfW(szMsg, L"创建窗口 %d:", pi.dwProcessId);
    //MessageBoxW(nullptr, szMsg, L"创建窗口 ", MB_OK | MB_ICONINFORMATION);

    //Sleep(1000);


    // edge 的话这样注入:
    //std::string command = "R:\\detours-x64\\withdll.exe -d C:\\base\\hook\\popup-resizer\\Release\\popup-resizer.dll --debug  ";
    //command += std::to_string(pi.dwProcessId); // 数字
    //int exit_code = std::system(command.data());


    // Close handles to the target process/thread if you don't need them immediately
    //CloseHandle(pi.hThread);
    //CloseHandle(pi.hProcess);
    //while (1) {

    //}

    // Your debugger logic continues here...
    // Optionally, call DebugSetProcessKillOnExit(FALSE) to let the target run
    // if your debugger process terminates.

    return 0;
}

代码需要整理,主体是deepseek写的,可以重建原始参数。

注入方法

导入.reg

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\【进程名】.exe]
"Debugger"="c:\\【注入器】.exe 【注入参数】"

注入器可以直接用支持启动注入的注入器,也可以自写先导程序,然后create process,得到pid后再用真实的注入器,注入dll,或显示浮窗遮挡。

推荐

Withdll: A small tool to perform DLL injections

Injector: Command line utility to inject and eject DLLs

注入器的编写不用研究,用现成的开源项目即可,功能已经完备。

需要自己写的是要注入的dll,也就是劫持逻辑,可以用 minhook 编写。

TsudaKageyu/minhook: The Minimalistic x86/x64 API Hooking Library for Windows

可以问豆包等,都能答得上来,不过有点笨,许多错误,下面给出可用代码,不到百行:

dllmain.cpp

#include <Windows.h>
#include "MinHook.h"
#include <iostream>


void UnHook();
HMODULE g_hDll;

// https://blog.csdn.net/weixin_44256803/article/details/102614168
HANDLE free(DWORD dwProcID, LPVOID lpModuleAddress)
{
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcID);

    HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");

    LPTHREAD_START_ROUTINE lpStartAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "FreeLibrary");

    HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, lpModuleAddress, 0, NULL);

    //WaitForSingleObject(hRemoteThread, 2000);
    //CloseHandle(hRemoteThread);
    //CloseHandle(hProcess);
    return hRemoteThread;
}

 // 注意,豆包的代码 需要修改类名为 OldSetWindowTextW,避免重复。

typedef BOOL(WINAPI *OldSetWindowTextW)(HWND, LPCWSTR);
OldSetWindowTextW fpSetWindowTextW = NULL;
BOOL WINAPI MySetWindowTextW(HWND hWnd, LPCWSTR lpString)
{
    BOOL ret = fpSetWindowTextW(hWnd, lpString); // 原始逻辑
    MessageBox(NULL, L"设置标题", lpString, MB_OK);  // 劫持后的逻辑
    return ret;
}


bool SetHook()
{
    if (MH_Initialize() == MB_OK)
    {
        MH_CreateHook(&SetWindowTextW, &MySetWindowTextW, reinterpret_cast<void**>(&fpSetWindowTextW));
        MH_EnableHook(&SetWindowTextW);
        // 安装其他钩子……
        return true;
    }
    return false;
}

void UnHook()
{
    //if (MH_DisableHook(&SetWindowTextW) == MB_OK)
    //{
    //    MH_Uninitialize();
    //}
    MH_DisableHook(&SetWindowTextW);
    MH_DisableHook(&GetMonitorInfoW);
    MH_DisableHook(&SetWindowPos);
    MH_Uninitialize();
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH: {
        //MessageBox(NULL, L"Ez Injected", L"Success", MB_OK);
        g_hDll = hModule;

        SetHook();
        //if(SetHook())
        //    MessageBox(NULL, L"DLL Injected", L"Success", MB_OK);
        //else
        //    MessageBox(NULL, L"DLL Injected", L"Error", MB_OK);
    }  break;
    case DLL_PROCESS_DETACH:
        //MessageBox(NULL, L"DLL UnInjected", L"Success", MB_OK);
        //UnHook();
        break;
    }
    return TRUE;
}

编译:将 minhook 关键代码下载下来,和 dllmain.cpp 放到一起,visual studio 建立项目,拖入全部源代码,编译成dll即可。

编译成功后,使用命令行测试,用注入器修改运行中的应用程序,如成功,修改标题时会弹窗。

有了这个基础,就可以随意让豆包劫持其他API。

防止循环注入

使用注册表自动注入,如果注入器写的不对,会导致死循环。

需要在 create process 时写好标志,或用 detour create process。

还有一个取巧的方法,我从这文中得到:

利用映像劫持(IFEO)实现特定进程运行参数检查 - 哔哩哔哩

先建立目标exe的副本,然后将 副本exe 放到任务栏(手动改 %appdata%\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar 中的快捷方式 )

,劫持后,在 injector.cpp 中转回本体。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment