1

I have a powershell script. This script runs perfectly fine if I open it in Powershell ISE, however, if I right click on the file and click 'run with powershell' the script throws an error.

Furthermore, I read in previous threads that the following execution pattern solved the issue for some people:

powershell -STA -File script.ps1

In this case this didn't solve the issue, however it did allow me to read the error:

At C:\Users\sancarn\AppData\Local\Temp\script.ps1:20 char:20
+         $parent = [System.Windows.Forms.TreeNode]$global:database.Ite ...
+                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unable to find type [System.Windows.Forms.TreeNode].
At C:\Users\sancarn\AppData\Local\Temp\script.ps1:27 char:36
+ ...          [void]$node.nodes.add([System.Windows.Forms.TreeNode]::new(" ...
+                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unable to find type [System.Windows.Forms.TreeNode].
At C:\Users\sancarn\AppData\Local\Temp\script.ps1:33 char:45
+ ... PSCustomObject]IWDBGetChildren([System.Windows.Forms.TreeNode]$node)  ...
+                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unable to find type [System.Windows.Forms.TreeNode].
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : TypeNotFound

This having been said, I'm not sure I can really do anything about this error specifically... I already load System.Windows.Forms and System.Drawing... Does anyone have any idea how to execute this file properly?

Edit

Other attempts at trying to fix the issue:

powershell -NoExit -STA -File script.ps1
powershell -NoExit -STA -File script.ps1 -Scope Global

Edit 2

I have also tried adding:

Add-Type -AssemblyName System.Windows.Forms

To the top of the powershell script. The issue remains unresolved however.


Edit:

Not sure why this is being flagged as a duplicate after

  1. this answer already has a recommended answer and
  2. the recommended answer demonstrates why this is different.

...

Sancarn
  • 2,575
  • 20
  • 45

1 Answers1

0

As @PetSerAl said in his comment, the answer is in https://stackoverflow.com/a/34637458

Every PowerShell script is completely parsed before the first statement in the script is executed. An unresolvable type name token inside a class definition is considered a parse error. To solve your problem, you have to load your types before the class definition is parsed, so the class definition has to be in a separate file.

Ultimately to resolve the issue I need to either load my class definitions as a separate file, or store my declarations in a header file, which calls the class definitions (and the rest of the script).

I am amazed this was even an issue but it works now, so that's good...

Edit

In my case it is a bit different to the post linked by the solution author. I'm actually building a ruby library for executing Powershell on windows machines. Currently I don't write data to a file and I can't guarantee that a here-string/invoke-expression will work.

Invoke-Expression @"
$anotherString=@"
    hello world!
"@    
"@ <--- Powershell will throw an error here!

Instead I've decided to encode the body if a header is present, and then decode and execute at runtime

require 'base64'
if header!=""
    encoded_body = Base64.strict_encode64(body.encode("utf-16le"))
    body = <<-END_TRANSFORMATION
        #{header}

        $encoded_body = "#{encoded_body}"
        Invoke-Expression $([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($encoded_body)))
    END_TRANSFORMATION
end

So far this has appeared to work in all cases, doesn't need external files, and will work even if users use here-doc strings in their Powershell scripts!

mklement0
  • 382,024
  • 64
  • 607
  • 775
Sancarn
  • 2,575
  • 20
  • 45
  • You can't nest here-strings that use the same delimiter, but you can make your `Invoke-Expression` command if you use `@'...'@` as the outer delimiters. – mklement0 May 05 '18 at 22:54
  • Indeed, the problem here is that the ruby class is specifically being designed to allow for anyone to execute any Powershell... I can't forsee whether someone is going to use `@'...'@` or `@"..."@`. – Sancarn May 05 '18 at 23:10
  • So users will type their code _literally_ inside the here-string passed to `Invoke-Expression`? – mklement0 May 05 '18 at 23:12
  • Yup, very literally. It's a Ruby library for executing Powershell. So the users will be developers. The script itself, was simply injected as an encoded command before. So yeah, they literally type the here-string passed to Invoke-Expression. – Sancarn May 06 '18 at 00:24
  • Note - you could use `powershell.exe -encodedCommand ...` to execute the encoded string directly: (subject to the line length limitations of course). See `powershell.exe -?' for information. – Bruce Payette May 06 '18 at 19:36
  • Hi @BrucePayette I already do so, but that doesn't solve the original issue... xD It is however what inspired me to use this method ([see the script](https://gist.github.com/sancarn/00e44231eba3ac20123e10601f236175/b03b4da532a0d74f68faf2d6c4c1f1b2d65505ca#file-powershell-rb) if you'd like.) – Sancarn May 06 '18 at 21:12
  • @Sancarn I wondered if that might have been the case :-) Other alternatives (until we finish `using assembly System.Windows.Forms`) would be to (1) if you can, create a module that loads the necessary assemblies then add `#requires -Module moduleThatLoadsWinforms` to your script. The module will be loaded before the script is parsed. or (2) Use something like `ps2exe -noconsole` to wrap your script into a Windows executable that is already linked to winforms. – Bruce Payette May 06 '18 at 21:45
  • Interesting idea to use `#requires`, can you do this within the same Powershell file? Or does it require seperate files. – Sancarn May 06 '18 at 22:45
  • Separate files I'm afraid. – Bruce Payette May 15 '18 at 17:23