首页 > 解决方案 > VB.Net 中的 UNC 空间

问题描述

我在这里(和其他地方)看到了很多帖子,这些帖子解决了在 UNC 路径上查找可用空间的问题,但这些都涉及使用 GetDiskFreeSpaceEx,它似乎只在 C 中可用。

我已经将它包含在一些测试代码中并得到:

Error   BC30451 'GetDiskFreeSpaceEx' is not declared. It may be inaccessible due to its protection level.   

IDE 提供了创建方法或属性的解决方案。

在控制台程序中尝试这个...

VB 相当于什么?

标签: vb.net

解决方案


使用 P/Invoke 时,我通常使用方法名称编写一个,而不仅仅是方法声明本身,并尝试以 .NET 方式而不是低级 C++ 样式公开功能。

例如,在本机函数的方法描述中GetDiskFreeSpaceEx提到,如果提供的路径是 UNC 路径,则后面的反斜杠是强制性的!

  • C++风格:写在描述中,如果调用者没有以那种方式提供它,他们将自责,RTFM。
  • .NET 风格:我们为调用者调整它,他们不必担心这样的实现细节。

我还将为每个可用信息提供 3 种不同的方法(我为使用的空间提供了第 4 种方法),以及一次获得多个值的常用方法。

它的外观如下:

Imports System
Imports System.ComponentModel
Imports System.IO
Imports System.Runtime.InteropServices

Namespace PInvoke.Kernel32

    Public Class GetDiskFreeSpaceEx

        ''' <summary>
        ''' Retrieves the number of bytes currently available to the caller. This takes into account any quotas etc. that may
        ''' exist on the folder resp. file share.
        ''' </summary>
        ''' <param name="folderName">The name of the path to retrieve the for me available bytes from.</param>
        ''' <returns>The maximum number of bytes that are currently available for me to use.</returns>
        Public Shared Function GetAvailableBytesToCaller(folderName As String) As UInt64
            Dim result As SizeInfo = Invoke(folderName)
            Return result.BytesAvailableToCaller
        End Function

        ''' <summary>
        ''' Retrieves the number of bytes currently available on the according volume. This may be more than I can use,
        ''' see <see cref="GetAvailableBytesToCaller(String)" /> for a method that respects quotas etc.
        ''' </summary>
        ''' <param name="folderName">The name of the path to retrieve the (in general) available bytes from.</param>
        ''' <returns>The maximum number of bytes that are available for all users together.</returns>
        Public Shared Function GetAvailableBytesInTotal(folderName As String) As UInt64
            Dim result As SizeInfo = Invoke(folderName)
            Return result.BytesAvailableInTotal
        End Function

        ''' <summary>
        ''' Retrieves the number of bytes currently used on the according volume. This value corresponds to 
        ''' <see cref="GetBytesInTotal(String)"/> - <see cref="GetAvailableBytesInTotal(String)"/>.
        ''' </summary>
        ''' <param name="folderName">The name of the path to retrieve the used bytes from.</param>
        ''' <returns>The number of bytes that are already used by all users together.</returns>
        Public Shared Function GetUsedBytesInTotal(folderName As String) As UInt64
            Dim result As SizeInfo = Invoke(folderName)
            Return result.BytesUsedInTotal
        End Function

        ''' <summary>
        ''' Retrieves the size in bytes of the according volume (the total of already used and available space).
        ''' </summary>
        ''' <param name="folderName">The name of the path to retrieve the (in general) available bytes from.</param>
        ''' <returns>The maximum number of bytes that are available for all users together.</returns>
        Public Shared Function GetBytesInTotal(folderName As String) As UInt64
            Dim result As SizeInfo = Invoke(folderName)
            Return result.TotalNumberOfBytes
        End Function

        ''' <summary>
        ''' Retrieves a <see cref="SizeInfo"/> object containing the information about how many bytes are available at
        ''' the given path in general or for the current user account, how much is the total and how much is already
        ''' used.
        ''' </summary>
        ''' <param name="folderName">The name of the path from which to retrieve the size info.</param>
        ''' <returns>The according size info object.</returns>
        Public Shared Function Invoke(folderName As String) As SizeInfo
            'Check argument
            If (String.IsNullOrWhiteSpace(folderName)) Then
                Throw New ArgumentNullException(NameOf(folderName), "The folder's name must not be null, empty or white-space!")
            End If
            'Expand environment variables
            Try
                folderName = Environment.ExpandEnvironmentVariables(folderName)
            Catch ex As Exception
                Throw New ArgumentException($"Unable to expand possible environment variables of folder '{folderName}'! See inner exception for details...", ex)
            End Try
            'Get full path
            Try
                folderName = Path.GetFullPath(folderName)
            Catch ex As Exception
                Throw New ArgumentException($"Unable to retrieve absolute path of folder '{folderName}'! See inner exception for details...", ex)
            End Try
            'Append final back-slash (which is mandatory for UNC paths)
            folderName = folderName.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)
            If (Not folderName.EndsWith(Path.DirectorySeparatorChar)) Then
                folderName &= Path.DirectorySeparatorChar
            End If
            'Invoke method
            Dim bytesAvailableToCaller As UInt64
            Dim bytesInTotal As UInt64
            Dim bytesAvailableInGeneral As UInt64
            Dim success As Boolean = Invoke(folderName, bytesAvailableToCaller, bytesInTotal, bytesAvailableInGeneral)
            If (Not success) Then Throw New Win32Exception()
            'Return result
            Return New SizeInfo(bytesAvailableToCaller, bytesInTotal, bytesAvailableInGeneral)
        End Function

        'Private Methods

        <DllImport("kernel32.dll", EntryPoint:="GetDiskFreeSpaceExW", ExactSpelling:=True, SetLastError:=True, CharSet:=CharSet.Unicode)>
        Private Shared Function Invoke(folderName As String, ByRef bytesAvailableToCaller As UInt64, ByRef bytesInTotal As UInt64, ByRef bytesAvailableInGeneral As UInt64) As <MarshalAs(UnmanagedType.Bool)> Boolean
        End Function


        '************************************************************************************************************************
        ' Inner Class "SizeInfo"
        '************************************************************************************************************************

        Public Class SizeInfo

            Public Sub New(freeBytesAvailableToCaller As UInt64, totalNumberOfBytes As UInt64, freeBytesAvailableInTotal As UInt64)
                Me.BytesAvailableToCaller = freeBytesAvailableToCaller
                Me.BytesAvailableInTotal = freeBytesAvailableInTotal
                Me.TotalNumberOfBytes = totalNumberOfBytes
            End Sub

            Public ReadOnly Property BytesAvailableToCaller As UInt64

            Public ReadOnly Property BytesAvailableInTotal As UInt64

            Public ReadOnly Property BytesUsedInTotal As UInt64
                Get
                    Dim total As UInt64 = TotalNumberOfBytes
                    Dim available As UInt64 = BytesAvailableInTotal
                    If (total <= available) Then Return 0
                    Return total - available
                End Get
            End Property

            Public ReadOnly Property TotalNumberOfBytes As UInt64

        End Class

    End Class

End Namespace

推荐阅读