Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 335
  • Last Modified:

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

0
SniperCode Sheva
Asked:
SniperCode Sheva
  • 10
  • 8
  • 3
1 Solution
 
WalkaboutTiggerCommented:
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
 
Kyle AbrahamsSenior .Net DeveloperCommented:
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
 
SniperCode ShevaAuthor Commented:
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
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
WalkaboutTiggerCommented:
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
 
SniperCode ShevaAuthor Commented:
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
 
Kyle AbrahamsSenior .Net DeveloperCommented:
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
 
SniperCode ShevaAuthor Commented:
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
 
Kyle AbrahamsSenior .Net DeveloperCommented:
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
 
SniperCode ShevaAuthor Commented:
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
 
Kyle AbrahamsSenior .Net DeveloperCommented:
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
 
WalkaboutTiggerCommented:
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
 
SniperCode ShevaAuthor Commented:
Can you show me an example ?
0
 
Kyle AbrahamsSenior .Net DeveloperCommented:
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
 
SniperCode ShevaAuthor Commented:
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
 
SniperCode ShevaAuthor Commented:
Any idea ??
0
 
Kyle AbrahamsSenior .Net DeveloperCommented:
Sorry, wrote the code by hand.  The dictionary takes just the key, value:

mapping.Add(processId, deleteButton);
0
 
SniperCode ShevaAuthor Commented:
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
 
Kyle AbrahamsSenior .Net DeveloperCommented:
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
 
SniperCode ShevaAuthor Commented:
I have put it in a class and then call it after in forms later
0
 
SniperCode ShevaAuthor Commented:
If I put it in a class, so how I put it as global variable and once ?
0
 
Kyle AbrahamsSenior .Net DeveloperCommented:
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: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

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.

  • 10
  • 8
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now