none
SaveAs PDF Generates Messageboxes RRS feed

  • Question

  • Hello everybody!

    In my AddIn, I am storing all files as PDF. Now I've recieved an Word Document with some MergedFields with fillin commands. If I want to save this file with my Addin or with the Word Application (without any Addin or code snippets) itself, the Office Word Application generates Messageboxes, which require an acknowledgement.
    The Mergefieldcode looks like this:

    {FILLIN [myPetName] \* MERGEFORMAT}

    Code I use to save the files looks like this:

    this._application.ActiveDocument.ExportFileAsFixedFormat(path, wdformatPDF)

    Or

    this._application.ActiveDocument.SaveAs(path, formatPDF)

    One further information: The Word document has a permission (sadly i can't remove it in code)

    Looks like the save-Routine of Word is generating these boxes within the updating of the fields.
    Is there some way, to save the file silent and force the Word application to hide these messageboxes or just handle them in some way (for example clicking the "OK")?

    I've already tried:

    • DisplayAlerts = false;
    • Permission.RemoveAllPermission();
    • Updating all fields.
    • Unlinking the fields (Doesn't work because of the permission)
    • Setting the CompatibilityMode of the document

    • Edited by DebugGandalf Thursday, November 27, 2014 7:14 AM
    Tuesday, November 25, 2014 3:03 PM

Answers

  • The only reason I can see for that behaviour, then, is if the field is in the document Header/Footer. Although you could lock (or unlink) the fields to prevent them updating, I suspect you really wouldn't want to save a document with its functionality killed that way, besides which, you'd have to unprotect the document before doing so (but you already said "The Word document has a permission (sadly i can't remove it in code)").

    An alternative approach would be to use an ASK field in the document body and use a cross-reference to its bookmark in the document Header/Footer. Such an arrangement won't produce the prompt if you use the code I've already provided.


    Cheers
    Paul Edstein
    [MS MVP - Word]

    Thursday, November 27, 2014 8:46 AM
  • You may be able to cancel any Fillin and Ask dialog boxes by using win32 APIs to
     a. set a timer
     b. periodically look for windows of the required type and closing them

    The following code seems to do that, but (a) I haven't used it in a production environment, (b) you need to be pretty sure that nothing else is creating the specified window class, etc.

    The starting point is to put the code in a module and call startTimer when you start your process - as currently set up, the timer should fire every second and check for the required type of box, so you should not need to know whether or not a document has FILLIN or ASK fields. Stop the timer at the end.

    Option Explicit
    
    ' Win32 Timer APIs
    
    Declare Function SetTimer Lib "user32" ( _
      ByVal hwnd As Long, _
      ByVal nIDEvent As Long, _
      ByVal uElapse As Long, _
      ByVal lpTimerFunc As Long) As Long
    
    Declare Function KillTimer Lib "user32" ( _
      ByVal hwnd As Long, _
      ByVal nIDEvent As Long) As Long
    
    ' Win32 Window and Message APIs
    
    Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
      ByVal lpClassName As Any, _
      ByVal lpWindowName As Any) As Long
    
    Declare Function SendMessage Lib "user32" Alias "SendMessageA" ( _
      ByVal hwnd As Long, _
      ByVal Msg As Long, _
      ByVal wParam As Long, _
      lParam As Any) As Long
    
    Declare Function IsWindowEnabled Lib "user32" ( _
      ByVal hwnd As Long) As Long
    
    Declare Function GetWindow Lib "user32" ( _
      ByVal hwnd As Long, _
      ByVal uCmd As Long) As Long
    
    Const WM_CLOSE = &H10
    Const lngInterval As Long = 1000 ' Milliseconds
    Const strFillinAskClass As String = "bosa_sdm_msword"
    
    Dim lngTimerID As Long
    
    Sub TimerProc( _
      ByVal hwnd As Long, _
      ByVal uMsg As Long, _
      ByVal idEvent As Long, _
      ByVal dwTime As Long)
    
    ' After the timer is started,
    ' this sub is called each time the timer fires.
    ' So give it a list of things to do
    Call CloseFillinAsk
    Debug.Print Now
    
    End Sub
    
    Sub startTimer()
    lngTimerID = SetTimer(0, 0, lngInterval, AddressOf TimerProc)
    If lngTimerID = 0 Then
      Debug.Print "Could not start the timer."
    End If
    End Sub
    
    Sub stopTimer()
    lngTimerID = KillTimer(0, lngTimerID)
    If lngTimerID = 0 Then
      Debug.Print "Could not stop the timer."
    End If
    End Sub
            
    Sub CloseFillinAsk()
    Dim wndFillinAsk As Long
        
    DoEvents
    ' this will attempt to close *any* window of this type,
    ' i.e. we do not attempt to check the thread/process/instance etc.
    ' so beware...
    wndFillinAsk = FindWindow(strFillinAskClass, vbNullString)
    If wndFillinAsk <> 0 Then
      ' Necessary?
      If IsWindowEnabled(wndFillinAsk) <> 0 Then
        SendMessage wndFillinAsk, WM_CLOSE, 0, 0
      End If
    End If
    End Sub
    As for whether you can remove the fields some other way, it may depend on whether IRM and/or encryption have been used or not. If it has not, then one approach may be to export the document using the Flat OPC .xml format, remove all the XML permStart and permEnd  elements, and re-open the document. I can't check what happens if IRM has been used, but I assume such stuff is then encrypted and that that approach would not work.

    Peter Jamieson


    Saturday, November 29, 2014 11:23 AM
  • The field:
    {FILLIN [myPetName] \* MERGEFORMAT}
    is NOT a MERGEFIELD - it's a FILLIN field. Updating fields in a document containing FILLIN fields will cause them to prompt you for data entry. Code like:

    ActiveDocument.SaveAs2 FileName:="C:\Users\" & Environ("UserName") & "\Documents\TestPDF.PDF", Fileformat:=wdFormatPDF

    will not cause a FILLIN field to prompt unless you also have Word configured to 'update fields before printing'. The simple solution, therefore, is to use code like:

    Dim bUpdt As Boolean
    bUpdt = Application.Options.UpdateFieldsAtPrint
    Application.Options.UpdateFieldsAtPrint = False
    ActiveDocument.SaveAs2 FileName:="C:\Users\" & Environ("UserName") & "\Documents\TestPDF.PDF", Fileformat:=wdFormatPDF
    Application.Options.UpdateFieldsAtPrint = bUpdt


    Cheers
    Paul Edstein
    [MS MVP - Word]

    Wednesday, November 26, 2014 2:07 AM

All replies

  • The field:
    {FILLIN [myPetName] \* MERGEFORMAT}
    is NOT a MERGEFIELD - it's a FILLIN field. Updating fields in a document containing FILLIN fields will cause them to prompt you for data entry. Code like:

    ActiveDocument.SaveAs2 FileName:="C:\Users\" & Environ("UserName") & "\Documents\TestPDF.PDF", Fileformat:=wdFormatPDF

    will not cause a FILLIN field to prompt unless you also have Word configured to 'update fields before printing'. The simple solution, therefore, is to use code like:

    Dim bUpdt As Boolean
    bUpdt = Application.Options.UpdateFieldsAtPrint
    Application.Options.UpdateFieldsAtPrint = False
    ActiveDocument.SaveAs2 FileName:="C:\Users\" & Environ("UserName") & "\Documents\TestPDF.PDF", Fileformat:=wdFormatPDF
    Application.Options.UpdateFieldsAtPrint = bUpdt


    Cheers
    Paul Edstein
    [MS MVP - Word]

    Wednesday, November 26, 2014 2:07 AM
  • Hello Paul and thank you for the quick reply!

    I've tried you code but unfortunately the Option UpdateFieldsAtPrint is already False.
    I've also tried the UpdateLinksAtPrint to False but that doesn't help either.
    The boxes are still there.

    Code:

    Dim upPrints as boolean
    'Dim upLinks as boolean

    'upLinks = Application.Options.UpdateLinksAtPrint
    upPrints = Application.Options.UpdateFieldsAtPrint

    Application.Options.UpdateFieldsAtPrint = False
    'Application.Options.UpdateLinksAtPrint = False

    Application.ActiveDocument.SaveAs2 FileName:="SomePath\test.PDF", Fileformat:=wdFormatPDF

    Application.Options.UpdateFieldsAtPrint = upPrints
    Application.Options.UpdateLinksAtPrint= upLinks

    Is there something else I have to do?
    I am using Word 2013 and Word 2010 on a different machine.

    Thursday, November 27, 2014 7:34 AM
  • The only reason I can see for that behaviour, then, is if the field is in the document Header/Footer. Although you could lock (or unlink) the fields to prevent them updating, I suspect you really wouldn't want to save a document with its functionality killed that way, besides which, you'd have to unprotect the document before doing so (but you already said "The Word document has a permission (sadly i can't remove it in code)").

    An alternative approach would be to use an ASK field in the document body and use a cross-reference to its bookmark in the document Header/Footer. Such an arrangement won't produce the prompt if you use the code I've already provided.


    Cheers
    Paul Edstein
    [MS MVP - Word]

    Thursday, November 27, 2014 8:46 AM
  • Thank you again for the quick reply!

    I have just checked the document and, yes, the field is in the Header/Footer area!
    But now I have no idea how to solve this problem.

    As you said, I can't unlink or lock the fields because the application throws immediately the exception, that the field is in a protected area of the document.

    Is there a different way to remove the permission on the Header/Footer area than calling these functions?

    Application.ActiveDocument.Permission.RemoveAll

    Application.ActiveDocument.Protect(wdNoProtection)

    (The second throws an exception)

    Maybe that's my mistake... .
    One further point: Seems like the document is opening in a View Mode... does it not have to be in the Window.Editing mode?

    At least, I just want to generate a PDF file out of the document and don't save it back again.


    • Edited by DebugGandalf Thursday, November 27, 2014 9:31 AM
    Thursday, November 27, 2014 9:30 AM
  • I have just checked the document and, yes, the field is in the Header/Footer area!
    But now I have no idea how to solve this problem.


    The final paragraph in my last reply already tells you how you can do that! If the document is modified that way, it will no longer produce the unwanted prompt. Anything else requires unprotecting the document, about which you've already said "i can't remove it in code."

    Unless you're allowed to remove the protection in code (for which you'll also need the password if it has one), you'll have to either modify the document or put up with the prompt.


    Cheers
    Paul Edstein
    [MS MVP - Word]

    Thursday, November 27, 2014 9:54 AM
  • You may be able to cancel any Fillin and Ask dialog boxes by using win32 APIs to
     a. set a timer
     b. periodically look for windows of the required type and closing them

    The following code seems to do that, but (a) I haven't used it in a production environment, (b) you need to be pretty sure that nothing else is creating the specified window class, etc.

    The starting point is to put the code in a module and call startTimer when you start your process - as currently set up, the timer should fire every second and check for the required type of box, so you should not need to know whether or not a document has FILLIN or ASK fields. Stop the timer at the end.

    Option Explicit
    
    ' Win32 Timer APIs
    
    Declare Function SetTimer Lib "user32" ( _
      ByVal hwnd As Long, _
      ByVal nIDEvent As Long, _
      ByVal uElapse As Long, _
      ByVal lpTimerFunc As Long) As Long
    
    Declare Function KillTimer Lib "user32" ( _
      ByVal hwnd As Long, _
      ByVal nIDEvent As Long) As Long
    
    ' Win32 Window and Message APIs
    
    Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
      ByVal lpClassName As Any, _
      ByVal lpWindowName As Any) As Long
    
    Declare Function SendMessage Lib "user32" Alias "SendMessageA" ( _
      ByVal hwnd As Long, _
      ByVal Msg As Long, _
      ByVal wParam As Long, _
      lParam As Any) As Long
    
    Declare Function IsWindowEnabled Lib "user32" ( _
      ByVal hwnd As Long) As Long
    
    Declare Function GetWindow Lib "user32" ( _
      ByVal hwnd As Long, _
      ByVal uCmd As Long) As Long
    
    Const WM_CLOSE = &H10
    Const lngInterval As Long = 1000 ' Milliseconds
    Const strFillinAskClass As String = "bosa_sdm_msword"
    
    Dim lngTimerID As Long
    
    Sub TimerProc( _
      ByVal hwnd As Long, _
      ByVal uMsg As Long, _
      ByVal idEvent As Long, _
      ByVal dwTime As Long)
    
    ' After the timer is started,
    ' this sub is called each time the timer fires.
    ' So give it a list of things to do
    Call CloseFillinAsk
    Debug.Print Now
    
    End Sub
    
    Sub startTimer()
    lngTimerID = SetTimer(0, 0, lngInterval, AddressOf TimerProc)
    If lngTimerID = 0 Then
      Debug.Print "Could not start the timer."
    End If
    End Sub
    
    Sub stopTimer()
    lngTimerID = KillTimer(0, lngTimerID)
    If lngTimerID = 0 Then
      Debug.Print "Could not stop the timer."
    End If
    End Sub
            
    Sub CloseFillinAsk()
    Dim wndFillinAsk As Long
        
    DoEvents
    ' this will attempt to close *any* window of this type,
    ' i.e. we do not attempt to check the thread/process/instance etc.
    ' so beware...
    wndFillinAsk = FindWindow(strFillinAskClass, vbNullString)
    If wndFillinAsk <> 0 Then
      ' Necessary?
      If IsWindowEnabled(wndFillinAsk) <> 0 Then
        SendMessage wndFillinAsk, WM_CLOSE, 0, 0
      End If
    End If
    End Sub
    As for whether you can remove the fields some other way, it may depend on whether IRM and/or encryption have been used or not. If it has not, then one approach may be to export the document using the Flat OPC .xml format, remove all the XML permStart and permEnd  elements, and re-open the document. I can't check what happens if IRM has been used, but I assume such stuff is then encrypted and that that approach would not work.

    Peter Jamieson


    Saturday, November 29, 2014 11:23 AM