简体   繁体   中英

Set to foreground a third party dialog in a Windows Form Application

Good morning.

I'm going nuts looking forward to a solution to a very corner-case problem; hope someone have some experience to share.

I'm working on a OCR sort-of software; to communicate with document scanners, I use the good NTwain library.
When scanner driver has something to tell (error occurred, paper jam, feeder empty and so on), it raises dialog boxes per se, so you have no control on them.
The problem is those messages remains in background, hidden by my app main form, and I have no idea on how to put them in foreground.

Using user32.dll interop methods is an option, but I can figure out the process that raises scanner's driver dialogs; giving the user the possibility to user different models by different manufacturer, I can't rely on dialogs title or similar, because they are different from model to model.

Do someone have an idea?
In Windows there are a C:\\Windows\\TWAIN.dll and a C:\\Windows\\twain_32.dll that let's the OS to communicate with scanner drivers: with user32.dll there's a way to look for a window opened from a particular .dll, like you can do with processes?

I'm crossing fingers :)
Bye, Nando

I finally found a tricky/partial solution to my problem.
Unlike said before, (at least for Canon scanners) it seems that drivers dialog message boxes are children of my main process windows ; with some User32.dll interop black magic and a timer I finally moved that damn little windows to foreground, letting the user read them and choose what to do.

Here the code.

#region Usings
using System;
using System.Collections;


#endregion


namespace EProm.Common.PInvoke
{
    /// <summary>
    /// Catch process child windows, setting them in foreground.
    /// <see cref="http://stackoverflow.com/questions/28559726/set-to-foreground-a-third-party-dialog-in-a-windows-form-application"/>
    /// </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
    }
}

Bye bye tumbleweeds.... :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM