FastReport調用進程句柄,設置窗口置頂

應用場景

在使用第三方打印插件,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;
	}
}

應用延伸

此方式可實現多種窗口呈現順序,排序等情況

相關技術資料

SetWindowPos介紹

SetForegroundWindow介紹


Showwindow及參數介紹

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章