Powershell: Passing command options forward slash changing to backward - powershell

I am trying to use the invoke (ii) command to open an access database that has command line options. What I would like to have executed is below (yes there is a space in the name of the access database). The database is in the same folder as the Powershell script.
What I want: program name.accdb /cmd Rester
What I get: program name.accdb \cmd Rester
The exact commands I am using are:
$Path_To_EXE = "program name.accdb /cmd Rester"
&ii $Path_To_EXE
I am new to Powershell and have done some searching but can't seem to find an answer. I can create a work around by creating a separate .bat file but that seems like going backwards.
Thoughts?

You should also give a shot to the start-process cmdlet :
$Path_To_EXE = "c:\program.exe"
#Notice the simple quotes ...
$Arguments = #( "name.accdb", '/cmd' , "Rester" )
start-process -FilePath $Path_To_EXE -ArgumentList $Arguments -Wait
I'm not quite sure of the format of the answer you'll get tough ...
for database interaction, I'll rather use JGreenwell's Approach, since the answer that you'll get will be much easier to read/debug ...
Let me know if it works.

If you want to run a VBA script while passing it a parameter with powershell:
$aApp = New-Object -ComObject access.application
$aApp.Application.OpenCurrentDatabase("some program.accdb")
$aApp.Application.Run("VBAScriptName", [ref] "Raster")
First according to Microsoft Support you can use ;; for /cmd from the command line. Second because of the way call quotes and dequotes variables you have to include the /cmd flag separate from the variable (well, its the easiest way). Third, you might consider creating a new com-object to handle running Access with Powershell as it allows for a lot more options (just ask and I can add some examples of this). This being said try:
$Path_To_EXE = "program name.accdb"
&ii $Path_To_EXE ;;Rester #Try ;;"Rester" if it doesn't work.
#if that works then its a problem in Rester
#fyi another way is:
$Path_To_EXE = #("program name.accdb", ";;Rester")
&ii $Path_To_EXE
If you want to use an ActiveX Object Controller to open and perform operations on Access look at this blog from technet <- Read the link there are pitfalls to avoid.
$adOpenStatic = 3
$adLockOptimistic = 3
$objConnection = New-Object -com "ADODB.Connection"
$objRecordSet = New-Object -com "ADODB.Recordset"
$objConnection.Open("Provider = Microsoft.Jet.OLEDB.4.0; Data Source = C:\Scripts\Test.mdb")
$objRecordset.Open("Select * From Computers", $objConnection,$adOpenStatic,$adLockOptimistic)
$objRecordSet.AddNew()
$objRecordSet.Fields.Item("ComputerName").Value = "atl-ws-001"
$objRecordSet.Fields.Item("SerialNumber").Value = "192ATG43R"
$objRecordSet.Update()
$objRecordSet.Close()
$objConnection.Close()

Related

Changing the caption permanently

In my script, I start a third-party non-GUI application. I'm sort of trying to run this embedded in my script itself, so I will be able to change the icon and the windows caption.
I have two restriction:
I have to use & 'application.exe' to start the application. I tested Start-Process -NoNewWindow, but that breaks the functionality of application.exe.
The application.exe needs to be running in my script. I can only change the icon when I compile my script with PS1 to Exe afterwards.
The challenge I'm now facing is related to the first restriction. I need to change the caption a-synchronously. The $host.ui.RawUI.WindowTitle = “New Title” is not working, because application.exe changes the caption right after execution. So I need to change it by using functions like SetWindowText(). This is working in VB.NET, but I'm looking for a way to start this function in parallel with the & 'application.exe'. When I use &, the application is executed and the script waits until it terminates. So I need to do the SetWindowText() in parallel.
Visual Basic/C has a BackgroundWorker functions for such cases. Is something like that also available in PowerShell?
Thanks for any help in advance!
Kind regards,
Eric
Everybody thank you very much for your help!
The solution proved to be a lot easier that I thought. You don't have to keep on renaming the window. You just have to start the cmd window, wait a bit (in the background it's doing something with conhost.exe) and then rename it once. Here's the code I used:
$titletext = "My New CMD Window Title"
# Start a thread job to change the window title to $titletext
$null = Start-ThreadJob { param( $rawUI, $windowTitle )
Start-Sleep -s 2 #Wait until cmd.exe is started
if ( $rawUI.WindowTitle -ne $windowTitle ) {
$rawUI.WindowTitle = $windowTitle
}
} -ArgumentList $host.ui.RawUI, $titletext
& 'c:\windows\system32\cmd.exe'
Kind regards,
Eric

How to change task triggers using powershell?

The script below removes the task triggers for each task ONLY if the tasks are in a folder. However my tasks are in the MAIN WINDOW in task scheduler. When you click on Task Scheduler Library, they are right there. They are not in any folder. However the $folder = $service.GetFolder('\') does not work. I tried it without the backlash and without the quotes, still does not work. What is going on ?
$service = New-Object -ComObject Schedule.Service
$service.Connect($env:COMPUTERNAME)
$folder = $service.GetFolder('\')
$tasks = $folder.gettasks(0)
foreach ($t in $tasks)
{
$definition = $t.Definition
$triggersCount = $definition.Triggers.Count
for($id=$triggersCount; $id -gt 0; $id--){
$definition.Triggers.Remove($id)
}
$folder.RegisterTaskDefinition($t.Name, $definition, 4, $null, $null, $null)
}
Get-ScheduledTask -TaskPath "\" may show all the tasks but there are no obvious commands or methods in the module to remove the triggers. I couldn't see a way and this answer seems to confirm it.
It's unclear from your question if $folder = $service.GetFolder('\') itself is producing an error, or if its the subsequent call to $tasks = $folder.gettasks(0). In this answer I'm assuming it's the latter, only because that's where I seem to have run into problems in my own environment.
I think this might be a combination of permissions and/or the hidden status of a task. Firstly, can you try running as elevated?
Reason I think this is after $folder = $service.GetFolder('\') I was able to get a list of tasks in the root folder, however only 2 of 9 were listed. When I ran in elevated I got 6 of 9.
I even tried the old PowerShell pack "TaskScheduler" module, which internally uses pretty much the same COM code, and I got the same results.
Now I was finally able to get the COM approach to show all 9 in the elevated session by flipping the GetTasks argument to 1:
$tasks = $folder.gettasks(1)
I believe the argument means to show or not to show hidden tasks, however it only accepts an [Int]. So, 0 = false, 1 = true. I confirmed this by looking at the code in the old "TaskScheduler" module. Putting a Boolean like $true in the argument doesn't work, only an [Int] will do.
Note: There is a hidden check box in the lower left, on the first tab
of the Task properties dialog. In may case the correlation matched what I ultimately found in the console.
At any rate give these 2 things a try:
Run as elevated.
Flip the argument to 1
Let me know how it turns out. Thanks.

Get target of shortcut (.lnk) file with powershell

I have a bunch of .lnk files and need to treat them differently depending on the target that the shortcut points to. I've found very little of how to this with other languages, but nothing about doing this with powershell.
I've tried this:
$sh = New-Object -COM WScript.Shell
$target = $sh.CreateShortcut('<path>').Target
But this returns an empty string even though I can see in the .lnk properties that the Target is specified.
Any idea on how to accomplish this?
You have made an error in the property; as wOxxOm suggests, you should be using TargetPath rather than Target:
$sh = New-Object -ComObject WScript.Shell
$target = $sh.CreateShortcut('<full-path-to-shortcut>').TargetPath
Google and MSDN were indeed helpful here; additionally, piping objects to Get-Member can often be useful and educational. This question also shows how to manipulate shortcuts using PowerShell, and uses the same technique as seen here.
If you want the arguments to the executable as well, those are stored separately:
$arguments = $sh.CreateShortcut('<full-path-to-shortcut>').Arguments
Again, piping objects to Get-Member - in this case, the object returned by WScript.Shell.CreateShortcut() - provides useful information.
It should be noted that there are issues with using this technique and these calls when the path contains Unicode emoji characters; there is a workaround for this case in this StackOverflow question.
It may seem obvious to experts but to us simpletons there seems to be a key lightbulb moment here:
<full-path-to-shortcut> = the Full Name! Doh!
Make sure you use .FullName if you use Get_ChildItem | ForEach-Object, etc. with the Shell .CreateShortcut call if you want the current target of a shortcut. For me:
.CreateShortcut($_.FullName) returned an appropriate value; whereas
.CreateShortcut($_) returned 'null'

Generating printer shortcuts from list

I am trying to generate a shortcut for every printer I have on a print server. The idea is to be able to email these shortcuts to people and when they click on them, it automatically installs that printer for them.
I've populated an array from a list of printer names exported from the print server:
$list = #((get-contnet $home\dekstop\plist.txt))
I then created a method to create a shortcut:
function Make-Shortcut
{
param ([string]$dest, [string]$source)
$WshShell = New-Object -comObject Wscript.Shell
$Shortcut = $WshShell.CreateShortcut($dest)
$Shortcut.TargetPath = $Source
$Shortcut.Save()
}
The function works fine. I was able to create standard shortcuts with no problem.
This next part is where I am getting stuck:
foreach ($i in $list)
{
Make-Shortcut "C:\pshort\$i.lnk" "C:\Windows\System32\rundll32.exe
printui.dll,PrintUIEntry /in /q /n\\printserver\$i"
}
When this runs, it does generate a shortcut with the same name as the printer for each printer on the list. However, the problem comes in at the target path. Instead of
C:\Windows\System32\rundll32.exe printui.dll,PrintUIEntry /in /q /n\\printserver\printername
it changes it to:
C:\Windows\System32\rundll32.exe printui.dll,PrintUIEntry \in \q \n\printserver\printername
The three problems with this are:
It is reversing the forward slash for the parameters
It is removing one of the backslashes preceding the server name
It is adding quotes to both sides. I need the quotes to come off for the shortcut to work properly.
I assume this is happening because Powershell thinks I am trying to make a standard shortcut and thinks I made mistakes while typing out the path.
I have tried putting a ` in front of each forward slash hoping the escape character would prevent it from reversing it, but no luck. I also tried using a hyphen for each parameter but that did not work either.
Is there anyway to stop this from happening? Or is there perhaps a better way to try to accomplish what I am trying to do?
You need to add arguments to the com object
Try adding a new param $arguments to your Make-Shortcut function and do:
Make-Shortcut "C:\pshort\$i.lnk" "C:\Windows\System32\rundll32.exe"
"printui.dll,PrintUIEntry /in /q /n\\printserver\$i"
add this in your function:
$Shortcut.Arguments = $arguments
So the link is created successfully ... but I have no idea if it works :)
Completely different answer but in a standard windows environment simply clicking a hyperlink to \printserver\printer will add a shared printer to someone's system?
So an email that simply lists :
\\PrintServer\Printer01
\\PrintServer\Printer02
\\PrintServer\Printer03
Would probably do the job just as well.

Does powershell have an equivalent to popen?

I need to be able to launch a process and read the output into a variable. Then based on the return of the command I can choose to show the full output or just a selected subset.
So to be clear, I want to launch a text based process (psexec actually) and read the output from that command (stdout, stderr, etc) into a variable rather than have it directly outputted to the console.
You left off some details regarding what kind of process, but I think this article from the Powershell Team Blog has whatever you'd like to do, either from piping the executable's output somewhere or utilizing System.Diagnostics.Process.
Now that the second option sounds like what you want to do, you can use the ProcessStartInfo class to feed in true as the RedirectStandardOutput property, and then read from the StandardOutput property of the Process object to do whatever you want with the output. StandardError works identically.
As far as reading stuff into variables is concerned, you should just be able to do something like
$output = ps
This will only capture stdout, though, not the verbose, warning or error streams. You can get the exit code of the previous command by testing the special variable $?.
I think a little more information would be of use to provide a more complete answer, but hopefully this is some way towards what you're looking for.
The PowerShell Community Extensions includes Start-Process. This actually returns a System.Diagnostics.Process.
> $proc = Start-Process pslist -NoShellExecute
However, while this returns the Process object it does not allow you to redirect the output prior to executing. To do that one can create their own process and execute it by first modifying the ProcessStartInfo members:
> $proc = New-Object System.Diagnostics.Process
> $proc.StartInfo = New-Object System.Diagnostics.ProcessStartInfo("pslist.exe")
> $proc.StartInfo.CreateNoWindow = $true
> $proc.StartInfo.UseShellExecute = $false
> $proc.StartInfo.RedirectStandardOutput = $true
> $proc.Start()
> $proc.StandardOutput.ReadToEnd()