FastReport调用进程句柄,设置窗口置顶

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yijunwanghaha/article/details/62421351

应用场景

在使用第三方打印插件,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及参数介绍

展开阅读全文

没有更多推荐了,返回首页