17

Consider this code:

using Microsoft.Office.Interop.Word;

ApplicationClass _application = new ApplicationClass();

Can I get the PID from the Winword.exe process that was launched by the _application?

I need the PID because with corrupted files, I just can't quit the ApplicationClass, even using this code:

_application.Quit(ref saveFile, ref missing, ref missing);
System.Runtime.InteropServices.Marshal.ReleaseComObject(_application);
GC.Collect();
GC.WaitForPendingFinalizers();

I can't search for the winword.exe process and kill it, because I will have several, and I don't know which one to kill. If I can get a PID for each ApplicationClass, I could just kill the correct winword.exe process that is giving me troubles to quit.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Ricardo
  • 1,015
  • 4
  • 13
  • 24

9 Answers9

10

Here is how to do it.

//Set the AppId
string AppId = ""+DateTime.Now.Ticks(); //A random title

//Create an identity for the app

this.oWordApp = new Microsoft.Office.Interop.Word.ApplicationClass();
this.oWordApp.Application.Caption = AppId;
this.oWordApp.Application.Visible = true;

while (GetProcessIdByWindowTitle(AppId) == Int32.MaxValue) //Loop till u get
{
    Thread.Sleep(5);
}

///Get the pid by for word application
this.WordPid = GetProcessIdByWindowTitle(AppId);

///You canh hide the application afterward            
this.oWordApp.Application.Visible = false;

/// <summary>
/// Returns the name of that process given by that title
/// </summary>
/// <param name="AppId">Int32MaxValue returned if it cant be found.</param>
/// <returns></returns>
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;
}
Sen Jacob
  • 3,384
  • 3
  • 35
  • 61
Mnyikka
  • 1,223
  • 17
  • 12
  • 2
    @Mmyikka can you post `StaticMethods` class? – BrunoLM Nov 25 '11 at 18:27
  • 3
    I tried this but all of my word processes MainWindowTitle are empty and setting the caption to a string doesn't seem to have an effect, Am i missing something? – Saber Sep 25 '13 at 12:58
5

There may be some error in the Word file. As a result, when you open the file with the method Word.ApplicationClass.Documents.Open(), there will be a dialog shown and the process will hang.

Use Word.ApplicationClass.Documents.OpenNoRepairDialog() instead. I found it fixed the problem.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Edwin Tai
  • 1,224
  • 1
  • 13
  • 19
3
    public void OpenWord(string Path, bool IsVisible)
    {

        MessageFilter.Register();
        object oMissing = Missing.Value;
        GUIDCaption = Guid.NewGuid().ToString();
        wordApp = new Microsoft.Office.Interop.Word.ApplicationClass();
        wordApp.Visible = IsVisible;
        wordApp.Caption = GUIDCaption;
        object oPath = Path;
        wordDoc = wordApp.Documents.Open(ref  oPath, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing);

    }
    /// <summary>
    /// 关闭文件
    /// </summary>
    public void CloseWord()
    {
        object oMissing = Missing.Value;
        GUIDCaption = "";
        if (null != wordDoc)
        {
            wordDoc.Close(ref oMissing, ref oMissing, ref oMissing);
        }
        if (null != wordApp)
        {
            wordApp.Quit(ref oMissing, ref oMissing, ref oMissing);
        }
        MessageFilter.Revoke();
        GC.Collect();
        KillwordProcess();

    }
    /// <summary>
    /// 结束word进程
    /// </summary>
    public void KillwordProcess()
    {
        try
        {
            Process[] myProcesses;
            //DateTime startTime;
            myProcesses = Process.GetProcessesByName("WINWORD");

            //通过进程窗口标题判断
            foreach (Process myProcess in myProcesses)
            {
                if (null != GUIDCaption && GUIDCaption.Length > 0 && myProcess.MainWindowTitle.Equals(GUIDCaption))
                {
                    myProcess.Kill();
                }
            }
            MessageFilter.Revoke();
        }
        catch (Exception e)
        {
            throw e;
        }
    }
Daz
  • 2,833
  • 2
  • 23
  • 30
Bing
  • 31
  • 1
3

The usual way to get it is to change Word's title to something unique and hop through the top-level window list until you find it (EnumWindows).

Joshua
  • 40,822
  • 8
  • 72
  • 132
2

http://www.codekeep.net/snippets/7835116d-b254-466e-ae66-666e4fa3ea5e.aspx

///Return Type: DWORD->unsigned int
///hWnd: HWND->HWND__*
///lpdwProcessId: LPDWORD->DWORD*
[System.Runtime.InteropServices.DllImportAttribute( "user32.dll", EntryPoint = "GetWindowThreadProcessId" )]
public static extern int GetWindowThreadProcessId ( [System.Runtime.InteropServices.InAttribute()] System.IntPtr hWnd, out int lpdwProcessId );


private int _ExcelPID = 0;
Process _ExcelProcess;

private Application _ExcelApp = new ApplicationClass();
GetWindowThreadProcessId( new IntPtr(_ExcelApp.Hwnd), out _ExcelPID );
_ExcelProcess = System.Diagnostics.Process.GetProcessById( _ExcelPID );

...

_ExcelProcess.Kill();
Douglas
  • 53,759
  • 13
  • 140
  • 188
2

Finally, I figure out the way to solved File in Use lock by user 'name' while word document not open by user but our program will produce blank title in background processes, we can kill those dummy WINWORD on background as well.

enter image description here

enter image description here

By this code we no need to use ReleaseComObject(_application) I really don't know this is a good idea because when I re-open that file first release can't see any content of document at least need to close and open again.

    public static void KillProcessBlankMainTitle(string name) // WINWORD
    {
        foreach (Process clsProcess in Process.GetProcesses())
        {

            if (Process.GetCurrentProcess().Id == clsProcess.Id)
                continue;
            if (clsProcess.ProcessName.Equals(name))
            {
                if(clsProcess.MainWindowTitle.Equals(""))
                    clsProcess.Kill();
            }
        }
    }
Vanda Ros
  • 107
  • 1
  • 5
2

And on our street came the holiday! How much blood "Word" drank from me ...

What are the ways to get a PID:

  1. Get a list of processes, run WORD, get a list of processes, among which will be the desired process. Drawback if multithreading, it will be a big problem!!!
  2. Through the installation a unique name for the window. Mr. "Bing" brought a good idea with Guid. The disadvantage is a feature of Windows OS, if the window generated by the process does not have a display property, the process loses the Title attribute and it will be empty. I.e., either wordApp.Visible = true must always be. Either we do true, perform magic, and then false again.
  3. You can get the Hwnd through the window of the opened document! As suggested in one of the messages https://stackoverflow.com/a/59146157/4143902 The disadvantage is that you need to create an invisible document window for a moment and get the Hwnd, close it and continue working. Best option.

Example:

// Setting up the environment
app.Visible = false;
app.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;
app.AutomationSecurity = Microsoft.Office.Core.MsoAutomationSecurity.msoAutomationSecurityForceDisable;
app.ScreenUpdating = false;

// The interface is a collection of documents
Word.Documents docs = app.Documents;

// Getting the window handle
Word.Document trashDoc = docs.Add();
Word.Window wind = trashDoc.ActiveWindow;
// Converting the handle to the process id
int intProcId;
GetWindowThreadProcessId(wind.Hwnd, out intProcId);
// If we want multithreading, we store the dictionary task - >window id
lock (objWorkThreadLock)
{
    if (dictThread.ContainsKey(idTask)) dictThread[idTask] = intProcId;
}
trashDoc.Saved = true;
trashDoc.Close(Word.WdSaveOptions.wdDoNotSaveChanges, Type.Missing, Type.Missing);
if (trashDoc != null) Marshal.ReleaseComObject(trashDoc);
if (wind != null) Marshal.ReleaseComObject(trashDoc);

// Work further
Word.Document doc = null;
doc = docs.OpenNoRepairDialog( ... )

...

[DllImport("user32.dll")]
public static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);

public void Killing(int intProcId)
{
    if (intProcId != 0)
    {
        try
        {
            Process proc = Process.GetProcessById(intProcId);
            proc.Kill();
        }
        catch (Exception ex)
        {
        }
    }
}
KUL
  • 391
  • 2
  • 15
1

Before you start your application, list all running Word processes, start your application, and list running Word processes again. The process found in the second list and not found in the first one is the right one:

var oPL1 = from proc in Process.GetProcessesByName("WINWORD") select proc.Id;
var app = new Word.Application();

var oPL2 = from proc in Process.GetProcessesByName("WINWORD") select proc.Id;
var pid = (from p in oPL2 where !oPL1.Contains(p) select p).ToList()[0];

The method has obvious timing issues, but it is the only one I've found which works reliably most of the time.

Björn Lindqvist
  • 19,221
  • 20
  • 87
  • 122
  • this cannot be the answer, because there is no guarantee that only your application is opening winword processes! – Saber Jun 14 '14 at 14:28
1

No, unfortunately there is no way to associate an instance of ApplicationClass with a running process of Word.

Why do you need to kill the instance of Word? Couldn't you just ask it to close all of its documents and then simply stop using that instance? If you remove all references to the class eventually the GC will kick in and take down the COM server.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 3
    I just can't close the document because it is not opened yet. The document is corrupted so a window dialog appears waiting for a human intervention. However, I use the code in a service, and I open thousands of word documents, and a human intervention is impossible. I investigate a little more, and Excel ApplicationClass have a Hwnd. With: [DllImport("user32.dll")] static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId); I can get the PID. But Word ApplicationClass doesn't have a Hwnd... What a shame... – Ricardo May 02 '09 at 15:40