Solved

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

Posted on 2016-09-14
21
119 Views
Last Modified: 2016-09-23
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

0
Comment
Question by:SniperCode Sheva
  • 10
  • 8
  • 3
21 Comments
 
LVL 15

Expert Comment

by:WalkaboutTigger
ID: 41798406
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?
0
 
LVL 40

Expert Comment

by:Kyle Abrahams
ID: 41798610
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

0
 

Author Comment

by:SniperCode Sheva
ID: 41799306
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...
0
Free Tool: Postgres Monitoring System

A PHP and Perl based system to collect and display usage statistics from PostgreSQL databases.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
LVL 15

Expert Comment

by:WalkaboutTigger
ID: 41800126
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

0
 

Author Comment

by:SniperCode Sheva
ID: 41800205
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.
0
 
LVL 40

Expert Comment

by:Kyle Abrahams
ID: 41800217
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.
0
 

Author Comment

by:SniperCode Sheva
ID: 41800221
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.
0
 
LVL 40

Expert Comment

by:Kyle Abrahams
ID: 41800237
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.
0
 

Author Comment

by:SniperCode Sheva
ID: 41800243
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...
0
 
LVL 40

Expert Comment

by:Kyle Abrahams
ID: 41800250
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.
0
 
LVL 15

Expert Comment

by:WalkaboutTigger
ID: 41800285
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.
0
 

Author Comment

by:SniperCode Sheva
ID: 41800294
Can you show me an example ?
0
 
LVL 40

Accepted Solution

by:
Kyle Abrahams earned 500 total points
ID: 41800437
You can set the caption and find it that way:

 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;

//create a dictionary
Dictionary<int, Button> mapping = new Dictionary<int, button>();
//add mapping
mapping.Add(new KeyValuePair<int, Button>(processId, deleteButton));







//found this online:
/// <summary>
/// Returns the name of that process given by that title
/// </summary>
/// <param name="AppId">Int32MaxValue re
public static int GetProcessIdByWindowTitle(string AppId)
{
   Process[] P_CESSES = Process.GetProcesses();
   for (int p_count = 0; p_count < P_CESSES.Length; p_count++)
   {
        if (P_CESSES[p_count].MainWindowTitle.Equals(AppId))
        {
                    return P_CESSES[p_count].Id;
        }
   }

    return Int32.MaxValue;
}

Open in new window



Removal would follow the same process:
//on quit:
//refind the process id:
string guid = Guid.NewGuid().ToString();
wdApp.Application.Caption = guid;
int processId = GetProcessIdByWindowTitle(guid);
// remove the button corresponding to the processid
PIC_Barre.Controls.Remove(mapping[processId]));       
// remove the key from the dictionary
dictionary.pop(processId, 'None');

Open in new window

0
 

Author Comment

by:SniperCode Sheva
ID: 41801150
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”
0
 

Author Comment

by:SniperCode Sheva
ID: 41801940
Any idea ??
0
 
LVL 40

Expert Comment

by:Kyle Abrahams
ID: 41802090
Sorry, wrote the code by hand.  The dictionary takes just the key, value:

mapping.Add(processId, deleteButton);
0
 

Author Comment

by:SniperCode Sheva
ID: 41804521
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 ?
0
 
LVL 40

Expert Comment

by:Kyle Abrahams
ID: 41804989
mapping = new Dictionary<int, Button>();

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

Author Comment

by:SniperCode Sheva
ID: 41804992
I have put it in a class and then call it after in forms later
0
 

Author Comment

by:SniperCode Sheva
ID: 41805018
If I put it in a class, so how I put it as global variable and once ?
0
 
LVL 40

Expert Comment

by:Kyle Abrahams
ID: 41805324
if you put it in a class . . . just initialize (new) the dictionary in the constructor.  You can leave it global in the class.
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This is written from a 'VBA for MS Word' perspective, but I am sure it applies to most other MS Office components where VBA is used.  One thing that really bugs me is slow code, ESPECIALLY when it's mine!  In programming there are so many ways to…
Performance in games development is paramount: every microsecond counts to be able to do everything in less than 33ms (aiming at 16ms). C# foreach statement is one of the worst performance killers, and here I explain why.
This video shows the viewer how to set up and create Footnotes in their document. Click on the References tab: Select "Insert Footnote": Type in desired text:
This video walks the viewer through the process of creating envelopes and labels, with multiple names and addresses. Navigate to the “Start Mail Merge” button in the Mailings tab: Follow the step-by-step process until asked to find the address doc…

860 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question