Solved

Opening word document programmatically blocks read access to file

Posted on 2012-04-09
9
727 Views
Last Modified: 2012-04-16
Hi. We're trying to integrate better with Word using the Microsoft.Office.Interop dlls and running into issues.

The problem is simple.

1. User picks a file to open from the server. Server ships the file over and displays it in the client.
2. User makes changes to file in the client
3. User hits "Save Button"

What I'd like to happen at this point is to call the WordDocument.Save routine on the client and then once that is finished send a copy of the saved version back over to the server to keep the files in sync. The problem is that just having the file open in word blocks read access to the file!

If I'm not running code and just in Windows I seem to have read access as I can open a file in word and then copy/paste copies of the file all day long in windows explorer.... but if I'm in code the file is locked. What am I doing wrong?

FYI Im running office 2007

Here is a little code to demonstrate.. as you can see all I do is open a word file and then try to create a read only stream from it and it bombs.

 
    

Public Sub Test()

        Dim FilePath As String = "C:\Change to Your Path\Test Doc.docx"

        ' Create a new instance of word and open some document in it
        Dim WordInstance As New Microsoft.Office.Interop.Word.Application
        Dim Document As Microsoft.Office.Interop.Word.Document = WordInstance.Documents.Open(FilePath)

        ' Now try to create a readonly stream (to send a copy of our 
        ' changes back to the server)
        Dim fStream As FileStream = Nothing

        Try
            fStream = New FileStream(Me._FilePath, FileMode.Open, FileAccess.Read, FileShare.Read)
        Catch ex As Exception
            MessageBox.Show("Error: " & ex.Message)
        Finally
            If fStream IsNot Nothing Then
                fStream.Close()
                fStream = Nothing
            End If
        End Try

        WordInstance.Quit()
    End Sub

Open in new window

0
Comment
Question by:PaperlessEnvironments
  • 5
  • 3
9 Comments
 
LVL 40

Expert Comment

by:Jacques Bourgeois (James Burger)
Comment Utility
Do not use a Stream to copy the document, simply SaveAs to the server before quitting Word.
0
 
LVL 18

Expert Comment

by:DarrenD
Comment Utility
Hi,

You could use some commercial tool like TXTextControl

http://www.textcontrol.com

It's not free but it does allow you to open documents by loading the source directly into the control. This means that you can save and load the binary from the server directly.

I've had problems with word and trying to use WebDav in the past so I decided to byte the bullet and go with something that suited our needs.


Just a thought....

Cheers,

Darren
0
 

Author Comment

by:PaperlessEnvironments
Comment Utility
@JamesBurger: I cannot simply save to the server. For security file system is locked down and only the server has access to those directories. We use remoting to connect to the server and ship the stream back to the other side of "the wall" so I don't have much choice but to use a stream.

@DarrenD: We used a third party control as well but ran into problems with support so we decided to write our own. I only posted a simple version of my code but we're actually embedding the control into our forms as well... actually way easier than you'd expect. Let me know if you're interested and I'll post the code to do so.

With that said, a third party tool still wont help me because of the issues I mentioned in my reply to @JamesBurger.


There must be some kind of setting in the WordApplication object that allows me to do this. After all, I'm only wanting read access to the file and windows explorer seems to have read access to a file open in Word so it must be possible.
0
 
LVL 18

Expert Comment

by:DarrenD
Comment Utility
Hi again,

Yes, it would be great if you could post the code you mentioned.

I'm not sure I undertand what the issue is with the third party control.

For example, I take the binary from the third party control and call a WCF method to upload this to the server. It actually resides in a database table and so I don't need access to directories etc... Also FYI: I've used the support with TXTextControl and they have always been extremely helpful.

Darren
0
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 

Author Comment

by:PaperlessEnvironments
Comment Utility
The problem I've found with most third party controls is that they are doing the exact same thing as the code I'm posting so they seem to  have the same problem I have of having the file locked. After all, the word viewer isn't really 3rd party... it's the actual word application just embedded in a win forms app.

I haven't checked out TxTextControl though so I may do so. Only problem now is I'm so close to getting this working 100% in house. If I can get through this issue then I'm done. And I hate having 3rd party code that I can't touch if I find a bug... it sucks to have to tell the customer that the bug is actually not in our software but is in a 3rd party that we're waiting on a fix for.

Here is the code I mentioned. Sorry it's not cleaner but this is out of my proof of concept project and I've been doing some tinkering. The trick to getting this to work is make sure the parent of MyOfficeViewer is shown before calling open. I've just been calling the open in the form load and it's been working fine... real life Word in a windows forms app.
0
 

Author Comment

by:PaperlessEnvironments
Comment Utility
Oops forgot the code...

Imports System.Runtime.InteropServices
Imports Microsoft.Office.Interop

Public Class MyOfficeViewer

    <DllImport("user32.dll")> _
    Public Shared Function FindWindow(strclassName As String, strWindowName As String) As Integer
    End Function

    <DllImport("user32.dll")> _
    Private Shared Function SetParent(hWndChild As Integer, hWndNewParent As Integer) As Integer
    End Function

    ' handle to window
    ' placement-order handle
    ' horizontal position
    ' vertical position
    ' width
    ' height
    <DllImport("user32.dll", EntryPoint:="SetWindowPos")> _
    Private Shared Function SetWindowPos(hWnd As Integer, hWndInsertAfter As Integer, X As Integer, Y As Integer, cx As Integer, cy As Integer, uFlags As UInteger) As Boolean
        ' window-positioning options
    End Function

    <DllImport("user32.dll", EntryPoint:="MoveWindow")> _
    Private Shared Function MoveWindow(hWnd As Integer, X As Integer, Y As Integer, nWidth As Integer, nHeight As Integer, bRepaint As Boolean) As Boolean
    End Function

    Const SWP_DRAWFRAME As Integer = &H20
    Const SWP_NOMOVE As Integer = &H2
    Const SWP_NOSIZE As Integer = &H1
    Const SWP_NOZORDER As Integer = &H4

    Const VERTICAL_WINDOW_OFFSET As Integer = 32

    Private Shared _WordInstance As Microsoft.Office.Interop.Word.Application
    Private Shared _Document As Microsoft.Office.Interop.Word.Document

    Public Shared wordWnd As Integer = 0

    Private components As System.ComponentModel.Container = Nothing

    Public ReadOnly Property IsOpened As Boolean
        Get
            Return _Document IsNot Nothing
        End Get
    End Property

    Public ReadOnly Property IsDirty As Boolean
        Get
            Return True

            'If _Document Is Nothing Then
            '    Return False
            'Else
            '    Return Not _Document.Saved
            'End If
        End Get
    End Property

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.     
    End Sub

    Public Sub Open(ByVal FilePath As String)

        If _WordInstance Is Nothing Then
            _WordInstance = New Word.Application
        End If

        'If wordWnd = 0 Then
        wordWnd = FindWindow("Opusapp", Nothing)
        'End If

        If wordWnd <> 0 Then
            SetParent(wordWnd, Me.Handle.ToInt32)

            _Document = _WordInstance.Documents.Open(FilePath)

            Try
                _WordInstance.ActiveWindow.DisplayRightRuler = False
                _WordInstance.ActiveWindow.DisplayScreenTips = False
                _WordInstance.ActiveWindow.DisplayVerticalRuler = False
                _WordInstance.ActiveWindow.DisplayRightRuler = False

                _WordInstance.ActiveWindow.ActivePane.DisplayRulers = False
                _WordInstance.ActiveWindow.ActivePane.View.Type = Word.WdViewType.wdWebView ' wdNormalView
            Catch
                ' Do nothing
            End Try

            Dim counter As Integer = _WordInstance.ActiveWindow.Application.CommandBars.Count

            For i As Integer = 0 To counter - 1
                Try
                    _WordInstance.ActiveWindow.Application.CommandBars(i).Enabled = False
                Catch ex As Exception

                End Try
            Next

            Try
                _WordInstance.Visible = True
                _WordInstance.Activate()

                SetWindowPos(wordWnd, Me.Handle.ToInt32, 0, 0, Me.Bounds.Width, Me.Bounds.Height, SWP_NOZORDER Or SWP_NOMOVE Or SWP_DRAWFRAME)
                MoveWindow(wordWnd, 0, -VERTICAL_WINDOW_OFFSET, Me.Bounds.Width, Me.Bounds.Height + VERTICAL_WINDOW_OFFSET, True)
            Catch ex As Exception
                ' Do nothing
            End Try

            Me.Parent.Focus()
        End If
    End Sub

    Public Sub Save()
        If _Document IsNot Nothing Then
            _Document.Save()
        End If
    End Sub

    Public Sub PrintPreview()
        If _Document IsNot Nothing Then
            _Document.PrintPreview()
        End If
    End Sub

    Public Sub Close()
        If _Document IsNot Nothing Then
            _Document.Close()
            _Document = Nothing
        End If

        If _WordInstance IsNot Nothing Then
            _WordInstance.Quit()
            _WordInstance = Nothing
        End If
    End Sub

    Private Sub MyOfficeViewer_SizeChanged(sender As Object, e As System.EventArgs) Handles Me.SizeChanged
        If wordWnd <> 0 Then
            MoveWindow(wordWnd, 0, -VERTICAL_WINDOW_OFFSET, Me.Bounds.Width, Me.Bounds.Height + VERTICAL_WINDOW_OFFSET, True)
        End If
    End Sub
End Class

Open in new window

0
 
LVL 18

Expert Comment

by:DarrenD
Comment Utility
Hi again,

Yeah, your right when you have to blame the third party software it can be a real pain...

thanks for the code snippet, I'll check out the code when I get a spare few minutes.

Sorry, I can't help any further at the moment.

Let me know how you get on

Cheers,

Darren
0
 

Accepted Solution

by:
PaperlessEnvironments earned 0 total points
Comment Utility
Ok this is coming straight from the horse's mouth as I just got off the phone with Microsoft.

The fix is easy... I need to open the stream like this..

 fStream = New FileStream(Me._FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)

Open in new window


Instead of using the FileShare.Read param as in my above code.

Apparently what is going on is that Word is opening with the FileShare.ReadWrite flag and so if I open a stream to that file my FileShare flag has to match.
0
 

Author Closing Comment

by:PaperlessEnvironments
Comment Utility
Found the solution through additional research.
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

My experience with Windows 10 over a one year period and suggestions for smooth operation
If you’re thinking to yourself “That description sounds a lot like two people doing the work that one could accomplish,” you’re not alone.
This video shows where to find the word count, how to display it, and what it breaks down to in Microsoft Word.
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now