PageRenderTime 54ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/System/System.Diagnostics/Win32EventLog.cs

https://github.com/iainlane/mono
C# | 907 lines | 799 code | 64 blank | 44 comment | 79 complexity | 807ef8ad84beaa9ce070d49f363a3ae6 MD5 | raw file
  1. //
  2. // System.Diagnostics.Win32EventLog.cs
  3. //
  4. // Author:
  5. // Gert Driesen <driesen@users.sourceforge.net>
  6. //
  7. // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
  8. //
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. using System;
  30. using System.Collections;
  31. using System.ComponentModel;
  32. using System.Diagnostics;
  33. using System.Globalization;
  34. using System.IO;
  35. using System.Runtime.InteropServices;
  36. using System.Text;
  37. using System.Threading;
  38. using Microsoft.Win32;
  39. namespace System.Diagnostics
  40. {
  41. internal class Win32EventLog : EventLogImpl
  42. {
  43. private const int MESSAGE_NOT_FOUND = 317;
  44. private ManualResetEvent _notifyResetEvent;
  45. private IntPtr _readHandle;
  46. private Thread _notifyThread;
  47. private int _lastEntryWritten;
  48. private bool _notifying;
  49. public Win32EventLog (EventLog coreEventLog)
  50. : base (coreEventLog)
  51. {
  52. }
  53. public override void BeginInit ()
  54. {
  55. }
  56. public override void Clear ()
  57. {
  58. int ret = PInvoke.ClearEventLog (ReadHandle, null);
  59. if (ret != 1)
  60. throw new Win32Exception (Marshal.GetLastWin32Error ());
  61. }
  62. public override void Close ()
  63. {
  64. if (_readHandle != IntPtr.Zero) {
  65. CloseEventLog (_readHandle);
  66. _readHandle = IntPtr.Zero;
  67. }
  68. }
  69. public override void CreateEventSource (EventSourceCreationData sourceData)
  70. {
  71. using (RegistryKey eventLogKey = GetEventLogKey (sourceData.MachineName, true)) {
  72. if (eventLogKey == null)
  73. throw new InvalidOperationException ("EventLog registry key is missing.");
  74. bool logKeyCreated = false;
  75. RegistryKey logKey = null;
  76. try {
  77. logKey = eventLogKey.OpenSubKey (sourceData.LogName, true);
  78. if (logKey == null) {
  79. ValidateCustomerLogName (sourceData.LogName,
  80. sourceData.MachineName);
  81. logKey = eventLogKey.CreateSubKey (sourceData.LogName);
  82. logKey.SetValue ("Sources", new string [] { sourceData.LogName,
  83. sourceData.Source });
  84. UpdateLogRegistry (logKey);
  85. using (RegistryKey sourceKey = logKey.CreateSubKey (sourceData.LogName)) {
  86. UpdateSourceRegistry (sourceKey, sourceData);
  87. }
  88. logKeyCreated = true;
  89. }
  90. if (sourceData.LogName != sourceData.Source) {
  91. if (!logKeyCreated) {
  92. string [] sources = (string []) logKey.GetValue ("Sources");
  93. if (sources == null) {
  94. logKey.SetValue ("Sources", new string [] { sourceData.LogName,
  95. sourceData.Source });
  96. } else {
  97. bool found = false;
  98. for (int i = 0; i < sources.Length; i++) {
  99. if (sources [i] == sourceData.Source) {
  100. found = true;
  101. break;
  102. }
  103. }
  104. if (!found) {
  105. string [] newSources = new string [sources.Length + 1];
  106. Array.Copy (sources, 0, newSources, 0, sources.Length);
  107. newSources [sources.Length] = sourceData.Source;
  108. logKey.SetValue ("Sources", newSources);
  109. }
  110. }
  111. }
  112. using (RegistryKey sourceKey = logKey.CreateSubKey (sourceData.Source)) {
  113. UpdateSourceRegistry (sourceKey, sourceData);
  114. }
  115. }
  116. } finally {
  117. if (logKey != null)
  118. logKey.Close ();
  119. }
  120. }
  121. }
  122. public override void Delete (string logName, string machineName)
  123. {
  124. using (RegistryKey eventLogKey = GetEventLogKey (machineName, true)) {
  125. if (eventLogKey == null)
  126. throw new InvalidOperationException ("The event log key does not exist.");
  127. using (RegistryKey logKey = eventLogKey.OpenSubKey (logName, false)) {
  128. if (logKey == null)
  129. throw new InvalidOperationException (string.Format (
  130. CultureInfo.InvariantCulture, "Event Log '{0}'"
  131. + " does not exist on computer '{1}'.", logName,
  132. machineName));
  133. // remove all eventlog entries for specified log
  134. CoreEventLog.Clear ();
  135. // remove file holding event log entries
  136. string file = (string) logKey.GetValue ("File");
  137. if (file != null) {
  138. try {
  139. File.Delete (file);
  140. } catch (Exception) {
  141. // .NET seems to ignore failures here
  142. }
  143. }
  144. }
  145. eventLogKey.DeleteSubKeyTree (logName);
  146. }
  147. }
  148. public override void DeleteEventSource (string source, string machineName)
  149. {
  150. using (RegistryKey logKey = FindLogKeyBySource (source, machineName, true)) {
  151. if (logKey == null) {
  152. throw new ArgumentException (string.Format (
  153. CultureInfo.InvariantCulture, "The source '{0}' is not"
  154. + " registered on computer '{1}'.", source, machineName));
  155. }
  156. logKey.DeleteSubKeyTree (source);
  157. string [] sources = (string []) logKey.GetValue ("Sources");
  158. if (sources != null) {
  159. ArrayList temp = new ArrayList ();
  160. for (int i = 0; i < sources.Length; i++)
  161. if (sources [i] != source)
  162. temp.Add (sources [i]);
  163. string [] newSources = new string [temp.Count];
  164. temp.CopyTo (newSources, 0);
  165. logKey.SetValue ("Sources", newSources);
  166. }
  167. }
  168. }
  169. public override void Dispose (bool disposing)
  170. {
  171. Close ();
  172. }
  173. public override void EndInit ()
  174. {
  175. }
  176. public override bool Exists (string logName, string machineName)
  177. {
  178. using (RegistryKey logKey = FindLogKeyByName (logName, machineName, false)) {
  179. return (logKey != null);
  180. }
  181. }
  182. [MonoTODO] // ParameterResourceFile ??
  183. protected override string FormatMessage (string source, uint messageID, string [] replacementStrings)
  184. {
  185. string formattedMessage = null;
  186. string [] msgResDlls = GetMessageResourceDlls (source, "EventMessageFile");
  187. for (int i = 0; i < msgResDlls.Length; i++) {
  188. formattedMessage = FetchMessage (msgResDlls [i],
  189. messageID, replacementStrings);
  190. if (formattedMessage != null)
  191. break;
  192. }
  193. return formattedMessage != null ? formattedMessage : string.Join (
  194. ", ", replacementStrings);
  195. }
  196. private string FormatCategory (string source, int category)
  197. {
  198. string formattedCategory = null;
  199. string [] msgResDlls = GetMessageResourceDlls (source, "CategoryMessageFile");
  200. for (int i = 0; i < msgResDlls.Length; i++) {
  201. formattedCategory = FetchMessage (msgResDlls [i],
  202. (uint) category, new string [0]);
  203. if (formattedCategory != null)
  204. break;
  205. }
  206. return formattedCategory != null ? formattedCategory : "(" +
  207. category.ToString (CultureInfo.InvariantCulture) + ")";
  208. }
  209. protected override int GetEntryCount ()
  210. {
  211. int entryCount = 0;
  212. int retVal = PInvoke.GetNumberOfEventLogRecords (ReadHandle, ref entryCount);
  213. if (retVal != 1)
  214. throw new Win32Exception (Marshal.GetLastWin32Error ());
  215. return entryCount;
  216. }
  217. protected override EventLogEntry GetEntry (int index)
  218. {
  219. // http://msdn.microsoft.com/library/en-us/eventlog/base/readeventlog.asp
  220. // http://msdn.microsoft.com/library/en-us/eventlog/base/eventlogrecord_str.asp
  221. // http://www.whitehats.ca/main/members/Malik/malik_eventlogs/malik_eventlogs.html
  222. index += OldestEventLogEntry;
  223. int bytesRead = 0;
  224. int minBufferNeeded = 0;
  225. byte [] buffer = new byte [0x7ffff]; // according to MSDN this is the max size of the buffer
  226. ReadEventLog (index, buffer, ref bytesRead, ref minBufferNeeded);
  227. MemoryStream ms = new MemoryStream (buffer);
  228. BinaryReader br = new BinaryReader (ms);
  229. // skip first 8 bytes
  230. br.ReadBytes (8);
  231. int recordNumber = br.ReadInt32 (); // 8
  232. int timeGeneratedSeconds = br.ReadInt32 (); // 12
  233. int timeWrittenSeconds = br.ReadInt32 (); // 16
  234. uint instanceID = br.ReadUInt32 ();
  235. int eventID = EventLog.GetEventID (instanceID);
  236. short eventType = br.ReadInt16 (); // 24
  237. short numStrings = br.ReadInt16 (); ; // 26
  238. short categoryNumber = br.ReadInt16 (); ; // 28
  239. // skip reservedFlags
  240. br.ReadInt16 (); // 30
  241. // skip closingRecordNumber
  242. br.ReadInt32 (); // 32
  243. int stringOffset = br.ReadInt32 (); // 36
  244. int userSidLength = br.ReadInt32 (); // 40
  245. int userSidOffset = br.ReadInt32 (); // 44
  246. int dataLength = br.ReadInt32 (); // 48
  247. int dataOffset = br.ReadInt32 (); // 52
  248. DateTime timeGenerated = new DateTime (1970, 1, 1).AddSeconds (
  249. timeGeneratedSeconds);
  250. DateTime timeWritten = new DateTime (1970, 1, 1).AddSeconds (
  251. timeWrittenSeconds);
  252. StringBuilder sb = new StringBuilder ();
  253. while (br.PeekChar () != '\0')
  254. sb.Append (br.ReadChar ());
  255. br.ReadChar (); // skip the null-char
  256. string sourceName = sb.ToString ();
  257. sb.Length = 0;
  258. while (br.PeekChar () != '\0')
  259. sb.Append (br.ReadChar ());
  260. br.ReadChar (); // skip the null-char
  261. string machineName = sb.ToString ();
  262. sb.Length = 0;
  263. while (br.PeekChar () != '\0')
  264. sb.Append (br.ReadChar ());
  265. br.ReadChar (); // skip the null-char
  266. string userName = null;
  267. if (userSidLength != 0) {
  268. // TODO: lazy init ?
  269. ms.Position = userSidOffset;
  270. byte [] sid = br.ReadBytes (userSidLength);
  271. userName = LookupAccountSid (machineName, sid);
  272. }
  273. ms.Position = stringOffset;
  274. string [] replacementStrings = new string [numStrings];
  275. for (int i = 0; i < numStrings; i++) {
  276. sb.Length = 0;
  277. while (br.PeekChar () != '\0')
  278. sb.Append (br.ReadChar ());
  279. br.ReadChar (); // skip the null-char
  280. replacementStrings [i] = sb.ToString ();
  281. }
  282. byte [] data = new byte [dataLength];
  283. ms.Position = dataOffset;
  284. br.Read (data, 0, dataLength);
  285. // TODO: lazy fetch ??
  286. string message = this.FormatMessage (sourceName, instanceID, replacementStrings);
  287. string category = FormatCategory (sourceName, categoryNumber);
  288. return new EventLogEntry (category, (short) categoryNumber, recordNumber,
  289. eventID, sourceName, message, userName, machineName,
  290. (EventLogEntryType) eventType, timeGenerated, timeWritten,
  291. data, replacementStrings, instanceID);
  292. }
  293. [MonoTODO]
  294. protected override string GetLogDisplayName ()
  295. {
  296. return CoreEventLog.Log;
  297. }
  298. protected override string [] GetLogNames (string machineName)
  299. {
  300. using (RegistryKey eventLogKey = GetEventLogKey (machineName, true)) {
  301. if (eventLogKey == null)
  302. return new string [0];
  303. return eventLogKey.GetSubKeyNames ();
  304. }
  305. }
  306. public override string LogNameFromSourceName (string source, string machineName)
  307. {
  308. using (RegistryKey logKey = FindLogKeyBySource (source, machineName, false)) {
  309. if (logKey == null)
  310. return string.Empty;
  311. return GetLogName (logKey);
  312. }
  313. }
  314. public override bool SourceExists (string source, string machineName)
  315. {
  316. RegistryKey logKey = FindLogKeyBySource (source, machineName, false);
  317. if (logKey != null) {
  318. logKey.Close ();
  319. return true;
  320. }
  321. return false;
  322. }
  323. public override void WriteEntry (string [] replacementStrings, EventLogEntryType type, uint instanceID, short category, byte [] rawData)
  324. {
  325. IntPtr hEventLog = RegisterEventSource ();
  326. try {
  327. int ret = PInvoke.ReportEvent (hEventLog, (ushort) type,
  328. (ushort) category, instanceID, IntPtr.Zero,
  329. (ushort) replacementStrings.Length,
  330. (uint) rawData.Length, replacementStrings, rawData);
  331. if (ret != 1) {
  332. throw new Win32Exception (Marshal.GetLastWin32Error ());
  333. }
  334. } finally {
  335. DeregisterEventSource (hEventLog);
  336. }
  337. }
  338. private static void UpdateLogRegistry (RegistryKey logKey)
  339. {
  340. // TODO: write other Log values:
  341. // - MaxSize
  342. // - Retention
  343. // - AutoBackupLogFiles
  344. if (logKey.GetValue ("File") == null) {
  345. string logName = GetLogName (logKey);
  346. string file;
  347. if (logName.Length > 8) {
  348. file = logName.Substring (0, 8) + ".evt";
  349. } else {
  350. file = logName + ".evt";
  351. }
  352. string configPath = Path.Combine (Environment.GetFolderPath (
  353. Environment.SpecialFolder.System), "config");
  354. logKey.SetValue ("File", Path.Combine (configPath, file));
  355. }
  356. }
  357. private static void UpdateSourceRegistry (RegistryKey sourceKey, EventSourceCreationData data)
  358. {
  359. if (data.CategoryCount > 0)
  360. sourceKey.SetValue ("CategoryCount", data.CategoryCount);
  361. if (data.CategoryResourceFile != null && data.CategoryResourceFile.Length > 0)
  362. sourceKey.SetValue ("CategoryMessageFile", data.CategoryResourceFile);
  363. if (data.MessageResourceFile != null && data.MessageResourceFile.Length > 0) {
  364. sourceKey.SetValue ("EventMessageFile", data.MessageResourceFile);
  365. } else {
  366. // FIXME: write default once we have approval for shipping EventLogMessages.dll
  367. }
  368. if (data.ParameterResourceFile != null && data.ParameterResourceFile.Length > 0)
  369. sourceKey.SetValue ("ParameterMessageFile", data.ParameterResourceFile);
  370. }
  371. private static string GetLogName (RegistryKey logKey)
  372. {
  373. string logName = logKey.Name;
  374. return logName.Substring (logName.LastIndexOf ("\\") + 1);
  375. }
  376. private void ReadEventLog (int index, byte [] buffer, ref int bytesRead, ref int minBufferNeeded)
  377. {
  378. const int max_retries = 3;
  379. // if the eventlog file changed since the handle was
  380. // obtained, then we need to re-try multiple times
  381. for (int i = 0; i < max_retries; i++) {
  382. int ret = PInvoke.ReadEventLog (ReadHandle,
  383. ReadFlags.Seek | ReadFlags.ForwardsRead,
  384. index, buffer, buffer.Length, ref bytesRead,
  385. ref minBufferNeeded);
  386. if (ret != 1) {
  387. int error = Marshal.GetLastWin32Error ();
  388. if (i < (max_retries - 1)) {
  389. CoreEventLog.Reset ();
  390. } else {
  391. throw new Win32Exception (error);
  392. }
  393. }
  394. }
  395. }
  396. [MonoTODO ("Support remote machines")]
  397. private static RegistryKey GetEventLogKey (string machineName, bool writable)
  398. {
  399. return Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Services\EventLog", writable);
  400. }
  401. private static RegistryKey FindSourceKeyByName (string source, string machineName, bool writable)
  402. {
  403. if (source == null || source.Length == 0)
  404. return null;
  405. RegistryKey eventLogKey = null;
  406. try {
  407. eventLogKey = GetEventLogKey (machineName, writable);
  408. if (eventLogKey == null)
  409. return null;
  410. string [] subKeys = eventLogKey.GetSubKeyNames ();
  411. for (int i = 0; i < subKeys.Length; i++) {
  412. using (RegistryKey logKey = eventLogKey.OpenSubKey (subKeys [i], writable)) {
  413. if (logKey == null)
  414. break;
  415. RegistryKey sourceKey = logKey.OpenSubKey (source, writable);
  416. if (sourceKey != null)
  417. return sourceKey;
  418. }
  419. }
  420. return null;
  421. } finally {
  422. if (eventLogKey != null)
  423. eventLogKey.Close ();
  424. }
  425. }
  426. private static RegistryKey FindLogKeyByName (string logName, string machineName, bool writable)
  427. {
  428. using (RegistryKey eventLogKey = GetEventLogKey (machineName, writable)) {
  429. if (eventLogKey == null) {
  430. return null;
  431. }
  432. return eventLogKey.OpenSubKey (logName, writable);
  433. }
  434. }
  435. private static RegistryKey FindLogKeyBySource (string source, string machineName, bool writable)
  436. {
  437. if (source == null || source.Length == 0)
  438. return null;
  439. RegistryKey eventLogKey = null;
  440. try {
  441. eventLogKey = GetEventLogKey (machineName, writable);
  442. if (eventLogKey == null)
  443. return null;
  444. string [] subKeys = eventLogKey.GetSubKeyNames ();
  445. for (int i = 0; i < subKeys.Length; i++) {
  446. RegistryKey sourceKey = null;
  447. try {
  448. RegistryKey logKey = eventLogKey.OpenSubKey (subKeys [i], writable);
  449. if (logKey != null) {
  450. sourceKey = logKey.OpenSubKey (source, writable);
  451. if (sourceKey != null)
  452. return logKey;
  453. }
  454. } finally {
  455. if (sourceKey != null)
  456. sourceKey.Close ();
  457. }
  458. }
  459. return null;
  460. } finally {
  461. if (eventLogKey != null)
  462. eventLogKey.Close ();
  463. }
  464. }
  465. private int OldestEventLogEntry {
  466. get {
  467. int oldestEventLogEntry = 0;
  468. int ret = PInvoke.GetOldestEventLogRecord (ReadHandle, ref oldestEventLogEntry);
  469. if (ret != 1) {
  470. throw new Win32Exception (Marshal.GetLastWin32Error ());
  471. }
  472. return oldestEventLogEntry;
  473. }
  474. }
  475. private void CloseEventLog (IntPtr hEventLog)
  476. {
  477. int ret = PInvoke.CloseEventLog (hEventLog);
  478. if (ret != 1) {
  479. throw new Win32Exception (Marshal.GetLastWin32Error ());
  480. }
  481. }
  482. private void DeregisterEventSource (IntPtr hEventLog)
  483. {
  484. int ret = PInvoke.DeregisterEventSource (hEventLog);
  485. if (ret != 1) {
  486. throw new Win32Exception (Marshal.GetLastWin32Error ());
  487. }
  488. }
  489. private static string LookupAccountSid (string machineName, byte [] sid)
  490. {
  491. // http://www.pinvoke.net/default.aspx/advapi32/LookupAccountSid.html
  492. // http://msdn.microsoft.com/library/en-us/secauthz/security/lookupaccountsid.asp
  493. StringBuilder name = new StringBuilder ();
  494. uint cchName = (uint) name.Capacity;
  495. StringBuilder referencedDomainName = new StringBuilder ();
  496. uint cchReferencedDomainName = (uint) referencedDomainName.Capacity;
  497. SidNameUse sidUse;
  498. string accountName = null;
  499. while (accountName == null) {
  500. bool retOk = PInvoke.LookupAccountSid (machineName, sid, name, ref cchName,
  501. referencedDomainName, ref cchReferencedDomainName,
  502. out sidUse);
  503. if (!retOk) {
  504. int err = Marshal.GetLastWin32Error ();
  505. if (err == PInvoke.ERROR_INSUFFICIENT_BUFFER) {
  506. name.EnsureCapacity ((int) cchName);
  507. referencedDomainName.EnsureCapacity ((int) cchReferencedDomainName);
  508. } else {
  509. // TODO: write warning ?
  510. accountName = string.Empty;
  511. }
  512. } else {
  513. accountName = string.Format ("{0}\\{1}", referencedDomainName.ToString (),
  514. name.ToString ());
  515. }
  516. }
  517. return accountName;
  518. }
  519. private static string FetchMessage (string msgDll, uint messageID, string [] replacementStrings)
  520. {
  521. // http://msdn.microsoft.com/library/en-us/debug/base/formatmessage.asp
  522. // http://msdn.microsoft.com/msdnmag/issues/02/08/CQA/
  523. // http://msdn.microsoft.com/netframework/programming/netcf/cffaq/default.aspx
  524. IntPtr msgDllHandle = PInvoke.LoadLibraryEx (msgDll, IntPtr.Zero,
  525. LoadFlags.LibraryAsDataFile);
  526. if (msgDllHandle == IntPtr.Zero)
  527. // TODO: write warning
  528. return null;
  529. IntPtr lpMsgBuf = IntPtr.Zero;
  530. IntPtr [] arguments = new IntPtr [replacementStrings.Length];
  531. try {
  532. for (int i = 0; i < replacementStrings.Length; i++) {
  533. arguments [i] = Marshal.StringToHGlobalAuto (
  534. replacementStrings [i]);
  535. }
  536. int ret = PInvoke.FormatMessage (FormatMessageFlags.ArgumentArray |
  537. FormatMessageFlags.FromHModule | FormatMessageFlags.AllocateBuffer,
  538. msgDllHandle, messageID, 0, ref lpMsgBuf, 0, arguments);
  539. if (ret != 0) {
  540. string sRet = Marshal.PtrToStringAuto (lpMsgBuf);
  541. lpMsgBuf = PInvoke.LocalFree (lpMsgBuf);
  542. // remove trailing whitespace (CRLF)
  543. return sRet.TrimEnd (null);
  544. } else {
  545. int err = Marshal.GetLastWin32Error ();
  546. if (err == MESSAGE_NOT_FOUND) {
  547. // do not consider this a failure (or even warning) as
  548. // multiple message resource DLLs may have been configured
  549. // and as such we just need to try the next library if
  550. // the current one does not contain a message for this
  551. // ID
  552. } else {
  553. // TODO: report warning
  554. }
  555. }
  556. } finally {
  557. // release unmanaged memory allocated for replacement strings
  558. for (int i = 0; i < arguments.Length; i++) {
  559. IntPtr argument = arguments [i];
  560. if (argument != IntPtr.Zero)
  561. Marshal.FreeHGlobal (argument);
  562. }
  563. PInvoke.FreeLibrary (msgDllHandle);
  564. }
  565. return null;
  566. }
  567. private string [] GetMessageResourceDlls (string source, string valueName)
  568. {
  569. // Some event sources (such as Userenv) have multiple message
  570. // resource DLLs, delimited by a semicolon.
  571. RegistryKey sourceKey = FindSourceKeyByName (source,
  572. CoreEventLog.MachineName, false);
  573. if (sourceKey != null) {
  574. string value = sourceKey.GetValue (valueName) as string;
  575. if (value != null) {
  576. string [] msgResDlls = value.Split (';');
  577. return msgResDlls;
  578. }
  579. }
  580. return new string [0];
  581. }
  582. private IntPtr ReadHandle {
  583. get {
  584. if (_readHandle != IntPtr.Zero)
  585. return _readHandle;
  586. string logName = CoreEventLog.GetLogName ();
  587. _readHandle = PInvoke.OpenEventLog (CoreEventLog.MachineName,
  588. logName);
  589. if (_readHandle == IntPtr.Zero)
  590. throw new InvalidOperationException (string.Format (
  591. CultureInfo.InvariantCulture, "Event Log '{0}' on computer"
  592. + " '{1}' cannot be opened.", logName, CoreEventLog.MachineName),
  593. new Win32Exception ());
  594. return _readHandle;
  595. }
  596. }
  597. private IntPtr RegisterEventSource ()
  598. {
  599. IntPtr hEventLog = PInvoke.RegisterEventSource (
  600. CoreEventLog.MachineName, CoreEventLog.Source);
  601. if (hEventLog == IntPtr.Zero) {
  602. throw new InvalidOperationException (string.Format (
  603. CultureInfo.InvariantCulture, "Event source '{0}' on computer"
  604. + " '{1}' cannot be opened.", CoreEventLog.Source,
  605. CoreEventLog.MachineName), new Win32Exception ());
  606. }
  607. return hEventLog;
  608. }
  609. public override void DisableNotification ()
  610. {
  611. if (_notifyResetEvent != null) {
  612. _notifyResetEvent.Close ();
  613. _notifyResetEvent = null;
  614. }
  615. if (_notifyThread != null) {
  616. if (_notifyThread.ThreadState == System.Threading.ThreadState.Running)
  617. _notifyThread.Abort ();
  618. _notifyThread = null;
  619. }
  620. }
  621. public override void EnableNotification ()
  622. {
  623. _notifyResetEvent = new ManualResetEvent (false);
  624. _lastEntryWritten = OldestEventLogEntry + EntryCount;
  625. if (PInvoke.NotifyChangeEventLog (ReadHandle, _notifyResetEvent.Handle) == 0)
  626. throw new InvalidOperationException (string.Format (
  627. CultureInfo.InvariantCulture, "Unable to receive notifications"
  628. + " for log '{0}' on computer '{1}'.", CoreEventLog.GetLogName (),
  629. CoreEventLog.MachineName), new Win32Exception ());
  630. _notifyThread = new Thread (new ThreadStart (NotifyEventThread));
  631. _notifyThread.IsBackground = true;
  632. _notifyThread.Start ();
  633. }
  634. private void NotifyEventThread ()
  635. {
  636. while (true) {
  637. _notifyResetEvent.WaitOne ();
  638. lock (this) {
  639. // after a clear, we something get notified
  640. // twice for the same entry
  641. if (_notifying)
  642. return;
  643. _notifying = true;
  644. }
  645. try {
  646. int oldest_entry = OldestEventLogEntry;
  647. if (_lastEntryWritten < oldest_entry)
  648. _lastEntryWritten = oldest_entry;
  649. int current_entry = _lastEntryWritten - oldest_entry;
  650. int last_entry = EntryCount + oldest_entry;
  651. for (int i = current_entry; i < (last_entry - 1); i++) {
  652. EventLogEntry entry = GetEntry (i);
  653. CoreEventLog.OnEntryWritten (entry);
  654. }
  655. _lastEntryWritten = last_entry;
  656. } finally {
  657. lock (this)
  658. _notifying = false;
  659. }
  660. }
  661. }
  662. #if NET_2_0
  663. public override OverflowAction OverflowAction {
  664. get { throw new NotImplementedException (); }
  665. }
  666. public override int MinimumRetentionDays {
  667. get { throw new NotImplementedException (); }
  668. }
  669. public override long MaximumKilobytes {
  670. get { throw new NotImplementedException (); }
  671. set { throw new NotImplementedException (); }
  672. }
  673. public override void ModifyOverflowPolicy (OverflowAction action, int retentionDays)
  674. {
  675. throw new NotImplementedException ();
  676. }
  677. public override void RegisterDisplayName (string resourceFile, long resourceId)
  678. {
  679. throw new NotImplementedException ();
  680. }
  681. #endif
  682. private class PInvoke
  683. {
  684. [DllImport ("advapi32.dll", SetLastError=true)]
  685. public static extern int ClearEventLog (IntPtr hEventLog, string lpBackupFileName);
  686. [DllImport ("advapi32.dll", SetLastError=true)]
  687. public static extern int CloseEventLog (IntPtr hEventLog);
  688. [DllImport ("advapi32.dll", SetLastError=true)]
  689. public static extern int DeregisterEventSource (IntPtr hEventLog);
  690. [DllImport ("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
  691. public static extern int FormatMessage (FormatMessageFlags dwFlags, IntPtr lpSource, uint dwMessageId, int dwLanguageId, ref IntPtr lpBuffer, int nSize, IntPtr [] arguments);
  692. [DllImport ("kernel32.dll", SetLastError=true)]
  693. public static extern bool FreeLibrary (IntPtr hModule);
  694. [DllImport ("advapi32.dll", SetLastError=true)]
  695. public static extern int GetNumberOfEventLogRecords (IntPtr hEventLog, ref int NumberOfRecords);
  696. [DllImport ("advapi32.dll", SetLastError=true)]
  697. public static extern int GetOldestEventLogRecord (IntPtr hEventLog, ref int OldestRecord);
  698. [DllImport ("kernel32.dll", SetLastError=true)]
  699. public static extern IntPtr LoadLibraryEx (string lpFileName, IntPtr hFile, LoadFlags dwFlags);
  700. [DllImport ("kernel32.dll", SetLastError=true)]
  701. public static extern IntPtr LocalFree (IntPtr hMem);
  702. [DllImport ("advapi32.dll", SetLastError=true)]
  703. public static extern bool LookupAccountSid (
  704. string lpSystemName,
  705. [MarshalAs (UnmanagedType.LPArray)] byte [] Sid,
  706. StringBuilder lpName,
  707. ref uint cchName,
  708. StringBuilder ReferencedDomainName,
  709. ref uint cchReferencedDomainName,
  710. out SidNameUse peUse);
  711. [DllImport ("Advapi32.dll", SetLastError = true)]
  712. public static extern int NotifyChangeEventLog (IntPtr hEventLog, IntPtr hEvent);
  713. [DllImport ("advapi32.dll", SetLastError=true)]
  714. public static extern IntPtr OpenEventLog (string machineName, string logName);
  715. [DllImport ("advapi32.dll", SetLastError=true)]
  716. public static extern IntPtr RegisterEventSource (string machineName, string sourceName);
  717. [DllImport ("Advapi32.dll", SetLastError=true)]
  718. public static extern int ReportEvent (IntPtr hHandle, ushort wType,
  719. ushort wCategory, uint dwEventID, IntPtr sid, ushort wNumStrings,
  720. uint dwDataSize, string [] lpStrings, byte [] lpRawData);
  721. [DllImport ("advapi32.dll", SetLastError=true)]
  722. public static extern int ReadEventLog (IntPtr hEventLog, ReadFlags dwReadFlags, int dwRecordOffset, byte [] buffer, int nNumberOfBytesToRead, ref int pnBytesRead, ref int pnMinNumberOfBytesNeeded);
  723. public const int ERROR_INSUFFICIENT_BUFFER = 122;
  724. public const int ERROR_EVENTLOG_FILE_CHANGED = 1503;
  725. }
  726. private enum ReadFlags
  727. {
  728. Sequential = 0x001,
  729. Seek = 0x002,
  730. ForwardsRead = 0x004,
  731. BackwardsRead = 0x008
  732. }
  733. private enum LoadFlags: uint
  734. {
  735. LibraryAsDataFile = 0x002
  736. }
  737. [Flags]
  738. private enum FormatMessageFlags
  739. {
  740. AllocateBuffer = 0x100,
  741. IgnoreInserts = 0x200,
  742. FromHModule = 0x0800,
  743. FromSystem = 0x1000,
  744. ArgumentArray = 0x2000
  745. }
  746. private enum SidNameUse
  747. {
  748. User = 1,
  749. Group,
  750. Domain,
  751. lias,
  752. WellKnownGroup,
  753. DeletedAccount,
  754. Invalid,
  755. Unknown,
  756. Computer
  757. }
  758. }
  759. }
  760. // http://msdn.microsoft.com/library/en-us/eventlog/base/eventlogrecord_str.asp:
  761. //
  762. // struct EVENTLOGRECORD {
  763. // int Length;
  764. // int Reserved;
  765. // int RecordNumber;
  766. // int TimeGenerated;
  767. // int TimeWritten;
  768. // int EventID;
  769. // short EventType;
  770. // short NumStrings;
  771. // short EventCategory;
  772. // short ReservedFlags;
  773. // int ClosingRecordNumber;
  774. // int StringOffset;
  775. // int UserSidLength;
  776. // int UserSidOffset;
  777. // int DataLength;
  778. // int DataOffset;
  779. // }
  780. //
  781. // http://www.whitehats.ca/main/members/Malik/malik_eventlogs/malik_eventlogs.html