I'm creating a power shall scrip to run exe file with arguments. The list of args are constructed in a way that if value of the arg is empty or null, the parameter shall not be passed
Below is my script
$runnerCommand = " "
[string]$splitRun = "20:1"
[string]$maxTestWorkers = "777"
[string]$retryTimes = "9"
[string]$testFilterInXmlFormat = "<filter><cat>XX</cat></filter>"
#$runnerCommand += '--testDllPath ' + $testDllPath + " "
if ($splitRun){
$runnerCommand+= "--splitRun '$splitRun' "
}
if ($maxTestWorkers){
$runnerCommand+= "--maxTestWorkers '$maxTestWorkers' "
}
if ($retryTimes){
$runnerCommand+= "--retryTimes '$retryTimes' "
}
if ($testFilterInXmlFormat){
$runnerCommand+= "--testFilterInXmlFormat '$testFilterInXmlFormat' "
}
$cmdPath = "C:\AutoTests\TestAutomation.Runner\bin\Debug\TestAutomation.Runner.exe"
& $cmdPath --testDllPath C:/AutoTests/Build/TestAutomation.TestsGUI.dll $runnerCommand
It looks like that PowerShell do a 'new line' before $runnerCommand in the last line of code that results in not passing the args from $runnerCommand
Please suggest how to solve the problem.
I tried different approaches
You're currently passing all of the arguments as a single string. You should instead use an array with each argument as a separate element. I can't actually test this, but something like this should work:
[string]$splitRun = "20:1"
[string]$maxTestWorkers = "777"
[string]$retryTimes = "9"
[string]$testFilterInXmlFormat = "<filter><cat>XX</cat></filter>"
$runnerArgs = #(
'--testDllPath', 'C:/AutoTests/Build/TestAutomation.TestsGUI.dll'
if ($splitRun) {
'--splitRun', $splitRun
}
if ($maxTestWorkers) {
'--maxTestWorkers', $maxTestWorkers
}
if ($retryTimes) {
'--retryTimes', $retryTimes
}
if ($testFilterInXmlFormat) {
'--testFilterInXmlFormat', $testFilterInXmlFormat
}
)
$cmdPath = "C:\AutoTests\TestAutomation.Runner\bin\Debug\TestAutomation.Runner.exe"
& $cmdPath $runnerArgs
Note that PowerShell allows expressions, including if-expressions, inside the array sub-expression operator (#()).
how would i implement the following javascript code snippet in powershell?
String.prototype.regexCount = function (pattern) {
if (pattern.flags.indexOf("g") < 0) {
pattern = new RegExp(pattern.source, pattern.flags + "g");
}
return (this.match(pattern) || []).length;
};
I'm thinking its something like this:
$regexCount = {
param(
$pattern
)
# ??????
if ($pattern.flags.indexOf("g") -lt 0) {
# ????
# $pattern = new RegExp(pattern.source, pattern.flags + "g");
$pattern = [regex]::new($pattern)
}
# ????
return ($this.match($pattern) || []).length;
}
I have almost the entire script converted into powershell except for this little nugget of code... Actually, i'm a little bit clueless when javascript starts creating lambda functions with regular expression objects...
for instance what's the significants of string.prototype.somename? wouldn't you just save the lambda to any variable name?
Using Update-TypeData, create a type-level ScriptMethod ETS member for the .NET string type (System.String):
Update-TypeData -TypeName System.String -MemberName RegexCount -MemberType ScriptMethod -Value {
param([regex] $Regex)
$Regex.Matches($this).Count
}
Now you can call the .RegexCount() method on any string instance, analogous to what your JavaScript code does.
Sample call:
'foo'.RegexCount('.') # -> 3
That is, 3 matches for regex . were found in the input string.
Looking for a way to access individual array elements (continuation of this question)
$CSharpCode = #"
using System;
namespace TestStructure
{
public struct TestStructure
{
public byte Field1;
public unsafe fixed byte Field2[4];
}
}
"#
$cp = New-Object System.CodeDom.Compiler.CompilerParameters
$cp.CompilerOptions = '/unsafe'
Add-Type -TypeDefinition $CSharpCode -CompilerParameters $cp
function ConvertTo-Struct
{
# Only accept struct types (sealed value types that are neither primitive nor enum)
param(
[Parameter(Mandatory = $true)]
[ValidateScript({ $_.IsValueType -and $_.IsSealed -and -not($_.IsPrimitive -or $_.IsEnum) })]
[Type]$TargetType,
[Parameter(Mandatory = $true)]
[byte[]]$BinaryData
)
# Start by calculating minimum size of the underlying memory allocation for the new struct
$memSize = [System.Runtime.InteropServices.Marshal]::SizeOf([type]$TargetType)
# Make sure user actually passed enough data to initialize struct
if($memSize -gt $BinaryData.Length){
Write-Error "Not enough binary data to create an instance of [$($TargetType.FullName)]"
return
}
# now we just need some unmanaged memory in which to create our struct instance
$memPtr = [IntPtr]::Zero
try {
$memPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($memSize)
# copy byte array to allocated unmanaged memory from previous step
[System.Runtime.InteropServices.Marshal]::Copy($BinaryData, 0, $memPtr, $memSize)
# then marshal the new memory region as a struct and return
return [System.Runtime.InteropServices.Marshal]::PtrToStructure($memPtr, [type]$TargetType)
}
finally {
# and finally remember to clean up the allocated memory
if($memPtr -ne [IntPtr]::Zero){
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($memPtr)
}
}
}
$testStructure = ConvertTo-Struct -TargetType ([TestStructure.TestStructure]) -BinaryData (1..100 -as [byte[]])
$testStructure.Field1
$testStructure.Field2
this produces the following output:
1
FixedElementField
-----------------
2
only the first array element of $Field2 is visible, can't access others using $testStructure.Field2[x]
Looking for a way to iterate over FixedBuffer of known type / size
$testStructure.Field2.GetType() says <Field2>e__FixedBuffer0
$testStructure.Field2.FixedElementField.GetType() is byte
can't see a way to access other elements of the array.
PowerShell's type adapter doesn't really have anything in place to handle unsafe fixed byte[] fields, effectively raw pointers from the underlying type system's point of view.
The declared size of the underlying memory allocation is only stored in metadata, which you can locate as follows:
# Locate appropriate field metadata
$fieldInfo = $testStructure.GetType().GetField('Field2')
# Discover fixed buffer attribute
$fixedBufferAttribute = $fieldInfo.CustomAttributes.Where({$_.AttributeType -eq [System.Runtime.CompilerServices.FixedBufferAttribute]}) |Select -First 1
Now we can figure out the size and which array element type is expected:
if($fixedBufferAttribute)
{
# Grab array element type + size from FixedBuffer attribute
$elemType,$size = $fixedBufferAttribute.ConstructorArguments
# Create array of appropriate size
$values = $elemType.MakeArrayType()::new($size)
# Copy values from fixed buffer pointer to managed array
try {
$fixedBufferHandle = [System.Runtime.InteropServices.GCHandle]::Alloc($TestStructure.Field2, 'Pinned')
[System.Runtime.InteropServices.Marshal]::Copy($fixedBufferHandle.AddrOfPinnedObject(), $values, 0, $size)
return $values
}
finally {
$fixedBufferHandle.Free()
}
}
You'll find $values now contains the expected byte values 2, 3, 4 and 5
How can I set a none data type.[Microsoft.Win32.RegistryValueKind]::None in Powershell 2.0?
Powershell 2.0 has types "Unknown, String, ExpandString, Binary, DWord, MultiString, QWord".
learn.microsoft.com
Add-Type -TypeDefinition #"
using System.Runtime.InteropServices;
using System.Security.Principal;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
namespace AdvapiSolution
{
public class Advapi
{
public enum HKEY : uint
{
HKEY_CLASSES_ROOT = 0x80000000,
HKEY_CURRENT_USER = 0x80000001,
HKEY_LOCAL_MACHINE = 0x80000002,
HKEY_USERS = 0x80000003,
HKEY_PERFORMANCE_DATA = 0x80000004,
HKEY_PERFORMANCE_TEXT = 0x80000050,
HKEY_PERFORMANCE_NLSTEXT = 0x80000060,
HKEY_CURRENT_CONFIG = 0x80000005
}
private enum VALUE_TYPE : uint
{
REG_NONE= 0,
REG_SZ = 1,
REG_EXPAND_SZ = 2,
REG_BINARY = 3,
REG_DWORD = 4,
REG_DWORD_LITTLE_ENDIAN = 4,
REG_DWORD_BIG_ENDIAN = 5,
REG_LINK = 6,
REG_MULTI_SZ = 7,
REG_RESOURCE_LIST = 8,
REG_FULL_RESOURCE_DESCRIPTOR = 9,
REG_RESOURCE_REQUIREMENTS_LIST = 10,
REG_QWORD_LITTLE_ENDIAN = 11
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto, BestFitMapping = false)]
private static extern int RegSetKeyValueW (
HKEY hkey,
string lpSubKey,
string lpValueName,
VALUE_TYPE type,
byte[] data,
uint dataLength);
public int set_key(HKEY hkey, string subkey, string valuename){
return RegSetKeyValueW(hkey, subkey, valuename, VALUE_TYPE.REG_NONE, null, 0);
}
}
}
"#
In my case data equals null and type is REG_NONE. You can change set_key as you like with object:
$Advapiobject = New-Object 'AdvapiSolution.Advapi'
$Advapiobject.set_key('HKEY_CURRENT_USER',$yoursubkey, $yourvaluename, 'REG_NONE')
Another way is using a static method set_key.
Change set_key to public static int set_key:
[AdvapiSolution.Advapi]::set_key('HKEY_CURRENT_USER',$yoursubkey, $yourvaluename, 'REG_NONE').
If it returns 0, it's okay. If the function fails, the return value is a nonzero error code defined in Winerror.h.
System Error Codes
(As implied in your question), [Microsoft.Win32.RegistryValueKind]::None (which translates to a REG_NONE registry value) requires at least .NET Framework 4, whereas Windows PowerShell v2 is built on .NET Framework 2. (Only Windows PowerShell v3 and above are built on .NET Framework 4 and above).
Unlike C#, PowerShell does not allow you to pass the numerical value of an enum-typed parameter if that value doesn't represent an officially defined member of that enumeration (at that time, in the underlying framework), so attempting to pass (-1), the value of [Microsoft.Win32.RegistryValueKind]::None, does not work in Windows PowerShell v2 - neither with New-ItemProperty / Set-ItemProperty's -Type parameter, nor with .NET API calls ([Microsoft.Win32.Registry]::SetValue(...)).
Therefore, in Windows PowerShell v2, calling the Windows API via P/Invoke declarations implemented via Add-Type is probably your only option - see the RegSetKeyValue() WinAPI function (among others).
Building on your own helpful answer, here's a streamlined solution, which:
uses Add-Type's -MemberDefinition parameter to simplify creation of types that wrap P/Invoke calls.
Exposes a static [WinApiHelper.Registry]::SetNoneValue() method, which:
focuses only on creating REG_NONE values
also supports creating such values with byte data ([byte[]] arrays)
has no return value, but throws a Win32Exception should an error occur.
Add-Type -Namespace WinApiHelper -Name Registry -MemberDefinition #'
public enum HKEY : uint
{
HKEY_CLASSES_ROOT = 0x80000000,
HKEY_CURRENT_CONFIG = 0x80000005,
HKEY_CURRENT_USER = 0x80000001,
HKEY_LOCAL_MACHINE = 0x80000002,
HKEY_USERS = 0x80000003
}
[DllImport("advapi32.dll")]
private static extern int RegSetKeyValue(
HKEY hkey,
string lpSubKey,
string lpValueName,
UInt32 type,
byte[] data,
UInt32 dataLength
);
public static void SetNoneValue(HKEY hkey, string subkey, string valuename, byte[] bytes)
{
int rc = RegSetKeyValue(hkey, subkey, valuename, 0 /* REG_NONE */, bytes, (UInt32) (bytes == null ? 0 : bytes.Length));
if (rc != 0) {
// Access the error code in PowerShell with $Error[0].Exception.InnerException.NativeErrorCode
throw new System.ComponentModel.Win32Exception(rc);
}
}
// Overload that creates an empty value.
// Note: .NET 2 doesn't support optional parameters, so an explicit overload is required.
public static void SetNoneValue(HKEY hkey, string subkey, string valuename) {
SetNoneValue(hkey, subkey, valuename, null);
}
'#
Sample calls:
# Create an empty REG_NONE value named 'bar1' in key HKEY_CURRENT_USER\foo.
[WinApiHelper.Registry]::SetNoneValue('HKEY_CURRENT_USER', 'foo', 'bar1')
# Create a REG_NONE value named 'bar2' with a byte array.
[WinApiHelper.Registry]::SetNoneValue('HKEY_CURRENT_USER', 'foo', 'bar2', [byte[]] (42, 43))
I am attempting to call a function in powershell, but pass it variables so I don't need to repeat the funxtion X amount of times with different values. I would like it so that, when I call the function I simply type:
Foo(Red,12,1.8)
And the generic function, foo, as follows:
function Foo(Colour, Age, Height){
<# Function does something with data here #>
}
Is this possible using powershell, and if so, would any changes made to those variables be saved after the function has completed?
Yes you can use parameters in functions.
By default the parameters only change within the scope of the function, but you can choose to return the same parameters as results if you like.
example (uses splatting)-
function Foo{
param(
[string]$Color,
[int]$Age,
[decimal]$Height
)
$Age += 1
$Height += 1
$ExampleOutput = [ordered]#{
Color = $Color
Age = $Age
Height = $Height
}
return ($ExampleOutput)
}
$Parameters = #{
Color = "Red"
Age = 12
Height = 1.8
}
Foo #Parameters
Yes try default values:
function Foo($Colour='Red',$Age=12,$Height=1.8){
<# Function does something with data here #>
}