Unable to set Comobject Value - powershell

I have a snippet of code that opens a word template, then attempts to set values of named FormFields.
$options = #{
'foo' ='bar';
'bizz' = 'buzz';
}
$document = 'C:\Form_template.doc'
$word = new-object -ComObject Word.application
$doc = $word.Documents.Open($document)
$word.visible = $true
$fields = $doc.FormFields
$fields.item('foo').Result = $options['foo']
$fields.item('bizz').Result = $options['bizz']
When running this snippet, the form fields are not set properly. However, when I run
$fields.item('foo').Result = 'bar'
$fields.item('bizz').Result = 'buzz'
The values are set as desired.
Edit: Here's an example in Interactive shell
PS C:\>$fields.item('foo').Result = $options['foo']
PS C:\>$fields.item('bizz').Result = $options['bizz']
PS C:\> $doc.FormFields.Item('foo').Result
PS C:\> $doc.FormFields.Item('bizz').Result
PS C:\>#Now let's try setting the values directly with a string.
PS C:\>$fields.item('foo').Result = 'bar'
PS C:\>$fields.item('bizz').Result = 'buzz'
PS C:\> $doc.FormFields.Item('foo').Result
bar
PS C:\> $doc.FormFields.Item('bizz').Result
buzz
Why am I not able to set the FormField values using values from the hash?

Per a suggestion from Ben casting the string with [string]$options['bizz'] resulted in setting the value correctly.
PS C:\>$fields.item('bizz').Result = [string]$options['bizz']
PS C:\> $doc.FormFields.Item('foo').Result
buzz
Upon further investigation I found that casting the hash value to string returned a different type vs using .toString()
PS C:\> $options['bizz'].getType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
PS C:\> $options['bizz'].toString().getType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
PS C:\> [string]$options['bizz'].getType()
string
I'm interested in why that is, but that would be a topic for another thread. Proper casting resolved my issue.

Related

powershell checking boolean environment variable

I ran into something strange, and I don't understand what is happening:
PS C:\> $env:x = $false
PS C:\> if($env:x){'what'}
what
PS C:\> $env:x
False
So the value is actually false, but what does the if check? Clearly not the value of x. What is happening?
The Environment provider (which implements the env: drive) only supports string items, and will coerce any assigned value to a [string]:
PS C:\> $env:x = $false
PS C:\> $env:x.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
... and the default [string]-to-[bool] conversion rules in PowerShell holds that non-empty strings are converted to $true:
PS C:\> $true -eq 'false'
True
PS C:\> [bool]'false'
True
PS C:\> [bool]''
False
To parse a string value (like "false") to its equivalent [bool] value, you need to either call [bool]::Parse():
if([bool]::Parse($env:x)){
'what'
}
... or, if the string value might not be a valid truthy/falsy value, use [bool]::TryParse():
$env:x = $false
$result = $false
if([bool]::TryParse($env:x, [ref]$result) -and $result){
'what'
}

Cast based on variable

I am trying to cast based on the value of a variable, ultimately to be able to actually test to see if $castFrom is of the $castTo type. I can of course do it with a switch like this
$castTo = '[xml]'
$castFrom = #"
<Settings>
<MachineLogFileArchiveFolder></MachineLogFileArchiveFolder>
</Settings>
"#
switch ($castTo) {
'[int]' {
$castResult = [int]$castFrom
}
'[xml]' {
$castResult = [xml]$castFrom
}
}
But that's a little ugly. What I really want too do is something more like this
$castResult = [($castTo)]$castFrom
or this
$castResult = [$($castTo)]$castFrom
but I am getting the impression the switch really is my only option.
Maybe you could use the ToType method:
$castResult = $castFrom.ToType($castTo, [System.Globalization.DateTimeFormatInfo]::CurrentInfo)
Somehow this doesn't work for your example but could be a starting point.
However, you can do this using the Invoke-Expression cmdlet:
$castResult = Invoke-Expression -Command ('[{0}]$castFrom' -f $castTo)
The -as and -is operators just seem simpler for this situation.
$castto = 'string'
$castfrom = 234
$castfrom.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
$castresult = $castfrom -as $castto
$castresult.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
$castfrom -is $castto
False
$castresult -is $castto
True

Create an array, hashtable and dictionary?

What is the proper way to create an array, hashtable and dictionary?
$array = [System.Collections.ArrayList]#()
$array.GetType() returns ArrayList, OK.
$hashtable = [System.Collections.Hashtable]
$hashtable.GetType() returns RuntimeType, Not OK.
$dictionary = ?
How to create a dictionary using this .NET way?
What is the difference between dictionary and hashtable? I am not sure when I should use one of them.
The proper way (i.e. the PowerShell way) is:
Array:
> $a = #()
> $a.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Hashtable / Dictionary:
> $h = #{}
> $h.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Hashtable System.Object
The above should suffice for most dictionary-like scenarios, but if you did explicitly want the type from Systems.Collections.Generic, you could initialise like:
> $d = New-Object 'system.collections.generic.dictionary[string,string]'
> $d.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Dictionary`2 System.Object
> $d["foo"] = "bar"
> $d | Format-Table -auto
Key Value
--- -----
foo bar
If you want to initialize an array you can use the following code:
$array = #() # empty array
$array2 = #('one', 'two', 'three') # array with 3 values
If you want to initialize hashtable use the following code:
$hashtable = #{} # empty hashtable
$hashtable2 = #{One='one'; Two='two';Three='three'} # hashtable with 3 values
Hashtable and dictionary in Powershell is pretty much the same, so I suggest using hashtable in almost all cases (unless you need to do something in .NET where Dictionary is required)

Properly Declare a List Collection?

In Powershell V4, how would I properly declare a List<> collection?
I tried declaring it like this
$listCollection = New-Object 'System.Collections.Generic.List<string>'
It didn't work and gave me errors.
In Powershell, you need to use square brackets [...] when specifying the type of the list's items:
PS > $listCollection = New-Object System.Collections.Generic.List[string]
PS > $listCollection.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True List`1 System.Object
PS >
Note that this is different from C#, which would use angle brackets <...>.

Return type mystery

I have something like the following in PowerShell
function x {
$result = New-Object 'System.Object[,,]' 1,1,1
'Type in function is: ' + $result.getType()
$result[0,0,0] = 'dummy-value'
return $result
}
$result = x
$result.GetType()
It is peculiar that type of result is Object[,,] in the method but suddenly becomes Object[] outside. I basically need a few arguments of type Object[,,] for some .Net library that I am using.
Any hint?
To understand what happened, just try to type :
PS C:\temp> $result[0]
Type in function is: System.Object[,,]
PS C:\temp> $result[1]
dummy-value
The explanation is everything that is output frm the function is put into an array.
To do what you want you have to write this (don't forgot the , before $result) :
function x {
$result = New-Object 'System.Object[,,]' 1,1,1
$result[0,0,0] = 'dummy-value'
return ,$result
}
then :
PS C:\temp> $a = x
PS C:\temp> $a.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[,,] System.Array