在 Windows 表单应用程序中设置前景第三方对话框
Set to foreground a third party dialog in a Windows Form Application
早上好。
我非常期待找到解决非常 corner-case 问题的方法;希望有人有经验可以分享。
我正在开发 OCR sort-of 软件;为了与文档扫描仪进行通信,我使用了很好的 NTwain 库。
当扫描仪 driver 有事情要报告时(发生错误、卡纸、进纸器空等),它本身会弹出对话框,因此您无法控制它们。
问题是那些消息保留在后台,被我的应用程序主窗体隐藏,我不知道如何将它们放在前台。
使用 user32.dll
互操作方法是一种选择,但我可以弄清楚引发扫描器 driver 对话框的过程;让用户可以使用不同制造商的不同型号,我不能依赖对话框标题或类似标题,因为它们因型号而异。
有人有想法吗?
在 Windows 中有一个 C:\Windows\TWAIN.dll
和一个 C:\Windows\twain_32.dll
让我们成为 O.S。与扫描仪 drivers 通信:使用 user32.dll 有一种方法可以查找从特定 .dll 打开的 window,就像您可以对进程做的那样?
我在祈祷 :)
再见,
南多
我终于找到了 tricky/partial 解决我的问题的方法。
与之前所说的不同,(至少对于佳能扫描仪而言)似乎 驱动程序对话框消息框是我的主进程 windows 的子进程;有了一些 User32.dll interop black magic 和一个计时器,我终于把那个该死的小 windows 移到了前台,让用户阅读它们并选择要做什么。
这里是代码。
#region Usings
using System;
using System.Collections;
#endregion
namespace EProm.Common.PInvoke
{
/// <summary>
/// Catch process child windows, setting them in foreground.
/// <see cref=""/>
/// </summary>
public class DialogsCatcher
{
#region Fields
private readonly ILog _log;
private readonly int _processId;
private readonly Timer _timer;
private readonly IntPtr _windowHandle;
#endregion
#region Constructors
public DialogsCatcher(int processId, int interval, IntPtr windowHandle)
{
_log = LogManager.GetLogger(GetType().Name);
_processId = processId;
_windowHandle = windowHandle;
_timer = new Timer();
_timer.Elapsed += new ElapsedEventHandler(CatchDialogs);
_timer.Enabled = true;
_timer.Interval = interval;
_log.Debug("DialogsCatcher initialized.");
}
#endregion
#region Public Methods
public void StartMonitoring()
{
_timer.Start();
_log.Debug("DialogsCatcher started.");
}
public void StopMonitoring()
{
_timer.Stop();
_log.Debug("DialogsCatcher stopped.");
}
#endregion
#region Private Methods
private void CatchDialogs(object sender, EventArgs e)
{
GetProcessOpenedWindowsByProcessId(_processId, _windowHandle);
}
//nando20150219: meaningful names, you're doin' it right! :)
private void GetProcessOpenedWindowsByProcessId(int processId, IntPtr windowHandle)
{
var shellWindowHandle = User32.GetShellWindow();
var windows = new Dictionary<IntPtr, string>();
EnumWindowsProc filter = (windowHandle, lp) =>
{
int length = User32.GetWindowTextLength(windowHandle);
var windowText = new StringBuilder(length);
User32.GetWindowText(windowHandle, windowText, length + 1);
windows.Add(windowHandle, windowText.ToString());
var isWindowVisible = User32.IsWindowVisible(windowHandle);
if (windowHandle == shellWindowHandle)
{
return true;
}
if (!isWindowVisible)
{
return true;
}
if (length == 0)
{
return true;
}
uint windowPid;
User32.GetWindowThreadProcessId(windowHandle, out windowPid);
if (windowPid != processId)
{
return true;
}
if (windowHandle != windowHandle)
{
//nando20150218: set window to foreground
User32.SetForegroundWindow(windowHandle);
_log.DebugFormat("Window \"{0}\" moved to foreground.", windowText);
}
return true;
};
User32.EnumWindows(filter, 0);
#if DEBUG
//foreach (var dictWindow in windows)
//{
// _log.DebugFormat("WindowHandle: {0} - WindowTitle: {1}", dictWindow.Key, dictWindow.Value);
//}
#endif
}
#endregion
}
#region Delegates
public delegate bool EnumWindowsProc(IntPtr windowHandle, IntPtr lp);
public delegate bool EnumedWindow(IntPtr windowHandle, ArrayList windsowHandles);
#endregion
/// <summary>
/// Windows User32.dll wrapper
/// </summary>
/// <see cref="http://pinvoke.net/"/>
public class User32
{
#region Public Methods
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", EntryPoint = "GetWindowText", ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpWindowText, int nMaxCount);
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hwnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.DLL")]
public static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);
[DllImport("user32.DLL")]
public static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.DLL")]
public static extern IntPtr GetShellWindow();
#endregion
}
}
再见风滚草.... :)
早上好。
我非常期待找到解决非常 corner-case 问题的方法;希望有人有经验可以分享。
我正在开发 OCR sort-of 软件;为了与文档扫描仪进行通信,我使用了很好的 NTwain 库。
当扫描仪 driver 有事情要报告时(发生错误、卡纸、进纸器空等),它本身会弹出对话框,因此您无法控制它们。
问题是那些消息保留在后台,被我的应用程序主窗体隐藏,我不知道如何将它们放在前台。
使用 user32.dll
互操作方法是一种选择,但我可以弄清楚引发扫描器 driver 对话框的过程;让用户可以使用不同制造商的不同型号,我不能依赖对话框标题或类似标题,因为它们因型号而异。
有人有想法吗?
在 Windows 中有一个 C:\Windows\TWAIN.dll
和一个 C:\Windows\twain_32.dll
让我们成为 O.S。与扫描仪 drivers 通信:使用 user32.dll 有一种方法可以查找从特定 .dll 打开的 window,就像您可以对进程做的那样?
我在祈祷 :)
再见,
南多
我终于找到了 tricky/partial 解决我的问题的方法。
与之前所说的不同,(至少对于佳能扫描仪而言)似乎 驱动程序对话框消息框是我的主进程 windows 的子进程;有了一些 User32.dll interop black magic 和一个计时器,我终于把那个该死的小 windows 移到了前台,让用户阅读它们并选择要做什么。
这里是代码。
#region Usings
using System;
using System.Collections;
#endregion
namespace EProm.Common.PInvoke
{
/// <summary>
/// Catch process child windows, setting them in foreground.
/// <see cref=""/>
/// </summary>
public class DialogsCatcher
{
#region Fields
private readonly ILog _log;
private readonly int _processId;
private readonly Timer _timer;
private readonly IntPtr _windowHandle;
#endregion
#region Constructors
public DialogsCatcher(int processId, int interval, IntPtr windowHandle)
{
_log = LogManager.GetLogger(GetType().Name);
_processId = processId;
_windowHandle = windowHandle;
_timer = new Timer();
_timer.Elapsed += new ElapsedEventHandler(CatchDialogs);
_timer.Enabled = true;
_timer.Interval = interval;
_log.Debug("DialogsCatcher initialized.");
}
#endregion
#region Public Methods
public void StartMonitoring()
{
_timer.Start();
_log.Debug("DialogsCatcher started.");
}
public void StopMonitoring()
{
_timer.Stop();
_log.Debug("DialogsCatcher stopped.");
}
#endregion
#region Private Methods
private void CatchDialogs(object sender, EventArgs e)
{
GetProcessOpenedWindowsByProcessId(_processId, _windowHandle);
}
//nando20150219: meaningful names, you're doin' it right! :)
private void GetProcessOpenedWindowsByProcessId(int processId, IntPtr windowHandle)
{
var shellWindowHandle = User32.GetShellWindow();
var windows = new Dictionary<IntPtr, string>();
EnumWindowsProc filter = (windowHandle, lp) =>
{
int length = User32.GetWindowTextLength(windowHandle);
var windowText = new StringBuilder(length);
User32.GetWindowText(windowHandle, windowText, length + 1);
windows.Add(windowHandle, windowText.ToString());
var isWindowVisible = User32.IsWindowVisible(windowHandle);
if (windowHandle == shellWindowHandle)
{
return true;
}
if (!isWindowVisible)
{
return true;
}
if (length == 0)
{
return true;
}
uint windowPid;
User32.GetWindowThreadProcessId(windowHandle, out windowPid);
if (windowPid != processId)
{
return true;
}
if (windowHandle != windowHandle)
{
//nando20150218: set window to foreground
User32.SetForegroundWindow(windowHandle);
_log.DebugFormat("Window \"{0}\" moved to foreground.", windowText);
}
return true;
};
User32.EnumWindows(filter, 0);
#if DEBUG
//foreach (var dictWindow in windows)
//{
// _log.DebugFormat("WindowHandle: {0} - WindowTitle: {1}", dictWindow.Key, dictWindow.Value);
//}
#endif
}
#endregion
}
#region Delegates
public delegate bool EnumWindowsProc(IntPtr windowHandle, IntPtr lp);
public delegate bool EnumedWindow(IntPtr windowHandle, ArrayList windsowHandles);
#endregion
/// <summary>
/// Windows User32.dll wrapper
/// </summary>
/// <see cref="http://pinvoke.net/"/>
public class User32
{
#region Public Methods
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", EntryPoint = "GetWindowText", ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpWindowText, int nMaxCount);
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hwnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.DLL")]
public static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);
[DllImport("user32.DLL")]
public static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.DLL")]
public static extern IntPtr GetShellWindow();
#endregion
}
}
再见风滚草.... :)