<   2010年 08月 ( 23 )   > この月の画像一覧
(C/C++)Windows APIのCreateProcessWithLogonW関数を使うとEXEが落ちる

(C++)別ユーザでプロセスを起動するにて、Windows API の CreateProcessWithLogonW 関数を使うことで別ユーザ権限でプロセスを起動できることを書きました。

で、このコードをコンソールアプリケーションとしてビルドしテストしたところ、特定のマシンだけ動かないという現象が起きたのです。(Visual C++ 2008 Express にて、Win32 としてビルド)
exeを起動した瞬間にコマンドプロンプト画面は表示されるものの一瞬で消えるという現象です。試しに、コードの最初の方に MessageBox も入れてみましたが、それも表示されません。どうやらexe起動時にほんとに落ちてるようです。

この現象が起きるのが Windows Server 2003 x64 版で5台のサーバのうち3台で発生します。
Windows Server 2003 の x86 環境ではおきません。

試しに Win32 コンソールアプリで、printf だけ使ったexeを動かしたところ、それは問題なく動きます。
ダメになるのは、CreateProcessWithLogonW 関数をコードの含めたときです。

MSサポート:32 ビット プロセスで CreateProcess 関数を呼び出しても別の 32 ビット プロセスが正しく起動されないによると、32bitアプリから、CreateProcess系の関数を動かすと、メモリリークのため開始できないとあります。
しかし、メモリリークしてる感じはないんですよね。exe立ち上げ瞬間落ちてますから。。

で、VC++のプロジェクト構成をいろいろ触っているうちに、構成プロパティ → C/C++ → 最適化 → 最適化 の値を変更することで動かなかった環境でも動くようになりました。
デフォルトで「最適化」の値が「実行速度 (/O2)」になってたんですが、これを「無効 (/Od)」もしくは「プログラム サイズ (/O1)」にすると動作するようになりました。

MSDN:リリース ビルド作成時によくある問題MSDN:コードの最適化によると、最適化によりまれに予期しないコードを生成し問題を引き起こすということが書かれてました。

結局詳細な原因がわからなかったのはモヤモヤしますが、最適化の設定の変更で動くようになったので、助かりました。
しかし、やはり C++ は難しいですね。
[PR]
by jehoshaphat | 2010-08-30 21:48 | C/C++開発 | Trackback | Comments(0)
(C++)別ユーザでプロセスを起動する
Windows API を使って別のユーザでプロセスを起動する方法です。
(.Net を使えば、ProcessStartInfo.UserName や ProcessStartInfo.Password 等で簡単にできるのかもしれませんが、.Net Framework が入ってない環境を考慮して C++ と Windows API で作成しました。またコマンドプロンプトで、 RUNAS を使うとできるのですが、パスワードを入力しないといけないので自作することとしました。)


別ユーザでプロセスを起動する Windows API としては、CreateProcessAsUserやCreateProcessWithLogonW、CreateProcessWithTokenW(Vista以降)などがあるようですが、今回は一番簡単そうな、CreateProcessWithLogonW 関数を使ってみました。

下記のような感じで可能です。administrator@hogedomain.local でWSH(VBScript)を実行するコードです。
#include "stdafx.h"
#include <windows.h>
 
 
STARTUPINFO sinfo;
PROCESS_INFORMATION pinfo;
ZeroMemory( &sinfo, sizeof( STARTUPINFO ) );
ZeroMemory( &pinfo, sizeof( PROCESS_INFORMATION ) );
sinfo.cb = sizeof( STARTUPINFO );
 
//別ユーザでプロセス起動
if( ! CreateProcessWithLogonW( _T("administrator"), //ユーザ名
_T("hogedomain.local"), //ドメイン名
_T("passwd"), //パスワード
0, //ログオン オプション LOGON_NETCREDENTIALS_ONLYを指定すると呼び出し元ユーザプロセスで動いてしまいました
NULL, //実行可能モジュール名
_T("cscript.exe \"d:\\a.vbs\""), //コマンドライン文字列
CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP , //作成フラグ
NULL, // 新しい環境ブロック
NULL, // カレントディレクトリの名前
&sinfo,
&pinfo)){
if (GetLastError() == ERROR_ACCESS_DENIED){
MessageBox(NULL, TEXT("アクセスが拒否されました。"), NULL, MB_ICONWARNING);
}
else
{
MessageBox(NULL, TEXT("プロセスの作成に失敗しました。"), NULL, MB_ICONWARNING);
}
return 0;
}
 
CloseHandle(pinfo.hThread);
CloseHandle(pinfo.hProcess);

上記は cscript でWSHを実行するため、コマンドライン文字列に定義しましたが、一般のexeを実行する場合は第5引数にexeのパスを指定するようです。
メモ帳を立ち上げる場合はこうなります。
CreateProcessWithLogonW( _T("administrator"), //ユーザ名
_T("hogedomain.local"), //ドメイン名
_T("passwd"), //パスワード
0, //ログオン オプション LOGON_NETCREDENTIALS_ONLYを指定すると呼び出し元ユーザプロセスで動いてしまいました
_T("notepad.exe"), //実行可能モジュール名
NULL, //コマンドライン文字列
CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP , //作成フラグ
NULL, // 新しい環境ブロック
NULL, // カレントディレクトリの名前
&sinfo,
&pinfo)


参考:
CreateProcessWithLogonW or CreateProcessAsUser - .NET Framework .NetのProcessStartInfo.UserName を使って、プロセスを起動した場合、CreateProcessWithLogonW が呼ばれるみたいです。
別ユーザーのプロセス 非常に参考なりました。
MSDN:CreateProcessWithLogonW
MSDN:別アカウントでプロセスを起動する方法

[PR]
by jehoshaphat | 2010-08-30 21:41 | C/C++開発 | Trackback | Comments(0)
WSHを使って特定コンピュータのみフォルダリダイレクトの設定 Part1
何回かにわたって特定コンピュータ使用時でのフォルダリダイレクトを実現する方法を調査してきましたが、最終的に WSH でグループポリシーのログインスクリプトで実行することとしました。

下記のようなVBスクリプトで実現可能です。SV1~SV4にログインしたときに、フォルダリダイレクトするようになります。
Option Explicit
 
Dim objNet
'コンピュータ名取得 対象コンピュータなら処理する
Set objNet = CreateObject("WScript.Network")
IF StrComp(objNet.ComputerName , "SV1" ,1 ) = 0 OR _
StrComp(objNet.ComputerName , "SV2" ,1 ) = 0 OR _
StrComp(objNet.ComputerName , "SV3" ,1 ) = 0 OR _
StrComp(objNet.ComputerName , "SV4" ,1 ) = 0 Then


'リダイレクト設定
Dim strRedirectRegKey
strRedirectRegKey = "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders\"
Dim strBasePath
strBasePath = "\\filesv\user\%username%\"
Dim objShell
Set objShell = WScript.CreateObject("WScript.Shell")
 
Dim nowDesktop , nowPersonal
'デスクトップフォルダ の現在のパスを確認
nowDesktop = objShell.RegRead(strRedirectRegKey & "Desktop")
'マイドキュメントの現在のパスを確認
nowPersonal = objShell.RegRead(strRedirectRegKey & "Personal")

'マイドキュメントリダイレクト
objShell.RegWrite strRedirectRegKey & "Personal", strBasePath & "マイドキュメント" , "REG_EXPAND_SZ"
'デスクトップリダイレクト
objShell.RegWrite strRedirectRegKey & "Desktop", strBasePath & "デスクトップ" , "REG_EXPAND_SZ"
End If

VBScriptでは、On Error Gotoが使えないのはおどろきでした。(参考:VBSでOn Error Goto ラベルは使えない! - x-bakerの試作日記)
まぁ、エラー処理をちょっと考えないといけませんね。


Windows Server 2003 R2 にターミナルサービスでログインして、フォルダリダイレクトされたマイドキュメントのファイルを削除しようとすると、下記のように削除の確認を聞いてきました。

'xxxx'をごみ箱に移しますか?

これで、はい を押下すると、下記のようなエラーになります。

ファイルまたはフォルダの削除エラー
xxxxx を削除できません。指定されたファイルが見つかりません。
パスとファイル名を確認してください。

しかし、ファイルの削除自体はできています。(ごみ箱には移動してません)

ちょうど、MSサポート:Netware サーバーにリダイレクトされた [マイ ドキュメント] フォルダ内のファイルを削除するとエラー メッセージが表示されるに書いてあるような内容と同じ現象です。
ただ、上記のサポートページはWindows2000向けで、SP3で修復されてるというのに、Winodows Server 2003 で発生するのはおかしい話です。

また、デスクトップ もリダイレクトしているんですが、こちらは削除しようとするとごみ箱を経由しない削除になります。(共有フォルダ上のファイル削除と同じ動作ですね。)

試しに、Windows Server 2008 R2 で同じことをやってみました。
すると、驚いたことに、リダイレクトしたデスクトップもマイドキュメントもごみ箱機能がちゃんと正しく動作するのです。
どうやら 2008 では、ネットワーク上にリダイレクトしてもごみ箱を使えるようにしたようですね。
削除したファイルは、リダイレクト先ルートの RECYCLER フォルダ内に置かれるようです。
これは非常に便利ですが、今回は Windows Server 2003 R2 を使うのでこの機能が使えません。

2003の時の削除エラー表示に関しては、ごみ箱機能を無効化することで回避することにしました。
ただし、サーバ側の全体でごみ箱機能を無効にすると全ユーザに影響が出てしまうので、リダイレクトを行うユーザのみ無効にするということにします。
(Windows Server 2003のとき、ごみ箱のプロパティを表示すると各ドライブ以外に マイドキュメント のタブもあるんですよね。ただこの設定もコンピュータ毎のようです。レジストリの HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\BitBucket\@\NukeOnDelete が0だとごみ箱有効のようです。普通 BitBucket の下は各ドライブ文字が入るんですが、なぜにマイドキュメントは @ 何でしょう?)

上記のリダイレクトスクリプトに下記のスクリプトを加えます。
Dim strRecycleRegKey
strRecycleRegKey = "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\NoRecycleFiles"
'ごみ箱使用しない
objShell.RegWrite strRecycleRegKey , 1 , "REG_DWORD"

上記のスクリプトは、グループポリシーで、ユーザの構成 → 管理テンプレート → Windowsコンポーネント → エクスプローラ → 削除されたファイルをごみ箱にいどうさせない を有効にしたとの同じ意味になります。

これで、Windows Server 2003 でのリダイレクト時のファイル削除は解決したように思ったんですが、上記のファイル削除に関するレジストリは一般ユーザでは読み取り専用になっており、書き込みができませんでした。(HKCU\Software\Microsoft\Windows\CurrentVersion\Policies 配下。本来グループポリシーの設定が入るところなので、管理者とシステム以外は書き込み禁止なのでしょうね。。)

管理者権限で実行したスクリプトで上記のレジストリを追加するといいんでしょうが、ログインスクリプトはそのログインしたユーザ権限でしか動きません。
別ユーザ権限でプロセスを実行する RUNAS コマンドもありますが、これは対話式にユーザとパスワードを入力しないといけないので、今回のようにログイン時に実行というのは無理です。

結局、別ユーザで実行するためのアプリケーションを一本作ることにしました。
そのアプリからWSHを管理者権限で動かして、レジストリに値を追加する方法です。

これについてはいずれ書きたいと思います。

参考:
StrComp 関数 | VBScript関数リファレンス WSHでの文字列比較はStrCompを使うようです。
@IT:WSHでのレジストリ書き込み
WSHを始めよう - @IT
ごみ箱のプロパティの設定が淡色表示でクリックできない
Windows VistaでのWSH(VBScript)の管理者権限への昇格方法, 思うところはあるがここには書かない Shell.ApplicationオブジェクトのShellExecuteメソッドでもユーザ変更できるようですが、RUNAS使うため対話式にユーザとパスワードを入れないといけないようです。
ishihara log フォルダのリダイレクト
[PR]
by jehoshaphat | 2010-08-29 21:49 | 豆知識 | Trackback | Comments(0)
DDNSサービスDynDNSのアカウント放置による削除に注意

今日、ダイナミックDNSサービスとして利用してるDynDNSから、「Remember us? We want you back at DynDNS」とうい件名のメールが届きました。

英語だったんであれですが、まぁDynDNS覚えてるかい?このままずっとほっとくと元顧客になっちまうぞ 的な内容でした。


以前もDynDNSからのメール放置しててアカウントが消されたことがありました。
確か、「DynDNS.com Account Expiration Warning」というメールが来てて放置するとアカウントが削除されるんですよね。

光回線にしてからCTUの電源リセットしない限りはIPが変わらなくなったので、こういうミスは要注意ですね。

とりあえず、DiCEで更新処理しといたのと、DyDNSにログインしておきました。
たぶんこれで大丈夫だとは思うんですが。。。

探してみると下記のように同様の経験したDDNS利用者の方々がおられました。
[ヅラド] "DynDNS.org Hostname Expired" を迷惑メールと勘違い
httpdrestart:DynDNSのアカウント削除 2度目は予告なしだった
[PR]
by jehoshaphat | 2010-08-26 23:19 | 豆知識 | Trackback | Comments(0)
(Linux)DNSサーバの指定
CentOS5の環境です。

IPアドレスやデフォルトゲートウェイなどは、/etc/sysconfig/network-scripts/ifcfg-eth0 などに書くのが一般的ですが、DNSサーバのIPアドレスの指定は別ファイルとなります。
設定ファイルは /etc/resolv.conf となり下記のように記述します。

nameserver 192.168.0.1
nameserver 192.168.0.2


参考:
サーバのネットワーク・IPアドレスの設定、ホスト名の変更 [Fedora, RedHat, CentOS] - Linux
[PR]
by jehoshaphat | 2010-08-26 22:50 | Linux | Trackback | Comments(0)
(Linux)ネットワーク設定を反映する
CentOS5の環境です。

今までIPアドレスやゲートウェイなどのネットワークの設定を変えたときは下記のようなコマンドで適用してました。

# service network resatart


しかし、複数インターフェイス(NIC)がついてる場合、特定のNICだけ適用したいってときもあります。
network 自体を restart すると既存の接続も切れてしまいますからね。。。

そのような場合は ifup , ifdown コマンドが使えるようです。

ifdown eth 0 →NIC停止
ifup eth0 →NIC起動


参考:
Linux のネットワーク設定:
[PR]
by jehoshaphat | 2010-08-26 22:49 | Linux | Trackback | Comments(0)
特殊フォルダリダイレクトのレジストリ操作 by コマンドプロンプト(バッチ)
(ActiveDirectory)グループポリシーのセキュリティフィルタとWMIフィルタで、リダイレクト設定をしたグループポリシーのフィルタについて取り上げました。WMIフィルタがよさげだけど、Windows 2000の存在する環境じゃダメということで、直接レジストリを操作するバッチファイルを作成し、ログインスクリプトで動かしてやろうと思いつきました。

具体的に、バッチファイルで、リダイレクト対象端末稼働のかチェック → 対象端末ならレジストリ直接変更 という流れです。

まず、特殊フォルダのリダイレクトについてですが、これらの設定はレジストリで各ユーザのレジストリで持っているようです。
場所は、下記の場所となります。
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders ってのもありますが、Shell Folders の内容は、ユーザーがログオンするたびに User Shell Folders の内容で上書きされるようです。従って、変更するのは User Shell Folders の内容だけでよいとのことです。
参考:Windows Tip - Customize the Registory Keys for User Folders:)

ここに、マイドキュメントを表す Personal や、デスクトップを表す Desktop など、多数のユーザ毎に持つ特殊フォルダのパスが設定されています。
ちなみに、データ型は REG_EXPAND_SZ なので、環境変数を指定してもちゃんと展開して理解してくれそうです。
(レジストリのデータ型については、@IT:.NET TIPS レジストリの値のデータ型を判別するには?参照。)

また、コマンドプロンプトからのレジストリ操作は、 reg.exe を使ったら容易にできるようです。
(これについては、@IT:Windows TIPS -- Tips:コマンド・プロンプトでレジストリを操作する参照。)

今回のフォルダリダイレクトを実現するためには下記のようにレジストリを設定したらいいようです。

reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" /v "Desktop" /d "\\sv1\user\%username%\デスクトップ"
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" /v "Personal" /d "\\sv1\user\%username%\マイドキュメント"

%username% 環境変数を使えるので、これでログインアカウント名のフォルダにリダイレクトできますね。

あとは、対象となるコンピュータでのみこのバッチファイルを実行するようにして、グループポリシーのログインスクリプトで流せばいいのですが、ここで詰まりました。

対象となるコンピュータかどうかは下記のコマンドで判断できるのですが。。。
if not /i "%HOSTNAME%" == "testsv1" (
EXIT
)

問題はこのバッチファイル内での IF 文で論理演算ができないことです。

今回対象となるコンピュータが複数台あるので、論理演算で OR や AND が使えないと意味がありません。

ということで、この方法も没ですが、コマンドプロンプトからレジストリ操作できるのと、バッチのIF文のダメダメな仕様に気付けてよかったです。

参考:
フォルダー リダイレクトでは、Windows のレジストリ設定
[PR]
by jehoshaphat | 2010-08-26 22:46 | 豆知識 | Trackback | Comments(0)
(ActiveDirectory)グループポリシーのセキュリティフィルタとWMIフィルタ
フォルダリダイレクトと移動ユーザープロファイルで、フォルダリダイレクトについて特定のサーバ利用時だけリダイレクトを行いたいという要件があり、これをグループポリシーのセキュリティフィルタで実装しようとしましたが、ダメでした。

具体的にGPMC(グループポリシーの管理)でスコープタブからもセキュリティフィルタ処理をかけれるんですが、より詳細に設定したいときは フォルダリダイレクトを設定したグループポリシー → 編集 → ツリールートのプロパティ → セキュリティタブからできるようです。

ここで、Authenticated Users をリストから除け、リダイレクトをしたいサーバ(コンピュータ)に「グループポリシーの適用」を許可とし、その他のコンピュータを拒否にしました。
(Authenticated Users をのけたのは、TechNet:GPMC を使用してセキュリティ フィルタ処理を行うに「Authenticated Users グループには、ユーザーとコンピュータの両方が含まれます。」とあったからです。このグループにコンピュータが含まれるのは知りませんでした。)

こうすると、リダイレクトさせたいサーバでも、そうじゃないコンピュータでもリダイレクトされません。
おそらく、Authenticated Usersグループを外したため、対象ユーザがいなくなったからでしょう。

そう思って、新たなセキュリティグループを作成し、そこにリダイレクト対象ユーザをメンバとして所属させ、グループポリシーのセキュリティフィルタに「グループポリシーの適用」を許可として追加しました。

そうすると今度はその他のコンピュータを拒否にしているにも関わらず、その他コンピュータでも、本来リダイレクトさせたいサーバでもリダイレクトされてしまいます。

ここからは憶測なので断言ではできませんが、セキュリティフィルタでコンピュータを追加した場合は、グループポリシーのコンピュータの構成に対してフィルタがかかり、セキュリティフィルタにユーザを追加した場合は、グループポリシーのユーザの構成に対してフィルタかかるというような仕様じゃないんでしょうか。。

ということで、セキュリティフィルタは今回の要件では使えないということに。。。


グループポリシーにはセキュリティフィルタの他にWMIフィルタというものがあるようです。
これを使うとより細かい単位でグループポリシーの適用範囲を定義することができるようです。
(ただ、パフォーマンスの影響や適用対象が動的に変わるので、適用範囲の見極めしずらくなったりするようです)

ということで、WMIフィルタを使ってみました。

WMI(Windows Management Instrumentation)クエリはSQLのクエリ構文に似た感じで、自身のコンピュータの状態を取得することができます。

今回は特定のコンピュータでだけグループポリシーを実行したいので、ホスト名を取得し、実行したいコンピュータと一致するかどうかのWMIクエリを作成してみます。

しかし、WMIは膨大な数のプロパティやメソッドがあるので、なかなか簡単には探せません。
そこで役立つのが、マイクロソフトが提供しているWMI Code Creator v1.0というツールです。

このツールを使うと比較的容易にWMIの目的のプロパティを探すこともできますし、WMIスクリプトも作成できます。
使い方は、WMIを使うスクリプトを簡単に作成する - @ITが参考になります。

で、今回作成したWMIフィルタは下記のような感じです。
名前空間:
root\CIMv2
クエリ:
SELECT * FROM Win32_ComputerSystem WHERE  Name ='TestSV1' OR Name = 'TestSV2'

これをGPMCでグループポリシーに関連付けしていざ評価してみたところ、想定通りの動きとなりました。
つまり、TestSV1,TestSV2でログイン(ローカル、リモート関係なく)したときのみ、フォルダリダイレクトが動作するようになりました。
しかし、この WMI フィルタなんですが、クライアントPCが Windows 2000 の場合は、フィルタが効かずグループポリシーが適用になってしまうようなので、Windows 2000 のPCがドメイン内にいる場合は要注意です。


補足:
WMIフィルタで特定のPCやユーザだけグループポリシーの対象にしたくない時は下記のようにWMIクエリを書けばいいようです。
SELECT * FROM Win32_ComputerSystem WHERE  Name <> 'TestSV1' AND UserName <> 'hoge\userA'


参考:
TechNet:セキュリティ グループを使用してフィルタ処理を行う
TechNet:セキュリティ グループ メンバシップに応じてグループ ポリシーのスコープをフィルタ処理する
リンクの継承と優先度およびフィルタ機能 - @IT
TechNet:GPMC を使用して WMI フィルタ処理を行う
[PR]
by jehoshaphat | 2010-08-24 04:28 | サーバがらみ | Trackback(1) | Comments(0)
(WindowsServer2003)フォルダリダイレクトと移動ユーザープロファイル
下記のような要件を満たす環境を作成したいと思ってます。
「ターミナルサービス利用時に、各ユーザのマイドキュメントやデスクトップの保存先を共有フォルダにしたい。
(複数台のターミナルサーバが存在し、どちらにログインしても同一のドキュメントを見れるようにするため。PC利用時はこの機能を用いない。)」
ターミナルサーバ利用時だけってのがちょいと特殊要件ですね。


Windows Server の機能を使ってこの要件を実現するには方法が2つあるようです。

一つがフォルダリダイレクト、もうひとつが移動プロファイル(ターミナルサーバ用)です。


1.フォルダリダイレクト
Windows Server 2003では各ユーザの下記のフォルダを共有フォルダにリダイレクトできるようです。
・Application Data
・デスクトップ
・マイドキュメント
・スタートメニュー
Windows Server 2008 だとリダイレクトできるフォルダが増えているようです。
[Think IT] 第3回:グループポリシーを使って楽々管理


基本的にユーザがリダイレクト対象フォルダに対して、何か操作を行うとリダイレクト先に即時に反映されます。
ちょうど普通に共有フォルダを使ってる感覚ですね。


設定はグループポリシーの ユーザーの構成 → Windowsの設定 → フォルダリダイレクト → 対象とするフォルダを右クリック → プロパティ で設定ダイアログを表示します。

基本的には、「基本 - 全員のフォルダを同じ場所にリダイレクトする」で、「ルートパスの下に各ユーザーのフォルダを作成する」になるかと思います。

この場合、フォルダ構成は下記のようになります。(\\sv1\userをルートパスとした場合)

\\sv1\user\ユーザ1\デスクトップ
\マイドキュメント
\\sv1\user\ユーザ2\デスクトップ
\マイドキュメント

これで、指定したコンピュータでユーザログイン時に指定したパスの下に各ユーザのフォルダが自動的に作成されます。

この時、リダイレクトの設定で「ユーザーにマイドキュメントに対して排他的な権限を与える」とすると、そのユーザしかアクセスできせん。
ここら辺の動作は、MS TechNet:GPMC のフォルダ リダイレクトの概要で書かれてます。(Windows Server 2008以降はこちらを参照)
まとめると下記のような感じです。
排他的権限のチェック On かつ リダイレクト先フォルダが無い:新しいフォルダ作成されるが、所有者である対象ユーザとローカルシステムのみフルコントロール。
排他的権限のチェック On かつ リダイレクト先フォルダが有る:フォルダ所有権が検証。別ユーザ所有の場合はリダイレクトされない。(アクセス許可もそのまま)
排他的権限のチェック Off : 既定のアクセス許可はすべて有効のまま(つまり誰でもアクセス可能となる)

排他的権限をオンにすると、管理者すらアクセスできなくのはちょっと管理上不便です。

上記のTechNetの資料から、フォルダ所有権を見て検証しているようなので、各ユーザのフォルダとその配下のリダイレクト先フォルダを、管理者と各ユーザのアクセス権を持つように作成し、所有権を各ユーザに与えるという運用にしました。
こうすると、リダイレクトもできますし、アクセス許可もリダイレクト先フォルダ作成時に指定した管理者でもアクセスできるアクセス権になります。


また、ルートとなるフォルダのNTFSアクセス許可とSMBアクセス許可も注意が必要なようで、これらも上記 TechNet のサイトに書かれてます。

NTFSアクセス許可が必須ということのなので、リダイレクト先を Linux ベースの NAS とかにはできませんね。


フォルダリダイレクトを設定するとデフォルトでオフラインでの使用許可になってしまいます。
今回はターミナルサーバと同一セグメントにファイルサーバを置くので、オフラインでの使用は想定してません。
また、オフライン設定有効だとリダイレクトされたフォルダをキャッシュすると利用可能なすべてのディスク領域が使用されるようなこともあり得るので、常にオンラインで利用するように設定します。
まずグループポリシーで ユーザの構成 → 管理用テンプレート → ネットワーク → オフラインファイル → リダイレクトされたフォルダを自動的にオフライン利用できるようにしない を有効にします。
そして、共有フォルダのプロパティのキャッシュの設定で、共有にあるファイルやプログラムはオフラインで利用可能にしない にチェックを入れるといいようです。


問題となったのは特定のコンピュータのみ適用できるのか(PCに対しては設定しないため)ということですが、グループポリシーにはセキュリティフィルタというのがあるようで、これで対象となるターミナルサーバを指定しました。
(セキュリティフィルタにより、グループポリシーを適用するユーザやコンピュータを指定できるようです。セキュリティフィルタについては、ここここを参照。)
これででターミナルサーバ利用時だけフォルダリダイレクトできるかと思ったんですが、ダメでした。
詳しくは別記事にしたいと思います。



フォルダリダイレクト参考:
@IT:Active Directoryを利用したユーザー管理 Win2000の場合ですが、%username%を使う方法はいいと思いました。
モバイルアクセス計画50 Vistaでフォルダリダイレクトする時の注意点 あげだま日記/ウェブリブログ キャッシュの無効の設定があります。


2.移動プロファイル(ターミナルサーバ用)

もうひとつが移動プロファイルを使う方法です。
プロファイルには、ローカルユーザープロファイル、移動ユーザープロファイル、固定ユーザープロファイルってのがあります。

ローカルユーザープロファイルは各ユーザの情報をローカルPCにもつものでデフォルトです。(C:\Documents and Settings や C:\Users(Vista,2008以降)に配置されます。)
移動ユーザープロファイルはユーザのプロファイルをネットワーク上に持つもので、どのPCにログインしても同じプロファイルが使用されるので、作業する端末が逐次異なる場合には便利ですね。

固定ユーザープロファイルは移動プロファイルの固定版ですね。つまり、固定ユーザプロファイルのユーザはログイン中に行った変更がログオフ時に破棄されるというものです。(キオスク的な使い方ができそうです)

さて、この移動プロファイルですが、ログイン時にユーザのプロファイルをサーバからローカルのキャッシュに取得し、ログイン中の変更はキャッシュに対して行います。
そして、ログオフ時に更新日付を見て変更のあったファイルをサーバ側に反映するという仕組みのようです。

いろいろ調査していると、ターミナルサーバ利用時だけ移動プロファイルを使うという設定ができることが分かりました。
"ActiveDirectoryユーザとコンピュータ" から、ユーザアカウントのプロパティを表示し、"ターミナルサービスのプロファイル" タブから "プロファイルパス" にターゲットとなる共有フォルダをパスを入れるといいようです。

これはグループポリシーでも一元的に設定可能で、その場合は、グループポリシーの コンピュータの構成 → 管理用テンプレート → ターミナルサービス → TS 移動プロファイルのパスを指定する から指定できます。

仮にユーザ1のターミナルサービスのプロファイルパスを "ActiveDirectoryユーザとコンピュータ" から \\sv1\user とすると、このようなフォルダが作成されます。

\\sv1\user\Application Data
\Cookies
\Favorites
\My Documents
\NetHood
\PrintHood
\Recent
\SendTo
\Templates
\WINDOWS
\スタート メニュー
\デスクトップ
\NTUSER.DAT
\ntuser.dat.LOG
\ntuser.ini
\ntuser.pol
\Sti_Trace.log


さて、このプロファイルにはユーザ毎のレジストリやアプリケーション設定情報も含まれるため注意が必要です。
それはバージョンの異なる Windows やソフトを使う場合ですね。

Windows Server 2003と2008ではユーザプロファイルの構成が大幅に変わってるので同時に使うのは難しいかもしれません。


また、ログオン時に取得、ログオフ時に更新ということから、マイドキュメント等が肥大化するとログインに時間がかかるという懸念もあります。
(ただ、特定のフォルダを移動プロファイルから除外する方法はあるようです。MSサポート:Windows Server 2003 で移動プロファイルからフォルダを除外する方法参考。)


保守性の点から言うとフォルダリダイレクトの方がいいかもしれませんが、両方組み合わせるようなこともできるようです。
今回の要件はフォルダリダイレクトを使うことにしました。(ターミナルサーバもWindows Serverのバージョンが異なるので...)

また、調査してる時に ホームフォルダ という言葉をちらほら見ました。
Windowsのヘルプとサポートによると次のように定義されてました。
ホーム フォルダ
管理者が、個々のユーザーまたはグループに割り当てることのできるフォルダ。通常はサーバー上にあります。管理者は、ホーム フォルダを利用することにより、ユーザー ファイルを特定のファイル サーバー上に集中管理でき、バックアップを容易に作成できるようになります。プログラムによっては、[開く] ダイアログ ボックスおよび [名前を付けて保存] ダイアログ ボックスの既定のフォルダとしてホーム フォルダを使うものもあります。"ホーム ディレクトリ" と呼ばれることもあります。

また、すぐできるWindowsサーバー強化術(第2回)GPOやコマンドで一括設定する - Windows読者限定:ITproでもホームフォルダの用途が書かれてました。
要は移動プロファイルを使った時に、マイドキュメントやデスクトップにファイルを保存するとログイン・ログオフに時間かかるため、ネットワークドライブとしてマウントされるホームフォルダに保存するとファイルサーバに即時書き込むため便利ですよってものらしいです。また、コマンドプロンプトのカレントディレクトリがホームフォルダとしてマウントしたネットワークドライブになります。
ただ、これはユーザが意識的にマイドキュメントやデスクトップを使わず、ネットワークドライブとしてマウントされたホームフォルダに保存するようにしないといけません。
あまり、ホームフォルダは実用価値が無いように感じます。ホームフォルダ使うんだったらフォルダリダイレクト使った方がいいと思いますね。(ここでもMSの見解がそうであると書かれています。)


ターミナルサーバプロファイル参考:
移動ユーザープロファイルの説明 | Windowsに関するちょっとしたこと 各種プロファイルの説明です。
ユーザープロファイルの管理とその課題 ≫ ocb - Citrix Community 移動ユーザプロファイルの課題等も説明されており、非常に参考になります。
Ask the Network & AD Support Team : プロファイル情報を格納したファイルサーバーの移行方法 移動プロファイルとリダイレクト、そしてオフラインキャッシュなどわかりやすく濃い内容です。
TechNet:ターミナル サービスのプロファイル
TechNet:ユーザーのターミナルサービスプロファイルのパスを変更する
TechNet:ユーザー プロファイルのヒント集 移動プロファイルの注意点が書かれてます。
[PR]
by jehoshaphat | 2010-08-20 23:11 | サーバがらみ | Trackback | Comments(0)
(.Net)自作クラスに終了処理を実装する
ファイル操作を行う自作クラスでインスタンスを利用し終わった後にリソース解放の終了処理をしたいと思っています。

C++ のときはデストラクタに終了処理を書けばよかったんですが、.Net Framework の場合はその方法は使わない方がいいようです。
(ガーベージコレクションでいつ解放されるかわからないので....)


で、自作クラスに終了処理を実装にするには IDisposable インターフェイスを実装して、そこに終了処理を書けばいいようです。

こんな感じです。(C#)
public class TestClass : IDisposable {
public TestClass() {
//コンストラクタ
}
 
public void Dispose() {
// 終了処理
}
}

このクラスを呼び出して使う側でもいくつか手順が必要なようです。

一つは finally で Dispose メソッドを呼び出す方法です。
TestClass cls = new TestClass();
try{
// 処理
}finally{
cls.Dispose();
}

C# にはもう一つ using 句を使う方法があるようです。
(using といっても名前空間とは別物のようです。)
使い方はこんな感じです。
using (TestClass cls = new TestClass()) {
// 処理
}

finally を明示的に書かなくて済むので解放忘れは防げそうですね。
今まで using 句はほとんど使ってこなかったでこれからは使っていこうかと思います。
ただ個人的には、インデントがついてしまうのが気になります。。


補足ですが、Disposeメソッドと共に終了処理として Close メソッドを実装しているクラスがいくつかありますよね。(Connection 系や IO Stream 系)
これらのクラスを Open メソッドで使った時は明示的 finally 句で Close してやらないといけないようです。
つまり、using だけではダメということです。
結局こうなります。
using (StreamReader rd = new StreamReader(@"C:\Hoge.txt", System.Text.Encoding.Default)) {

try {
//処理...
} finally {
// rd を閉じる
if (rd != null) {

rd.Close();
}
}
}

参考:
MSDN:Dispose メソッドの実装
@IT:.NET TIPS 確実な終了処理を行うには? - C#
C# の using ステートメントによる Dispose()
IDisposableとDispose - はらぐろブログラマン
C# オブジェクトの破棄を保証する
[PR]
by jehoshaphat | 2010-08-19 23:46 | Trackback | Comments(0)