Home > Software engineering >  C# Process.Start opens 2 instance of the external exe
C# Process.Start opens 2 instance of the external exe

Time:10-20

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 instance of the app. One runs good on the top but not in desired location. the second runs behind the first one and without controls and UI and has a black background.

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

You can see the picture below: enter image description here

CodePudding user response:

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 it's 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.cs)

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:

  • Related