7

I have a DLL file that I am importing into the PS session. This creates a new .NET class. At the start of the function I want to test if this class exists, and if it doesn't import the DLL file.

Currently I am trying to call the class. This works but I think it's causing problems with the Do {} Until () loop since I have to run the script twice.

my code. note the Do {} Until () loop isn't working. https://gist.github.com/TheRealNoob/f07e0d981a3e079db13d16fe00116a9a

I have found the [System.Type]::GetType() Method but when I run it against any kind of string, valid or invalid class, it doesn't do anything.

Ranadip Dutta
  • 8,857
  • 3
  • 29
  • 45
  • Are you able to see the methods and properties for that custom class? `[classname]::Method` .. Have you tried to access like this ? – Ranadip Dutta Apr 27 '17 at 02:22
  • Yes I can test methods that way. But is that really the best way? Surely there has to be real solutions to this. – Michael Timmerman Apr 27 '17 at 02:57
  • Yes, there is no harm in that. Its the dot net approach. YOu can store the result in a variable and use it wherever you wish – Ranadip Dutta Apr 27 '17 at 03:16
  • See also: [“How do I conditionally add a class with Add-Type -TypeDefinition if it isn't added already?”](/q/16552801). – Sasha Oct 02 '20 at 19:33

4 Answers4

9

You can cast a string to a [system.type] in powershell using -as:

PS C:\> 'int' -as [type]

IsPublic IsSerial Name    BaseType
-------- -------- ----    --------
True     True     Int32   System.ValueType

If the type cannot be found (as with other cast scenarios) then nothing is returned from the expression:

PS C:\> 'notatype' -as [type]

PS C:\> 

Thus you can retrieve a specific type (without iterating through all types of all assemblies loaded in the app domain) with:

$type = 'notatype' -as [type]

#or

if ('int' -as [type]) { 
  'Success!' 
}

#and conveniently

$typeName = 'Microsoft.Data.Sqlite.SqliteConnection'
if (-not($typeName -as [type])) {
   #load the type
}

el2iot2
  • 6,428
  • 7
  • 38
  • 51
  • h/t [SeeminglyScience's](https://www.reddit.com/user/SeeminglyScience/) response to [this reddit post](https://www.reddit.com/r/PowerShell/comments/7ak39n/how_to_use_systemtypegettype/dpapjb0/) – el2iot2 Aug 23 '19 at 14:58
3

In .Net it exists something called Reflexion which allow you deal with everything in your code.

$type = [System.AppDomain]::CurrentDomain.GetAssemblies() | % { $_.GetTypes() | where {$_.Name -eq 'String'}}

Here I look for the type String, but you can look for your type or better the version of the Assembly, you can even find if one method exists with the correct arguments. Have a look to C# - How to check if namespace, class or method exists in C#?.


@Timmerman comments ; he went with :

[System.AppDomain]::CurrentDomain.GetAssemblies() | % { $_.GetTypes() | where {$_.Name -like "SQLiteConnection"}}

or

[System.AppDomain]::CurrentDomain.GetAssemblies() | % { $_.GetTypes() | where {$_.AssemblyQualifiedName -like 'Assembly Qualified Name'}}
Community
  • 1
  • 1
JPBlanc
  • 70,406
  • 17
  • 130
  • 175
  • I have read that article. But I don't think you quite understand what I am looking to do. My DLL file creates (following your example) [System.AppDomain]. I need to know if that exists or not. I can't just write If ( (!( [System.AppDomain] -as [type])) ) { #do stuff } because that results in a terminating error. – Michael Timmerman Apr 27 '17 at 03:41
  • I think JAPBlanc, he is trying to externally load that and call that explicitly. – Ranadip Dutta Apr 27 '17 at 03:42
  • Ranadip Dutta, that is correct. you can test my predicament with any fake class. example: If ( (!( [System.NewClass] -as [type])) ) { Add-Type MyDLL.dll } – Michael Timmerman Apr 27 '17 at 03:45
  • You don't understand. PowerShell is a .NET EXE with an AppDomain. Add-type add an assembly to this AppDomain. This code just test if your class exists in the current PowerShell instance. – JPBlanc Apr 27 '17 at 03:52
  • JPBlanc, you are correct. Your solution works perfectly and then some :) To all future readers the command I am going with is as follows: **[System.AppDomain]::CurrentDomain.GetAssemblies() | % { $_.GetTypes() | where {$_.Name -like "SQLiteConnection"}}** OR **[System.AppDomain]::CurrentDomain.GetAssemblies() | % { $_.GetTypes() | where {$_.AssemblyQualifiedName -like 'Assembly Qualified Name'}}** – Michael Timmerman Apr 27 '17 at 23:32
2

This is what -is and -as were made for. You can also use -is with a try/catch statement to check if the class is there:

try
{
   [test.namespace] -is [type]
}
catch
{
    add-type -typedefinition test.namespace.dll
}

el2iot2 perfectly describes the use of the -as and would be my preferred approach. It is intended to not through errors during class conversion.

0

I recommend the el2iot2's approach with -as.

If you like the try-catch way more than -as, I recommend you to replace [SomeType]::Equals($null, $null) in my answer with [SomeType] -is [type], per the Shaun Stevens's answer.

But, if I had read neither el2iot2's nor Shaun Stevens's answer, I would use the following:

try {
    [SomeType]::Equals($null, $null) >$null
    Write-Host '"SomeType" exists'
}
catch {
    Write-Host '"SomeType" does not exist'
}

(Explanation: every type should have the Equals static method, which allows to call itself with two $nulls and returns $true in such case; trying to refer a non-existing type in this way should throw an instance of SystemException, which can be caught.)

It can be even used as one-liner:

$someTypeExists = try {[SomeType]::Equals($null, $null)} catch {$false}
Sasha
  • 3,599
  • 1
  • 31
  • 52