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