检查Word文档是否已打开,然后在C#中打开它

时间:2017-10-30 03:59:10

标签: c# ms-word

我在后台使用Word文档做了几件事,有时,如果应用程序失败,MS Word的一个实例将在后台运行。 重新启动后,它会尝试打开同一个文件,并且会出现与此相关的问题流。

我想了解(在这里找不到合适的答案)如何检查我正在尝试打开的文件是否已经打开。

我的代码:

Microsoft.Office.Interop.Word.Application wordApp = new Microsoft.Office.Interop.Word.Application();
Document wordDoc = new Document();
wordDoc = wordApp.Documents.Open(ref oTemplatePath, ref oMissing, ref oMissing, ref oMissing);
wordApp.Visible = false;

如何在执行wordApp.Documents.Open之前检查文件是否已打开?

谢谢!

4 个答案:

答案 0 :(得分:3)

您可以尝试打开该文件,如果该文件已经打开IOException将被抛出并指示该文件已打开:

public bool fileIsOpen(string path)
{
    System.IO.FileStream a = null;

    try
    {
        a = System.IO.File.Open(path,
        System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.None);
        return false;
    }
    catch (System.IO.IOException ex)
    {
        return true;
    }

    finally
    {
        if (a != null)
        {
            a.Close();
            a.Dispose();
        }
    }
}

答案 1 :(得分:2)

您可以枚举所有Word窗口并尝试使用该文档找到一个。此外,如果您尝试打开此文件,当它在Word中打开时,您将获得IOException。但它不是完美的解决方案:( 这是解决方案:

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

namespace WordExampleApp
{
    class Program
    {
        const int ERROR_SHARING_VIOLATION = -2147024864;
        static readonly string WORD_CLASS_NAME = "OpusApp";
        static readonly string WORD_DOCUMENT_CLASS_NAME = "_WwB";

        delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);

        static void Main(string[] args)
        {
            // Path to exist word document
            string filePath = @"C:\temp\asdasd.docx";

            string documentName = Path.GetFileNameWithoutExtension(filePath);
            var isDocOpened = IsDocumentOpened(documentName);
            Console.WriteLine("Is document opened: {0}", isDocOpened);

            bool canRead = CanReadFile(filePath);
            Console.WriteLine("Can read file: {0}", canRead);
        }
        private static bool CanReadFile(string path)
        {
            try {
                using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None)) { }
                return true;
            }
            catch (IOException ex) {
                if (ex.HResult == ERROR_SHARING_VIOLATION)
                    return false;
                else throw;
            }
        }

        private static bool IsDocumentOpened(string documentName)
        {
            IntPtr hwnd = FindWindow(WORD_CLASS_NAME, documentName + " - Word");
            List<IntPtr> childs = GetChildWindows(hwnd);
            var classText = new StringBuilder("", 1024);
            var windowText = new StringBuilder("", 1024);

            foreach (var childHwnd in childs)
            {
                if (0 == GetClassName(childHwnd, classText, 1024)) {
                    // something wrong
                }
                if (0 == GetWindowText(childHwnd, windowText, 1024)) {
                    // something wrong
                }
                var className = classText.ToString();
                var windowName = windowText.ToString();
                if (className == WORD_DOCUMENT_CLASS_NAME && windowName == documentName)
                    return true;

                classText.Clear();
                windowText.Clear();
            }
            return false;
        }
        public static List<IntPtr> GetChildWindows(IntPtr parent)
        {
            List<IntPtr> result = new List<IntPtr>();
            GCHandle gcHandle = GCHandle.Alloc(result);
            try {
                EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
                EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(gcHandle));
            }
            finally {
                if (gcHandle.IsAllocated)
                    gcHandle.Free();
            }
            return result;
        }
        private static bool EnumWindow(IntPtr handle, IntPtr pointer)
        {
            GCHandle gch = GCHandle.FromIntPtr(pointer);
            List<IntPtr> list = gch.Target as List<IntPtr>;
            if (list == null)
                throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
            list.Add(handle);
            return true;
        }

        [DllImport("user32", ExactSpelling = false, CharSet = CharSet.Auto)]
        internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowProc lpEnumFunc, IntPtr lParam);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpWindowText, int nMaxCount);
    }
}

答案 2 :(得分:1)

有同样的问题。我的解决方案是始终将文档以ReadOnly的形式打开并进行复制。我知道问题是一种检查文档是否已打开的方法,但是如果文档以只读方式打开,则不必检查此问题:

public static MsWord.Document CopyTemplate(string Template, MsWord.Application wordApp)
{
      var TemplateDoc = wordApp.Documents.Open(Template, ReadOnly: true);

      TemplateDoc.Select();
      wordApp.Selection.Copy();
      TemplateDoc.Close(SaveChanges: false);

      var NewDoc = wordApp.Documents.Add();
      NewDoc.Select();
      wordApp.Selection.PasteAndFormat(MsWord.WdRecoveryType.wdFormatOriginalFormatting);

      return NewDoc;
}

答案 3 :(得分:0)

好吧,我认为一种简单的方法是检查当前正在运行的进程的标题:

bool isWordFileAlreadyOpened = false;
var alreadyOpenedProcessForWordFile = Process.GetProcesses().FirstOrDefault(p => p.MainWindowTitle.Contains(alreadyOpenedProcessForWordFile));
isWordFileAlreadyOpened = alreadyOpenedProcess != null;