Powershell get return value from visual basic script - powershell

I have some old code in visual basic script. Instead of re-writing this old code into PowerShell, I'd like to call the VB scripts from PowerShell and capture their return value.
How can I get the return value of a visual basic script in powershell?
Something like this:
$returnValue = Invoke-Command -ScriptBlock{.\vbs\legacyVbsFunction.vbs}
The visual basic function may look like this
Function MyFunction() As Double
Return 3.87 * 2
End Function

It sounds like you want to capture a VBScript's (stdout) output:
$output = cscript.exe //nologo .\vbs\legacyVbsFunction.vbs
Note that $output will either be a single string - if the script outputs just 1 line - or an array of strings in case of multi-line output.
For example, assuming that .\vbs\legacyVbsFunction.vbs contains the following code:
Function MyFunction
MyFunction = 3.87 * 2
End Function
' Call the function and print it to stdout.
Wscript.Echo(MyFunction)
You could capture the output and convert it to a [double] as follows:
[double] $output = cscript.exe //nologo .\vbs\legacyVbsFunction.vbs
$output then contains 7.74.

You can actually embed a vbscript function right in powershell using a com object called ScriptControl, but it only works in 32-bit powershell, C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe: Embed VBS into PowerShell
function Call-VBScript
{
$sc = New-Object -ComObject ScriptControl
$sc.Language = 'VBScript'
$sc.AddCode('
Function MyFunction
MyFunction = 3.87 * 2
End Function
')
$sc.CodeObject
}
$vb = Call-VBScript
$returnvalue = $vb.MyFunction()
"returnvalue is " + $returnvalue
I found out you can run a job as 32-bit:
$returnvalue =
start-job {
function Call-VBScript {
$sc = New-Object -ComObject MSScriptControl.ScriptControl.1
$sc.Language = 'VBScript'
$sc.AddCode('
Function MyFunction
MyFunction = 3.87 * 2
End Function
')
$sc.CodeObject
}
$vb = call-vbscript
$vb.MyFunction()
} -runas32 | wait-job | receive-job
"returnvalue is " + $returnvalue

You don't really have an exit code in the VBS, you have a return for a function.
To actually have a return code you must close the script with something like:
wscript.quit(0)
or
wscript.quit(returncode)
In the powershell script you must execute the vbs something like this:
(Start-Process -FilePath "wscript.exe" -ArgumentList "Script.vbs" -Wait -Passthru).ExitCode

Related

How to run EXE in powershell with parameters, in one line

I need to run a .exe with parameters, currently this takes two lines. I would like to be able to pass a string as a variable to a function, and only need to do this once.
This is how I am currently doing it, which takes two variables:
function Foo{
$ExeToStart = "C:\Program Files\x\program.exe"
Start-Process $ExeToStart -ArgumentList "--arg1","--arg2"
}
Is there a way of combining this so that I can just define a variable, "y" and pass it into the function as one line similar to below?
$x = "C:\Program Files\x\program.exe -ArgumentList "--arg1","--arg2""
function Foo{
Start-Process $x
}
Here is a way you can achieve what you want.
# Create an array of the program and arguments
$x = 'C:\Program Files\x\program.exe','--arg1','--arg2'
# Example with no parameters
Function Foo {
Start-Process -FilePath $args[0][0] -ArgumentList $args[0][1..$args[0].Count]
}
# Example using parmeters
Function Foo {
param([string[]]$program)
$params = #{
FilePath = $program[0]
}
if ($program.Count -gt 1) {
$params['ArgumentList'] = $program[1..$program.Count]
}
Start-Process #Params
}
# Call the function
Foo $x

Submitting parameters to Invoke-Expression

I've written a sample Powershell Script with name C:\Script\Scrip1.ps1
Below is the code
Function Testfunction(){
Param(
$Node1,
$Node2
)
$SQLNodes = #($Node1, $Node2)
foreach ($node in $SQLNodes)
{
#Some code below is dummy code
"$node" | Out-File C:\File1.txt -Append
}
}
When i try to call this function using invoke-Expression it doesn't work
Used below method with no luck
$string = 'C:\Script\Script1.ps1 Testfunction -Node1 "test" -Node2 "test2"'
Invoke-Expression $string
I have opened a PS Window and ran below command without luck
.\Script1.ps1 -Node1 Hello -Node2 Aquib
or
.\Script1.ps1 Testfunction -Node1 Hello -Node2 Aquib
I do not see any file1 under C:\File1
when when I open the script file and then run the function, it does work and generate the file.
You don't need to use Invoke-Expression in your scenario.
If you want to make Testfunction visible in the current scope, you will need to "dot-source" your script:
PS C:\> . C:\Scripts\Script1.ps1
This executes Script.ps1 in the current scope, which will define Testfunction in the current scope, and then you can run the function:
PS C:\> Testfunction -Node1 "Test1" -Node2 "Test2"
Another alternative is to skip defining Testfunction as a function in a script, and just use it as a script itself:
# Script file
param(
$Node1,
$Node2
)
$SQLNodes = #($Node1, $Node2)
foreach ($node in $SQLNodes) {
#Some code below is dummy code
"$node" | Out-File C:\File1.txt -Append
}
If you name the script Testfunction.ps1, you can run it by typing the script's name:
PS C:\> C:\Scripts\Testfunction.ps1 -Node1 "Test1" -Node2 "Test2"

Powershell 2.0 - extract parameters from script block

Is there a way to extract the parameter list of a script block from outside the script block in PS 2.0 ?
Say we have
$scriptb = { PARAM($test) }
In Powershell 3.0 we can do this
$scriptb.Ast.ParamBlock.Parameters.Count == 1 #true
The Ast property however was included in powershel 3.0 so the above will not work in PS 2.0 https://msdn.microsoft.com/en-us/library/System.Management.Automation.ScriptBlock_properties%28v=vs.85%29.aspx
Do you know of a way to do this in PS 2.0 ?
Perhaps it is not a pretty solution but it gets the job done:
# some script block
$sb = {
param($x, $y)
}
# make a function with the scriptblock
$function:GetParameters = $sb
# get parameters of the function
(Get-Command GetParameters -Type Function).Parameters
Output:
Key Value
--- -----
x System.Management.Automation.ParameterMetadata
y System.Management.Automation.ParameterMetadata
What about this?
$Scriptb = {
PARAM($test,$new)
return $PSBoundParameters
}
&$Scriptb "hello" "Hello2"
It looks like I can do this
function Extract {
PARAM([ScriptBlock] $sb)
$sbContent = $sb.ToString()
Invoke-Expression "function ____ { $sbContent }"
$method = dir Function:\____
return $method.Parameters
}
$script = { PARAM($test1, $test2, $test3) }
$scriptParameters = Extract $script
Write-Host $scriptParameters['test1'].GetType()
It will return a list of System.Management.Automation.ParameterMetadata
https://msdn.microsoft.com/en-us/library/system.management.automation.parametermetadata_members%28v=vs.85%29.aspx
I think there should be a better way. Until then I will use a variation of the above code.

I want to make a powershell script that call another powershell script

So i have lets say a powershell script called CallMePlease.ps1
This script will take parameters / arguments and then does some a process. How do I append the arguments to the call when I call this script from MAIN.ps1? Code I have so far:
$ScriptPath = C:\Tmp\PAL\PAL\PAL\PAL.ps1
$Log 'C:\Users\k0530196\Documents\log.csv'
$ThresholdFile 'C:\Program Files\PAL\PAL\template.xml'
$Interval 'AUTO'
$IsOutputHtml $True
$HtmlOutputFileName '[LogFileName]_PAL_ANALYSIS_[DateTimeStamp].htm'
$IsOutputXml $True
$XmlOutputFileName '[LogFileName]_PAL_ANALYSIS_[DateTimeStamp].xml'
$AllCounterStats $False
$NumberOfThreads 1
$IsLowPriority $False
$cmd = "$ScriptPath\.\PAL.ps1"
Invoke-Expression "$cmd $Log $ThresholdFile $Interval $IsOutputHtml $HtmlOutputFileName $IsOutputXml $XmlOutputFileName $AllCounterStats $NumberOfThreads"
In the code that you posted you are missing several =s in your assignment statements. For instance this line:
$Log 'C:\Users\k0530196\Documents\log.csv'
Should be this:
$Log = 'C:\Users\k0530196\Documents\log.csv'
You will need to do that in all the instances where you are trying to assign a value to a variable.
I would do it like this:
. $cmd $Log $ThresholdFile $Interval $IsOutputHtml $HtmlOutputFileName $IsOutputXml $XmlOutputFileName $AllCounterStats $NumberOfThreads

Output from function to console, preserving color

I'm running msbuild from powershell, inside a function:
function Run-MSBuild()
{
msbuild.exe (lots of params)
}
Run-MSBuild
Works great, and I see colored MSBuild output on my console. Now, I want to know how long it took:
$time = Measure-Command { Run-MSBuild }
Works, but I don't see the MSBuild output anymore.
Attempt 1: Pipe to Write-Host:
function Run-MSBuild()
{
msbuild.exe (lots of params) | Write-Host
}
Result 1: I see MSBuild output on my console, but color is lost.
Attempt 2: Replace Measure-Command:
$timer = [diagnostics.stopwatch]::startnew()
Run-MSBuild
$time = $timer.Elapsed
Result 2: Works, but kinda ugly.
Any way to achieve this?
Include the timing command in the function?
function Run-MSBuild
{
$start = Get-Date
msbuild.exe $args
$End = Get-Date
$End-$Start
}