應用場景
在使用第三方打印插件,FastReport時,選擇打印xps,點擊打印,彈出文件另存爲對話框,但是此對話框不會出現在軟件的最前面,而且會一直佔用進程,點擊軟件界面出現假死情況。解決方案
思路1.設置關閉打印進度窗口,此窗口會一直置頂,而且取消按鈕失效
2.打印前開啓線程進行輪詢獲取打印進程
3.根據進程獲取窗口句柄,設置窗口位置爲最前
4.結束輪詢
實現
1.關閉打印進度窗口
設置ShowProgress爲false,具體參考http://blog.csdn.net/yijunwanghaha/article/details/60761033
2.啓動輪詢
據觀察,當彈出文件另存爲窗口時,系統會多出名爲splwow64的進程,我們可以通過此進程實現置頂功能,但是由於我們不知道用戶何時點擊window系統的打印按鈕,所以需要在程序執行FastReport打印前啓動一個輪詢,獲取進程,實現窗口置頂功能
// 打印是否完成
static bool isPrintCompleted = false;
// 設置窗口置頂
private static void SetWindowTop()
{
Task.Run(() =>
{
// 異步輪詢實現窗口置頂
while (!isPrintCompleted)
{
// 獲取窗口置頂
isPrintCompleted = WindowClass.SetWindowTop();
Thread.Sleep(100);
}
});
}
3.根據進程獲取窗口句柄,設置窗口位置爲最前首先我們需要獲取程序名爲splwow64的進程句柄
string processName = "splwow64";
Process[] processes = Process.GetProcesses();
foreach (Process process in processes)
{
if (process.ProcessName == processName)
{
// 根據進程Id獲取進程窗口句柄
IntPtr intPtr = process.MainWindowHandle;
while (intPtr == IntPtr.Zero) // 這裏要判斷句柄爲0的情況,只有彈出另存爲窗口纔會有值
{
Thread.Sleep(100);
// 重新獲取句柄
intPtr = process.MainWindowHandle;
}
}
}
獲取到句柄後,通過user32.dll接口設置置頂引用user32函數
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int Width, int Height, int flags);
[DllImport("user32.dll", EntryPoint = "SetForegroundWindow")]
public static extern IntPtr SetForegroundWindow(IntPtr hwnd);
[DllImport("user32.dll", EntryPoint = "ShowWindow", CharSet = CharSet.Auto)]
public static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
// 恢復原來窗口,並觸發窗口
public const int SW_RESTORE = 9;
實現
// 顯示窗口
ShowWindow(intPtr, SW_RESTORE);
// 窗口置頂
SetWindowPos(intPtr, -1, 0, 0, 0, 0, 1 | 2);
SetForegroundWindow(intPtr);
整體代碼Internal calss PrintClass
{
// 打印是否完成
static bool isPrintCompleted = false;
private static void SetWindowTop()
{
Task.Run(() =>
{
// 異步輪詢獲取窗口置頂
while (!isPrintCompleted)
{
// 獲取窗口置頂
isPrintCompleted = WindowClass.WindowPos();
Thread.Sleep(100);
}
});
}
public static void Print()
{
isPrintCompleted = false;
SetWindowTop()
r.PrintPrepared();
r.Print();
isPrintCompleted = true;
}
}
internal class WindowClass
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(PrintHelper));
// 該函數改變一個子窗口,彈出式窗口式頂層窗口的尺寸,位置和Z序
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int Width, int Height, int flags);
// 該函數將創建指定窗口的線程設置到前臺,並且激活該窗口
[DllImport("user32.dll", EntryPoint = "SetForegroundWindow")]
public static extern IntPtr SetForegroundWindow(IntPtr hwnd);
[DllImport("user32.dll", EntryPoint = "ShowWindow", CharSet = CharSet.Auto)]
public static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
// 恢復原來窗口,並觸發窗口
public const int SW_RESTORE = 9;
/// <summary>
/// 設置打印窗口置頂
/// </summary>
/// <returns></returns>
public static bool SetWindowTop()
{
try
{
// 進程名稱
string processName = "splwow64";
Process[] processes = Process.GetProcesses();
foreach (Process process in processes)
{
if (process.ProcessName == processName)
{
// 根據進程Id獲取進程窗口句柄
IntPtr intPtr = process.MainWindowHandle;
while (intPtr == IntPtr.Zero)
{
Thread.Sleep(100);
// 重新獲取句柄
intPtr = process.MainWindowHandle;
}
// 顯示窗口(有時窗口被最小化/隱藏)
ShowWindow(intPtr, SW_RESTORE);
// 更改窗口的Zorder,SetWindowPos使之最上
SetWindowPos(intPtr, -1, 0, 0, 0, 0, 1 | 2);
// 置頂激活
SetForegroundWindow(intPtr);
return true;
}
}
}
catch (Exception ex) { Logger.Info(ex.ToString()); }
return false;
}
}
應用延伸
此方式可實現多種窗口呈現順序,排序等情況相關技術資料
SetForegroundWindow介紹