お世話になります。
単刀直入にタスクマネージャの[ユーザー名]項目を
エラーなく取得したいです。こちらでいろいろと調べ
カレントのユーザとSYSTEMに関しては取得できました。
参考にしたリンクは下です。
http://support.microsoft.com/kb/111544/ja
ただし、このままだとSYSTEMの一部がOpenProcess()で
エラーとなるため自プロセスのアクセストークンに
SE_DEBUG_NAME特権を有効にしています。
これでも一部のSYSTEMやカレントユーザのプロセスが
OpenProcessToken()でエラーとなります。
GetLastError()では[アクセスが拒否されました。]
と返してきます。
どうしたらOpenProcessToken()でアクセスが拒否されずに
トークンを開けるのでしょうか?
また、NETWORK SERVICEとLOCAL SERVICEのプロセスは一切成功しません。
こちらもアクセスが拒否されるようです。
何をどうしたら良いのでしょうか?
教えて下さい。
お願い致します。(環境:Windows XP、VC++2003.NET 非MFC)
[エラーになるプロセス]
(ユーザ名) C:\Program Files\Common Files\Symantec Shared\ccApp.exe
SYSTEM C:\Program Files\Common Files\Symantec
Shared\AppCore\AppSvc32.exe
SYSTEM C:\Program Files\Common Files\Symantec Shared\CCPD-
LC\symlcsvc.exe
SYSTEM C:\Program Files\Common Files\Symantec Shared\ccSvcHst.exe
SYSTEM C:\Program Files\Symantec\LiveUpdate\ALUSchedulerSvc.exe
LOCAL SERVICE C:\WINDOWS\System32\alg.exe
LOCAL SERVICE C:\WINDOWS\System32\svchost.exe
LOCAL SERVICE C:\WINDOWS\system32\wdfmgr.exe
NETWORK SERVICE C:\WINDOWS\system32\svchost.exe
NETWORK SERVICE C:\WINDOWS\System32\svchost.exe
WMI(WBEM)のWin32_Processで取得される各インスタンスにGetOwner()を
飛ばしてみるとか・・・。
wbemtestで試す限りは大丈夫っぽいです。
Win32では試しておりませんのであしからず。
> ただし、このままだとSYSTEMの一部がOpenProcess()で
> エラーとなるため自プロセスのアクセストークンに
> SE_DEBUG_NAME特権を有効にしています。
この「SYSTEMの一部」が管理者権限を必要としていると言うことはないですか?
ん?「svchost.exe」、「svchost.exe」はまさに管理者権限がいるんじゃないかな?
よく読んでなかったです(^^;
> 単刀直入にタスクマネージャの[ユーザー名]項目エラーなく取得したいです。
これは管理者権限が必要でしょう。
ソフト起動前に、管理者権限の有無をチェックしないとだめだと思います。
玲音(st.lain)さんレスありがとう。
> WMI(WBEM)のWin32_Processで取得される各インスタンスにGetOwner()を
> 飛ばしてみるとか・・・。
WMIはよく分かりません。
こちらはC++とWin32で作成しています。
ネット検索するとWin32_Processが見つかります。
WMIとは何なのでしょうか?
ITOさんもレスありがとう。
> これは管理者権限が必要でしょう。
この管理者権限とはAdministratorsという事でしょうか?
制限付きのアカウントではなく管理者のアカウントで実行しています。
この環境でも管理者権限が必要なのでしょうか?
> ソフト起動前に、管理者権限の有無をチェックしないとだめだと思います。
ソフトは管理者のアカウントで実行することを前提にしています。
それでもOpenProcessToken()で[アクセスが拒否されました。]の
エラーが発生します。
いろいろと検索してSE_TCB_NAME権限を有効にすれば良いのかと思ってます。
ただ SE_TCB_NAME とは[オペレーティング システムの一部として機能]
という事だそうで通常では有効に出来ません。
いろいろと調べていたら過去に似たような質問がありました。
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200407/04070077.txt
ここでは Code Project を参考にして解決しているようですが
どこを参考にしたのかが書かれていないため分かりません。
ソースを見てはいますがどの部分が重要なのかが見つけられずにいます。
どなたかお力をお貸し下さい。
お、俺の立てたスレが挙がってる。
> WMIとは何なのでしょうか?
コンピュータの様々な情報にアクセスするための統一されたインターフェイスです。
C++ と Win32 でも使えますが、ちょっとめんどくさいです。
> ここでは Code Project を参考にして解決しているようですが
> どこを参考にしたのかが書かれていないため分かりません。
真似しないほうがいいコードです。忘れましょう。
シャノンさんレスありがとう。
> コンピュータの様々な情報にアクセスするための統一されたインターフェイスです。
> C++ と Win32 でも使えますが、ちょっとめんどくさいです。
インターフェイスの名前でしたか。
このインターフェイスとはCOMとか、OLEなんでしょうかね?
ちょっとWMI(Windows Management Instrumentation)について調べてみました。
http://www.microsoft.com/japan/technet/scriptcenter/resources/wmifaq.mspx
このWMIなら上手くユーザ名、ドメイン名が取得可能なのでしょうか?
> 真似しないほうがいいコードです。忘れましょう。
これはトリッキーだからすべきではないという事でしょうか?
トリッキーなら使うのは止めようと思います。
何から調べたら良いでしょうか?
助言をお待ちしています。
> このインターフェイスとはCOMとか、OLEなんでしょうかね?
WMI を C++ から使う場合は COM の形を取ります。
> このWMIなら上手くユーザ名、ドメイン名が取得可能なのでしょうか?
たぶん。
> これはトリッキーだからすべきではないという事でしょうか?
そうです。
> 何から調べたら良いでしょうか?
C++ から WMI を使う方法を調べること、かな。
「C++ WMI」とかで検索すればサンプルが見つかると思います。
それを試していく中でわからないことがあったら、まず自分なりに調べてみてから、また
別スレ立てて質問してください。
Win32APIでの手法も調べてみましたが、
・GetTokenInformaitonでユーザートークンを調べる (シャノンさんのスレどおり)
・WTSEnumerateProcesses (ターミナルサービス必須なので通常不能)
っぽいです。
もうちょっと簡単に取得できる方法が欲しいところなのですけどね・・・。
ぼちぼち調べて何かあれば書き込むカモ知れません。
> ・WTSEnumerateProcesses (ターミナルサービス必須なので通常不能)
やってみたらこっちはバッチリ取れましたよー!
ちなみに XP 以降なら、ユーザの簡易切り替え機能がターミナルサービスを応用して実装
されているので、ターミナルサービスは標準装備のはず。
あれですな、WMI にせよ WTS にせよ、それらは処理の実体はサービスだからできるんで
しょうな。
シャノンさん、情報 & 試行ありがとうす。
> ユーザの簡易切り替え機能がターミナルサービスを応用して実装されているので、
これが盲点というか落とし穴だったのですね。 orz
私的な問題(単にWin2000チック大スキーな人)により、インストール直後に真っ先に
ユーザの簡易切り替えをOFFにします。
ですから、その関係でしょうね、私の環境ではWTSEnumerateProcesses()がFALSE
(GetLastError()では0x6f8)が返ってきてました。
# 時間がまた取れたらユーザ切り替えがTS関連かどうかも今一度自分で確認して
# みたいところです。
シャノンさん。
ありがとうございます。
> WMI を C++ から使う場合は COM の形を取ります。
COMは最近になって始めたばかりです。
> 「C++ WMI」とかで検索すればサンプルが見つかると思います。
http://touzokudan.blog31.fc2.com/blog-entry-6.html
確かにすぐに見つかりました。
今回はWTSEnumerateProcesses関数の路線で解決しようと思います。
暇が出来たら[C++ WMI]でいろいろと調べてみます。
玲音(st.lain)さんも
ありがとうございます。
> Win32APIでの手法も調べてみましたが、
GetTokenInformaitonで調べる前にアクセス・トークンが
開けずに困っています。今回の質問で一番お聞きしたい点です。
> WTSEnumerateProcesses
この関数は初めてです。
> やってみたらこっちはバッチリ取れましたよー!
僕も試した結果、ユーザ名とドメイン名が見事に取得できました。
でもProcessID=0だけはパラメータのエラーが返ってきました。
タスクマネージャでは[System Idle Process]と表示されているヤツ。
まぁ仕方がないかな。
SID=0(NULL)ですからLookupAccountSid()関数でエラーです。
他の閲覧者さんの為にソースも貼っときます。
#include <stdio.h>
#include <windows.h>
#include <WtsApi32.h> // WtsApi32.Lib
// 記号定数
#define STR_STATUS No. Process Name Process ID Session
ID User SID User Name Domain Name \n
#define STR_HLINE --- ------------------------------ ---------- --------
-- ---------- -------------------- --------------------\n
// メイン関数
int main( void )
{
PWTS_PROCESS_INFO lpProcessInfo;
DWORD dwProcessInfo;
if ( WTSEnumerateProcesses
(WTS_CURRENT_SERVER_HANDLE,0,1,&lpProcessInfo,&dwProcessInfo) ){
printf( STR_STATUS );
printf( STR_HLINE );
for ( DWORD i = 0 ; i < dwProcessInfo ; i++ ){
TCHAR szName[ 256 ] = { 0 };
DWORD dwName = sizeof(szName);
TCHAR szDomain[ 256 ] = { 0 };
DWORD dwDomain = sizeof(szDomain);
SID_NAME_USE sidType;
printf( %03lu , (i + 1) );
printf( %-30.29s , lpProcessInfo[i].pProcessName );
printf( %10lu , lpProcessInfo[i].ProcessId );
printf( %10lu , lpProcessInfo[i].SessionId );
printf( 0x%p , lpProcessInfo[i].pUserSid );
BOOL bRet = LookupAccountSid( NULL, lpProcessInfo[i].pUserSid,
szName, &dwName, szDomain, &dwDomain, &sidType );
if ( bRet ){
printf( %-20.19s , szName );
printf( %-10.19s , szDomain );
printf( \n );
}
else{
printf( %ld:LookupAccountSid()関数のエラーです。\n,
GetLastError() );
}
}
printf( STR_HLINE );
WTSFreeMemory( lpProcessInfo );
return 0;
}
printf( どこかでエラーが発生した。\n );
return 1;
}
最後に皆様のお陰で見事に取得できました。
ありがとうございました。
そのコードはやばいです。
LookupAccountSid の 第4、第6引数は文字数単位ですが、このコードではバイト数を渡
しています。
具体的には、Unicode ビルドされた場合、確保した量の2倍のバッファがあると誤解して
バッファオーバーフローが発生する可能性があります。
DWORD dwName = _countof(szName);
か
DWORD dwName = sizeof(szName) / sizeof( TCHAR );
とするのがよいでしょう(_countof を使うには stdlib.h のインクルードが必要)
あと、Unicode 文字列を printf では正常に表示できないと思います。
locale.h をインクルードして setlocale( LC_ALL, Japanese ); した上で、_tprintf
(あるいは _tprintf_s)を使うのが良いでしょう。
シャノンさん。
ご指摘ありがとうございます。
あまりUnicodeを使ったことがなかったので気づきませんでした。
前に何処かのサイトで
> DWORD dwName = sizeof(szName) / sizeof( TCHAR );
となっていたのを思い出しました。
TCHARがWCHARの場合には文字数が一致しませんね。
> あと、Unicode 文字列を printf では正常に表示できないと思います。
こちらに関してはテストプログラムなのでマルチバイト設定にして
printfを使っています。
Unicode 文字列を正常に表示するには setlocale() 関数で適切なロケールを
設定した後で _tprintf() 関数を使えば良いのですね。
(※VC++2003.NETなので_tprintf_sは使えないようです)
http://www.geocities.jp/i96815/winprg/wp03.html
こちらを参考にしました。
今後テストプログラムでも _main() と _tprintf() 関数を
使って行きたいと思います。
お勉強になりました。