Search code examples
powershellpinvoke

Caling UnManaged Code from Powershell - Handling Enum Type


I am using Powershell to call the group policy update API from userenv.dll (see here and here). This works fine:

$sig =@'
[DllImport("userenv.dll", SetLastError = true, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool RefreshPolicy([MarshalAs(UnmanagedType.Bool)] bool bMachine);
'@
$mytype = add-type -memberdefinition $sig -Name "Win32RefreshGP" -Namespace Win32Functions -PassThru
$mytype::RefreshPolicy($false) # Refresh the user policy

When I import this type though as follows:

$sigEx =@'
public enum RefreshPolicyOption:uint
{ 
    RP_FORCE = 1,
    RP_SYNC = 2 
}

[DllImport("userenv.dll", SetLastError = true, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool RefreshPolicyEx([MarshalAs(UnmanagedType.Bool)] bool bMachine, RefreshPolicyOption dwOptions = 0);
'@
$mytypeEx = add-type -memberdefinition $sigEx -Name "Win32RefreshGP" -Namespace Win32Functions -PassThru
$mytypeEx::RefreshPolicyEx($false) # Refresh the user policy

The following is raised calling $mytypeEx::RefreshPolicyEx($false)

Method invocation failed because [System.Object[]] does not contain a method named 'RefreshPolicyEx'.
At line:1 char:1
+ $mytypeEx::RefreshPolicyEx($false) # Refresh the user policy
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

This seems to be because of the attempt to use the enum in the type defintion. Any ideas?

I have tried using single/double quotes. I'd epxect the type definition to work as it works in C#.


Solution

  • The issue is fairly simple, when you use -PassThru, Add-Type will output a type instance for every type defined in your C# inline code, in this case $mytypeEx will have 2 types, thus the error you're getting:

    PS /> $mytypeEx
    
       Namespace: Win32Functions
    
    Access        Modifiers           Name
    ------        ---------           ----
    public        class               Win32RefreshGP : object
    public        enum                Win32RefreshGP.RefreshPolicyOption : uint
    
    PS /> $mytypeEx.Count
    2
    

    Solution is as simple as indexing the Win32RefreshGP type and then call its static member:

    PS /> $mytypeEx[0]::RefreshPolicyEx($false)
    

    Or you can remove -PassThru and then call the static member from the type itself:

    PS /> [Win32Functions.Win32RefreshGP]::RefreshPolicyEx($false)