I am trying to use a powershell script to get the wordcount from a number of word files and then output that to a csv file. This works as expected when run from the powershell prompt, and works when called from the cmd prompt directly or from inside a perl script, however the script fails to work when called as a scheduled task.
This is not an ExecutionPolicy issue causing the script to not run at all. The script is running and produces some output, but when launched from task scheduler it is unable to open any word documents.
The relevant powershell code is below:
$folderpath = "C:\some\where\*"
$fileTypes = "*.docx"
$word = New-Object -ComObject word.application
$word.visible = $false
Get-ChildItem -path $folderpath -recurse -include $fileTypes |
foreach-object `
{
$path = ($_.fullname).substring(0,($_.FullName).lastindexOf("."))
try {
$doc = $word.documents.open($_.fullname, $confirmConversion, $readOnly, $addToRecent, $passwordDocument)
} catch {
"FROM CATCH UNABLE TO OPEN $($_.fullname)" >> $wordCountFile
}
if($doc) {
$wordCount = $doc.ComputeStatistics("wdStatisticWords")
"$($_.name), $wordCount" >> $wordCountFile
$doc.close([ref]$false)
} else {
"UNABLE TO OPEN $($_.fullname)" >> $wordCountFile
}
} #end Foreach-Object
$word.Quit()
(Note that alternative methods people cite for getting the word count from word documents do not actually return the correct count as this method does when it works.)
When run as a scheduled task (set to run with highest privileges and as an administrator user) using:
cmd /c start PowerShell.exe -NoLogo -NonInteractive -ExecutionPolicy Bypass -File "C:\path\to\script\CountsInWords.ps1"
or when run with a similar command from a perl script launched by a scheduled task (my normal use case) and in both cases the task is set to run with highest privileges the powershell script does not work correctly.
The catch block is apparently never reached, as the print statement never makes it to the file, however the $doc is always null.
Additionally, when started as a task the script leaves a word process open and using a 100% cpu for 1 thread which will eventually cripple my machine.
To summarise:
run script as human (no matter how many levels of indirection) -> works perfectly
run script from task (as administrator user) -> script runs but cannot access word documents, also unable to stop word process despite always hitting the $word.Quit line.
EDIT: With #TheMadTechnician's advice about office first time startup for a new user requiring some details: On further inspection, looking at the processes in task manager, I don't see the word processes unless I click on "show processes from all users", but then they show up, but have the user listed as me. How can a process be both explicitly listed as me, but count as another user?
Attempting to set $word.visible = $true in the script didn't actually make anything appear when launched from task scheduler, so I don't know either how to verify that it is waiting for input, or how to give it that input as the correct user to make it go away...
You may be able to follow the solution named at the question How to run a Windows 2008 task from the scheduler with "interact with desktop".
Summary: If you have 64-bit Windows: Create %SystemRoot%\SysWOW64\config\systemprofile\Desktop. If you have 32-bit Windows, create %SystemRoot%\system32\config\systemprofile\Desktop.
Related
I'm having an issue with automating part of an application install with powershell.
Part of the install is running a command line tool with parameters and entering credentials to access a database. This is supposed to be done without logging in.
For that purpose I've been using scheduled tasks to start my scripts at system startup.
I've been trying to get this particular step working by using AppActivate and SendWait to send the credentials to the window. This does not work though since there is no interactive window if I use the task scheduler.
This is the current script I've been using. It works if I would use the RunOnce key for example.
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")
powershell.exe add-type -AssemblyName microsoft.VisualBasic
add-type -AssemblyName System.Windows.Forms
$parms = 'create instance'
Start-Process "app.exe" -ArgumentList $parms -WorkingDirectory "C:\app\app.exe"
start-sleep -Milliseconds 1000
[Microsoft.VisualBasic.Interaction]::AppActivate("C:\app\app.exe")
[System.Windows.Forms.SendKeys]::SendWait("username")
start-sleep -Milliseconds 1000
[Microsoft.VisualBasic.Interaction]::AppActivate("C:\app\app.exe")
[System.Windows.Forms.SendKeys]::SendWait("password")
This script is called by another script which is run by the task scheduler.
Right now it fails with:
Exception calling "AppActivate" with "1" argument(s): "Process '{0}' was not found."
and:
Exception calling "SendWait" with "1" argument(s): "Access is denied"
Which is due to not being able to access the window.
Is there any way to send the username and password to the process without requiring an active windows? From what I've seen it doesn't seem to be supported by powershell but I'm still hoping that there is a way. Maybe using Handles? I can not use any applications like AutoHotkey or AutoIt.
Try this,
$proc = Get-process | where {$_.path -eq "C:\app\app.exe"}
[Microsoft.VisualBasic.Interaction]::AppActivate($Proc.id)
When you're in file explorer you can click on File > Open Windows Powershell(or its icon in the Quick Access Toolbar) to start an instance of Powershell in the directory that your file explorer is in. I would like to then automatically run a simple command in this directory and close the Powershell window after it is done.
I have tried adding my command to my Powershell Profile but it executes before the path variable has been set and it runs with $pwd being equal to C:\Users\MyUsername (my home directory) or C:\WINDOWS\system32 (seems to be a race condition of some sort, no idea why it does one or the other). To the best of my understanding this is because the file explorer "open in powershell button" opens powershell and THEN cd's to the directory I was in in file explorer. So when the profile.ps1 is ran it is using the only directories it knows if since the cd call hasn't been made yet. This is similar to running the command start powershell.exe in cmd vs start powershell.exe -command "cd 'C:\wherever'". The former correctly runs my profile command while the latter uses the current directory of cmd and not the C:\wherever.
So, obviously the $pwd variable is being assigned at different times in the case of opening it from cmd and opening it from file explorer. Is there some way to delay the execution of a command in the profile until after the shell has fully loaded? Simply sleeping the script doesn't help.
Alternatively, if anyone knows how to edit the registry so that I can change the behavior of clicking File > Open Windows Powershell (since it must have access to some variable storing the current directory and I assume it calls the Powershell executable with this variable as an argument being cd'd to), that would work too.
Then again I could be incredibly naive about how File > Open Windows Powershell and the Powershell instantiation process works.
Any help is greatly appreciated, thank you!
I figured it out in the most hacky, gross way ever, but without easy access to Windows internals this is the only working method I could find. I set up my powershell profile to make my window title my prompt like so:
function Prompt
{
$host.ui.RawUI.WindowTitle = $(get-location)
“PS> “
}
Then I set up a task in the Task Scheduler that was triggered by powershell reaching its prompt (there are 3 possible hooks, when the console is starting up, when it starts an IPC listening thread, and when the console is ready for input). I used the Event Viewer to do this (I was going to post screenshots but I don't have 10 reputation yet).
Then, I set the action on this task to run the script shown below, which reads from the window title of my first instance of powershell
Start-Sleep -s 1
$A = Get-Process -Name powershell | Where-Object -FilterScript {$_.Id -ne $PID}
$B = $A.MainWindowTitle
& C:\Program` Files\MyProgram\MyProgram.exe "$B"
stop-process -Id $A.Id
stop-process -Id $PID
This whole menagerie of events properly runs my program with the current file explorer directory as an argument (and then closes powershell) when I click the little powershell icon on the quick access toolbar in file explorer.
Found a much cleaner and faster way to do this. All I had to do was set up my profile to look like this, no tasks or second instance of powershell required
function Prompt
{
& C:\Program` Files\MyProgram\MyProgram.exe "$pwd"
stop-process -Id $PID
}
I need some help on this code, i use it into a batch file all day # 3 AM.
The goal is to close Outlook properly to make backup of Outlook PST file.
In a specific context, if some panel are opened like account parameter panel from Outlook, it cannot be able to do the action and the script is looping.
As i execute powershell script from a batch (.bat) like this start /wait PowerShell -NoLogo -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass -Command "& '%~dp0CloseOutlook.ps1'", the powershell script continues to work silently if i close my batch (powershell process continu to run with infinite loop).
In this case, if i go to other applications, he tries to make hotkey (ALT+Y), which could be dangerous.
Is there any way to modify Powershell script to do hotkey (ALT+Y) only in the Outlook application to prevent the script from interacting with other applications?
In order to avoid infinite loops in the background, can we also tell him to do 50 x ALT + Y to exit powershell script ?
This will avoid having a process that runs in a loop.
$isOutlookOpen = Get-Process outlook*
if($isOutlookOpen = $null){
# Outlook is already closed run code here:
}
else {
$isOutlookOpen = Get-Process outlook*
# while loop makes sure all outlook windows are closed before moving on to other code:
while($isOutlookOpen -ne $null){
Get-Process outlook* | ForEach-Object {$_.CloseMainWindow() | Out-Null }
sleep 5
If(($isOutlookOpen = Get-Process outlook*) -ne $null){
Write-Host "Outlook is Open.......Closing Outlook"
$wshell = new-object -com wscript.shell
$wshell.AppActivate("Microsoft Outlook")
$wshell.Sendkeys("%(Y)")
$isOutlookOpen = Get-Process outlook*
}
}
#Outlook has been closed run code here:
}
If you know a better way to improve this code to close Outlook properly, please advise :)
Just to be more precise, i don't want to make Shadow copy with Hobocopy, or Shadowspawn (new name), these tools are not able to handle quote with robocopy command, so there is no way possible to use it if you have space in paths... too bad !
Anyway, backing up Outlook pst files when Outlook is still open, regardless of program being used, is an invitation for problems (to play Russian roulette)
So it seems more secure to try to close Outlook to backup pst file as well.
Many thanks for all.
I am struggling with automating download of a file from website that opens a new tab when download is triggered and closes it right before the prompt to Save/Open/Close comes up in the main window. The script itself works just fine when launched manually until I try it in Task Scheduler. It runs great until it snags on the download. I've included a code to capture the URL of the file that needs to be downloaded and then pass it to Invoke-WebRequest which works fine out of PowerShell ISE but doesn't return any results when launched from Task Scheduler. I've included the counter and test file creation into the code to see if contents of do-while execute at all, and all 10 attempts were recorded. It's the part that searches through active URLs that's not returning results when launched via Task Scheduler.
# >> Keep trying to capture the CSV download link
$i = 1
Do {
$i | Add-Content -Path "C:\userfolder\try.txt"
$urls = (New-Object -ComObject Shell.Application).Windows() |
Where-Object {$_.LocationUrl -like "https://sitename.com/ReportViewer*"} |
Where-Object {$_.LocationUrl}
$reportURL = #($urls)[0].LocationURL
$i ++
}
While ($reportURL -eq $NULL -and $i -le 10)
# >> Send download link to web request and save to file
Invoke-WebRequest -Uri $reportURL -OutFile "C:\userfolder\ProfileList.csv"
I have tried the Wscript.Shell AppActivate and SendKeys('%S') before but the AppActivate wouldn't work probably because the PowerShell console window was hijakcing focus when launched via Task Scheduler. The task is set to execute powershell.exe with Arguments: C:\userfolder\CPdownload.ps1 -RunType $true, - and Start in: C:\userfolder. Run: only when user is logged in and with highest privileges; configured for Windows Server 2012 R2.
I've also tried launching the shell script through batch file from Task Scheduler with exactly the same result.
Thanks in advance for any pointers.
Probably your task needs to be run in interactive mode. By default scheduled tasks are run in Session 0. You can schedule task for interactive mode with /IT parameter. Check the following link for more info
https://msdn.microsoft.com/en-us/library/bb736357(VS.85).aspx
Old post however besides changing the script to interactive mode (this fixes it) you can change the Powershell execution policy to unrestricted (Set-ExecutionPolicy -ExecutionPolicy Unrestricted) if you wish to run the script outside of interactive mode. Just thought I'd post as recently come across this myself.
I am trying to get a local powershell script to trigger a VBS script inside a citrix instance. The events should be this:
Citrix Instance opening Windows Explorer ----> Network Path of script typed into the windows explorer session
I'm using the WfIcaLib.dll (ICOSDK) that came with the Citrix receiver install. Documentation PDF for the Citrix ICOSDK is available here
So this is the code I'm using, which works PERFECTLY in Powershell command line, but when I use the 32-bit ISE, it does nothing other than telling me the DLL has been loaded. I get no errors, but the Citrix Client never actually opens like it does when I run the same exact commands through Powershell command line.
#load Citrix ICA Client SDK
[System.Reflection.Assembly]::LoadFile("C:\Program Files (x86)\Citrix\ICA Client\WfIcaLib.dll")
$ICA = New-Object WFICALib.ICAClientClass
$ICA.Address = "***.***.***.***:****"
$ICA.Application = "Windows ExplorerFED6"
$ICA.Username = "******"
$ICA.Domain = "**"
$ICA.Launch = $true
$ICA.Outputmode = [WfIcaLib.OutputMode]::OutputModeNormal
$ICA.SetProp("Password", "*********")
$ICA.TWIMode=$true
$ICA.Connect()
Any ideas?
EDIT: SOLVED - after reopening under 32-bit ISE and getting code to work, I could not run the .ps1 file since it kept defaulting to 64-bit (even if using Open With on 32-bit powershell version). Running the script via command prompt or 32-bit powershell console both worked.
Using any method suggested by Mike Garuccio worked just fine. I will most likely end up using a Task Scheduler to run the script.
It looks like the problem is a versioning one, which you can deal with using start-job (was originally going to do this with a runspace but that's a lot more code for no real benefit). This will start a job to run your code under 32-bit powershell, it should still place any GUI elements or popups on screen but if you need to get any data out from the script later you'll need to receive the job. code would look something like below.
$SB = {
[System.Reflection.Assembly]::LoadFile("C:\Program Files (x86)\Citrix\ICA Client\WfIcaLib.dll")
$ICA = New-Object WFICALib.ICAClientClass
$ICA.Address = "***.***.***.***:****"
$ICA.Application = "Windows ExplorerFED6"
$ICA.Username = "******"
$ICA.Domain = "**"
$ICA.Launch = $true
$ICA.Outputmode = [WfIcaLib.OutputMode]::OutputModeNormal
$ICA.SetProp("Password", "*********")
$ICA.TWIMode=$true
$ICA.Connect()
}
Start-Job -ScriptBlock $SB -RunAs32
get-job | Receive-Job
Alternatively, you could also use a simple .bat file as a launcher with something like C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -file yourscript.ps1
or if you need everything to be wrapped in powershell and the job does not work you can use the solution Here that basically does the same thing but from within powershell (drop the if statement they use tho, just use the stuff contained inside it like below, with any changes you need to make wrt the profile and interactive settings.)
&"$env:windir\syswow64\windowspowershell\v1.0\powershell.exe" -noninteractive -noprofile -file "C:\Path o\script.ps1" -executionpolicy bypass
hope one of those works for you!