1

I have a scheduled task that runs a Powershell script. This Powershell script sends a HTTP request and searches through the returned HTML. As a result of this script, I am left with a bunch of dllhost processes that don't close. If I understand right, this is a result of COM objects.

The only variable related to COM objects is this:

$specifiedDiv = $request.ParsedHtml.Body.getElementsByTagName('div') |
    Where-Object{$_.getAttributeNode('class').Value -eq 'results'}

When I run $specifiedDiv.GetType(), I get this as the result:

IsPublic IsSerial Name                                     BaseType                                                                                                                                                              
-------- -------- ----                                     --------                                                                                                                                                              
True     False    __ComObject                              System.MarshalByRefObject 

My question is how can I close this object or prevent the script from creating the dllhost processes?

Edit:

    Function garbageCollect ([object]$ref){

            ([System.Runtime.Interopservices.Marshal]::ReleaseComObject([System.__ComObject]$ref) -gt 0)
            [System.GC]::Collect()
            [System.GC]::WaitForPendingFinalizers()
        }
$parsedHtml = $request.ParsedHtml
$body = $parsedHtml.Body
$divs = $body.getElementsByTagName('div')


$classAttribute = $divs | Where-Object{$_.getAttributeNode('class').Value -eq 'results-found'}

        Remove-Variable -Name classAttribute
        Remove-Variable -Name parsedHtml
        Remove-Variable -Name body
        Remove-Variable -Name divs

        garbageCollect($parsedHtml)
        garbageCollect($body)

        foreach($div in $divs)
        {
            garbageCollect($div)
        }
        foreach($thing in $classAttribute)
        {
            garbageCollect($div)
        }

I tried the above but I still get the dllhost process.

techguy1029
  • 743
  • 10
  • 29

1 Answers1

1

Well, it's more complicated than that. There is

[System.InteropServices.Marshal]::ReleaseComobject($specifiedDiv)

That might work... however, the way .NET works with COM objects, you might have a bunch of intermediate COM objects getting created all over the place. Such as $request, $request.ParsedHtml, $request.ParsedHtml.Body. Then there might be a whole bunch of other COM objects created in calls to $_.getAttributeNode('class').

$parsedHtml = $request.ParsedHtml
$body = $parsedHtml.Body
$divs = $body.getElementsByTagName('div')

foreach ($div in $divs)
{
   $attrNode = $div.getAttributeNode('class')

   if ($attrNode.Value -eq 'results')
   {
      $specificDiv = $attrNode
      break
   }
   else
   {
      [System.Runtime.Interopservices.Marshal]::ReleaseComObject($div)
   }
}

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($divs)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($body)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($parsedHtml)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($request)
### what is this ??? [System.Runtime.Interopservices.Marshal]::ReleaseComObject($requestHome)


#### Do something with $specificDiv if you want...however, you might create more COM objects...


[System.Runtime.Interopservices.Marshal]::ReleaseComObject($specifiedDiv)
Joseph Willcoxson
  • 5,853
  • 1
  • 15
  • 29
  • So would I have to iterate through all the properties of the variable and release the com objects? – techguy1029 Jun 11 '20 at 16:53
  • Well, what I do in C# code when I have COM/.NET reference counting issues... I create intermediate objects. Like $parsedHtml = $request.ParsedHtml, $body = $parsedHtml.Body, etc. Then, when done, call ReleaseComObject on all the individual objects. You might get away with setting $request = $null, and then calling the garbage collection functions. – Joseph Willcoxson Jun 11 '20 at 18:15
  • I created the intermediate objects and I still keep getting the dllhost process. See the edit – techguy1029 Jun 11 '20 at 20:11
  • You're also creating temporary objects in the where {} clause. Will need to write as a more specific foreach loop. So, foreach ($div in $divs) { } Call ReleaseComObject on every $div inside the loop except of course when you find your $specifiedDiv. You are also having a temporary object on the getAttributeNode, so you will need a temporary $classAttribute. If you can encapsulate and instead call [System.GC]::Collect() when you're done, then that might solve your problems as well. – Joseph Willcoxson Jun 11 '20 at 20:53
  • Something like `$classAttribute = $divs | Where-Object{$_.getAttributeNode('class')}` – techguy1029 Jun 12 '20 at 18:46
  • Is the COM object not being removed due to this: `Exception calling "ReleaseComObject" with "1" argument(s): "Object reference not set to an instance of an object` – techguy1029 Jun 15 '20 at 23:58