本文将和大家演示如何在 WPF 里面,使用 WNetUseConnection 连接 SMB 网络资源
为了方便起见,我编写了一个简单的界面,代码如下
<Grid> <StackPanel VerticalAlignment="Center"> <Grid MinWidth="300" HorizontalAlignment="Center"> <Grid.Resources> <Style TargetType="TextBlock"> <Setter Property="Margin" Value="0 5 5 5"></Setter> </Style> <Style TargetType="TextBox"> <Setter Property="Margin" Value="0 5 0 5"></Setter> </Style> </Grid.Resources> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions>
<TextBlock Text="Url:"></TextBlock> <TextBox x:Name="UrlTextBox" Grid.Row="0" Grid.Column="1" Text="\\nas.lindexi.com\Data\"></TextBox>
<TextBlock Grid.Row="1" Grid.Column="0" Text="账号:"></TextBlock> <TextBox x:Name="UserNameTextBox" Grid.Row="1" Grid.Column="1"></TextBox>
<TextBlock Grid.Row="2" Grid.Column="0" Text="密码:"></TextBlock> <TextBox x:Name="PasswordTextBox" Grid.Row="2" Grid.Column="1"></TextBox> </Grid>
<Button x:Name="ConnectButton" Width="100" Height="30" Margin="10" Click="ConnectButton_OnClick">连接</Button> </StackPanel>
</Grid>
运行起来的界面大概如下
点击连接按钮时,将尝试连接以上配置的各项内容
如上图所示,我将尝试连接到我的 SMB 上。对于 Windows 来说,以上的 \\nas.lindexi.com\Data\
就是一个非常合法的路径,连接完成之后,即可直接访问
本文的重点是调用 WNetUseConnection 方法进行连接,此方法的官方说明文档是 WNetUseConnectionW 函数 (winnetwk.h) - Win32 apps - Microsoft Learn
其 PInvoke 定义代码如下
[DllImport("Mpr.dll")] private static extern int WNetUseConnection ( IntPtr hwndOwner, NETRESOURCE lpNetResource, string lpPassword, string lpUserID, int dwFlags, string? lpAccessName, string? lpBufferSize, string? lpResult );
结构体 NETRESOURCE
的定义如下
[StructLayout(LayoutKind.Sequential)] private struct NETRESOURCE() { public int dwScope = 0; public int dwType = 0; public int dwDisplayType = 0; public int dwUsage = 0; public string lpLocalName = ""; public string lpRemoteName = ""; public string lpComment = ""; public string lpProvider = ""; }
为了方便调用,我编写了 NetworkShare 辅助类,代码如下
public static class NetworkShare{ public static int ConnectToShare(string uri, string username, string password) { //Create netresource and point it at the share NETRESOURCE netResource = new NETRESOURCE(); netResource.dwType = RESOURCETYPE_DISK; netResource.lpRemoteName = uri;
int result = WNetUseConnection(IntPtr.Zero, netResource, password, username, 0, null, null, null); return result; }
const int RESOURCETYPE_DISK = 0x00000001; const int CONNECT_UPDATE_PROFILE = 0x00000001;
...}
对于 Win32 调用来说,一般都有成对的释放代码,这里使用的是 WNetCancelConnection 进行断开
同样做简单的封装
public static class NetworkShare{ public static int DisconnectFromShare(string uri, bool force) { int result = WNetCancelConnection(uri, force); return result; }
[DllImport("Mpr.dll")] private static extern int WNetCancelConnection ( string lpName, bool fForce );
...}
整个 NetworkShare 的代码如下
public static class NetworkShare{ public static int ConnectToShare(string uri, string username, string password) { //Create netresource and point it at the share NETRESOURCE netResource = new NETRESOURCE(); netResource.dwType = RESOURCETYPE_DISK; netResource.lpRemoteName = uri;
int result = WNetUseConnection(IntPtr.Zero, netResource, password, username, 0, null, null, null); return result; }
public static int DisconnectFromShare(string uri, bool force) { int result = WNetCancelConnection(uri, force); return result; }
const int RESOURCETYPE_DISK = 0x00000001; const int CONNECT_UPDATE_PROFILE = 0x00000001;
[StructLayout(LayoutKind.Sequential)] private struct NETRESOURCE() { public int dwScope = 0; public int dwType = 0; public int dwDisplayType = 0; public int dwUsage = 0; public string lpLocalName = ""; public string lpRemoteName = ""; public string lpComment = ""; public string lpProvider = ""; }
[DllImport("Mpr.dll")] private static extern int WNetUseConnection ( IntPtr hwndOwner, NETRESOURCE lpNetResource, string lpPassword, string lpUserID, int dwFlags, string? lpAccessName, string? lpBufferSize, string? lpResult );
[DllImport("Mpr.dll")] private static extern int WNetCancelConnection ( string lpName, bool fForce );}
完成之后的调用代码如下
private void ConnectButton_OnClick(object sender, RoutedEventArgs e) { var success = NetworkShare.ConnectToShare(UrlTextBox.Text, UserNameTextBox.Text, PasswordTextBox.Text); if (success == 0) { foreach (var fileSystemEntry in Directory.EnumerateFileSystemEntries(UrlTextBox.Text)) { Debug.WriteLine(fileSystemEntry); }
NetworkShare.DisconnectFromShare(UrlTextBox.Text, force: true); } }
当输入的地址和账号密码正确时,预期可以在 ConnectButton_OnClick
里面枚举出当前 SMB 网络资源的各项文件夹和文件
我为了方便自己调试,我还引入了 https://github.com/dotnet-campus/dotnetCampus.Configurations 硬币配置文件库,将连接地址和账号密码存放在 COIN 硬币配置文件里,其代码如下
public MainWindow() { InitializeComponent();
var coinFile = @"C:\lindexi\Nas.coin"; if (File.Exists(coinFile)) { var fileConfigurationRepo = ConfigurationFactory.FromFile(coinFile,RepoSyncingBehavior.Static); var appConfigurator = fileConfigurationRepo.CreateAppConfigurator(); var nasConfiguration = appConfigurator.Of<NasConfiguration>(); UrlTextBox.Text = nasConfiguration.Url; UserNameTextBox.Text = nasConfiguration.UserName; PasswordTextBox.Text = nasConfiguration.Password; } }
class NasConfiguration : Configuration { public NasConfiguration() : base("") { }
public string Url => GetString(); public string UserName => GetString(); public string Password => GetString(); }
本文代码放在 github 和 gitee 上,可以使用如下命令行拉取代码。我整个代码仓库比较庞大,使用以下命令行可以进行部分拉取,拉取速度比较快
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
git initgit remote add origin https://gitee.com/lindexi/lindexi_gd.gitgit pull origin 75409caab083c6ccd3337de4e8159205314e6974
以上使用的是国内的 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码,可以发邮件向我要代码
git remote remove origingit remote add origin https://github.com/lindexi/lindexi_gd.gitgit pull origin 75409caab083c6ccd3337de4e8159205314e6974
获取代码之后,进入 WPFDemo/NaihunojojeaKeheakabearweabe 文件夹,即可获取到源代码
更多技术博客,请参阅 博客导航

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。 欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系。