locked
Process.WaitForExit not working for word 2010 and higher! RRS feed

  • Question

  • Hi

    in my app, i want to open the word via code and run some code after user close to word. my code is like this :

    ProcessStartInfo psi = new ProcessStartInfo("d:\\order_1.docx");
    Process p = Process.Start(psi);
    
    if (p == null)
    {
    	// Word is already running, and handled the file above...
            p = Process.GetProcessesByName("WINWORD")[0];
    }
    
    p.WaitForExit();
    p.Close();
    
    MessageBox("Word Closed");

    This code is worked for word 2007, but for 2010 and higher not working!

    i've searched a lot on the net, but nothing found!

    can anybody help me how to accomplish this task?

    thanks in advance


    http://www.codeproject.com/KB/codegen/DatabaseHelper.aspx


    • Edited by Hamed_1983 Thursday, October 26, 2017 6:42 AM
    Thursday, October 26, 2017 6:42 AM

All replies

  • Debug that code and step through it, then check the return docs for the Start() method:
            // Returns:
            //     A new System.Diagnostics.Process that is associated with the process resource,
            //     or null if no process resource is started. Note that a new process that’s started
            //     alongside already running instances of the same process will be independent from
            //     the others. In addition, Start may return a non-null Process with its System.Diagnostics.Process.HasExited
            //     property already set to true. In this case, the started process may have activated
            //     an existing instance of itself and then exited.

    Thursday, October 26, 2017 12:16 PM
  • What do you mean by "not working"? Please be specific.

    Paul ~~~~ Microsoft MVP (Visual Basic)

    Thursday, October 26, 2017 12:28 PM
  • It also doesn't work on my OS (Windows 10, Winword 2016)

    As workaround, I used EnumWindows() in a thread to test when the window (class "OpusApp", text containing the file name) is closed (not found).

    [DllImport("User32.dll")]
            private static extern int EnumWindows(EnumWindowsProc lpEnumFunc, ref IntPtr lParam);
    
    public delegate bool EnumWindowsProc(IntPtr hWnd, ref IntPtr lParam);

    Friday, October 27, 2017 6:10 PM
  • The code posted does not work since the behavior of word changed. I told the op to simply debug it and step through it, I also posted the doc string for the method which should provide insight into the behavior he is seeing (once he steps through it and sees it does not do what he thinks).
    Friday, October 27, 2017 7:00 PM
  • I doubt that Microsoft supports using the Process class to do it. Microsoft does provide an object model that they do support that can do it. I assume that current Office applications are managed; they are nearly certainly UWP applications. Managed applications work differently; UWP applications are "sandboxed". Many things, such as Spy++, do not work for managed applications. It is like trying to swim upstream to try to use traditional solutions for managed applications.


    Sam Hobbs
    SimpleSamples.Info

    Friday, October 27, 2017 7:17 PM
  • What do you mean by "not working"? Please be specific.

    Paul ~~~~ Microsoft MVP (Visual Basic)

    Hi all, sorry for late!!

    because in word 2007, process.WaitForExit() blocking until the word document exit or close, but in word 2010 and higher the above code does not block!

    can anybody have alternative way to work-around this problem?

    thanks in advance


    http://www.codeproject.com/KB/codegen/DatabaseHelper.aspx

    Saturday, October 28, 2017 2:52 PM
  • can anybody have alternative way to work-around this problem?

    I gave one above (with GetClassName() and GetWindowText() in the Callback to identify the window)

    (tested on Windows 10)

    Saturday, October 28, 2017 2:58 PM
  • can anybody have alternative way to work-around this problem?

    I gave one above (with GetClassName() and GetWindowText() in the Callback to identify the window)

    (tested on Windows 10)

    Can u give a full example which how to accomplish this task?

    thanks in advance


    http://www.codeproject.com/KB/codegen/DatabaseHelper.aspx

    Saturday, October 28, 2017 4:30 PM
  • Can u give a full example which how to accomplish this task?

    The test I did with a file "c:\test.docx" and Winword 2016
    For testing, I just call Beep() when the window is not found :

    public partial class Form1 : Form
    {
        [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool Beep(uint dwFreq, uint dwDuration);
    
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
    
        [DllImport("User32.dll", CharSet = CharSet.Unicode)]
        private static extern int GetWindowTextLength(IntPtr hWnd);
    
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
    
        [DllImport("User32.dll")]
        private static extern int EnumWindows(EnumWindowsProc lpEnumFunc, ref IntPtr lParam);
    
        public delegate bool EnumWindowsProc(IntPtr hWnd, ref IntPtr lParam);
    
        public static bool ListWindows(IntPtr hWnd, ref IntPtr lParam)
        {
            ENUMPARAM ep = (ENUMPARAM)Marshal.PtrToStructure(lParam, typeof(ENUMPARAM));
    
            StringBuilder sClass = new StringBuilder(260);
            GetClassName(hWnd, sClass, (int)(sClass.Capacity));
    
            StringBuilder sText;
            int nTextLength = GetWindowTextLength(hWnd);
            if (nTextLength++ > 0)
            {
                sText = new StringBuilder(nTextLength);
                GetWindowText(hWnd, sText, nTextLength);               
            }
            else
                sText = new StringBuilder();
    
            if ((sText.ToString().Contains(sFile) && sText.ToString() != string.Empty)
                  &&
                  (sClass.ToString() == ep.sClass && sClass.ToString() != string.Empty))
            {
                ep.hWnd = hWnd;
                Marshal.StructureToPtr(ep, lParam, false);
                return false;
            }
            return true;
        }
    
        public Form1()
        {
            InitializeComponent();
        }
    
        private Thread workerThread = null;
        private readonly AutoResetEvent isCanceled = new AutoResetEvent(false);
        static private string sFile = "test.docx", sWindowClass = "OpusApp";
    
        private void BackgroundTask()
        {
            EnumWindowsProc Callback = new EnumWindowsProc(ListWindows);
            for (; !isCanceled.WaitOne(0);)
            {
                ENUMPARAM ep = new ENUMPARAM();
                IntPtr plParam = Marshal.AllocHGlobal(Marshal.SizeOf(ep));
                ep.sClass = sWindowClass;
                ep.sText = sFile;
                Marshal.StructureToPtr(ep, plParam, false);
                EnumWindows(Callback, ref plParam);
                ENUMPARAM epret = (ENUMPARAM)Marshal.PtrToStructure(plParam, typeof(ENUMPARAM));
                Marshal.FreeHGlobal(plParam);
                if (epret.hWnd == IntPtr.Zero)
                {
                    Beep(5000, 200);
                    isCanceled.Set();
                }               
            }
        }
    
        [StructLayoutAttribute(LayoutKind.Sequential)]
        private struct ENUMPARAM
        {
            public string sClass;
            public string sText;
            public IntPtr hWnd;
        } 
    
        private void Form1_Load(object sender, EventArgs e)
        {
            EnumWindowsProc Callback = new EnumWindowsProc(ListWindows);
            ENUMPARAM ep = new ENUMPARAM();
            IntPtr plParam = Marshal.AllocHGlobal(Marshal.SizeOf(ep));
            ep.sClass = sWindowClass;
            ep.sText = sFile;
            Marshal.StructureToPtr(ep, plParam, false);
            EnumWindows(Callback, ref plParam);
            ENUMPARAM epret = (ENUMPARAM)Marshal.PtrToStructure(plParam, typeof(ENUMPARAM));
            Marshal.FreeHGlobal(plParam);
            if (epret.hWnd == IntPtr.Zero)
            {
                using (Process exeProcess = new Process())
                {
                    exeProcess.StartInfo.FileName = "C:\\" + sFile;
                    exeProcess.Start();
                    //System.Threading.Thread.Sleep(1000);
                    exeProcess.WaitForInputIdle();
                }
    
            }
            workerThread = new Thread(BackgroundTask);
            workerThread.IsBackground = true;
            workerThread.Name = "Background Task";
            workerThread.Start();
        }
    
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (workerThread != null)
                workerThread.Abort();
        }
    }



    • Edited by Castorix31 Saturday, October 28, 2017 5:08 PM
    Saturday, October 28, 2017 5:03 PM
  • I really think the object model is easier and it is the supported solution. The following works for me.

    static AutoResetEvent Done = new AutoResetEvent(false);
    static string filename = @"path to document";
    
    static void Main(string[] args)
    {
        Application WordApplication = new Application();
        if (WordApplication == null)
        {
            Console.WriteLine("WordApplication s null");
            return;
        }
        Console.WriteLine(WordApplication.GetType().Name);
        WordApplication.Visible = true;
        WordApplication.DocumentBeforeSave += new ApplicationEvents4_DocumentBeforeSaveEventHandler(
            Application_DocumentBeforeSave);
        object missing = System.Reflection.Missing.Value;
        object readOnly = false;
        object isVisible = true;
        Document doc = WordApplication.Documents.Open( filename, 
            ref missing, ref readOnly, ref missing, ref missing, ref missing,
            ref missing, ref missing, ref missing, ref missing, ref missing,
            ref isVisible, ref missing, ref missing, ref missing, ref missing);
        doc.Activate();
        Done.WaitOne();
        WordApplication.Quit(ref missing, ref missing, ref missing);
    }
    
    static void Application_DocumentBeforeSave(Document Doc, ref Boolean SaveAsUI, ref Boolean Cancel)
    {
        Cancel = false;
        Console.WriteLine("Saving");
        Done.Set();
    }
    



    Sam Hobbs
    SimpleSamples.Info

    Saturday, October 28, 2017 7:57 PM
  • I really think the object model is easier and it is the supported solution. The following works for me.

    static AutoResetEvent Done = new AutoResetEvent(false);
    static string filename = @"path to document";
    
    static void Main(string[] args)
    {
        Application WordApplication = new Application();
        if (WordApplication == null)
        {
            Console.WriteLine("WordApplication s null");
            return;
        }
        Console.WriteLine(WordApplication.GetType().Name);
        WordApplication.Visible = true;
        WordApplication.DocumentBeforeSave += new ApplicationEvents4_DocumentBeforeSaveEventHandler(
            Application_DocumentBeforeSave);
        object missing = System.Reflection.Missing.Value;
        object readOnly = false;
        object isVisible = true;
        Document doc = WordApplication.Documents.Open( filename, 
            ref missing, ref readOnly, ref missing, ref missing, ref missing,
            ref missing, ref missing, ref missing, ref missing, ref missing,
            ref isVisible, ref missing, ref missing, ref missing, ref missing);
        doc.Activate();
        Done.WaitOne();
        WordApplication.Quit(ref missing, ref missing, ref missing);
    }
    
    static void Application_DocumentBeforeSave(Document Doc, ref Boolean SaveAsUI, ref Boolean Cancel)
    {
        Cancel = false;
        Console.WriteLine("Saving");
        Done.Set();
    }



    Sam Hobbs
    SimpleSamples.Info

    Thanks Sam

    Almost this code is works! but i'm looking for an event when the word application has been exited (because, after exit, i'm delete temp file)!

    Do you have any idea to accomplish this task?

    Thanks in advance


    http://www.codeproject.com/KB/codegen/DatabaseHelper.aspx

    Saturday, October 28, 2017 10:11 PM
  • Almost this code is works! but i'm looking for an event when the word application has been exited (because, after exit, i'm delete temp file)!

    Do you have any idea to accomplish this task?


    Handle ApplicationEvents4_Event.Quit event
    Saturday, October 28, 2017 11:42 PM
  • First, it sounds as if you should be using a control for embedding Office in your program. I don't know the details of that but if it were to satisfy your requirements then it would be easier.

    Are you sure you want to do the delete in the application exit? My guess is that you want to do it when the document is closed. If the user has other documents open then it might be hours before Word finally exits. So probably the Document.CloseEvent Event is what you want. It is probably safe to delete the document after the close.

    The bad news about using the Office Object Models is that it is difficult to find the documentation. At least it is for me. For one thing, Microsoft seems to want us to just do everything with an add-in but what you are doing is not an add-in. You don't have to create an add-in and you don't want to create an add-in for this, I am just saying that it is difficult to find relevant documentation and samples. Once you find them it is easier from there.



    Sam Hobbs
    SimpleSamples.Info


    Sunday, October 29, 2017 2:11 AM