EternalWindows
共有管理 / 接続の確立

アプリケーションから共有リソースにアクセスするためには、 まずはそのリソースに接続を確立しなければなりません。 これには、WNetAddConnection2を呼び出します。

DWORD WNetAddConnection2(
  LPNETRESOURCE lpNetResource,
  LPCTSTR lpPassword,
  LPCTSTR lpUsername,
  DWORD dwFlags
);

lpNetResourceは、接続先のリモート情報を格納したNETRESOURCE構造体のアドレスを指定します。 lpPasswordは、接続に使用するパスワードを指定します。 lpUsernameは、接続に使用するユーザー名を指定します。 現在のユーザー名を指定する場合は、lpPasswordにNULLを指定することができます。 dwFlagsは、接続に関する定数を指定します。 CONNECT_INTERACTIVEを指定すると、ダイアログ経由でパスワードを指定することができます。

WNetAddConnection2で接続を確立した場合、 その接続先のコンピュータ上では具体的に何が起こるのでしょうか。 また、何を持ってアクセスの成否を判定しているのでしょうか。 これについて理解するためには、リモートコンピュータ上の名前を要求する関数について 考察してみると分かりやすいといえます。 次の関数を見てください。

OpenSCManager(szComputerName, NULL, SC_MANAGER_ALL_ACCESS);

OpenSCManagerは、サービスアプリケーションを管理するSCMに接続する関数です。 第1引数には、リモートコンピュータの名前を指定することができ、 これを指定すればリモートコンピュータ上のサービスを管理できるように思えますが、 これだけではアクセス拒否のエラーが発生することになります。 なぜなら、OpenSCManagerの呼び出し要求を受け取ったリモートコンピュータが ローカル上でOpenSCManagerを呼び出す際に、参照すべきユーザーを特定できないからです。 つまり、どのユーザーとして関数を呼び出せばよいかが分からないということです。 この問題を解決するためには、事前にリモートコンピュータ上へネットワークログオンをしておく必要があるのですが、 これを行うのがWNetAddConnection2です。 この関数を実行すると、指定したユーザー名とパスワードを持つユーザーがリモートコンピュータ上に存在するかが調べられ、 存在する場合はログオンタイプがNetworkであるログオンセッションが作成されることになります。 この状態で、OpenSCManagerを呼び出せば、参照するユーザーをログオンセッションから解決することができるため、 そのユーザーに関数の実行が許可されていれば、関数の呼び出しは成功することになります。 この原理は、OpenSCManagerに限らず、リモートコンピュータの名前を要求する他の関数にも共通しています。

WNetAddConnection2は、Web DAVフォルダへの接続にも利用することができます。 たとえば、webサイトのWindows Sysinternalsは、 \\live.sysinternals.comとしてWeb DAVフォルダを公開しているため、 このパスをNETRESOURCE構造体のlpRemoteNameに指定すればよいことになります。 ただし、完全にWeb DAVフォルダを共有フォルダとして扱えるわけではなく、 ローカルドライブへのマッピングやリソースの列挙などは失敗するようです。 Web DAVフォルダへのアクセスには、Web Client Networkいうネットワークプロバイダが使用されますが、 これをNETRESOURCE構造体のlpProviderに明示的に指定しても特に変化はないようです。 SSLを実装するWeb DAVフォルダへの接続には、Web DAV APIであるDavAddConnectionを呼び出す必要があります。

WNetAddConnection2で確立した接続を切断したい場合は、WNetCancelConnection2を呼び出します。 この関数を呼び出すと、WNetAddConnection2で作成されたログオンセッションはしばらしくて削除されます。

DWORD WNetCancelConnection2(
  LPCTSTR lpName,
  DWORD dwFlags,
  BOOL fForce
);

lpNameは、切断するリモートコンピュータの名前を指定します。 dwFlagsは、0で問題ありません。 fForceは、ファイルをオープンしている状態などでも、強制的に接続を切断するかどうかを示す値を指定します。 FALSEを指定した場合は、強制的に接続を切断しません。

今回のプログラムは、リモートコンピュータ上のコンピュータに接続し、 そのコンピュータ上の共有フォルダ内のファイルをオープンします。

#include <windows.h>

#pragma comment (lib, "mpr.lib")

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	BOOL        bConnection = TRUE;
	DWORD       dwResult;
	TCHAR       szBuf[256];
	TCHAR       szComputerName[] = TEXT("\\\\RemoteName");
	TCHAR       szSharePath[] = TEXT("\\\\RemoteName\\share\\sample.txt");
	NETRESOURCE netResource;
	HANDLE      hFile;

	if (bConnection) {
		ZeroMemory(&netResource, sizeof(NETRESOURCE));
		netResource.dwType       = RESOURCETYPE_ANY;
		netResource.lpRemoteName = szComputerName;

		dwResult = WNetAddConnection2(&netResource, TEXT("pass"), TEXT("username"), 0);
		if (dwResult != NO_ERROR) {
			wsprintf(szBuf, TEXT("接続に失敗しました。 error %d"), dwResult);
			MessageBox(NULL, szBuf, NULL, MB_ICONWARNING);
			return 0;
		}

		MessageBox(NULL, TEXT("接続を確立しました。"), TEXT("OK"), MB_OK);

		hFile = CreateFile(szSharePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
		if (hFile == INVALID_HANDLE_VALUE) {
			wsprintf(szBuf, TEXT("ファイルのオープンに失敗しました。 error %d"), GetLastError());
			MessageBox(NULL, szBuf, NULL, MB_ICONWARNING);
			return FALSE;
		}
		
		MessageBox(NULL, TEXT("ファイルをオープンしました。"), TEXT("OK"), MB_OK);
		
		CloseHandle(hFile);
	}
	else {
		dwResult = WNetCancelConnection2(szComputerName, 0, FALSE);
		if (dwResult != NO_ERROR) {
			wsprintf(szBuf, TEXT("接続の切断に失敗しました。error %d"), dwResult);
			MessageBox(NULL, szBuf, NULL, MB_ICONWARNING);
			return 0;
		}

		MessageBox(NULL, TEXT("接続を切断しました。"), TEXT("OK"), MB_OK);
	}

	return 0;
}

bConnectionがTRUEの場合は、WNetAddConnection2を呼び出してリモートコンピュータに接続します。 このとき、NETRESOURCE構造体のdwTypeにはRESOURCETYPE_ANYを指定し、 lpRemoteNameには接続先のコンピュータ名を指定します。 WNetAddConnection2が成功した場合は、接続を確立したメッセージが表示されますが、 この時点で既にリモートコンピュータにはいくつかの変化が生じています。 たとえば、ログオンしたユーザーのログオンセッションが作成されていますし、 NetConnectionEnumを呼び出せばIPC$に対する接続も確認できます。 IPC$は、認証を行うための便宜的な共有リソースであり、 認証が成功した場合は必ず接続されることになります。 また、全ての共有リソースへのアクセスは、IPC$での認証を通過している必要があるため、 何らかの共有リソースにアクセスしているということは、 IPC$への接続も行われているということになります。

WNetAddConnection2の呼び出しに成功したら、CreateFileで共有フォルダ内のファイルをオープンします。 szSharePathは、コンピュータ名の部分はszComputerNameと同一にしておき、 共有名とファイル名は実際に存在するものを指定します。 WNetAddConnection2を事前に呼び出していない場合は、 ファイルへのアクセス時に参照すべきユーザーを解決できないことからエラーになると思われますが、 リモートコンピュータ上でGuestアカウントが有効になっている場合はこの限りではありません。 これについては、次節で取り上げます。 ファイルのオープンに成功すれば、後はそのファイルを読み取ったりするなど、 通常のファイル操作を行います。 なお、ファイルがオープンされている状態で、 リモートコンピュータがNetFileEnumを呼び出すと、 そのオープンされたファイルが返ることになります。

これまでの話から分かるように、リモートコンピュータ上の共有リソース(関数呼び出しも含む)にアクセスするためには、 少なくとも2つの条件を満たしておく必要があります。 1つは、IPC$による認証であり、リモートコンピュータ上に存在するユーザーの名前とパスワードを正しく指定します。 そしてもう1つは、共有リソース自身に対するアクセスチェックであり、 ネットワークログオンしたユーザーが、そのリソースに対してアクセスが許可されている必要があります。

ローカルドライブへのマッピング

WNetAddConnection2では、リモートコンピュータ上のリソースに接続すると共に、 そのリソースをローカルドライブにマッピングすることも可能になっています。 たとえば、リソースをKというドライブにマッピングしたのであれば、 これからはKドライブを通じてリソースの中身をブラウジングできるようになります。 次に、コード例を示します。

#include <windows.h>

#pragma comment (lib, "mpr.lib")

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	BOOL        bConnection = TRUE;
	DWORD       dwResult;
	TCHAR       szBuf[256];
	TCHAR       szLocalDrive[] = TEXT("K:");
	TCHAR       szSharePath[] = TEXT("\\\\RemoteName\\share");
	NETRESOURCE netResource;

	if (bConnection) {
		ZeroMemory(&netResource, sizeof(NETRESOURCE));
		netResource.dwType       = RESOURCETYPE_ANY;
		netResource.lpLocalName  = szLocalDrive;
		netResource.lpRemoteName = szSharePath;

		dwResult = WNetAddConnection2(&netResource, TEXT("pass"), TEXT("username"), 0);
		if (dwResult != NO_ERROR) {
			wsprintf(szBuf, TEXT("マッピングに失敗しました。 error %d"), dwResult);
			MessageBox(NULL, szBuf, NULL, MB_ICONWARNING);
			return 0;
		}

		MessageBox(NULL, TEXT("ローカルドライブへマッピングしました。"), TEXT("OK"), MB_OK);
	}
	else {
		dwResult = WNetCancelConnection2(szLocalDrive, 0, FALSE);
		if (dwResult != NO_ERROR) {
			wsprintf(szBuf, TEXT("マッピングの解除に失敗しました。 error %d"), dwResult);
			MessageBox(NULL, szBuf, NULL, MB_ICONWARNING);
			return 0;
		}

		MessageBox(NULL, TEXT("マッピングを解除しました。"), TEXT("OK"), MB_OK);
	}

	return 0;
}

NETRESOURCE構造体のlpLocalNameメンバにドライブ文字を指定すれば、 WNetAddConnection2の成功時にそのドライブへリソースがマッピングされます。 たとえば、エクスプローラでマイコンピュータを開くと、 Kドライブが割り当てられているのを確認できるはずです。 マッピングを解除する場合は、WNetCancelConnection2の第1引数にドライブ文字を指定します。



戻る