こんにちは。
RegSaveKeyと、RegReplaceKeyを使って、
HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings
以下のキーや値をエクスポート、インポートするようなものを作っています。
で、エクスポートする方で早速躓いてしまいました。
エクスポート用の関数として、以下のようなサンプル関数を作ってみました。
//インターネットの設定をエクスポートする関数
BOOL ExportInternetSettings(char *RegFileName)
{
HKEY l_hKey;
DWORD dwDisposition;
LPVOID lpMessageBuffer;
//レジストリを開く
if(RegCreateKeyEx(HKEY_CURRENT_USER, INTERNET_CONNECTION_KEY, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &l_hKey, &dwDisposition) != ERROR_SUCCESS)
// if(RegOpenKeyEx(HKEY_CURRENT_USER, INTERNET_CONNECTION_KEY, 0, KEY_ALL_ACCESS, &l_hKey) != ERROR_SUCCESS)
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMessageBuffer, 0, NULL);
MessageBox(NULL, (LPCSTR)lpMessageBuffer, "エラー", MB_OK);
LocalFree( lpMessageBuffer );
return FALSE;
}
//レジストリをエクスポート
if(RegSaveKey(l_hKey, RegFileName, NULL) != ERROR_SUCCESS)
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMessageBuffer, 0, NULL);
MessageBox(NULL, (LPCSTR)lpMessageBuffer, "エラー", MB_OK);
LocalFree( lpMessageBuffer );
RegCloseKey(l_hKey);
return FALSE;
}
RegCloseKey(l_hKey);
return TRUE;
}
実行してみたところ、RegSaveKeyで失敗をしているのですが・・・
FormatMessageでGetLastErrorの情報を取得しても「正常に終了」と・・・
MSDNなどを見てもわからず・・・
どなたかご存じの方、何がいけないかご教授いただけませんでしょうか?
ちなみに、開発環境はWindows XP SP2 + VS.NET 2005 Team + 最新Windows SDK です。
よろしくお願いいたします。
済みません、ソースそのまま貼り付けたら見づらい上に、一つ定義が抜けていました・・・
ソース部分だけ訂正します。
#define INTERNET_CONNECTION_KEY "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" //IE設定保存先レジストリキー
//インターネットの設定をエクスポートする関数
BOOL ExportInternetSettings(char *RegFileName);
//WinMain関数
int APIENTRY WinMain(HINSTANCE hInstCurrent, HINSTANCE hInstPrev, LPTSTR lpszCmdLine, int nCmdShow)
{
・・・省略(ここでエクスポート関数を実行)
}
//インターネットの設定をエクスポートする関数
BOOL ExportInternetSettings(char *RegFileName)
{
HKEY l_hKey;
DWORD dwDisposition;
LPVOID lpMessageBuffer;
//レジストリを開く
if(RegCreateKeyEx(HKEY_CURRENT_USER,
INTERNET_CONNECTION_KEY,
0, NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&l_hKey, &dwDisposition) != ERROR_SUCCESS)
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL,
SUBLANG_DEFAULT),
(LPTSTR) &lpMessageBuffer,
0,
NULL);
MessageBox(NULL, (LPCSTR)lpMessageBuffer, "エラー", MB_OK);
LocalFree( lpMessageBuffer );
return FALSE;
}
//レジストリをエクスポート
if(RegSaveKey(l_hKey, RegFileName, NULL) != ERROR_SUCCESS)
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL,
SUBLANG_DEFAULT),
(LPTSTR) &lpMessageBuffer,
0,
NULL);
MessageBox(NULL, (LPCSTR)lpMessageBuffer, "エラー", MB_OK);
LocalFree( lpMessageBuffer );
RegCloseKey(l_hKey);
return FALSE;
}
RegCloseKey(l_hKey);
return TRUE;
}
よろしくお願いいたします。
RegSaveKeyのページ
http://msdn.microsoft.com/en-us/library/ms724917(VS.85).aspx
を見ますと、どこにも「失敗時には GetLastError を使え」と書いてありません。
関数の戻り値を FormatMessage に与えてみてはいかがでしょうか。
>シャノンさん
レスありがとうございます。
まさにその通りですね・・・。
で、確認してみたところ、「クライアントは要求された特権を保有していませ
ん。」だそうです。
でも、実行ユーザはローカル管理者を持ってますし・・・どういうことなんで
しょ?
ちなみに、実際にはUsers権限のアカウントがログオンしているときに、HKCU
の値をエクスポート・インポート(リストア)しようと思っているのですが・・
コマンドから実行するしかないんですかね・・・?
> 「クライアントは要求された特権を保有していません。」だそうです。
そんな気がしていました。
同じページに、こうも書いてあります。
> The calling process must have the SE_BACKUP_NAME privilege enabled.
ということで、この特権を有効にする必要があります。
特権には、「持っている/持っていない」と「有効/無効」という状態があり、「持っていて有効」「持っていて無効」「持っていない」の3通りの組み合わせがあります(持っていない場合は有効にできません)。
で、Administrators と Backup Operators に属するユーザはこの特権を持っているので有効にできますが、Users は持っていないので有効にできません。
従って、Users ではこれらの関数は使用できないということになります。
設定次第では Users にこの特権を持たせることもできるのですが、そうすると、HKCU のみならず、システム上の全ファイルに対して(ACLに関係なく)読み取りアクセス権を持ってしまうので、過ぎた権限と言えます。
ちなみに、RegSaveKey が生成するのはバイナリファイルで、RegEdit のエクスポートで生成できるようなテキストファイルではありません。
RegEnumKeyEx / RegQueryValueEx / WritePrivateProfileString 等の API を使って自分でロジックを組めば、RegEdit を模倣した ini 形式のファイルを作ることは可能です。
レストアするにも RegSetValueEx 等を何度も呼ばなければいけないので面倒ではありますが、これらの API を使うだけなら、SE_BACKUP_NAME 特権は要りません。
個人的にはそっちをおすすめしますが、いかがでしょうか?
外部コマンドを使ってもいいのなら、reg.exe が使えます。
この save / restore コマンドが RegSaveKey / RegLoadKey で、export / import コマンドが、RegQueryValueEx / RegSetValueEx 等で実現されています。
シャノンさん
ありがとうございます。
やっぱりそうなのですね・・・
作成しているツールでは、名前付きパイプを使ってシステムサービスに信号を
送って別の処理をしてもらっているところがあるので、そこを使ってできない
か試してみて(ローカルシステムアカウントで実行した場合に、現在のログオ
ンアカウントのHKCUを取得できるかが鍵ですね・・・)、それでダメであれば、
コマンドでエクスポート&インポートするようにします。
いつもありがとうございます。
LoadUserProfile 関数を使えば、他のユーザの HKCU キーにアクセスできそうです。
あるいは、RegOpenKeyEx( HKEY_CURRENT_USER, NULL, ... ) で取得したハンドルをパイプを通じて渡してもいいかも。
> それでダメであれば、コマンドでエクスポート&インポートするようにします。
一般ユーザ権限で reg.exe save が成功するかどうか試してみた方がいいと思います。
シャノンさん
ありがとうございました!!
あの後、一つ参考ページを見つけたのですが
( http://support.microsoft.com/kb/128731/ja )
実は、今のところ管理者権限でもうまく言っていなかったんです。
んで、上記のサイトを参考に、以下のようにエクスポート、インポートの関数
を修正しました。
//インターネットの設定をエクスポートする関数
BOOL ExportInternetSettings(char *RegFileName)
{
HKEY l_hKey;
DWORD dwDisposition;
LPVOID lpMessageBuffer;
LONG l_Ret;
HANDLE l_hToken;
LUID l_luid;
TOKEN_PRIVILEGES l_tp;
//RegSaveKeyを行う為の特権を有効する
//まずはトークンを開く
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &l_hToken))
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMessageBuffer, 0, NULL);
MessageBox(NULL, (LPCSTR)lpMessageBuffer, "OpenPricessToke実行エラー", MB_OK);
LocalFree( lpMessageBuffer );
return FALSE;
}
//次にLUID取得
if(!LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &l_luid))
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMessageBuffer, 0, NULL);
MessageBox(NULL, (LPCSTR)lpMessageBuffer, "LookupPrivilegeValue実行エラー", MB_OK);
LocalFree( lpMessageBuffer );
CloseHandle(l_hToken);
return FALSE;
}
//トークン内の特権を有効にする
l_tp.PrivilegeCount = 1;
l_tp.Privileges[0].Luid = l_luid;
l_tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(l_hToken, FALSE, &l_tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL );
if(GetLastError() != ERROR_SUCCESS)
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMessageBuffer, 0, NULL);
MessageBox(NULL, (LPCSTR)lpMessageBuffer, "AdjustTokenPrivileges実行エラー", MB_OK);
LocalFree( lpMessageBuffer );
AdjustTokenPrivileges(l_hToken, TRUE, NULL, 0, NULL, NULL);
CloseHandle(l_hToken);
return FALSE;
}
//レジストリを開く
l_Ret = RegCreateKeyEx(HKEY_CURRENT_USER, INTERNET_CONNECTION_KEY, 0, NULL, REG_OPTION_BACKUP_RESTORE, KEY_QUERY_VALUE, NULL, &l_hKey, &dwDisposition);
if(l_Ret != ERROR_SUCCESS)
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, l_Ret, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMessageBuffer, 0, NULL);
MessageBox(NULL, (LPCSTR)lpMessageBuffer, "RegCreateKeyEx実行エラー", MB_OK);
LocalFree( lpMessageBuffer );
AdjustTokenPrivileges(l_hToken, TRUE, NULL, 0, NULL, NULL);
CloseHandle(l_hToken);
return FALSE;
}
//レジストリをエクスポート
l_Ret = RegSaveKey(l_hKey, RegFileName, NULL);
if(l_Ret != ERROR_SUCCESS)
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, l_Ret, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMessageBuffer, 0, NULL);
MessageBox(NULL, (LPCSTR)lpMessageBuffer, "RegSaveKey実行エラー", MB_OK);
LocalFree( lpMessageBuffer );
AdjustTokenPrivileges(l_hToken, TRUE, NULL, 0, NULL, NULL);
CloseHandle(l_hToken);
RegCloseKey(l_hKey);
return FALSE;
}
RegCloseKey(l_hKey);
AdjustTokenPrivileges(l_hToken, TRUE, NULL, 0, NULL, NULL);
CloseHandle(l_hToken);
return TRUE;
}
//インターネットの設定をインポートする関数
BOOL ImportInternetSettings(char *RegFileName)
{
HKEY l_hKey;
DWORD dwDisposition;
LPVOID lpMessageBuffer;
LONG l_Ret;
HANDLE l_hToken;
LUID l_luid;
TOKEN_PRIVILEGES l_tp;
//RegSaveKeyを行う為の特権を有効する
//まずはトークンを開く
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &l_hToken))
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMessageBuffer, 0, NULL);
MessageBox(NULL, (LPCSTR)lpMessageBuffer, "OpenPricessToke実行エラー", MB_OK);
LocalFree( lpMessageBuffer );
return FALSE;
}
//次にLUID取得
if(!LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &l_luid))
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMessageBuffer, 0, NULL);
MessageBox(NULL, (LPCSTR)lpMessageBuffer, "LookupPrivilegeValue実行エラー", MB_OK);
LocalFree( lpMessageBuffer );
CloseHandle(l_hToken);
return FALSE;
}
//トークン内の特権を有効にする
l_tp.PrivilegeCount = 1;
l_tp.Privileges[0].Luid = l_luid;
l_tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(l_hToken, FALSE, &l_tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL );
if(GetLastError() != ERROR_SUCCESS)
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMessageBuffer, 0, NULL);
MessageBox(NULL, (LPCSTR)lpMessageBuffer, "AdjustTokenPrivileges実行エラー", MB_OK);
LocalFree( lpMessageBuffer );
AdjustTokenPrivileges(l_hToken, TRUE, NULL, 0, NULL, NULL);
CloseHandle(l_hToken);
return FALSE;
}
//レジストリを開く
l_Ret = RegCreateKeyEx(HKEY_CURRENT_USER, INTERNET_CONNECTION_KEY, 0, NULL, REG_OPTION_BACKUP_RESTORE, KEY_QUERY_VALUE, NULL, &l_hKey, &dwDisposition);
if(l_Ret != ERROR_SUCCESS)
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, l_Ret, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMessageBuffer, 0, NULL);
MessageBox(NULL, (LPCSTR)lpMessageBuffer, "RegCreateKeyEx実行エラー", MB_OK);
LocalFree( lpMessageBuffer );
AdjustTokenPrivileges(l_hToken, TRUE, NULL, 0, NULL, NULL);
CloseHandle(l_hToken);
return FALSE;
}
//レジストリをインポート
l_Ret = RegRestoreKey(l_hKey, RegFileName, REG_FORCE_RESTORE);
if(l_Ret != ERROR_SUCCESS)
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, l_Ret, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMessageBuffer, 0, NULL);
MessageBox(NULL, (LPCSTR)lpMessageBuffer, "RegRestoreKey実行エラー", MB_OK);
LocalFree( lpMessageBuffer );
RegCloseKey(l_hKey);
AdjustTokenPrivileges(l_hToken, TRUE, NULL, 0, NULL, NULL);
CloseHandle(l_hToken);
return FALSE;
}
RegCloseKey(l_hKey);
AdjustTokenPrivileges(l_hToken, TRUE, NULL, 0, NULL, NULL);
CloseHandle(l_hToken);
return TRUE;
}
このように、特権を有効にしなければならなかったのですね・・・
これで、とりあえず管理者ではできそうです!!
後は、シャノンさんのアイデアのパイプにキーのハンドルを渡す方法を試して
みます!!
ナイスなアイデア、ありがとうございます☆
この場合、パイプで渡したキーのハンドルは、どちらがわでCloseHandleして
も大丈夫なんですかね?
コマンドの方は、おっしゃるとおりなので、適当にバッチでもつくって試して
みます。
個人的にはAPIでバイナリで出力した方が、一見中身がわからないのでいいか
な〜とは思っています。
本当にありがとうございました!!
あー…ハンドルってプロセスに関連付けられてるんでしたっけ?
だとしたらダメか。
サービス側で LoadUserProfile を呼び出すにしても、アプリ側のトークンが必要。
いずれにせよ、アプリ側で開いたハンドルをサービス側に渡さなければならないんだけど、原則としてカーネルハンドルはプロセスに固有なので、そのままではサービス側では使えない。
そこで、パイプを通じてアプリのプロセス ID も一緒に渡して、サービス側で DuplicateHandle する、という方法がある。
もしくは、ImpersonateNamedPipeClient を使えば、もっと簡単にできるかもしれない。
この関数でサービス側がアプリ側のユーザーを偽装して RegOpenKeyEx で HKCR を開いて、RevertToSelf で元に戻ってから RegSaveKey でいけるかなぁ。
シャノンさん
マニアックな質問にいつも丁寧に回答いただき、感謝します。
すでにパイプで渡すデータの構造体や初期化関数などはここ以前にできてしま
っているので、まずはそれを壊さずにできそうなImpersonateNamedPipeClient
を試してみようかと思います。
初心者に毛が生えたような分際で、こんなマニアックな質問をして済みません。
そしていつもありがとうございます!!
結果はまたご報告しようと思います!!
ツイート | ![]() |