3流プログラマのメモ書き

元開発職→社内SE→派遣で営業支援→開発戻り浦島太郎状態の三流プログラマのIT技術メモ書き。 このメモが忘れっぽい自分とググってきた技術者の役に立ってくれれば幸いです。

(.Net)アプリ上で共有フォルダの認証を行いファイルアクセスをする

ドメインに参加してないPC上で動く.Netアプリケーションから、ドメイン内のファイルサーバにアクセスしたいと言う要件です。

当然.Netアプリケーション内で共有フォルダの認証を行わなくてはなりません。

で、調べるとプログラム上で認証情報(アカウントとパスワード)をセットして、ネットワーク上に共有されたファイルをコピーする - ComponentGeek Articleにどんぴしゃな情報が載ってました。

.Net のクラスライブラリ自体にはそのような機能は提供されてないようなので、Windows API を使うことになるようです。

WNetAddConnection2 というAPIWNetAddConnection3 というAPIがあるようです。違いは後者は"ネットワーク資源のプロバイダがダイアログボックスのオーナーウィンドウとして利用できるウィンドウのハンドルという1つのパラメータが追加されている"だけのようです。要は3の方は認証ダイアログウィンドウのハンドルを指定することができるようです。

(WNetAddConnectionってのもあるようですが16bit版みたいです。)

ちなみに、WNetAddConnection2 で接続する前にすでに対象の共有フォルダに接続されている場合はERROR_SESSION_CREDENTIAL_CONFLICT(1219)のエラーが返ります。

なので一旦 WNetCancelConnection2 で現在の接続を切断してからつなぐ方が安心かもしれません。

ということで一旦切断してから接続するサンプルコードです。(C#)

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Diagnostics;

using System.IO;

using System.Runtime.InteropServices;

 

public partial class Main : Form

{

//接続切断するWin32 API を宣言

[DllImport("mpr.dll", EntryPoint = "WNetCancelConnection2", CharSet = System.Runtime.InteropServices.CharSet.Unicode)]

private static extern int WNetCancelConnection2(string lpName, Int32 dwFlags, bool fForce);

 

//認証情報を使って接続するWin32 API宣言

[DllImport("mpr.dll", EntryPoint = "WNetAddConnection2", CharSet = System.Runtime.InteropServices.CharSet.Unicode)]

private static extern int WNetAddConnection2(ref NETRESOURCE lpNetResource, string lpPassword, string lpUsername, Int32 dwFlags);

//WNetAddConnection2に渡す接続の詳細情報の構造体

[StructLayout(LayoutKind.Sequential)]

internal struct NETRESOURCE

{

public int dwScope;//列挙の範囲

public int dwType;//リソースタイプ

public int dwDisplayType;//表示オブジェクト

public int dwUsage;//リソースの使用方法

[MarshalAs(UnmanagedType.LPWStr)]

public string lpLocalName;//ローカルデバイス名。使わないならNULL。

[MarshalAs(UnmanagedType.LPWStr)]

public string lpRemoteName;//リモートネットワーク名。使わないならNULL

[MarshalAs(UnmanagedType.LPWStr)]

public string lpComment;//ネットワーク内の提供者に提供された文字列

[MarshalAs(UnmanagedType.LPWStr)]

public string lpProvider;//リソースを所有しているプロバイダ名

}

 

 

private void button1_Click(object sender, EventArgs e)

{

string destFilePath = @"C:\test.txt"; // コピー先のローカルパス

string sourceFilePath = @"\\filesv\共有フォルダ\test.txt"; // コピー対象の共有されたファイルのUNCパス

string shareName = @"\\filesv\フォルダ"; // 共有パス \\sparePC\C$などもOK

try

{

//とりあえずコピーできるかやってみる

System.IO.File.Delete(destFilePath);

System.IO.File.Copy(sourceFilePath, destFilePath);

}

//認証に失敗すると UnauthorizedAccessException が発生する(Exceptionでキャッチしてもいいかも)

catch (UnauthorizedAccessException)

{

// 接続情報を設定

NETRESOURCE netResource = new NETRESOURCE();

netResource.dwScope = 0;

netResource.dwType = 1;

netResource.dwDisplayType = 0;

netResource.dwUsage = 0;

netResource.lpLocalName = ""; // ネットワークドライブにする場合は"z:"などドライブレター設定

netResource.lpRemoteName = shareName;

netResource.lpProvider = "";

 

string password = "passwd";

string userId = @"hogedomain.local\hoge";

 

int ret = 0;

try

{

//既に接続してる場合があるので一旦切断する

ret = WNetCancelConnection2(shareName, 0, true);

//共有フォルダに認証情報を使って接続

ret = WNetAddConnection2(ref netResource, password, userId, 0);

}

catch (Exception)

{

//エラー処理

}

Console.WriteLine(ret);

//ファイルコピー

System.IO.File.Delete(destFilePath);

System.IO.File.Copy(sourceFilePath, destFilePath);

 

}

Console.WriteLine("終了");

Console.ReadLine();

}

}

なお NETRESOURCE 構造体に設定できるのは下記の値になるようです。

dwScope(列挙の範囲)

RESOURCE_CONNECTED(0x1) 現在接続されたリソースを列挙する。dwUsageメンバーを指定できない。

RESOURCE_GLOBALNET(0x2) ネットワークに関するすべてのリソースを列挙する。

RESOURCE_REMEMBERED(0x3) 接続を列挙する。dwUsageメンバーを指定できない。

dwType(リソースタイプ)

RESOURCETYPE_ANY(0x0) すべてのリソース

RESOURCETYPE_DISK(0x1) ディスクリソース

RESOURCETYPE_PRINT(0x2) プリンタリソース

dwDisplayType(表示オブジェクト)

RESOURCEDISPLAYTYPE_DOMAIN(0x1) ドメインオブジェクトを表示する

RESOURCEDISPLAYTYPE_SERVER(0x2) サーバオブジェクトを表示する

RESOURCEDISPLAYTYPE_SHARE(0x3) シェアオブジェクトを表示する

RESOURCEDISPLAYTYPE_GENERIC(0x0) オブジェクトの表示は重要ではないことを示す

dwUsage(リソースの使用方法)

RESOURCEUSAGE_CONNECTABLE(0x1) 接続可能なリソースであることを示す。lpRemoteNameメンバーによって示された名前はWNetAddConnection機能に通過できる。

RESOURCEUSAGE_CONTAINER(0x2) リソースはコンテナリソースです。lpRemoteNameメンバーによって示された名前はWNetOpenEnum機能に通過できる。

MSDNには構造体に指定できる値を書いてるんですが、定数で書いてるんですよね。.Netだとその定数が使えないので実値を指定しないといけません。.NetでもWindowsの定数使えないものですかね。。。

APIの戻りのエラーコードはWin32エラーコード一覧が参考になります。