0

I want to open an external EXE file (written in Delphi), put it on the top and in a specific location of the screen.

This piece of code is used:

ProcessStartInfo psi = new ProcessStartInfo("UT512_UT513.exe");
psi.WindowStyle = ProcessWindowStyle.Normal;
p = Process.Start(psi);
Thread.Sleep(5000);
SetWindowPos(p.MainWindowHandle, HWND_TOPMOST, panel1.Location.X, panel1.Location.Y, 500, 500, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
MoveWindow(p.MainWindowHandle, panel1.Location.X, panel1.Location.Y, 500, 500, true);

SetParent(p.MainWindowHandle, this.Handle);

But the problem is it opens two instances of the application. One runs good on the top, but not in the desired location. The second runs behind the first one and without controls and UI and has a black background.

Closing the application closes the second one and remains the first one running.

You can see the picture below:

Enter image description here

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Rezaeimh7
  • 1,467
  • 2
  • 23
  • 40
  • 2
    [Is it legal to have a cross-process parent/child or owner/owned window relationship?](https://devblogs.microsoft.com/oldnewthing/20130412-00/?p=4683): "It is also technically legal to juggle chainsaws... they become near-impossible to manage if *one or both of the windows involved is unaware that it is participating in a cross-process window tree*" – Damien_The_Unbeliever Oct 18 '21 at 12:19
  • maybe set `psi.CreateNoWindow = true`? and `psi.WindowStyle = ProcessWindowStyle.Hidden`? – Marc Gravell Oct 18 '21 at 12:20
  • @MarcGravell it didn't help. – Rezaeimh7 Oct 18 '21 at 12:25
  • 1
    @m.r226 if you run the application from, say, explorer; do you get the same thing there? – Marc Gravell Oct 18 '21 at 12:31
  • @MarcGravell Running My application both from IDE (in debug mode) and also by double click on it's exe file has the same result as posted but The external app runs good from explorer without problem – Rezaeimh7 Oct 18 '21 at 12:45
  • @m.r226 perhaps also `UseShellExecute = false` – Marc Gravell Oct 18 '21 at 12:47
  • @MarcGravell applied all the options you said, nothing changed. The external app is written in Delphi. May it causes the problem? – Rezaeimh7 Oct 18 '21 at 12:51
  • 1
    Try `UseShellExecute = true`. However, you don't want the handle of the Process window. You want to find the handle for your program (Delphi) window. Search for `pinvoke enumwindows` and `pinvoke FindWindow`. – Tu deschizi eu inchid Oct 18 '21 at 13:49

1 Answers1

1

The following shows how to use System.Diagnostics.Process to start a program (.exe) that has a window and once that program has started, move its window to a specified point (x, y) on the specified monitor (ex: 1, 2, etc...).

It's been tested with UT512/UT513 Interface Program (UT512 UT513 software installation file).

Create a class (name: Helper)

File Helper.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;

namespace MoveWindowTest
{
    public class Helper
    {

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        public enum SpecialWindowHandles : int
        {
            /// <summary>
            ///     Places the window at the top of the Z order.
            /// </summary>
            HWND_TOP = 0,
            /// <summary>
            ///     Places the window at the bottom of the Z order. If
            ///     the hWnd parameter identifies a topmost window, the
            ///     window loses its topmost status and is placed at
            ///     the bottom of all other windows.
            /// </summary>
            HWND_BOTTOM = 1,
            /// <summary>
            ///     Places the window above all non-topmost windows. The
            ///     window maintains its topmost position even when
            ///     it is deactivated.
            /// </summary>
            HWND_TOPMOST = -1,
            /// <summary>
            ///     Places the window above all non-topmost windows (that
            ///     is, behind all topmost windows). This flag has no
            ///     effect if the window is already a non-topmost window.
            /// </summary>
            HWND_NOTOPMOST = -2
        }

        [Flags]
        public enum SetWindowPosFlags : uint
        {
            /// <summary>
            ///     If the calling thread and the thread that owns the
            ///     window are attached to different input queues, the
            ///     system posts the request to the thread that owns
            ///     the window. This prevents the calling thread
            ///     from blocking its execution while other
            ///     threads process the request.
            /// </summary>
            SWP_ASYNCWINDOWPOS = 0x4000,

            /// <summary>
            ///     Prevents generation of the WM_SYNCPAINT message.
            /// </summary>
            SWP_DEFERERASE = 0x2000,

            /// <summary>
            ///     Draws a frame (defined in the window's class
            ///     description) around the window.
            /// </summary>
            SWP_DRAWFRAME = 0x0020,

            /// <summary>
            ///     Applies new frame styles set using the SetWindowLong
            ///     function. Sends a WM_NCCALCSIZE message to the
            ///     window, even if the window's size is not being
            ///     changed. If this flag is not specified,
            ///     WM_NCCALCSIZE is sent only when the window's
            ///     size is being changed.
            /// </summary>
            SWP_FRAMECHANGED = 0x0020,

            /// <summary>
            ///     Hides the window.
            /// </summary>
            SWP_HIDEWINDOW = 0x0080,

            /// <summary>
            ///     Does not activate the window. If this flag is not
            ///     set, the window is activated and moved to the top
            ///     of either the topmost or non-topmost group
            ///     (depending on the setting of the hWndInsertAfter parameter).
            /// </summary>
            SWP_NOACTIVATE = 0x0010,

            /// <summary>
            ///     Discards the entire contents of the client area. If
            ///     this flag is not specified, the valid contents of
            ///     the client area are saved and copied back into the
            ///     client area after the window is sized or repositioned.
            /// </summary>
            SWP_NOCOPYBITS = 0x0100,

            /// <summary>
            ///     Retains the current position (ignores X and Y parameters).
            /// </summary>
            SWP_NOMOVE = 0x0002,

            /// <summary>
            ///     Does not change the owner window's position in the Z order.
            /// </summary>
            SWP_NOOWNERZORDER = 0x0200,

            /// <summary>
            ///     Does not redraw changes. If this flag is set, no
            ///     repainting of any kind occurs. This applies to
            ///     the client area, the nonclient area (including
            ///     the title bar and scroll bars), and any part
            ///     of the parent window uncovered as a result of
            ///     the window being moved. When this flag is set,
            ///     the application must explicitly invalidate or
            ///     redraw any parts of the window and parent
            ///     window that need redrawing.
            /// </summary>
            SWP_NOREDRAW = 0x0008,

            /// <summary>
            ///     Same as the SWP_NOOWNERZORDER flag.
            /// </summary>
            SWP_NOREPOSITION = 0x0200,

            /// <summary>
            ///     Prevents the window from receiving the WM_WINDOWPOSCHANGING message.
            /// </summary>
            SWP_NOSENDCHANGING = 0x0400,

            /// <summary>
            ///     Retains the current size (ignores the cx and cy parameters).
            /// </summary>
            SWP_NOSIZE = 0x0001,

            /// <summary>
            ///     Retains the current Z order (ignores
            ///     the hWndInsertAfter parameter).
            /// </summary>
            SWP_NOZORDER = 0x0004,

            /// <summary>
            ///     Displays the window.
            /// </summary>
            SWP_SHOWWINDOW = 0x0040,
        }


        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);


        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);

        [DllImport("User32.dll")]
        public static extern Int32 SetWindowPos(IntPtr hwnd,
                                                SpecialWindowHandles hwndInsertAfter,
                                                int x,
                                                int y,
                                                int cx,
                                                int cy,
                                                SetWindowPosFlags flags);

        public static System.Drawing.Point GetCurrentWindowPosition(IntPtr hwnd)
        {
            Point p = new Point(-1, -1);

            RECT rect = new RECT();

            if (GetWindowRect(hwnd, ref rect))
            {
                p = new Point(rect.Left, rect.Top);
            }

            return p;
        }

        public static void MoveWindowToMonitor(IntPtr hwnd, int monitorNumber, int x, int y)
        {
            RECT rect = new RECT();

            if (GetWindowRect(hwnd, ref rect))
            {
                SetWindowPos(hwnd,
                             SpecialWindowHandles.HWND_TOP,
                             System.Windows.Forms.Screen.AllScreens[monitorNumber].WorkingArea.Left + x,
                             System.Windows.Forms.Screen.AllScreens[monitorNumber].WorkingArea.Top + y,
                             rect.Right,
                             rect.Bottom,
                             SetWindowPosFlags.SWP_SHOWWINDOW | SetWindowPosFlags.SWP_NOSIZE);
            }
        }

        public static void StartProcess(string fullyQualifiedExeFilename,
                                        string windowTitle,
                                        int monitorNumber,
                                        int x,
                                        int y)
        {
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.FileName = fullyQualifiedExeFilename;

            startInfo.CreateNoWindow = true; // Don't create a window
            //startInfo.UseShellExecute = true; // If true, uses 'ShellExecute'; if false, uses 'CreateProcess'
            startInfo.UseShellExecute = false; // If true, uses 'ShellExecute'; if false, uses 'CreateProcess'
            startInfo.WindowStyle = ProcessWindowStyle.Hidden;

            using (Process p = new Process { StartInfo = startInfo, EnableRaisingEvents = true })
            {
                p.Start(); //start

                // Don't wait for exit
            }

            // Give the window time to be created.
            // Periodically check if the window has been created yet.
            // The sleep time can be adjusted as desired,
            // as well as maxCount.

            int count = 0;
            int maxCount = 50;
            IntPtr hwnd = IntPtr.Zero;

            do
            {
                System.Threading.Thread.Sleep(75);
                //System.Threading.Thread.Sleep(125);

                // Find window
                hwnd = FindWindow(null, windowTitle);

                //Debug.WriteLine("hwnd: " + hwnd.ToString("X8") + " count: " + count.ToString());

                if (hwnd != IntPtr.Zero)
                {
                    break;
                }

                count++; // Increment
            } while (count < maxCount);

            // Move window
            MoveWindowToMonitor(hwnd, monitorNumber, x, y);
        }
    }
}

Usage:

string filename = System.IO.Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
                    @"DMM\UT512_UT513\UT512_UT513.exe");

if (!System.IO.File.Exists(filename))
{
    filename = System.IO.Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
                    @"DMM\UT512_UT513\UT512_UT513.exe");
}

if (!System.IO.File.Exists(filename))
{
    throw new Exception("Filename '" + filename + "' not found.");
}

// Start 'UT512_UT513 V2.00.exe' and move it to monitor 1; x = 100, y = 200
Helper.StartProcess(filename, "UT512/UT513 Interface Program V2.00", 1, 100, 200);

Note: The window text (title) can be found by using Spy++.

Resources:

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tu deschizi eu inchid
  • 4,117
  • 3
  • 13
  • 24
  • great job @user9938 . one more question. How to put the external program on top? – Rezaeimh7 Oct 19 '21 at 12:47
  • 1
    When it opens, it is on top. Are you wanting it to always be on top, even if another window is activated? If so, change `SpecialWindowHandles.HWND_TOP` to `SpecialWindowHandles.HWND_TOPMOST` – Tu deschizi eu inchid Oct 19 '21 at 16:09