本文将和大家演示如何在 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 init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 75409caab083c6ccd3337de4e8159205314e6974
以上使用的是国内的 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码,可以发邮件向我要代码
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 75409caab083c6ccd3337de4e8159205314e6974
获取代码之后,进入 WPFDemo/NaihunojojeaKeheakabearweabe 文件夹,即可获取到源代码
更多技术博客,请参阅 博客导航
本文会经常更新,请阅读原文: https://blog.lindexi.com/post/WPF-%E4%BD%BF%E7%94%A8-WNetUseConnection-%E8%BF%9E%E6%8E%A5-SMB-%E7%BD%91%E7%BB%9C%E8%B5%84%E6%BA%90.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
如果你想持续阅读我的最新博客,请点击 RSS 订阅,推荐使用RSS Stalker订阅博客,或者收藏我的博客导航
本作品采用
知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议
进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:
https://blog.lindexi.com
),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请
与我联系
。
无盈利,不卖课,做纯粹的技术博客
以下是广告时间
推荐关注 Edi.Wang 的公众号
欢迎进入 Eleven 老师组建的 .NET 社区
以上广告全是友情推广,无盈利