Search
Module:
Directory

   Desktop Functions:

   Smart Device Functions:


Show Recent Changes
Subscribe (RSS)
Misc. Pages
Comments
FAQ
Helpful Tools
Playground
Suggested Reading
Website TODO List
Download Visual Studio Add-In

WNetGetUniversalName (advapi32)
 
.
Summary
The WNetGetUniversalName function takes a drive-based path for a network resource and returns an information structure that contains a more universal form of the name. What this means is that you can take a file name from a network drive can be converted to a UNC version that may be used from other computers on the network that do not have the same drive mappings.

C# Signature:

[DllImport("mpr.dll")]
[return:MarshalAs(UnmanagedType.U4)]
static extern int WNetGetUniversalName(
    string lpLocalPath,
    [MarshalAs(UnmanagedType.U4)] int dwInfoLevel,
    IntPtr lpBuffer,
    [MarshalAs(UnmanagedType.U4)] ref int lpBufferSize);

VB Signature:

Declare Function WNetGetUniversalName Lib "advapi32.dll" (TODO) As TODO

Constants:

const int UNIVERSAL_NAME_INFO_LEVEL = 0x00000001;
const int REMOTE_NAME_INFO_LEVEL = 0x00000002;

const int ERROR_MORE_DATA = 234;
const int NOERROR = 0;

Wrapper

    using System.Runtime.InteropServices;

    static string GetUniversalName(string localPath)
    {
        // The return value.
        string retVal = null ;

        // The pointer in memory to the structure.
        IntPtr buffer = IntPtr.Zero;

        // Wrap in a try/catch block for cleanup.
        try
        {
        // First, call WNetGetUniversalName to get the size.
        int size = 0;

        // Make the call.
        // Pass IntPtr.Size because the API doesn't like null, even though
        // size is zero.  We know that IntPtr.Size will be
        // aligned correctly.
        int apiRetVal = WNetGetUniversalName(localPath, UNIVERSAL_NAME_INFO_LEVEL, (IntPtr) IntPtr.Size, ref size);

        // If the return value is not ERROR_MORE_DATA, then
        // raise an exception.
        if (apiRetVal != ERROR_MORE_DATA)
            // Throw an exception.
            throw new Win32Exception(apiRetVal);

        // Allocate the memory.
        buffer = Marshal.AllocCoTaskMem(size);

        // Now make the call.
        apiRetVal = WNetGetUniversalName(localPath, UNIVERSAL_NAME_INFO_LEVEL, buffer, ref size);

        // If it didn't succeed, then throw.
        if (apiRetVal != NOERROR)
            // Throw an exception.
            throw new Win32Exception(apiRetVal);

        // Now get the string.  It's all in the same buffer, but
        // the pointer is first, so offset the pointer by IntPtr.Size
        // and pass to PtrToStringAuto.
        retVal = Marshal.PtrToStringAuto(new IntPtr(buffer.ToInt64() + IntPtr.Size));
        }
        finally
        {
        // Release the buffer.
        Marshal.FreeCoTaskMem(buffer);
        }

        // First, allocate the memory for the structure.

        // That's all folks.
        return retVal;
    }

Notes:

Note by Günter Prossliner 2005-08-29

PtrToStringAuto doesn't work on my system. I have tested in on a clean WindowsXP professional maschine. The PtrToStringAuto function just returns garbage. After stepping into the function with the debugger I was looking a the Memory - Windows pointing to the address of "buffer". What I've seen there was a "normal" zero - terminated ANSI String.

After i have changed

retVal = Marshal.PtrToStringAuto(new IntPtr(buffer.ToInt64() + IntPtr.Size));

into

retVal = Marshal.PtrToStringAnsi(new IntPtr(buffer.ToInt64() + IntPtr.Size), size);

all worked fine.

If you take a look at the IL - Code of Marshal.PtrToStringAuto(IntPtr) you can see that this function calls 'kernel32:lstrlen' and 'kernel32:lstrcpy' using a buffer (StringBuilder) with the size of lstrlen.

The code of Marshal.PtrToStringAuto(IntPtr, Int32 size) looks quite different: It checks out the Marshal.SystemDefaultCharSize field and simply calls 'PtrToStringAnsi' or 'PtrToStringUni'.

Tips & Tricks:

Please add some!

Sample Code:

/* This needs cleaning up -- errr but I'm paid by the hour so I don't have the time. I mostly ripped the wrapper and modified it. It provides an alternative to pointer arithmetic though...*/
/*
typedef struct _REMOTE_NAME_INFO
{
    LPTSTR lpUniversalName;
    LPTSTR lpConnectionName;
    LPTSTR lpRemainingPath;
} REMOTE_NAME_INFO;
*/

[StructLayout(LayoutKind.Sequential)]
struct _REMOTE_NAME_INFO
{
    public IntPtr lpUniversalName;
    public IntPtr lpConnectionName;
    public IntPtr lpRemainingPath;
}

public struct RemoteNameInfo
{
    public string universalName;
    public string connectionName;
    public string remainingPath;
}

public class WNet
{
    const int UNIVERSAL_NAME_INFO_LEVEL = 0x00000001;
    const int REMOTE_NAME_INFO_LEVEL = 0x00000002;

    const int ERROR_MORE_DATA = 234;
    const int ERROR_NOT_CONNECTED = 2250;
    const int NOERROR = 0;

    [DllImport("mpr.dll", CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.U4)]
    private static extern int WNetGetUniversalNameW(
        string lpLocalPath,
        [MarshalAs(UnmanagedType.U4)] int dwInfoLevel,
        IntPtr lpBuffer,
        [MarshalAs(UnmanagedType.U4)] ref int lpBufferSize);


    public static RemoteNameInfo GetRemoteNameInfo(string localPath)
    {
        // The return value.
        RemoteNameInfo retVal;
        _REMOTE_NAME_INFO rni;

        // The pointer in memory to the structure.
        IntPtr buffer = IntPtr.Zero;

        // Wrap in a try/catch block for cleanup.
        try
        {
            // First, call WNetGetUniversalName to get the size.
            int size = 0;

            // Make the call.
            // Pass IntPtr.Size because the API doesn't like null, even though
            // size is zero.  We know that IntPtr.Size will be
            // aligned correctly.
            int apiRetVal = WNetGetUniversalNameW(localPath, REMOTE_NAME_INFO_LEVEL, (IntPtr)IntPtr.Size, ref size);

            //  if the return value is ERROR_NOT_CONNECTED, then
            //  this is a local path
            if (apiRetVal == ERROR_NOT_CONNECTED)
            {
                retVal = new RemoteNameInfo();
                retVal.connectionName = Path.GetPathRoot(localPath);
                retVal.remainingPath = localPath.Substring(Path.GetPathRoot(localPath).Length);
                retVal.universalName = localPath;
                return retVal;
            }

            // If the return value is not ERROR_MORE_DATA, then
            // raise an exception.
            if (apiRetVal != ERROR_MORE_DATA)
                // Throw an exception.
                throw new System.ComponentModel.Win32Exception();

            // Allocate the memory.
            buffer = Marshal.AllocCoTaskMem(size);

            // Now make the call.
            apiRetVal = WNetGetUniversalNameW(localPath, REMOTE_NAME_INFO_LEVEL, buffer, ref size);

            // If it didn't succeed, then throw.
            if (apiRetVal != NOERROR)
                // Throw an exception.
                throw new System.ComponentModel.Win32Exception();

            // Now get the string.  It's all in the same buffer, but
            // the pointer is first, so offset the pointer by IntPtr.Size
            // and pass to PtrToStringAuto.
            //retVal = Marshal.PtrToStringAuto(new IntPtr(buffer.ToInt64() + IntPtr.Size));

            rni = (_REMOTE_NAME_INFO)Marshal.PtrToStructure(buffer, typeof(_REMOTE_NAME_INFO));

            retVal.connectionName = Marshal.PtrToStringAuto(rni.lpConnectionName);
            retVal.remainingPath = Marshal.PtrToStringAuto(rni.lpRemainingPath);
            retVal.universalName = Marshal.PtrToStringAuto(rni.lpUniversalName);

            return retVal;
        }
        finally
        {
            // Release the buffer.
            Marshal.FreeCoTaskMem(buffer);
        }

        // First, allocate the memory for the structure.
        // That's all folks.
    }
}

.NET Use in VB

by Mike Gerbi

12/05/2007

Summary
This utilizes the WNetGetUniversalName method in MPR.dll. Once implemented, all you have to do is create an instance of the MPR_DotNET_Version class and use the GetUniversalName method. You can also add on to this class for other functionality if you choose. Hope this helps.

Make A New Class

Imports System.Runtime.InteropServices

Namespace Custom.Win32

    'You can add more functionality from this DLL in this namespace and import it in future projects to get everything.
    Public Class MPR_DotNET_Version
    Private bufSize As Integer = 1000
    Private myPath As String = ""
    Private lpBuffer As IntPtr = Marshal.AllocHGlobal(bufSize)

    Public Enum INFO_LEVEL As Integer
        UNIVERSAL_NAME_INFO_LEVEL = 1
        REMOTE_NAME_INFO_LEVEL = 2
    End Enum

    Public Sub New(ByVal path As String)
        myPath = path
    End Sub

    Public Function GetUnivseralName() As String
        Dim uname As New UNIVERSAL_NAME_INFO

        Try
        Dim result As String = myPath
        Dim ret As Int32 = GetUName(result, INFO_LEVEL.UNIVERSAL_NAME_INFO_LEVEL, lpBuffer, bufSize)

        If ret = 0 Then
            Marshal.PtrToStructure(lpBuffer, uname)
        ElseIf ret = 2250 Then
            Throw New ArgumentException("Not Connected")
        End If
        Catch ex As Exception
        MsgBox(ex.Message)
        Finally
        Marshal.FreeHGlobal(lpBuffer)
        End Try

        Return uname.UniversalName.ToString

    End Function
    <DllImport("mpr.dll", Entrypoint:="WNetGetUniversalName", CharSet:=CharSet.Auto, SetLastError:=False)> _
    Private Shared Function GetUName(ByVal Path As String, ByVal outName As INFO_LEVEL, _
        ByVal bObj As IntPtr, ByRef bSize As Integer) As Integer

    End Function

    End Class

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto), Serializable()> Class UNIVERSAL_NAME_INFO
    <MarshalAs(UnmanagedType.LPTStr)> Public UniversalName As String
    <MarshalAs(UnmanagedType.LPTStr)> Public ConnectionName As String
    <MarshalAs(UnmanagedType.LPTStr)> Public RemainingPath As String

    End Class

End Namespace

Create An instance

Imports ScanningTools.Custom.Win32

Public Sample Class

Private path as String

Public Sub GetPath()

    Dim result As DialogResult = mainFolderBrowserDialog.ShowDialog
        If result = Windows.Forms.DialogResult.OK Then
        Dim dirInfo As DirectoryInfo = New DirectoryInfo(mainFolderBrowserDialog.SelectedPath)
        path = mainFolderBrowserDialog.SelectedPath

           'Here I get the UNC path and use it later in another routine.
        Dim UNI As MPR_DotNET_Version = New MPR_DotNET_Version(path)
        path = UNI.GetUnivseralName

        For Each f As FileInfo In dirInfo.GetFiles
            If f.Extension = ".txt" Then
            TextFileListView.Items.Add(f.Name)
            ElseIf f.Extension = ".pdf" Then
            PDFListView.Items.Add(f.Name)
            End If
        Next

        ElseIf result = Windows.Forms.DialogResult.Cancel Then
        Me.Close()
        Else
        Throw New System.Exception
        End If

    Catch ex As System.Exception
        MsgBox(ex.Message)
        WriteToErrorLog(ex)
    End Try

End Sub

Alternative VB.Net Translation

by Sean Kendrick (Jaeden "Sifo Dyas" al'Raec Ruiner)

After some overview, and minor deliberation, I've flushed out a full port of the above manifestations,

finding not only the best of both the other contributions, but also a little of my own designs for

personal use. I needed this design for SQL Server, as utilizing Ad-Hoc queries across network lines

the Server inherently treats "C:\" as the local drive on the Server, so for a query on a client system

that needs to be translated to "\\MyComputer\C$" in order to be functional. I apologize as well for my

little extension, but with my enjoyment of C style assignment and comparison in one step, I created that

for my own use, and did not wish to alter my class in order to post it here, so I included it.

I also noticed that the Marshal.PtrToStringAnsi() did not work for me, while PtrToStringAuto did, so

I am currently unaware how I might test for which is the best to use for any given situation, but I

figure anyone can make that minor alteration if necessary on their own.

   <System.Runtime.CompilerServices.Extension()> _
   Public Function Assign(Of T)(ByRef self As T, ByVal Val As T) As T
      self = Val
      Return Val
   End Function

    ''' <summary>
    ''' Not Inhertitable Shared Class that Computes and Returns the UNC Path for any file or directory,
    ''' Providing not only the Original Path String, but also the UNC formatted string using Default Shares
    ''' for Local Files.
    ''' </summary>
    ''' <remarks></remarks>
    Public NotInheritable Class UNCPath
       Implements IDisposable

    #Region "-------------::< Fields & Constants >::-------------"
       Private Const UNIVERSAL_NAME_INFO_LEVEL As Integer = 1
       Private Const REMOTE_NAME_INFO_LEVEL As Integer = 2
       Private Const NO_ERROR As Integer = 0
       Private Const ERROR_MORE_DATA As Integer = 234
       Private Const ERROR_NOT_CONNECTED As Integer = 2250
       Private Const UNCFMT As String = "\\{0}\{1}$\{2}"

       Private _origpath As String = ""
       Private _local As Boolean = False
       Private _error As Integer = NO_ERROR
       Private _fi As IO.FileInfo = Nothing
       Private _di As IO.DirectoryInfo = Nothing
       Private _isfile As Boolean = False
       Private disposedValue As Boolean = False      ' To detect redundant calls
       Private _unc As String
       Private _connect As String
       Private _path As String
    #End Region

    #Region "-------------::< Class Properties >::-------------"
       ''' <summary>
       ''' Gets UNC Fully Qualified Path to File or Directory
       ''' </summary>
       ''' <returns>String</returns>
       ''' <remarks></remarks>
       Public ReadOnly Property UNCName() As String
          Get
             Return _unc
          End Get
       End Property

       ''' <summary>
       ''' Gets the Connection or UNC Initial Share Point [\\ComputerName\Share]
       ''' </summary>
       ''' <returns>String</returns>
       ''' <remarks></remarks>
       Public ReadOnly Property Connection() As String
          Get
             Return _connect
          End Get
       End Property

       ''' <summary>
       ''' Gets the Path location within the Connection to the Directory or File
       ''' </summary>
       ''' <returns>String</returns>
       ''' <remarks></remarks>
       Public ReadOnly Property Path() As String
          Get
             Return _path
          End Get
       End Property

       ''' <summary>
       ''' Gets the Original File/Directory provided the UNCPath Class
       ''' </summary>
       ''' <returns>String</returns>
       ''' <remarks></remarks>
       Public ReadOnly Property OriginalPath() As String
          Get
             Return _origpath
          End Get
       End Property

       ''' <summary>
       ''' Gets a flag indicating if the Original File is on a Local Drive
       ''' </summary>
       ''' <returns>Boolean</returns>
       ''' <remarks></remarks>
       Public ReadOnly Property IsLocal() As Boolean
          Get
             Return _local
          End Get
       End Property

       ''' <summary>
       ''' Gets the Last Error information incase it all goes pear shaped.
       ''' </summary>
       ''' <returns>Integer</returns>
       ''' <remarks></remarks>
       Public ReadOnly Property LastError() As Integer
          Get
             Return _error
          End Get
       End Property

       ''' <summary>
       ''' Gets a flag indicating if the Target is a File
       ''' </summary>
       ''' <returns>Boolean</returns>
       ''' <remarks></remarks>
       Public ReadOnly Property IsFile() As Boolean
          Get
             Return _isfile
          End Get
       End Property

       ''' <summary>
       ''' Gets an IO.FileInfo Object Pointing to the UNC Shared File
       ''' </summary>
       ''' <returns>IO.FileInfo</returns>
       ''' <remarks>Returns Nothing if the Provided Path was a Directory</remarks>
       Public ReadOnly Property File() As IO.FileInfo
          Get
             Return _fi
          End Get
       End Property

       ''' <summary>
       ''' Gets an IO.DirectoryInfo Object pointing to the UNC Share
       ''' </summary>
       ''' <returns>IO.DirectoryInfo</returns>
       ''' <remarks>Returns the Directory of the File if the Provided Path was a File</remarks>
       Public ReadOnly Property Directory() As IO.DirectoryInfo
          Get
             Return _di
          End Get
       End Property
    #End Region

    #Region "-------------::< Class Constructors >::-------------"
       ''' <summary>
       ''' Private Constructor
       ''' </summary>
       ''' <param name="Path">Fully Qualified Path to File or Directory</param>
       ''' <remarks></remarks>
       Private Sub New(ByVal Path As String)
          _origpath = Path
          GetUnivseralName()
          If _error = NO_ERROR Then
             If _isfile.Assign(IO.File.Exists(Path)) Then
                _fi = New IO.FileInfo(_unc)
                _di = _fi.Directory
             Else : _di = New IO.DirectoryInfo(_unc)
             End If
          End If
       End Sub
    #End Region

    #Region "-------------::< Shared Methods >::-------------"
       ''' <summary>
       ''' Returns a IO.FileInfo object to the UNC Qualified Path to a File.  
       ''' </summary>
       ''' <param name="filepath">Fully Qualifie Path, either with UNC, Local, or Mapped Drive to a file</param>
       ''' <returns>If the Path is not a file or an error, returns Nothing, else IO.FileInfo</returns>
       ''' <remarks></remarks>
       Public Shared Function GetUNCFile(ByVal filepath As String) As IO.FileInfo
          Dim unc As UNCPath = GetUNCPath(filepath)
          Dim fi As IO.FileInfo = unc.File
          unc.Free()
          Return fi
       End Function

       ''' <summary>
       ''' Returns a IO.DirectoryInfo object to the UNC Qualified Path to a Directory, or the Directory containing a File
       ''' </summary>
       ''' <param name="filepath">Fully Qualifie Path, either with UNC, Local, or Mapped Drive to either a File or a Directory</param>
       ''' <returns>On Error, Returns Nothing, else IO.DirectoryInfo</returns>
       ''' <remarks></remarks>
       Public Shared Function GetUNCDir(ByVal filepath As String) As IO.DirectoryInfo
          Dim unc As UNCPath = GetUNCPath(filepath)
          Dim di As IO.DirectoryInfo = unc.Directory
          unc.Free()
          Return di
       End Function

       ''' <summary>
       ''' Creates a UNCPath object to a File or Directory and returns whether or the operation was successful.
       ''' </summary>
       ''' <param name="FilePath">Fully Qualifie Path, either with UNC, Local, or Mapped Drive</param>
       ''' <param name="UNC">Reference Passed UNCPath Object to Return</param>
       ''' <returns>Boolean - Success/Failure</returns>
       ''' <remarks></remarks>
       Public Shared Function GetUNCPath(ByVal FilePath As String, ByRef UNC As UNCPath) As Boolean
          UNC = GetUNCPath(FilePath)
          Return (UNC IsNot Nothing) AndAlso (UNC.LastError = NO_ERROR)
       End Function

       ''' <summary>
       ''' Returns a UNCPath object to a File or Directory
       ''' </summary>
       ''' <param name="FilePath">Fully Qualifie Path, either with UNC, Local, or Mapped Drive to either a File or a Directory</param>
       ''' <returns>UNCPath</returns>
       ''' <remarks></remarks>
       Public Shared Function GetUNCPath(ByVal FilePath As String) As UNCPath
          Return New UNCPath(FilePath)
       End Function
    #End Region

    #Region "-------------::< Public Methods >::-------------"
    #Region " IDisposable Support "
       ''' <summary>
       ''' This code added by Visual Basic to correctly implement the disposable pattern.
       ''' </summary>
       ''' <remarks></remarks>
       Public Sub Dispose() Implements IDisposable.Dispose
          ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
          Dispose(True)
          GC.SuppressFinalize(Me)
       End Sub
    #End Region
    #End Region

    #Region "-------------::< Private Methods >::-------------"
       ''' <summary>
       ''' Retrieves the Unversal Naming Convention for the provided Path, using the Win32 API MPR.DLL
       ''' </summary>
       ''' <remarks></remarks>
       Private Sub GetUnivseralName()
          Dim lpBuffer As IntPtr = IntPtr.Zero
          Try
             Dim size As Integer = 0
             _error = WNetGetUniversalName(_origpath, REMOTE_NAME_INFO_LEVEL, CType(IntPtr.Size, IntPtr), size)
             If _local.Assign(_error = ERROR_NOT_CONNECTED) Then
                ' local file so process locally, original file still maintains the C:\ notation
                ' but UNCName is formatted to appear using the default [Drive]$ share of the system.
                _connect = IO.Path.GetPathRoot(_origpath)
                _path = _origpath.Substring(_connect.Length)
                _unc = String.Format(UNCFMT, My.Computer.Name, _connect(0), _path)
             ElseIf _error = ERROR_MORE_DATA Then
                'received buffer size from initial call so allocate memory an re-call for actual data
                lpBuffer = Marshal.AllocCoTaskMem(size)
                If _error.Assign(WNetGetUniversalName(_origpath, REMOTE_NAME_INFO_LEVEL, lpBuffer, size)) = NO_ERROR Then
                   Dim rni As _REMOTE_NAME_INFO 'if call successful convert data over from buffer structure to actual strings
                   rni = CType(Marshal.PtrToStructure(lpBuffer, GetType(_REMOTE_NAME_INFO)), _REMOTE_NAME_INFO)
                   _connect = Marshal.PtrToStringAuto(rni.lpConnectionName)
                   _path = Marshal.PtrToStringAuto(rni.lpRemainingPath)
                   _unc = Marshal.PtrToStringAuto(rni.lpUniversalName)
                Else : Throw New System.ComponentModel.Win32Exception(_error)
                End If
             Else : Throw New System.ComponentModel.Win32Exception(_error)
             End If
          Finally
             Marshal.FreeCoTaskMem(lpBuffer) 'finalize marshal allocated memory
          End Try
       End Sub

       ''' <summary>
       ''' Implementis the IDisposable Inteface for usage with the Using operator
       ''' </summary>
       ''' <param name="disposing">Flag to Dispose Managed Code</param>
       ''' <remarks></remarks>
       Private Sub Dispose(ByVal disposing As Boolean)
          If Not Me.disposedValue Then
             If disposing Then
             End If
             _fi = Nothing
             _di = Nothing
          End If
          Me.disposedValue = True
       End Sub

    #Region "===========[ API DLL HEADERS ]============"
       ''' <summary>
       ''' DLL Header for the WNetGetUniversalName
       ''' </summary>
       ''' <param name="lpLocalPath">String containing the Full Path to the Object</param>
       ''' <param name="dwInfoLevel">Integer Flag indicating the Type of Structure to Return</param>
       ''' <param name="lpBuffer">IntPtr to the Buffer to receive the data</param>
       ''' <param name="lpBufferSize">Reference Passed Integer for the Size of the Buffer</param>
       ''' <returns>Integer</returns>
       ''' <remarks></remarks>
       <DllImport("mpr.dll", Entrypoint:="WNetGetUniversalName", CharSet:=CharSet.Unicode, SetLastError:=False)> _
       Private Shared Function WNetGetUniversalName(ByVal lpLocalPath As String, _
                                              <MarshalAs(UnmanagedType.U4)> ByVal dwInfoLevel As Integer, _
                                             ByVal lpBuffer As IntPtr, _
                                             <MarshalAs(UnmanagedType.U4)> ByRef lpBufferSize As Integer) As <MarshalAs(UnmanagedType.U4)> Integer
       End Function
    #End Region

    #Region "====[ INTERNAL STRUCTURE DECLARATION ]===="
       ''' <summary>
       ''' Remote Name Info Structure for use with the WNetGetUniveralName API Call
       ''' </summary>
       ''' <remarks></remarks>
       <StructLayout(LayoutKind.Sequential)> _
       Private Structure _REMOTE_NAME_INFO
          Public lpUniversalName As IntPtr
          Public lpConnectionName As IntPtr
          Public lpRemainingPath As IntPtr
       End Structure
    #End Region
    #End Region
    End Class

Alternative Managed API:

Do you know one? Please contribute it!

Documentation

Please edit this page!

Do you have...

  • helpful tips or sample code to share for using this API in managed code?
  • corrections to the existing content?
  • variations of the signature you want to share?
  • additional languages you want to include?

Select "Edit This Page" on the right hand toolbar and edit it! Or add new pages containing supporting types needed for this API (structures, delegates, and more).

 
Access PInvoke.net directly from VS:
Terms of Use
Find References
Show Printable Version
Revisions