Solved

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

Posted on 2016-09-14
21
171 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
 
LVL 1

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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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
 
LVL 1

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
 
LVL 1

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
 
LVL 1

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
 
LVL 1

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
 
LVL 1

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
 
LVL 1

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
 
LVL 1

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
 
LVL 1

Author Comment

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

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

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

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

I would like to show you some basics you can do with Mailings in MS Word. It´s quite handy feature you can use for creating envelopes, labels, personalized letters etc. First question could be what is this feature good for? Mailing can really he…
Microsoft Word is a program we have all encountered at some point, but very few of us have dug deep into its full scope of features, let alone customized it to suit our needs. Luckily making the ribbon (aka toolbar, first introduced in Word 2007) wo…
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…
This video shows where to find templates, what they are used for, and how to create and save a custom template using Microsoft Word.

734 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