BlackWasp
Network and Internet
.NET 1.1

Mapping a Drive Letter Programmatically

Some legacy applications do not permit the use of UNC paths when accessing network folders, instead requiring that a drive letter be mapped. When interacting with such software it may be necessary to map a drive letter and later remove mappings using C#.

UNC Paths and Drive Letters

Modern computers are often connected to a network with access to many other remote resources, including shared folders, printers and other devices. These resources are usually referenced with a unique path name defined using the Uniform Naming Convention (UNC). UNC paths generally specify the name of a machine, or cluster of devices, and the name of a folder, file or other resource that is to be accessed. An example of a UNC path that you may use in Windows is "\\BlackWasp\ImageGallery\Wasp.ico". This path leads to the Wasp.ico file, which is held in a shared folder named "ImageGallery" on the "BlackWasp" server.

Earlier in the history of PC operating systems, DOS and Windows-base software relied on the use of drive letters to access files. In the same way that C: usually refers to the main hard disk drive in a PC, other letters would be mapped to networked resources. This permitted applications to open and save files to local and networked folders using the same naming conventions. For example, the "\\BlackWasp\ImageGallery" could be mapped to I:, allowing the file described earlier to be accessed using the path, "I:\Wasp.ico".

The problem with the drive letter approach is that it only permits a maximum of twenty-six local and remote folders to be accessed simultaneously. In a modern office environment this may not be enough. Handily, the use of the UNC is supported by Windows and by the .NET framework without additional effort. However, if you need to interact with older software you may need to add and remove drive letters programmatically.

In this article I will describe some of the manners in which you can map drive letters using C# code. For a demonstration of these methods, download the sample application using the link at the top of this article.

Mapping Drive Letters

The .NET framework does not provide a means of mapping a drive letter. Instead, a Windows API function must be executed using Platform Invocation Services, also known as P/Invoke. To use P/Invoke, classes from the InteropServices namespace are required. To make the code in the samples below more readable, it makes sense to add a using statement for this namespace:

using System.Runtime.InteropServices;

Referencing WNetAddConnection Methods

There are two API functions that can be used to directly map a drive letter to a folder represented by a UNC path. These are called WNetAddConnection2 and WNetAddConnection3. These functions supersede a previous method named WNetAddConnection, which is now present only to allow the correct operation of 16-bit applications.

The two functions are found in the mpr library of the Windows API. We can add references to the functions using the following code:

[DllImport("mpr.dll")]
static extern UInt32 WNetAddConnection2(ref NETRESOURCE lpNetResource,
string lpPassword, string lpUsername, uint dwFlags);

[DllImport("mpr.dll")]
static extern UInt32 WNetAddConnection3(IntPtr hWndOwner, ref NETRESOURCE
lpNetResource, string lpPassword, string lpUserName, uint dwFlags);

You can see that the two declarations only differ in one respect. When using WNetAddConnection3, an additional parameter is provided. This parameter, named hWndOwner in the sample above, receives the handle of a window. If the call to the API function results in the display of interactive dialog boxes, the parent window of these dialog boxes will be the one with the specified handle. In all other respects, the two methods are essentially identical.

When selecting between the two functions, use WNetAddConnection2 where there is no visible user interface or where no user interaction will be required. Use WNetAddConnection3 when calling the function from a form within a Windows application.

Defining the NETRESOURCE Structure

The first parameter of WNetAddConnection2, and the second of WNetAddConnection3, accepts a NETRESOURCE structure. This structure contains information relating to the remote resource that you wish to connect to and the drive letter that you want to assign. Before you can use the mapping commands you must declare this structure, ensuring that it exactly matches that expected by the API. To create the structure, add the following:

[StructLayout(LayoutKind.Sequential)]
public struct NETRESOURCE
{
    public uint dwScope;
    public uint dwType;
    public uint dwDisplayType;
    public uint dwUsage;
    public string lpLocalName;
    public string lpRemoteName;
    public string lpComment;
    public string lpProvider;
}

Although the structure defines eight fields, only four of these are required when mapping drive letters. The first, dwType, is used to specify that we want to map to a network folder. This value is always set to a constant value named RESOURCETYPE_DISK. This constant must be defined within the code as follows:

const uint RESOURCETYPE_DISK = 1;

The lpLocalName field holds the drive letter that will be assigned to the mapped path, which is specified in lpRemoteName. The fourth value that must be provided is lpProvider. This string value determines the network provider that will be used to make the connection. However, normally this is set to null to specify that the operating system should automatically determine the provider to use.

Connection Flags

When the connection functions are called, a series of flags can be passed to the command to affect its behaviour. There are six flags, each defined in the table below:

Flag NameValueDescription
CONNECT_UPDATE_PROFILE0x1If this flag is set, the operating system is instructed to remember the mapping of the drive letter in the user's profile. This means that if the user logs off, when they log on again at a later date, an attempt to restore the connection will be made.
CONNECT_INTERACTIVE0x8When this flag is set, the operating system is permitted to ask the user for authentication information before attempting to map the drive letter.
CONNECT_PROMPT0x10When set, this flag indicates that any default user name and password credentials will not be used without first giving the user the opportunity to override them. This flag is ignored if CONNECT_INTERACTIVE is not also specified.
CONNECT_REDIRECT0x80This flag forces the redirection of a local device when making the connection. For the functionality described in this article the flag has no effect. It is included here for completeness.
CONNECT_COMMANDLINE0x800This flag indicates that if the operating system needs to ask for a user name and password, it should do so using the command line rather than by using dialog boxes. This flag is ignored if CONNECT_INTERACTIVE is not also specified. It is not available to Windows 2000 or earlier versions of the operating system.
CONNECT_CMD_SAVECRED0x1000If set, this flag specifies that any credentials entered by the user will be saved. If it is not possible to save the credentials or the CONNECT_INTERACTIVE is not also specified then the flag is ignored.

We can create these values in the program by defining the following constants:

const uint CONNECT_UPDATE_PROFILE = 0x1;
const uint CONNECT_INTERACTIVE = 0x8;
const uint CONNECT_PROMPT = 0x10;
const uint CONNECT_REDIRECT = 0x80;
const uint CONNECT_COMMANDLINE = 0x800;
const uint CONNECT_CMD_SAVECRED = 0x1000;

Calling WNetAddConnection2

We have now prepared all of the constants and structures that are used by the WNetAddConnection functions. Let's now look at calling the simpler of the two methods, WNetAddConnection2. To do so, we use the following syntax:

result = WNetAddConnection2(ref lpNetResource, lpPassword, lpUserName, dwFlags);

The result value returns an unsigned 32-bit integer. A returned zero indicates that the process completed successfully. All other values represent a standard system error code that explains why the call failed. This value should be checked as the function does not throw exceptions to indicate failure.

lpNetResource holds a NETRESOURCE value. This item contains the details of the shared folder and the local drive letter to be mapped. Note that this parameter must be passed by reference.

lpPassword and lpUserName should contain the login credentials of the user that you wish to use to make the connection to the shared resource. These values may be different to that of the currently logged in interactive user, or of the user that the process is executing under. If you pass null to both of these values, the operating system will automatically use the details from the current user context.

dwFlags is an unsigned integer containing the flags that will be used to control the call's exact behaviour. The flags are described in the earlier. They should be combined using logical bitwise operations.

Example 1 - Simple Mapping

The simplest way to call the WNetAddConnection2 function is to pass the details of the network resource but not specify any user credentials or flags. Using this method, the system will simply try to map a drive letter using the current credentials without prompting the user for login details. The example below tries to map Z: to the path "\\BlackWasp\ImageGallery". To try it, change the drive letter to one that is not already mapped on your system and the UNC path to a shared resource that you have access to.

NETRESOURCE networkResource = new NETRESOURCE();
networkResource.dwType = RESOURCETYPE_DISK;
networkResource.lpLocalName = "Z:";
networkResource.lpRemoteName = @"\\BlackWasp\ImageGallery";
networkResource.lpProvider = null;

uint result = WNetAddConnection2(ref networkResource, null, null, 0);
MessageBox.Show(result.ToString());

Example 2 - Specifying User Credentials

In the second example, we will specify the user name and password to use when connecting the new drive letter to the shared folder. This method is useful when you have requested these details from the user at an earlier time, particularly if you are mapping several drive letters using the same login details. You should not use this method with hard-coded credentials, as in the sample code, as this could pose a security risk.

To try this sample, disconnect the previously mapped drive and modify the call to WNetAddConnection2 as follows. You should include a login name and password that is valid for the UNC path.

uint result = WNetAddConnection2(ref networkResource, "password", "username", 0);

Example 3 - Requesting Credentials

In the third example we will allow the operating system to request the logon credentials to use when accessing the shared resource. We will also instruct the O/S to remember these login details and add the drive mapping to the user's profile. This is achieved by modifying the call as follows:

uint flags = CONNECT_INTERACTIVE | CONNECT_PROMPT
    | CONNECT_CMD_SAVECRED | CONNECT_UPDATE_PROFILE;
    
uint result = WNetAddConnection2(ref networkResource, null, null, flags);

Calling WNetAddConnection3

Calling the WNetAddConnection3 function is almost identical to WNetAddConnection2, except for the addition parameter for a window handle. If you are calling the function from directly within a form's class, you can simply pass the form's handle using the Handle property, as in the next sample. If you are calling from any other class you will need to obtain a window handle first.

NETRESOURCE networkResource = new NETRESOURCE();
networkResource.dwType = RESOURCETYPE_DISK;
networkResource.lpLocalName = "Z:";
networkResource.lpRemoteName = @"\\BlackWasp\ImageGallery";
networkResource.lpProvider = null;

uint result = WNetAddConnection3(this.Handle, ref networkResource, null, null, 0);
MessageBox.Show(result.ToString());

Removing a Drive Mapping

Once you have finished using a drive letter mapping, you may want to disconnect it. If you do, you can use the WNetCancelConnection2 function of the Windows API. In this case there is only a single reference to be created, as there is no definition that includes passing a window handle.

Referencing WNetCancelConnection2

To declare the WNetCancelConnection2 function, use the following code:

[DllImport("mpr.dll")]
static extern uint WNetCancelConnection2(string lpName, uint dwFlags, bool bForce);

Calling WNetCancelConnection2

The cancel command requires three parameters. The first, "lpName", accepts a string containing the drive letter that you wish to disconnect. The second parameter specifies the flags that modify the operation of the command. Only two options are permitted. Passing zero simply disconnects the drive letter, whereas passing CONNECT_UPDATE_PROFILE also removes the drive mapping from the user's profile so that it will not be reconnected after a reboot.

The final parameter, "bForce", is a Boolean value. Usually this should be false. This prevents the mapping from being removed if it is in use. Setting the parameter to true forces disconnection, possibly causing processes using the drive letter to fail.

The following example permanently cancels the Z: mapping, as long as it is not in use:

uint result = WNetCancelConnection2("Z:", CONNECT_UPDATE_PROFILE, false);
MessageBox.Show(result.ToString());
Link to this Page21 September 2008
RSS RSS Feed