101

I am looking for the best way to check if a Com Object exists.

Here is the code that I have; I'd like to improve the last line:

$ie = New-Object -ComObject InternetExplorer.Application
$ie.Navigate("http://www.stackoverflow.com")
$ie.Visible = $true

$ie -ne $null #Are there better options?
CJBS
  • 15,147
  • 6
  • 86
  • 135
LaPhi
  • 5,675
  • 21
  • 56
  • 78

9 Answers9

122

I would stick with the $null check since any value other than '' (empty string), 0, $false and $null will pass the check: if ($ie) {...}.

Keith Hill
  • 194,368
  • 42
  • 353
  • 369
  • 2
    To use if ($val){...} is better for booleans All other checks should be if ($val -ne $null){..} Tested myself. TY @Keith Hill – Ilya Gurenko Jul 25 '19 at 12:20
79

You can also do

if ($ie) {
    # Do Something if $ie is not null
}
CJBS
  • 15,147
  • 6
  • 86
  • 135
ravikanth
  • 24,922
  • 4
  • 60
  • 60
22

What all of these answers do not highlight is that when comparing a value to $null, you have to put $null on the left-hand side, otherwise you may get into trouble when comparing with a collection-type value. See: https://github.com/nightroman/PowerShellTraps/blob/master/Basic/Comparison-operators-with-collections/looks-like-object-is-null.ps1

$value = @(1, $null, 2, $null)
if ($value -eq $null) {
    Write-Host "$value is $null"
}

The above block is (unfortunately) executed. What's even more interesting is that in Powershell a $value can be both $null and not $null:

$value = @(1, $null, 2, $null)
if (($value -eq $null) -and ($value -ne $null)) {
    Write-Host "$value is both $null and not $null"
}

So it is important to put $null on the left-hand side to make these comparisons work with collections:

$value = @(1, $null, 2, $null)
if (($null -eq $value) -and ($null -ne $value)) {
    Write-Host "$value is both $null and not $null"
}

I guess this shows yet again the power of Powershell !

Dag Wieers
  • 1,663
  • 13
  • 11
  • Surprised this answer is not more upvoted as it contains the **critical** detail of putting `$null` on the left-hand side – sonyisda1 Jul 01 '19 at 20:47
17

In your particular example perhaps you do not have to perform any checks at all. Is that possible that New-Object return null? I have never seen that. The command should fail in case of a problem and the rest of the code in the example will not be executed. So why should we do that checks at all?

Only in the code like below we need some checks (explicit comparison with $null is the best):

# we just try to get a new object
$ie = $null
try {
    $ie = New-Object -ComObject InternetExplorer.Application
}
catch {
    Write-Warning $_
}

# check and continuation
if ($ie -ne $null) {
    ...
}
Roman Kuzmin
  • 40,627
  • 11
  • 95
  • 117
  • 1
    If the COM Object type doesn't exist, New-Object will throw an exception. But I don't see how it could return a null. Also, ignoring the Exception only to then test for null is bad form. – JasonMArcher Dec 15 '10 at 17:04
  • @JasonMArcher: I agree with the last remark, absolutely. But really, what would you expect me to write in the demo example? Besides, depending on a scenario even this code might be just fine. – Roman Kuzmin Dec 15 '10 at 18:00
  • Essentially, put all the code that uses $ie inside the try{}. That way it gets skipped if there is an exception. – JasonMArcher Dec 15 '10 at 18:14
  • 2
    Then it would not the code showing a case when we need to check for $null. – Roman Kuzmin Dec 15 '10 at 18:23
1

Type-check with the -is operator returns false for any null value. In most cases, if not all, $value -is [System.Object] will be true for any possible non-null value. (In all cases, it will be false for any null-value.)

My value is nothing if not an object.

Aron
  • 11
  • 1
1

I believe this question (albeit is old) really is a question of checking for null values. Checking for null (or not null) values in PowerShell is tricky. Using ($null -eq $value) or ($null -ne $value) does not always work. Neither does if($value). Using them can even cause problems later on.
Just read this Microsoft article below (IN IT'S ENTIRETY) to get a grasp of how tricky nulls can be in Powershell.

[https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-null?view=powershell-7.1][1]

I wrote these two functions below for checking for null (or not null) values in PowerShell. I "believe" they should work for any and all values and data types.

I hope someone finds them helpful. I am not sure why MS hasn't put something like this into PowerShell natively to make handling nulls easier (and less dangerous) in PowerShell.

I hope this helps someone.

If anyone knows of an unseen "pitfall" or problem with this method, please post a comment here so we can know that. Thanks!

<#
*********************  
FUNCTION: ValueIsNull
*********************  
Use this function ValueIsNull below for checking for null values
rather using -eq $null or if($value) methods.  Those may not work as expected.     
See reference below for more details on $null values in PowerShell. 
[https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-null?view=powershell-7.1][1]

An if statement with a call to ValueIsNull can be written like this:
if (ValueIsNull($TheValue))    
#>

function ValueIsNull {
  param($valueToCheck)
  # In Powershell when a parameter of a function does not have a data type defined,
  # it will create the parameter as a PSObject. It will do this for 
  # an object, an array, and a base date type (int, string, DateTime, etc.)
  # However if the value passed in is $null, then it will still be $null.
  # So, using a function to check null gives us the ability to determine if the parameter
  # is null or not by checking if the parameter is a PSObject or not.
  # This function could be written more efficiently, but intentionally 
  # putting it in a more readable format.
  # Special Note: This cannot tell the difference between a parameter
  # that is a true $null and an undeclared variable passed in as the parameter.
  # ie - If you type the variable name wrong and pass that in to this function it will see it as a null.
  
  [bool]$returnValue = $True
  [bool]$isItAnObject=$True
  [string]$ObjectToString = ""
  
  try { $ObjectToString =  $valueToCheck.PSObject.ToString() } catch { $isItAnObject = $false }

  if ($isItAnObject)
  {
    $returnValue=$False
  }

  return $returnValue
}
<# 
************************
FUNCTION: ValueIsNotNull 
************************
Use this function ValueIsNotNull below for checking values for 
being "not-null"  rather than using -ne $null or if($value) methods.
Both may not work as expected.

See notes on ValueIsNull function above for more info.

ValueIsNotNull just calls the ValueIsNull function  and then reverses
the boolean result. However, having ValueIsNotNull available allows
you to avoid having  to use -eq and\or -ne against ValueIsNull results.
You can disregard this function and just use !ValueIsNull($value). 
But, it is my preference to have both for easier readability of code.

An if statement with a call to ValueIsNotNull can be written like this:
if (ValueIsNotNull($TheValue))    
#>

function ValueIsNotNull {
  param($valueToCheck)
  [bool]$returnValue = !(ValueIsNull($valueToCheck))
  return $returnValue
}

You can use the following list of calls to ValueIsNull to test it out.

  $psObject = New-Object PSObject
  Add-Member -InputObject $psObject -MemberType NoteProperty -Name customproperty -Value "TestObject"
  $valIsNull = ValueIsNull($psObject)

  $props = @{
    Property1 = 'one'
    Property2 = 'two'
    Property3 = 'three'
    }
  $otherPSobject = new-object psobject -Property $props
  $valIsNull = ValueIsNull($otherPSobject)
  # Now null the object
  $otherPSobject = $null
  $valIsNull = ValueIsNull($otherPSobject)
  # Now an explicit null
  $testNullValue = $null
  $valIsNull = ValueIsNull($testNullValue)
  # Now a variable that is not defined (maybe a type error in variable name)
  # This will return a true because the function can't tell the difference
  # between a null and an undeclared variable.
  $valIsNull = ValueIsNull($valueNotDefine)

  [int32]$intValueTyped = 25
  $valIsNull = ValueIsNull($intValueTyped)
  $intValueLoose = 67
  $valIsNull = ValueIsNull($intValueLoose)
  $arrayOfIntLooseType = 4,2,6,9,1
  $valIsNull = ValueIsNull($arrayOfIntLooseType)
  [int32[]]$arrayOfIntStrongType = 1500,2230,3350,4000
  $valIsNull = ValueIsNull($arrayOfIntStrongType)
  #Now take the same int array variable and null it.
  $arrayOfIntStrongType = $null
  $valIsNull = ValueIsNull($arrayOfIntStrongType)
  $stringValueLoose = "String Loose Type"
  $valIsNull = ValueIsNull($stringValueLoose)
  [string]$stringValueStrong = "String Strong Type"
  $valIsNull = ValueIsNull($stringValueStrong)
  $dateTimeArrayLooseValue = @("1/1/2017", "2/1/2017", "3/1/2017").ForEach([datetime])
  $valIsNull = ValueIsNull($dateTimeArrayLooseValue)
  # Note that this has a $null in the array values.  Still returns false correctly.
  $stringArrayLooseWithNull = @("String1", "String2", $null, "String3")
  $valIsNull = ValueIsNull($stringArrayLooseWithNull)
0

I had the same Problem. This solution works for me.

$Word = $null
$Word = [System.Runtime.InteropServices.Marshal]::GetActiveObject('word.application')
if ($Word -eq $null)
{
    $Word = new-object -ComObject word.application
}

https://msdn.microsoft.com/de-de/library/system.runtime.interopservices.marshal.getactiveobject(v=vs.110).aspx

0

Incase you you're like me and you landed here trying to find a way to tell if your PowerShell variable is this particular flavor of non-existent:

COM object that has been separated from its underlying RCW cannot be used.

Then here's some code that worked for me:

function Count-RCW([__ComObject]$ComObj){
   try{$iuk = [System.Runtime.InteropServices.Marshal]::GetIUnknownForObject($ComObj)}
   catch{return 0}
   return [System.Runtime.InteropServices.Marshal]::Release($iuk)-1
}

example usage:

if((Count-RCW $ExcelApp) -gt 0){[System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($ExcelApp)}

mashed together from other peoples' better answers:

and some other cool things to know:

Gregor y
  • 1,762
  • 15
  • 22
0

To test with an if statement on a PsObject instantiated like below, you can use the following. If you use a different type of object, you will have to tweak the value next to greater than

$tempObject = New-Object -TypeName PsObject;

if (($tempObject | Get-Member).Count -gt 4)

$tempObject | Get-Member

TypeName: System.Management.Automation.PSCustomObject

Name MemberType Definition
---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType()
ToString Method string ToString()

Bbb
  • 517
  • 6
  • 27