如何在WPF启动闪屏中显示DLL加载状态?
Great question! Implementing a splash screen that shows real-time DLL loading progress—just like Visual Studio's Output window—is totally feasible, especially if you're targeting Windows (since you referenced VS). Let's break down how to make this work step by step:
The key here is twofold: first, create a lightweight splash window to display logs, and second, capture DLL loading events (either by controlling the load process yourself or hooking system-level loading calls) to feed updates to that window.
1. Build the Splash Screen Window
You'll need a simple, top-most window with a text control to display real-time logs. Here's how to do it for two common frameworks:
Win32 API (C/C++)
- Register a custom window class with a minimal style (no title bar, borderless).
- Create a multi-line
EDITcontrol inside the window, settingES_READONLYandWS_VSCROLLto make it scrollable and non-editable. - Ensure the window stays on top using
SetWindowPos(hwnd, HWND_TOPMOST, ...).
.NET (C#/VB)
- Create a new
Form, disable its border (FormBorderStyle = None) and setTopMost = true. - Add a
RichTextBoxorTextBoxcontrol, setReadOnly = true, dock it to fill the form, and enable vertical scrolling.
2. Capture DLL Loading Events
You have two main options here, depending on whether you control the DLL loading or need to capture all system-initiated loads:
Option 1: Manual DLL Loading (Recommended for Control)
If your app explicitly loads DLLs (instead of relying on automatic system loading of dependencies), you can log each load action directly before and after calling the load function.
C# Example:
// Assume splashScreen is your splash form instance with a WriteLine method foreach (var dllPath in dllToLoadList) { var dllName = Path.GetFileName(dllPath); splashScreen.WriteLine($"Loading {dllName}..."); try { Assembly.LoadFrom(dllPath); splashScreen.WriteLine($"Successfully loaded {dllName}"); } catch (Exception ex) { splashScreen.WriteLine($"Failed to load {dllName}: {ex.Message}"); } }
Win32 Example:
// Send log messages to the splash screen's EDIT control void LogToSplash(HWND hEdit, const wchar_t* message) { // Get current text length to append new content int len = GetWindowTextLength(hEdit); SendMessage(hEdit, EM_SETSEL, len, len); SendMessage(hEdit, EM_REPLACESEL, FALSE, (LPARAM)message); SendMessage(hEdit, EM_REPLACESEL, FALSE, (LPARAM)L"\r\n"); } // Load DLL and log HMODULE hModule = LoadLibrary(L"mydll.dll"); LogToSplash(hSplashEdit, L"Loading mydll.dll..."); if (hModule != NULL) { LogToSplash(hSplashEdit, L"Successfully loaded mydll.dll"); } else { LogToSplash(hSplashEdit, L"Failed to load mydll.dll"); }
Option 2: Hook System-Level DLL Loading
If you need to capture all DLL loads (including system dependencies loaded automatically at app startup), you'll need to hook Windows' internal DLL loading functions. The Detours library is a popular tool for this.
C++ Detours Example:
#include <detours.h> #include <windows.h> #include <string> // Define the original function signature typedef NTSTATUS(WINAPI* LdrLoadDll_t)(PWCHAR, ULONG, PUNICODE_STRING, PHMODULE); LdrLoadDll_t OriginalLdrLoadDll = nullptr; // Hooked function to log loads NTSTATUS WINAPI HookedLdrLoadDll(PWCHAR PathToFile, ULONG Flags, PUNICODE_STRING ModuleFileName, PHMODULE ModuleHandle) { std::wstring dllName = ModuleFileName->Buffer; LogToSplash(hSplashEdit, (L"Loading " + dllName + L"...").c_str()); NTSTATUS result = OriginalLdrLoadDll(PathToFile, Flags, ModuleFileName, ModuleHandle); if (NT_SUCCESS(result)) { LogToSplash(hSplashEdit, (L"Loaded " + dllName + L" successfully").c_str()); } else { LogToSplash(hSplashEdit, (L"Failed to load " + dllName + L", error: " + std::to_wstring(result)).c_str()); } return result; } // Initialize the hook void SetupHook() { DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&(PVOID&)OriginalLdrLoadDll, HookedLdrLoadDll); DetourTransactionCommit(); }
⚠️ Note: Hooking system functions may require admin privileges, and you'll need to handle thread safety (DLL loads can happen on background threads, so update the splash UI only on the main thread).
3. Update the Splash Screen in Real Time
Ensure log updates are efficient and thread-safe:
- For .NET, use
Control.InvokeorControl.BeginInvokewhen updating the text from a non-UI thread to avoid cross-thread exceptions:public void WriteLine(string text) { if (txtLog.InvokeRequired) { txtLog.Invoke(new Action<string>(WriteLine), text); return; } txtLog.AppendText(text + Environment.NewLine); // Auto-scroll to the bottom txtLog.SelectionStart = txtLog.Text.Length; txtLog.ScrollToCaret(); } - For Win32, use
EM_REPLACESELinstead ofWM_SETTEXTto append text without re-rendering the entire control, which is much faster.
4. Close the Splash Screen
Once all DLLs are loaded and your main application window is ready, close the splash screen. Make sure to do this cleanly—avoid forcing it closed before all logs are written.
- Thread Safety: Always update the splash UI from the main thread, as UI controls aren't thread-safe.
- Performance: Avoid heavy UI operations during DLL loading to prevent slowing down the startup process.
- Window Focus: Set the splash window to be non-focusable if you don't want it to interfere with user input (use
WS_EX_NOACTIVATEfor Win32, orShowInTaskbar = falsefor .NET). - Compatibility: Hooking system functions may behave differently across Windows versions—test thoroughly on your target OSes.
内容的提问来源于stack exchange,提问作者Saoirse

