繁体   English   中英

从 MS-Word ApplicationClass 获取 PID?

[英]Get PID from MS-Word ApplicationClass?

考虑这个代码:

using Microsoft.Office.Interop.Word;

ApplicationClass _application = new ApplicationClass();

我可以从 _application 启动的 Winword.exe 进程中获取 PID 吗?

我需要 PID,因为对于损坏的文件,即使使用以下代码,我也无法退出 ApplicationClass:

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

找不到winword.exe进程杀掉,因为我会有好几个,不知道杀哪一个。 如果我可以为每个 ApplicationClass 获得一个 PID,我就可以杀死正确的 winword.exe 进程,它给我带来了退出的麻烦。

这是如何做到的。

//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;
}

Word 文件中可能存在一些错误。 因此,当您使用Word.ApplicationClass.Documents.Open()方法打开文件时,将显示一个对话框并且进程将挂起。

请改用Word.ApplicationClass.Documents.OpenNoRepairDialog() 我发现它解决了问题。

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();

最后,我找到了解决用户'name'锁定正在使用的文件的方法,而用户没有打开word文档,但我们的程序会在后台进程中产生空白标题,我们也可以在后台杀死那些虚拟的WINWORD。

在此处输入图片说明

在此处输入图片说明

通过这段代码,我们不需要使用ReleaseComObject(_application)我真的不知道这是一个好主意,因为当我重新打开该文件时,第一次发布无法看到任何文档内容,至少需要关闭并再次打开。

    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();
            }
        }
    }

获取它的常用方法是将 Word 的标题更改为唯一的内容,然后在顶级窗口列表中跳转,直到找到它 (EnumWindows)。

在启动应用程序之前,列出所有正在运行的 Word 进程,启动应用程序,然后再次列出正在运行的 Word 进程。 在第二个列表中找到而在第一个列表中找不到的过程是正确的:

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];

该方法存在明显的计时问题,但它是我发现的唯一一种在大多数情况下都能可靠工作的方法。

    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;
        }
    }

在我们的街道上放假了! “字”从我这里喝了多少血……

获取PID的方法有哪些:

  1. 获取进程列表,运行 WORD,获取进程列表,其中将是所需的进程。 缺点是多线程的话,问题就大了!!!
  2. 通过安装一个唯一的窗口名称。 “Bing”先生用 Guid 带来了一个好主意。 缺点是 Windows 操作系统的一个特性,如果进程生成的窗口没有显示属性,进程将失去 Title 属性,它将为空。 即,要么 wordApp.Visible = true 必须始终是。 要么我们做真,表演魔术,然后再次做假。
  3. 您可以通过打开的文档窗口获取Hwnd! 正如其中一条消息https://stackoverflow.com/a/59146157/4143902 中所建议的那样,缺点是您需要暂时创建一个不可见的文档窗口并获取 Hwnd,将其关闭并继续工作。 最佳选择。

例子:

// 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)
        {
        }
    }
}

不,不幸的是,无法将 ApplicationClass 的实例与正在运行的 Word 进程相关联。

为什么需要杀掉Word的实例? 难道您不能只要求它关闭所有文档,然后就停止使用该实例吗? 如果您删除对类的所有引用,最终GC将启动并关闭COM服务器。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM