Solved

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

Posted on 2016-09-14
21
77 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 39

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
 
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 39

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 39

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 39

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
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
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 39

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 39

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 39

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 39

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

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

A few years ago I was very much a beginner at VBA, and that very much remains the case today.  I'll do my best to explain things as I go in the hope that other beginners can follow.  If you just want to check out a tool that creates a Select Case fu…
Using Word 2013, I was experiencing some incredible lag when typing.  Here's what worked for me....
This video shows and describes the main difference between both orientations in Microsoft Word. Viewers will understand when to use each orientation and how to get the most out of them.
This Micro Tutorial well show you how to find and replace special characters in Microsoft Word. This is similar to carriage returns to convert columns of values from Microsoft Excel into comma separated lists.

757 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

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now