Run PowerShell Script within Windows 10 service to convert docx to pdf - powershell

I have a powershell script to convert docx to pdf. when running in a user session on windows 10 it runs without problems. However, I need to run it within a windows service.
$docx_filename = 'C:\temp\doc2pdf.docx'
$pdf_filename = 'C:\temp\doc2pdf1.pdf'
$word_app = New-Object -ComObject Word.Application
$document = $word_app.Documents.Open($docx_filename)
$document.SaveAs([ref] $pdf_filename, [ref] 17)
$document.Close()
$word_app.Quit()
The script is called with the following command
powershell.exe -executionpolicy Bypass -NoProfile -NoLogo -NonInteractive -WindowStyle Hidden -File -File "docx2pdf.ps1" -docxFilename "docx2pdf.docx" -pdfFilename "docx2pdf.pdf"
With PSSet-Debug -Trace 2 the following is logged
DEBUG: 14+ >>>> $word_app = New-Object -ComObject Word.Application
DEBUG: ! SET $word_app = 'Microsoft.Office.Interop.Word.ApplicationClass'.
DEBUG: 16+ >>>> $document = $word_app.Documents.Open($docxFilename)
How to get this to work within Windows 10 service?
thanks

You have to create a batch file which can trigger a powershell script like this:
#echo off
Powershell.exe set-executionpolicy remotesigned -File ScriptPath
Save it as "startps.bat"
Then you can use the sc command to create a windows service by mentioning this batch file path like this:
Sc create PSScriptService Displayname= "What you like" binpath= "Startps.bat" start= auto

You must set an account on the service's Log On tab. Be sure to specify an account where you can log in interactively and run the Powershell script successfully (i.e. where you have installed Word).
But even with that change, you may still run into trouble because Office is not entirely suitable for operation in a Windows Service. Some things work, but some things don't. Try it and let us know how you get on.

Related

Run Powershell Encoded Command, single .vbs file, no third-party tools [duplicate]

How is it possible to run a PowerShell script without displaying a window or any other sign to the user?
In other words, the script should run quietly in the background without any sign to the user.
Extra credit for an answer that does not use third party components :)
You can either run it like this (but this shows a window for a while):
PowerShell.exe -WindowStyle hidden { your script.. }
Or you use a helper file I created to avoid the window called PsRun.exe that does exactly that. You can download the source and exe file from Run scheduled tasks with WinForm GUI in PowerShell. I use it for scheduled tasks.
Edited: as Marco noted this -WindowStyle parameter is available only for V2 and above.
I was having this same issue. I found out if you go to the Task in Task Scheduler that is running the powershell.exe script, you can click "Run whether user is logged on or not" and that will never show the powershell window when the task runs.
You can use the PowerShell Community Extensions and do this:
start-process PowerShell.exe -arg $pwd\foo.ps1 -WindowStyle Hidden
You can also do this with VBScript: http://blog.sapien.com/index.php/2006/12/26/more-fun-with-scheduled-powershell/
Schedule Hidden PowerShell Tasks (Internet Archive)
More fun with scheduled PowerShell (Internet Archive)
(Via this forum thread.)
The answer with -WindowStyle Hidden is great but the windows will still flash.
I've never seen a window flash when calling it via cmd /c start /min "".
Your machine or setup may differ but it works well for me.
1. Call a file
cmd /c start /min "" powershell -WindowStyle Hidden -ExecutionPolicy Bypass -File "C:\Users\username\Desktop\test.ps1"
2. Call a file with arguments
cmd /c start /min "" powershell -WindowStyle Hidden -ExecutionPolicy Bypass -Command ". 'C:\Users\username\Desktop\test me.ps1' -Arg1 'Hello' -Arg2 'World'"ps1'; -Arg1 'Hello' -Arg2 ' World'"
Powershell content for 2. Call a file with arguments is:
Param
(
[Parameter(Mandatory = $true, HelpMessage = 'The 1st test string parameter.')]
[String]$Arg1,
[Parameter(Mandatory = $true, HelpMessage = 'The 2nd test string parameter.')]
[String]$Arg2
)
Write-Host $Arg1
Write-Host $Arg2
3. Call a file with a function and arguments
cmd /c start /min "" powershell -WindowStyle Hidden -ExecutionPolicy Bypass -Command ". 'C:\Users\username\Desktop\test me.ps1'; Get-Test -stringTest 'Hello World'"
Powershell content for 3. Call a file with a function and arguments is:
function Get-Test() {
[cmdletbinding()]
Param
(
[Parameter(Mandatory = $true, HelpMessage = 'The test string.')]
[String]$stringTest
)
Write-Host $stringTest
return
}
In case you need to run this in Task Scheduler then call %comspec% as the Program/Script and then code for calling the file above as the argument.
Note: All examples work when the PS1 file has spaces in its path.
Here's an approach that that doesn't require command line args or a separate launcher. It's not completely invisible because a window does show momentarily at startup. But it then quickly vanishes. Where that's OK, this is, I think, the easiest approach if you want to launch your script by double-clicking in explorer, or via a Start menu shortcut (including, of course the Startup submenu). And I like that it's part of the code of the script itself, not something external.
Put this at the front of your script:
$t = '[DllImport("user32.dll")] public static extern bool ShowWindow(int handle, int state);'
add-type -name win -member $t -namespace native
[native.win]::ShowWindow(([System.Diagnostics.Process]::GetCurrentProcess() | Get-Process).MainWindowHandle, 0)
Here's a one-liner:
mshta vbscript:Execute("CreateObject(""Wscript.Shell"").Run ""powershell -NoLogo -Command """"& 'C:\Example Path That Has Spaces\My Script.ps1'"""""", 0 : window.close")
Although it's possible for this to flash a window very briefly, that should be a rare occurrence.
ps1 hidden from the Task Scheduler and shortcut too
mshta vbscript:Execute("CreateObject(""WScript.Shell"").Run ""powershell -ExecutionPolicy Bypass & 'C:\PATH\NAME.ps1'"", 0:close")
I think that the best way to hide the console screen of the PowerShell when your are running a background scripts is this code ("Bluecakes" answer).
I add this code in the beginning of all my PowerShell scripts that I need to run in background.
# .Net methods for hiding/showing the console in the background
Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'
function Hide-Console
{
$consolePtr = [Console.Window]::GetConsoleWindow()
#0 hide
[Console.Window]::ShowWindow($consolePtr, 0)
}
Hide-Console
If this answer was help you, please vote to "Bluecakes" in his answer in this post.
I was having this problem when running from c#, on Windows 7, the "Interactive Services Detection" service was popping up when running a hidden powershell window as the SYSTEM account.
Using the "CreateNoWindow" parameter prevented the ISD service popping up it's warning.
process.StartInfo = new ProcessStartInfo("powershell.exe",
String.Format(#" -NoProfile -ExecutionPolicy unrestricted -encodedCommand ""{0}""",encodedCommand))
{
WorkingDirectory = executablePath,
UseShellExecute = false,
CreateNoWindow = true
};
Here's a fun demo of controlling the various states of the console, including minimize and hidden.
Add-Type -Name ConsoleUtils -Namespace WPIA -MemberDefinition #'
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'#
$ConsoleMode = #{
HIDDEN = 0;
NORMAL = 1;
MINIMIZED = 2;
MAXIMIZED = 3;
SHOW = 5
RESTORE = 9
}
$hWnd = [WPIA.ConsoleUtils]::GetConsoleWindow()
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.MAXIMIZED)
"maximized $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.NORMAL)
"normal $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.MINIMIZED)
"minimized $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.RESTORE)
"restore $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.HIDDEN)
"hidden $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.SHOW)
"show $a"
When you scheduled task, just select "Run whether user is logged on or not" under the "General" tab.
Alternate way is to let the task run as another user.
Create a shortcut that calls the PowerShell script and set the Run option to Minimized. This will prevent a window from flashing although you will still get a momentary blip of the script running on the Task Bar.
For easy command line usage, there is a simple wrapper app:
https://github.com/stax76/run-hidden
Example command line:
run-hidden powershell -command calc.exe
I got really tired of going through answers only to find it did not work as expected.
Solution
Make a vbs script to run a hidden batch file which launches the powershell script. Seems silly to make 3 files for this task but atleast the total size is less than 2KB and it runs perfect from tasker or manually (you dont see anything).
scriptName.vbs
Set WinScriptHost = CreateObject("WScript.Shell")
WinScriptHost.Run Chr(34) & "C:\Users\leathan\Documents\scriptName.bat" & Chr(34), 0
Set WinScriptHost = Nothing
scriptName.bat
powershell.exe -ExecutionPolicy Bypass C:\Users\leathan\Documents\scriptName.ps1
scriptName.ps1
Your magical code here.
I have created a small tool passing the call to any console tool you want to start windowless through to the original file:
https://github.com/Vittel/RunHiddenConsole
After compiling just rename the executable to "<targetExecutableName>w.exe" (append a "w"), and put it next to the original executable.
You can then call e.G. powershellw.exe with the usual parameters and it wont pop up a window.
If someone has an idea how to check whether the created process is waiting for input, ill be happy to include your solution :)
Wait until Powershell is executed and get the result in vbs
This is an improved version of the Omegastripes code Hide command prompt window when using Exec()
Splits the confused responses from cmd.exe into an array instead of putting everything into a hard-to-parse string.
In addition, if an error occurs during the execution of cmd.exe, a message about its occurrence will become known in vbs.
Option Explicit
Sub RunCScriptHidden()
strSignature = Left(CreateObject("Scriptlet.TypeLib").Guid, 38)
GetObject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}").putProperty strSignature, Me
objShell.Run ("""" & Replace(LCase(WScript.FullName), "wscript", "cscript") & """ //nologo """ & WScript.ScriptFullName & """ ""/signature:" & strSignature & """"), 0, True
End Sub
Sub WshShellExecCmd()
For Each objWnd In CreateObject("Shell.Application").Windows
If IsObject(objWnd.getProperty(WScript.Arguments.Named("signature"))) Then Exit For
Next
Set objParent = objWnd.getProperty(WScript.Arguments.Named("signature"))
objWnd.Quit
'objParent.strRes = CreateObject("WScript.Shell").Exec(objParent.strCmd).StdOut.ReadAll() 'simple solution
Set exec = CreateObject("WScript.Shell").Exec(objParent.strCmd)
While exec.Status = WshRunning
WScript.Sleep 20
Wend
Dim err
If exec.ExitCode = WshFailed Then
err = exec.StdErr.ReadAll
Else
output = Split(exec.StdOut.ReadAll,Chr(10))
End If
If err="" Then
objParent.strRes = output(UBound(output)-1) 'array of results, you can: output(0) Join(output) - Usually needed is the last
Else
objParent.wowError = err
End If
WScript.Quit
End Sub
Const WshRunning = 0,WshFailed = 1:Dim i,name,objShell
Dim strCmd, strRes, objWnd, objParent, strSignature, wowError, output, exec
Set objShell = WScript.CreateObject("WScript.Shell"):wowError=False
strCmd = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass Write-Host Hello-World."
If WScript.Arguments.Named.Exists("signature") Then WshShellExecCmd
RunCScriptHidden
If wowError=False Then
objShell.popup(strRes)
Else
objShell.popup("Error=" & wowError)
End If
powershell.exe -windowstyle hidden -noexit -ExecutionPolicy Bypass -File <path_to_file>
then set the run: Minimized
should work as expected without added code for hidden window flash
just slightly more delayed execution.
Here is a working solution in windows 10 that does not include any third-party components. It works by wrapping the PowerShell script into VBScript.
Step 1: we need to change some windows features to allow VBScript to run PowerShell and to open .ps1 files with PowerShell by default.
-go to run and type "regedit". Click on ok and then allow it to run.
-paste this path "HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell" and press enter.
-now open the entry on the right and change the value to 0.
-open PowerShell as an administrator and type "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned", press enter and confirm the change with "y" and then enter.
Step 2: Now we can start wrapping our script.
-save your Powershell script as a .ps1 file.
-create a new text document and paste this script.
Dim objShell,objFSO,objFile
Set objShell=CreateObject("WScript.Shell")
Set objFSO=CreateObject("Scripting.FileSystemObject")
'enter the path for your PowerShell Script
strPath="c:\your script path\script.ps1"
'verify file exists
If objFSO.FileExists(strPath) Then
'return short path name
set objFile=objFSO.GetFile(strPath)
strCMD="powershell -nologo -command " & Chr(34) & "&{" &_
objFile.ShortPath & "}" & Chr(34)
'Uncomment next line for debugging
'WScript.Echo strCMD
'use 0 to hide window
objShell.Run strCMD,0
Else
'Display error message
WScript.Echo "Failed to find " & strPath
WScript.Quit
End If
-now change the file path to the location of your .ps1 script and save the text document.
-Now right-click on the file and go to rename. Then change the filename extension to .vbs and press enter and then click ok.
DONE! If you now open the .vbs you should see no console window while your script is running in the background.
c="powershell.exe -ExecutionPolicy Bypass (New-Object -ComObject Wscript.Shell).popup('Hello World.',0,'ОК',64)"
s=Left(CreateObject("Scriptlet.TypeLib").Guid,38)
GetObject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}").putProperty s,Me
WScript.CreateObject("WScript.Shell").Run c,0,false
Out of all the solutions I've tried, this is by far the best and easiest to set up. Download hiddenw.exe from here - https://github.com/SeidChr/RunHiddenConsole/releases
Let's say you want to run Powershell v5 consoleless. Simply rename hiddenw.exe to powershellw.exe. If you want to do this for cmd, then rename to cmdw.exe. If you want to do it for Powershell v7 (pwsh), then rename to pwshw.exe. You can create multiple copies of hiddenw.exe and just rename to the actual process with the letter w at the end. Then, simply add the process to your system environmental PATH, so you can call it from anywhere. Or just copy to C:\Windows. Then, just call it, like this:
powershellw .\example.ps1
I found compiling to exe was the easiest way to achieve this. Theres a number of ways to compile a script, but you can try ISE Steroids
Open "Windows PowerShell ISE", install and run ISESteroids:
Install-Module -Name "ISESteroids" -Scope CurrentUser -Repository PSGallery -Force
Start-Steroids
Then go to Tools->Turn code into EXE, select 'Hide Console Window', and then create the application.
You can run this directly from task scheduler without the need for wrappers or 3rd party apps.
What I do is transform the .ps1 file into an invisible .exe file using an awesome app called Ps1 To Exe which you can download here : https://www.majorgeeks.com/files/details/ps1_to_exe.html
Maybe this helps (although I hope after 12 years you have found a suitable solution... 🙂)
In other words, the script should run quietly in the background without any sign to the user.
Extra credit for an answer that does not use third party components :)
I found a way to do this by compiling a PowerShell script to a Windows executable. Third party modules are required to build the executable but not to run it. My end goal was to compile a one line PowerShell script that ejects a DVD on my system:
(New-Object -com "WMPlayer.OCX.7").cdromcollection.item(0).eject()
My target system is running Windows 7. The specific WMF update needed varies based on Windows version:
Download and install the WMF 5.1 package
The required PowerShell modules should be applicable to any Windows version. Here are the exact commands I used to install the necessary modules and compile the exe. You'll need to tweak the drive, directory and filename details for your system:
mkdir i:\tmp\wmf
cd i:\tmp\wmf
pkunzip ..\Win7AndW2K8R2-KB3191566-x64.zip
c:\windows\system32\windowspowershell\v1.0\powershell.exe
Set-ExecutionPolicy RemoteSigned
.\Install-WMF5.1.ps1
<click> "Restart Now"
c:\Windows\System32\WindowsPowerShell\v1.0\powershell -version 3.0
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
Install-Module -Name ps2exe -RequiredVersion 1.0.5
ps2exe i:\utils\scripts\ejectDVD.ps1 -noConsole

Powershell Script won't run correctly in Task Scheduler

I recently created some PowerShell code that runs perfectly fine on its own or via a command prompt (or a .bat file). Hell, it runs fine as a .cmd file too. When I create a task scheduler task for it to run automatically, the result claims that the job runs 'successfully' but there isn't any output in the folder destination.
The whole goal of the script is to look at a particular subfolder within outlook and find files with a particular name. Then it outputs the latest related attachments to a specific destination.
PowerShell Script
$olFolderInbox = 6
$limit = (Get-Date).AddDays(-1)
$filepath = "C:\Scripts\attachment Project\Spreadsheets"
$ol = New-Object -com outlook.application;
$ns = $ol.GetNamespace("MAPI");
$acct = $ns.GetDefaultFolder($olFolderInbox)
$targetfolder = $acct.Folders | where-object { $_.name -eq "Training" }
$targetfolder.Items | foreach {
if ($_.ReceivedTime -ge $limit) {
$_.attachments |
foreach {
Write-Host "attached file: ",$_.filename
If ($_.filename -match 'SessionCompletions' -or $_.filename -match 'Certifications'){
$_.saveasfile((Join-Path $filepath $_.FileName))
$ol.quit()
}
}
}
}
I've tried all kinds of things to get the Task within Task Scheduler to work and nothing so far seems to help. To start I tried using a basic bat file to run the task. Mind you, this basic format has worked with many other PowerShell related tasks that I set up to run via a bat file.
Basic Bat Script
powershell -file extract-spreadsheets.ps1
Now sometimes I've needed to include an -executionpolicy argument as well but it sadly didn't work in this case.
A little more complex Script
Powershell.exe -executionpolicy RemoteSigned -File "C:\Scripts\attachment Project\extract spreadsheets.ps1"
Note: I made sure the get-executionpolicy on the computer was / is set to RemoteSigned. I've also tried Unrestricted as well.
I've also tried even more complex / different approaches such as . . .
Even more Complex of a Script
#ECHO OFF
SET ThisScriptsDirectory=C:\Scripts\David attachment Project\
SET PowerShellScriptPath=%ThisScriptsDirectory%extract-spreadsheets.ps1
PowerShell -NoProfile -ExecutionPolicy RemoteSigned -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy RemoteSigned -File ""%PowerShellScriptPath%""' -Verb RunAs}";
And I've even tried getting a .cmd file going and trying to use that within Task Scheduler:
##:: This prolog allows a PowerShell script to be embedded in a .CMD file.
##:: Any non-PowerShell content must be preceeded by "##"
##setlocal
##set POWERSHELL_BAT_ARGS=%*
##if defined POWERSHELL_BAT_ARGS set POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%
##PowerShell -Command Invoke-Expression $('$args=#(^&{$args} %POWERSHELL_BAT_ARGS%);'+[String]::Join(';',$((Get-Content '%~f0') -notmatch '^^##'))) & goto :EOF
$olFolderInbox = 6
$limit = (Get-Date).AddDays(-1)
$filepath = "C:\Scripts\attachment Project\Spreadsheets"
$ol = New-Object -com outlook.application;
$ns = $ol.GetNamespace("MAPI");
$acct = $ns.GetDefaultFolder($olFolderInbox)
$targetfolder = $acct.Folders | where-object { $_.name -eq "Training" }
$targetfolder.Items | foreach {
if ($_.ReceivedTime -ge $limit) {
$_.attachments |
foreach {
Write-Host "attached file: ",$_.filename
If ($_.filename -match 'SessionCompletions' -or $_.filename -match 'Certifications'){
$_.saveasfile((Join-Path $filepath $_.FileName))
$ol.quit()
}
}
}
}
In terms of Task Scheduler, I've tried a multitude of things. To start . . .
For Program / script, I tried various relative and explicit destinations / paths for bat, cmd and PowerShell.
Some examples:
Powershell.exe (again I've also done the full path as well)
C:\Scripts\attachment Project\Training_Reports.bat
Training_Reports.bat
C:\Scripts\attachment Project\extract-spreadsheets.cmd
Note: I've added the above Programs / Actions with and without quotes.
Again, the more frustrating thing is the basic format of Training_Reports.bat with the start in (optional) field of:
C:\Scripts\attachment Project or C:\Scripts\attachment Project\
Has worked with every other script / task I have set up in this format!
I've tried numerous arguments for Execution Policy such as RemoteSigned, Bypass and Unrestricted. I've Added other arguments like: -NoProfile, -noninteractive, -File, -command, -NoLogo etc etc.
Examples of what the Arguments Field has looked like within Task Scheduler:
-ExecutionPolicy Bypass -File "C:\Scripts\attachment Project\extract-spreadsheets.ps1"
-NoLogo -NonInteractive -ExecutionPolicy RemoteSigned -File "C:\Scripts\attachment Project\extract-spreadsheets.ps1"
-NoProfile -ExecutionPolicy Unrestricted -Command "C:\Scripts\attachment Project\extract-spreadsheets.ps1"
I tried using various accounts to 'run the task'.
my domain account (which my account is a global domain admin and has
full permissions)
local Admin account
another Domain Admin Account
I've tried even SYSTEM
None of the accounts above have changed the outcome. Other settings I've looked at that people claim could mess with Tasks.
Turned off 'Start the task only if the computer is on AC power'
Ran the task whether user is logged on or not
Also ran it as when user is logged on
Set it for 'Highest privileges'
I tried running the task in 'configure for' drop down menu for
Windows 10 and Windows 7 / Server 2008.
Other things I have tried:
The initial PowerShell Script had mapped drive destinations. I changed the code to use C drive locations.
Explicitly giving that user account full control over the
directories involved.
Changing ownership of the directories involved to the same user.
Changing the security permissions on the ps1, cmd, bat, outlook.exe files to always Run as Administrator.
Tried running the script with Outlook running and not running.
Note: running it manually works regardless if Outlook is running.
Why is this task within Task Scheduler not outputting correctly?

PowerShell Install MSI with Property File

I looking to replace our old command files with PowerShell file to install msi on a number of servers with a view of automating installations remotely.
What I am trying to do, and this is how can you get PowerShell to install an msi where the installation relies on a details coming from a property file as one argument along with logging out the installation as a log file and setting a new user name and password?
Currently our old cmd file looks a bit like this:
msiexec /I mymsi.msu /quiet /lv mymsi.log USERNAME=AName PASSWORD=APassword CONFIG="C:\Some.properties.env"
What I want to do is recreate this but in PowerShell, but I have not been able to find an example that works.
I'd suggest using Start-Process and Splatting to make it readable and functional:
$startProcessParams = #{
'FilePath' = "$Env:SystemRoot\System32\MSIEXEC.exe"
'ArgumentList' = #(
'/i', 'mymsi.msu'
'/quiet'
'/lv', 'mymsi.log'
'USERNAME=AName', 'PASSWORD=APassword', 'CONFIG="C:\Some.properties.env"'
)
'WorkingDirectory' = $PSScriptRoot
'Wait' = $true
'PassThru' = $true
}
$process = Start-Process #startProcessParams
return $process.ExitCode
Be sure to set WorkingDirectory to where your .msu file is living.
about_Splatting
Start-Process

Play video with powershell on Windows 10

I tried to use the code of this post.
When I use this code directly on PowerShell terminal it run correctly.
Add-Type -AssemblyName presentationCore
$filepath = "C:\Temp\test\Wildlife.wmv"
$wmplayer = New-Object System.Windows.Media.MediaPlayer
$wmplayer.Open($filepath)
Start-Sleep 2
$duration = $wmplayer.NaturalDuration.TimeSpan.Seconds
$wmplayer.Close()
start playing
$proc = Start-process -FilePath wmplayer.exe -ArgumentList $filepath -PassThru
But when I run the code on .bat file, a cmd window appears and disappears in a few seconds and without further action.
If I run the .bat file on CMD, this errors appear:
enter image description here
The code inserted in .bat file is:
Add-Type -AssemblyName presentationCore
$filepath = [uri] "C:\Users\??????\Desktop\small.mp4"
$wmplayer = New-Object System.Windows.Media.MediaPlayer
$wmplayer.Open($filepath)
Start-Sleep 2 # This allows the $wmplayer time to load the audio file
$duration = $wmplayer.NaturalDuration.TimeSpan.TotalSeconds
$wmplayer.Play()
Start-Sleep $duration
$wmplayer.Stop()
$wmplayer.Close()
I would be most thankful if you could help me solving this problem.
Thanks.
You are attempting to run PowerShell commands within a .bat file (as a result the PowerShell engine isn't being used to execute the code so the commands fail).
You need to save the script as a .ps1 file, then execute it from the command-line either via it's full path name or by changing to the directory where the script exists and entering:
.\scriptname.ps1
Where scriptname is the name you saved the file at.
If you want to execute the script via a .bat file, you still need to save it as a .ps1 and then create a .bat file with the following content:
Powershell.exe -File C:\path\to\my\script\myscript.ps1
Obviously correcting the path accordingly. Note there is no advantage to running the script this way, but one reason you might use a .bat file is if you needed to change the execution policy to allow script execution (I don't think you do in your case) as follows:
Powershell.exe -executionpolicy unrestricted -File C:\path\to\my\script\myscript.ps1

How to run exe with/without elevated privileges from PowerShell

I would like an easy way to run a process with different privileges from the same user without asking or knowing his/her password. A dialog is okay if necessary. I would prefer not to launch a PowerShell sub-process to accomplish this.
Scenario 1:
PowerShell script is running in admin-mode. I want to launch a script or an .exe without admin privileges but on the same user.
Scenario 2:
PowerShell script is running in normal mode. I want to launch a script or an .exe with admin privileges on the same user.
Let's split this into three parts.
First determine if current session is running with admin privileges:
$CurrentID = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$CurrentPrincipal = new-object System.Security.Principal.WindowsPrincipal($CurrentID)
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
# Check to see if session is currently with admin privileges
if ($CurrentPrincipal.IsInRole($adminRole)) {
write-host "Yes we are running elevated."
}else{
write-host "No this is a normal user session."
}
Now, if we are running with or without elevation, you can start a new process with elevated privileges like this:
$newProc = new-object System.Diagnostics.ProcessStartInfo "PowerShell"
# Specify what to run
$newProc.Arguments = "powershell.exe"
# If you set this, process will be elevated
$newProc.Verb = "runas"
[System.Diagnostics.Process]::Start($newProc)
And lastly, if we have elevated privileges, but would like to start a new process without...
I have no idea. Will have to try to find the answer to this, but as it is not a common scenario, I had no luck so far.
EDIT: I have now seen a couple of “solutions” for this scenario. There is no native way to do this in .NET/PowerShell. Some are quite complicated (Calls to some 12 COM objects). This vista-7-uac-how-to-lower-process-privileges is a good reference.
The one that seems most elegant to me, is exploiting a “bug” in explorer.exe.
Just launch you .exe using explorer.exe and the resulting process runs without privilege elevation again.
$newProc = new-object System.Diagnostics.ProcessStartInfo "PowerShell"
# Specify what to run, you need the full path after explorer.exe
$newProc.Arguments = "explorer.exe C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
[System.Diagnostics.Process]::Start($newProc)
EDIT #2: Another way I have just found to start a new non-elevated process from an already elevated environment is to use the runas.exe with the 0x20000 (Basic User) trust level:
C:\> runas /showtrustlevels
The following trust levels are available on your system:
0x20000 (Basic User)
C:\> runas /trustlevel:0x20000 devenv
I use this as first command in all scripts that requires elevated mode, it transfer the script to another elevated process if I forgot to start up as Admin. You have to confirm so it's not suitable for automated tasks
If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
$arguments = "& '" + $myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
Break }