私は現在、OutputDebugStringメッセージをキャプチャするための単純なアプリケーションに取り組んでいます(Windows Sysinternals DbgViewまたはDBMon.NETに類似しています)。ローカルセッション(つまり、Local \ DBWIN_BUFFER_READY、Local \ DBWIN_DATA_READY、Local \ DBWIN_BUFFER)からOutputDebugStringメッセージにアクセスすると、すべてが期待どおりに機能します。
ただし、セッション0からの出力(つまり、Global \ DBWIN_BUFFER_READYなど)にアクセスしようとすると、出力が返されません。DbgViewの動作に基づいて、アプリケーションをある程度の管理者権限で実行する必要があることを前提に運用しています。SecurityDescriptorを正しく構成していないか、Global OutputDebugStringメッセージにアクセスするための何かが完全に欠落していると思っています(次のように読んでください...私は今や問題について少し迷っています)。
以下にコードのスニペットを強調表示しましたが、完全なソースはCodePlexにあります。
この問題に関するヘルプや洞察をいただければ幸いです。前もって感謝します!
セキュリティ記述子の構成
いくつかの異なる構成を試しましたが、コミットされたコードは現在次のようになっています。
[DllImport("advapi32.dll", SetLastError = true)]
private static extern Boolean InitializeSecurityDescriptor(ref SecurityDescriptor sd, UInt32 dwRevision);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern Boolean SetSecurityDescriptorDacl(ref SecurityDescriptor sd, Boolean daclPresent, IntPtr dacl, Boolean daclDefaulted);
public SecurityDescriptor InitializeSecurityDescriptor()
{
const Int32 securityDescriptorRevision = 1;
var securityDescriptor = new SecurityDescriptor();
// Initialize the security descriptor.
if (!InitializeSecurityDescriptor(ref securityDescriptor, securityDescriptorRevision))
throw new Win32Exception(Marshal.GetLastWin32Error());
// Set information in a discretionary access control list
if (!SetSecurityDescriptorDacl(ref securityDescriptor, true, IntPtr.Zero, false))
throw new Win32Exception(Marshal.GetLastWin32Error());
return securityDescriptor;
}
このコードは、予想どおり、最終的にDbWinMessageSourceクラスのセットアップで呼び出されます...
_windowsApi.Advanced.InitializeSecurityDescriptor();
SecurityAttributesとイベント
現在CodePlexでコミットされているコードは、Local \ **プレフィックスを使用していますが、唯一の違いは、私が理解している限り、Local\**がGlobal\**に置き換えられていることです。ただし、これは期待どおりに出力をキャプチャしていないようです。繰り返しますが、関連するコードスニペット...
public const Int32 ErrorAlreadyExists = 183;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateEvent(ref SecurityAttributes sa, Boolean bManualReset, Boolean bInitialState, String lpName);
public Handle CreateLocalEvent(ref SecurityAttributes securityAttributes, String objectName)
{
Verify.NotWhitespace(objectName);
return CreateEvent(ref securityAttributes, "Local", objectName);
}
public Handle CreateGlobalEvent(ref SecurityAttributes securityAttributes, String objectName)
{
Verify.NotWhitespace(objectName);
return CreateEvent(ref securityAttributes, "Global", objectName);
}
private static Handle CreateEvent(ref SecurityAttributes securityAttributes, String objectNamePrefix, String objectName)
{
IntPtr handle = CreateEvent(ref securityAttributes, false, false, String.Format(@"{0}\{1}", objectNamePrefix, objectName));
if(Marshal.GetLastWin32Error() == ErrorAlreadyExists)
throw new Win32Exception(ErrorAlreadyExists);
if (handle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
return new Handle(handle, CloseHandle);
}
繰り返しますが、最終的には次のようにDbWinMessageSourceのセットアップで呼び出されます。
_dbwinBufferReadyEvent = _windowsApi.Basic.CreateGlobalEvent(ref securityAttributes, "DBWIN_BUFFER_READY");
_dbwinDataReadyEvent = _windowsApi.Basic.CreateGlobalEvent(ref securityAttributes, "DBWIN_DATA_READY");
_dbwinBufferFile = _windowsApi.Basic.CreateGlobalFileMapping(ref securityAttributes, "DBWIN_BUFFER");
私は、OutputDebugStringアペンダーが構成されたLog4Netを使用する単純なWebアプリケーションでテストしています。Visual Studioを介してアプリケーションを実行しているときは、期待どおりにすべてのローカル出力をキャプチャします。アプリケーションをIISに移動し、グローバルセッションで何かをキャプチャするようにコードを構成すると、何も得られません。DbgViewがIISからの出力を期待どおりにキャプチャしていることを確認しました(したがって、これは間違いなく私が間違っていることです)。
うまくいけば、それで十分なコンテキストですが、より多くの情報や詳細が必要な場合は、お知らせ下さい。
注:違いが生じる場合は、Windows7Professionalで開発してください。
編集
Tyranid(およびLuke)が指摘しているように、必要なのは管理者権限とSE_CREATE_GLOBAL_NAMEだけです。さらにいくつかのテストを実行しましたが、上記のコード設定は実際にいくつかのグローバルメッセージをキャプチャしています(たとえば、IISRESET中)。ただし、上記のコードは、アプリケーションがIIS内で実行されている場合(VSセッション中にLocal \ **を介してルーティングされる場合)、Log4NetOutputDebugStringアペンダーからデータをキャプチャしていません。すべてのWin32API呼び出しは正常に返され、呼び出し時にエラーも返されません。Marshal.GetLastWin32Error().
適切な方法として、現在のWindowsトークンにSE_CREATE_GLOBAL_NAMEがあることを確認するためのコードを追加しました。ラフインされたコードは次のようになりました。
using (var identity = WindowsIdentity.GetCurrent())
{
if (identity == null)
return;
TokenPrivilege tp;
tp.Count = 1;
tp.Luid = 0;
tp.Attr = SE_PRIVILEGE_ENABLED;
if (!LookupPrivilegeValue(null, SE_CREATE_GLOBAL_NAME, ref tp.Luid))
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!AdjustTokenPrivileges(identity.Token, false, ref tp, Marshal.SizeOf(tp), IntPtr.Zero, IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
}
これに関するさらなる洞察をいただければ幸いです。