views:

376

answers:

4

My current PowerShell script:

$document = "C:\\test.doc"
$word = new-object -comobject word.application
$word.Visible = $false
$word.DisplayAlerts = "wdAlertsNone"
$word.AutomationSecurity = "msoAutomationSecurityForceDisable"
$doc = $word.Documents.Open($document)
$word.ActivePrinter = "\\http://ptr-server:631\pdf-printer"
$background = $false
$doc.PrintOut([ref]$background)
$doc.close([ref]$false)
$word.quit()

But it results in an alert box The macros in this project are disabled. Please refer to the online help or documentation of the host application to determine how to enable macros.

How can I open the document without it running the AutoOpen macro or displaying any sort of dialog prompt?

Environment Details:

  • Word 2003 SP3
  • Windows Server 2003 R2 - Standard Edition - Service Pack 2
  • Powershell Version 1.0
+3  A: 

The issue you're experencing is documented at KB-886633 (Nevermind that it is talking about Office for Mac - the same applies on PCs):

Additionally, if a macro tries to open a file that contains a macro, the attempt will fail if both of the following conditions are true:

  • The Application.AutomationSecurity property option is set to
    msoAutomationSecurityForceDisable.
  • The attempt to open the file is made through an Office API macro. This includes macros that are written in VBA, XLM in Excel, and WordBasic in Word.

Both bullet points apply to your script.

The only way I know to get around it is to go old-school - with WordBasic - to disable all Auto Macros (AutoOpen, AutoExec, etc.). Insert $word.WordBasic.DisableAutoMacros right before $word.AutomationSecurity = "msoAutomationSecurityForceDisable". Note that there is no equivalant to this routine in VBA.

Otaku
This is not working for me. I still getting that alert box.
grom
@grom - do you have other macros in there besides the `AutoOpen` one? also, is it still giving you the same prompt or something different? Try setting to this instead: `$word.WordBasic.DisableAutoMacros -1` and before you close the document set to: `$word.WordBasic.DisableAutoMacros -0`.
Otaku
When I try to set a value to DisableAutoMacros I get: Property 'DisableAutoMacros' cannot be found on this object
grom
$word.WordBasic | Get-Member does not list anything that looks useful either.
grom
@grom: I've put a comment above. I'm pretty sure we can get this resolved, will just need some more details in your post if you can edit it. Also, you never did mention if you have other macros besides any of the Auto ones.
Otaku
No there is just the AutoOpen one
grom
@grom: Got it. I'll try to replicate this in your exact environment (I have a VM of this environ, just need to dig it up) and come back to soon-ish.
Otaku
btw, the $word.AutomationSecurity = "msoAutomationSecurityForceDisable"turns off the macros. But it is what causes the alert box about macros being disabled from coming up. So if there was just some way to turn off these security warnings it would work.
grom
@grom: yeah, I'm finding that with PS1.0 that `$word.WordBasic.DisableAutoMacros` is causing some issues. I'm checking on another solution right now as well.
Otaku
@grom: turns out that `$word.AutomationSecurity = "msoAutomationSecurityForceDisable"` will turn off the macros, BUT 1) the alert box cannot be dismissed, there is no way and 2) it will turn off any future macros you're running like after you open the document. `$word.WordBasic.DisableAutoMacros` appears to be the only way, i haven't had much luck in powershell 1.0 with it. i'll keep researching.
Otaku
+1  A: 

I do not know if this will work, just an idea: Could you download and use Wordviewer? This does not execute macros, so probably would not show a warning. However, I do not know if it can be called via API.

Frank
+1  A: 

I tried a few different things ...

You're supposed to be able to use $word.WordBasic.DisableAutoMacros(1) but PowerShell is miserable at COM, because you can't (really) cast to interfaces in PowerShell, and so, casting a COM object to the IDispatch interface that you need seems hopeless, and I can't see a way to do this. Brandon (BSonPosh) and I just gave up on this for a networking interface he was trying to use and resorted to Add-Type to embed some C# to make the method call in his case. That would most likely work here too...

What WOULD work (I am sure) is automating clicking on the button. You can use System.Windows.UIAutomation or the PowerShell WASP module to do that pretty simply.

Jaykul
So you wrote C# to call the method through COM, and then called that from powershell?
grom
@grom: That's what I was trying to do for this, haven't quite worked it out as even in COM it requires `InvokeMember`
Otaku
+1  A: 

Turns out this is MUCH easier to do in VB.NET than in C# (which I never could figure out). But all you would need to do is create, say, a console application with a single routine. Here are the instructions:

Code

Imports word = Microsoft.Office.Interop.Word
Module Module1
    Sub Main()
        Dim args() As String = Environment.GetCommandLineArgs
        Dim path = args(1)
        Dim printer = args(2)
        Dim wordApp As word.Application = New word.Application
        wordApp.WordBasic.DisableAutoMacros(1)
        wordApp.Visible = False
        Dim doc As word.Document = wordApp.Documents.Open(path)
        wordApp.ActivePrinter = printer
        Dim background As Object = False
        doc.PrintOut(background)
        doc.Close(False)
        wordApp.WordBasic.DisableAutoMacros(0)
        wordApp.Quit()
    End Sub
End Module

Steps to recreate solution:

  1. Open VS2008 and create a new Console Application in VB.NET.
  2. Set a reference to Microsoft.Office.Interop.Word (version 11)
  3. Delete any code in Module1 and insert the code above.
  4. Save the project and name it "wordprinter". Build the project.
  5. Nav to the Release folder and grab the "wordprinter.exe" and put it anywhere you like. This will be your $wordprinterpath.
  6. Note the paths to your document and printer. This will be your $doc and $printer, respectively.
  7. Enter the following in PS:
        $wordprinterpath = "C:\\path\\wordprinter.exe"
        $doc ="""C:\\Users\\me\\Documents\\Your doc.doc"""
        $printer = "\\http://ptr-server:631\pdf-printer"
        Invoke-Expression "$wordprinterpath $doc $printer" | out-Null

You should be good to go after this. I haven't tested the printing part of this, so that may need some work, but disabling of the auto-macros and opening the doc works.

Otaku
How are you making it available to PowerShell?
grom
@grom: I've updated with instructions above.
Otaku