Search code examples
c#winapipowershellwnet

Calling WNetAddConnection2 from PowerShell


I am trying to directly call some Win32 API's from a PowerShell script. I managed to make WNetAddConnection work with the code below:

$cp = New-Object Microsoft.CSharp.CSharpCodeProvider
$cpar = New-Object System.CodeDom.Compiler.CompilerParameters

$Code = @"
using System;
using System.Runtime.InteropServices;
namespace Win32Api
{
    public class Net
    {
        [DllImport("mpr.dll", EntryPoint = "WNetAddConnection")]
        public static extern uint Map(string lpRemoteName, string lpPassword, string lpLocalName);
        [DllImport("mpr.dll", EntryPoint = "WNetCancelConnection")]
      public static extern uint Delete(string lpName, byte fForce);     
    }
}
"@

$cp.CompileAssemblyFromSource($cpar, $code)
[Win32Api.Net]::Map("\\REMKOLAPTOP\C$", $null, "W:")

Now I would like to do the same WNetAddConnection2, although I found the proper declarations for C# I am unsure how to declare this for usage in PowerShell. So my questions is: what is the proper "translation" and an example of calling the API would be great.

Please note that I do not want to use non api methods like the one below:

$net = $(New-Object -Com WScript.Network)
$net.MapNetworkDrive("u:", "\\computer\share")

Solution

  • I was able to get the following code working on PowerShell 2.0. Note that I also use one PSCX (http://pscx.codeplex.com) function that just converts a Windows error code into a message. I also setup the options to do an interactive prompt for username and password. Commented out script is included that shows how you could do the same using PowerShell's Get-Credential cmdlet.

    BTW if you supply the username/password via variables to the function be sure to get the order right, it is password followed by username. That bit me for 10 minutes until I finally figured out I had the order wrong. Doh! One other tidbit, for interop help like this be sure to checkout http://www.pinvoke.net. Anyway, hope this helps.

    $WNetAddConnection2WrapperSource = @'
    using System;
    using System.Runtime.InteropServices;
    
    namespace Win32Api {
        public enum ResourceScope {
            RESOURCE_CONNECTED = 1,
            RESOURCE_GLOBALNET,
            RESOURCE_REMEMBERED,
            RESOURCE_RECENT,
            RESOURCE_CONTEXT
        };
    
        public enum ResourceType {
            RESOURCETYPE_ANY,
            RESOURCETYPE_DISK,
            RESOURCETYPE_PRINT,
            RESOURCETYPE_RESERVED = 8
        };
    
        [Flags]
        public enum ResourceUsage {
            RESOURCEUSAGE_CONNECTABLE = 0x00000001,
            RESOURCEUSAGE_CONTAINER = 0x00000002,
            RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
            RESOURCEUSAGE_SIBLING = 0x00000008,
            RESOURCEUSAGE_ATTACHED = 0x00000010,
            RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE | 
                                 RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
        };
    
        public enum ResourceDisplayType {
            RESOURCEDISPLAYTYPE_GENERIC,
            RESOURCEDISPLAYTYPE_DOMAIN,
            RESOURCEDISPLAYTYPE_SERVER,
            RESOURCEDISPLAYTYPE_SHARE,
            RESOURCEDISPLAYTYPE_FILE,
            RESOURCEDISPLAYTYPE_GROUP,
            RESOURCEDISPLAYTYPE_NETWORK,
            RESOURCEDISPLAYTYPE_ROOT,
            RESOURCEDISPLAYTYPE_SHAREADMIN,
            RESOURCEDISPLAYTYPE_DIRECTORY,
            RESOURCEDISPLAYTYPE_TREE,
            RESOURCEDISPLAYTYPE_NDSCONTAINER
        };
    
        [StructLayout(LayoutKind.Sequential)]
        public class NetResource {
            public ResourceScope Scope;
            public ResourceType Type;
            public ResourceDisplayType DisplayType;
            public ResourceUsage Usage;
            public string LocalName;
            public string RemoteName;
            public string Comment;
            public string Provider;
        };
    
        [Flags]
        public enum AddConnectionOptions {
            CONNECT_UPDATE_PROFILE = 0x00000001,
            CONNECT_UPDATE_RECENT  = 0x00000002,
            CONNECT_TEMPORARY      = 0x00000004,
            CONNECT_INTERACTIVE    = 0x00000008,
            CONNECT_PROMPT         = 0x00000010,
            CONNECT_NEED_DRIVE     = 0x00000020,
            CONNECT_REFCOUNT       = 0x00000040,
            CONNECT_REDIRECT       = 0x00000080,
            CONNECT_LOCALDRIVE     = 0x00000100,
            CONNECT_CURRENT_MEDIA  = 0x00000200,
            CONNECT_DEFERRED       = 0x00000400,
            CONNECT_RESERVED       = unchecked((int)0xFF000000),
            CONNECT_COMMANDLINE    = 0x00000800,
            CONNECT_CMD_SAVECRED   = 0x00001000,
            CONNECT_CRED_RESET     = 0x00002000
        }
    
        public static class NativeMethods {
            [DllImport("mpr.dll",  EntryPoint="WNetAddConnection2")]    
            public static extern int WNetAddConnection2(
                NetResource netResource, string password, 
                string username, AddConnectionOptions options);
        }
    }
    '@
    
    Add-Type -TypeDefinition $WNetAddConnection2WrapperSource
    
    $netResource = new-object Win32Api.NetResource
    $netResource.Type = [Win32Api.ResourceType]::RESOURCETYPE_DISK
    $netResource.LocalName = 'P:'
    $netResource.RemoteName = '\\AnotherPC\C'
    
    # Get username and password
    #$cred = Get-Credential
    #$username = $cred.UserName
    #$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($cred.Password)
    #$password = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
    #[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
    
    $opts = [Win32Api.AddConnectionOptions]::CONNECT_INTERACTIVE -bor
            [Win32Api.AddConnectionOptions]::CONNECT_PROMPT -bor
            [Win32Api.AddConnectionOptions]::CONNECT_UPDATE_PROFILE
    
    $res = [Win32Api.NativeMethods]::WNetAddConnection2($netResource, 0, 0, $opts)
    if ($res -ne 0) {
        # This function comes with PSCX http://pscx.codeplex.com
        Get-ExceptionForWin32 $res  
        throw "Failed to connect"
    }
    # Display results
    net use