Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if Word document is already open before opening it in C#

Tags:

c#

ms-word

I'm doing several things with a Word document in the background and sometimes, if the app fails, an instance of MS Word is left running in the background. Upon restart, it tries to open the same file and there's a stream of issues related to that.

I'd like to understand (couldn't quite find an applicable answer here) how to check if the file I'm trying to open is already open.

My code:

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;

How can I check for the file being already open before executing wordApp.Documents.Open?

Thanks!

like image 1000
Paulo Hgo Avatar asked Oct 30 '17 03:10

Paulo Hgo


People also ask

Can you see when a Word document was opened?

To view properties for an open document in Word, click the File tab and click on Info. On the right side of the window, the document properties are listed, including the Last Modified date, the date the document was Created, the Author, and the number of Words in the document.

How do I find previously opened Word documents?

Click the File tab. Click Recent. Select the check box to Quickly access this number of recent documents: and choose how many files you want to see.

How can I tell where a document is open?

Search File Explorer: Open File Explorer from the taskbar or right-click on the Start menu, choose File Explorer and then select a location from the left pane to search or browse. For example, select This PC to look in all devices and drives on your computer, or select Documents to look only for files stored there.


2 Answers

You can try to open the file, if the file is already open IOException will be thrown and indicate that the file is open:

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();
        }
    }
}
like image 180
Jonathan Applebaum Avatar answered Sep 21 '22 11:09

Jonathan Applebaum


You can enumerate all Word windows and try to find one with that document. Also if you try to open this file, while it's opened in Word, you will get IOException. But it's not perfect solution :( Here is solution:

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);
    }
}
like image 31
Artavazd Balayan Avatar answered Sep 20 '22 11:09

Artavazd Balayan