Avatar of SniperCode Sheva
SniperCode Sheva
 asked on

How to check if an MS Word is closed in C#

Hello experts, I am doing an application in C# which open exe files like notepad, calculator.
So, my aim is when I open an exe file like Word, a button is created automatically
and then when I close the Word app, the button will be removed.
Each app will have its own button when it is launched.
I tried to do it but I have some issues with the closing of the app.
Here is what I did:
var applicationWord = new Microsoft.Office.Interop.Word.Application();


            applicationWord.Visible = true;
               applicationWord.DocumentBeforeClose += DocumentBeforeClose;



               if (applicationWord.Visible == true)
               {


                       button = new Button();
                       button.Image = Properties.Resources.word_80;
                       PIC_Barre.Controls.Add(button);
                       button.AutoSize = true;
                       //     button.Tag = proc.Id;
                       PIC_Barre.Controls.Add(button);


                   foreach (Process proc in Process.GetProcessesByName("WINWORD"))
                   {
                       if (proc.ProcessName.Contains("WINWORD"))
                       {
                           proc.WaitForInputIdle();
                           {
                               Thread.Sleep(500);
                               SetWindowPos(proc.MainWindowHandle.ToInt32(),
                                   (int)SetWinPos_ZOrderOpt.HWND_TOPMOST,
                                   0, 0, 0, 0,
                                   (int)(SetWinPosFlags.SWP_NOSIZE |
                                         SetWinPosFlags.SWP_NOMOVE));
                           }
                           button.Click += (s, e) => { ShowWindowAsync(proc.MainWindowHandle, (int)ShowWindowCommands.Normal); };
                           proc.Exited += (s, e) =>
                           {
                               var method = (Action)(() => PIC_Barre.Controls.Remove(button));
                               // button.Visible = false;
                               if (button.InvokeRequired)
                               {
                                   button.Invoke(method);
                               }


                           };

                       }

                   }
               }

Open in new window

ASP.NETMicrosoft WordC#

Avatar of undefined
Last Comment
Kyle Abrahams, PMP

8/22/2022 - Mon
Darrell Porter

I am a novice to C#, but it appears as if you are closing Word then attempting to remove the button (control), but when adding the button, you're opening Word then creating the button.

Should your exit code not mirror your launch code:  Remove button then exit Word?
Kyle Abrahams, PMP

What issues are you having?  It looks like you're invoking the button click on exiting the application . . . don't think you want that?

I found this code online which should work for you:

 public bool IsProcessOpen(string name)
    {
        foreach (Process clsProcess in Process.GetProcesses()) 
        {
            if (clsProcess.ProcessName.Contains(name))
            {
                return true;
            }
        }

        return false;
    }

Open in new window


Can be called with:
if (IsProcessOpen("WinWord.exe"))
 {
  // do something
} 

Open in new window

SniperCode Sheva

ASKER
My issue is that when I close the MS Word app I need to remove the button also at the same time, I can create a button when I launch the MS Word but can't remove it...
Your help has saved me hundreds of hours of internet surfing.
fblack61
Darrell Porter

Remember when I said I was a novice in C#?

Did you look at this code from MSDN?

private void DeleteControl()
{
    Microsoft.Office.Tools.Word.Controls.Button deleteButton =
        this.Controls.AddButton(25, 75, 80, 30, "deleteButton");
    deleteButton.Text = "Click to delete";
    deleteButton.Click += new EventHandler(deleteButton_Click);
}

// Delete the clicked button.
void deleteButton_Click(object sender, EventArgs e)
{
    Microsoft.Office.Tools.Word.Controls.Button clickedButton =
        (Microsoft.Office.Tools.Word.Controls.Button)sender;

    clickedButton.Delete();
}

Open in new window

SniperCode Sheva

ASKER
Look at what I did : so that you will have an idea of what I am trying to do.
try
            {
                
                              

                Word.Application wdApp = new Word.Application();
                wdApp.Visible = true;
                if( wdApp.Visible == true)
                {
                    
                    deleteButton = new Microsoft.Office.Tools.Word.Controls.Button();
                    PIC_Barre.Controls.Add(deleteButton);

                    
                }
                ((Word.ApplicationEvents4_Event)wdApp).Quit += () =>
                {
                    var method = (Action)(() => PIC_Barre.Controls.Remove(deleteButton));
                
                    if (deleteButton.InvokeRequired)
                {
                    deleteButton.Invoke(method);
                }
                };

Open in new window

When I launch an instance of Word, a button is created directly and when I close the window of the instance , the button must be removed.
Kyle Abrahams, PMP

if (deleteButton.InvokeRequired)
                {
                    deleteButton.Invoke(method);
                }

But there's no reason to invoke the button.  Calling the remove should suffice.

Try this:

                Word.Application wdApp = new Word.Application();
                wdApp.Visible = true;
                if( wdApp.Visible == true)
                {
                    
                    deleteButton = new Microsoft.Office.Tools.Word.Controls.Button();
                    PIC_Barre.Controls.Add(deleteButton);
                    
                }
                ((Word.ApplicationEvents4_Event)wdApp).Quit += () =>
                {
                   PIC_Barre.Controls.Remove(deleteButton);                       
                };

Open in new window


If the invoke is really required you would use the PIC_Barre to invoke the remove, not the button itself.
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
SniperCode Sheva

ASKER
Yes, I did it , but here if I launch many instances it only remove the last button the others keep there even if I close the other instances.
Kyle Abrahams, PMP

You're losing your reference to the last button.

Within the word class is there a to distinguish between one instance and another?

You'll need some kind of list<Button> or dictionary <WordAppInstance, Button> to define the relationship between the button and the instance of the app.

Once you have that linking set you can find the button that corresponds to that instance, and then remove that instance of the button.
SniperCode Sheva

ASKER
I don't know very well about the interop it is my first time that I use it but your idea is a solution. Associate the window with the button but how to do it...
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
Kyle Abrahams, PMP

That's going to require some research.  Given the other code I gave you
Process clsProcess in Process.GetProcesses()


See if there is a process id or some other distinguishing variables.  
I would either look there or within the Word Application class itself.  There has to be a way of knowing what instance of word was closed.

Once that's done is just a matter of tracking the association of the app to the specific button created.
Darrell Porter

The technique I have most often seen is at application launch, enumerate all PIDs of Word running, start your own instance of Word, enumerate all PIDs of Word running, then perform a diff check to see which instances exist in the second pass which did not exist in the first.

Another technique I have seen is instantiate an instance of Excel which does have the method to distinguish PID and launch a word application object from within that (hidden) instance of Excel.

Then you can track the button on a per-instance basis by associating the PID of the word process with the button.
SniperCode Sheva

ASKER
Can you show me an example ?
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
ASKER CERTIFIED SOLUTION
Kyle Abrahams, PMP

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
SniperCode Sheva

ASKER
Hello, I have an error here
the mapping.Add(new KeyValuePair<int, Button>(processId, deleteButton));

Open in new window

I am getting this error:  “No overload method for Add takes 1 argument”
SniperCode Sheva

ASKER
Any idea ??
Kyle Abrahams, PMP

Sorry, wrote the code by hand.  The dictionary takes just the key, value:

mapping.Add(processId, deleteButton);
Experts Exchange has (a) saved my job multiple times, (b) saved me hours, days, and even weeks of work, and often (c) makes me look like a superhero! This place is MAGIC!
Walt Forbes
SniperCode Sheva

ASKER
Here is the code:
 try
            {

                Word.Application wdApp = new Word.Application();
                string oldCaption = wdApp.Application.Caption;
                string guid = Guid.NewGuid().ToString();
                //set caption to random value
                wdApp.Application.Caption = guid;
                //make sure app is visible:
                wdApp.Visible = true;
                //find random value to get process id
                int processId = GetProcessIdByWindowTitle(guid);
           
                
                //reset caption
                wdApp.Application.Caption = oldCaption;
           
              
                if( wdApp.Visible == true)
                {
                    
                    deleteButton = new Microsoft.Office.Tools.Word.Controls.Button();
                    //create a dictionary
                    mapping = new Dictionary<int, Button>();
                    //add mapping
                    mapping.Add(processId, deleteButton);
                    PIC_Barre.Controls.Add(deleteButton);

                   


                    //PIC_Barre.Controls.Add(_button);
                }
                ((Word.ApplicationEvents4_Event)wdApp).Quit += () =>
                {
                 
                // remove the button corresponding to the processid
                     var method = (Action)(() =>
                PIC_Barre.Controls.Remove(mapping[processId]));
                     if (mapping[processId].InvokeRequired)
                     {
                         mapping[processId].Invoke(method);
                       
                     }
                 // remove the key from the dictionary
                  
                };
                Debugger.Break();
             

            
            }
            catch
            {

            }
   

Open in new window

I can remove only the last button I create, when I want to remove the others  I can't... I put breakpoint in order to know the value of the different variables .
But when I create 2 buttons, the count in the mapping.add is always equal to 1... Is it normal ?
Kyle Abrahams, PMP

mapping = new Dictionary<int, Button>();

You should only do that once.  Keep that initialized on your form as global variable.  Only "new" it once.
SniperCode Sheva

ASKER
I have put it in a class and then call it after in forms later
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
SniperCode Sheva

ASKER
If I put it in a class, so how I put it as global variable and once ?
Kyle Abrahams, PMP

if you put it in a class . . . just initialize (new) the dictionary in the constructor.  You can leave it global in the class.