EventLogAppender.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. #region Apache License
  2. //
  3. // Licensed to the Apache Software Foundation (ASF) under one or more
  4. // contributor license agreements. See the NOTICE file distributed with
  5. // this work for additional information regarding copyright ownership.
  6. // The ASF licenses this file to you under the Apache License, Version 2.0
  7. // (the "License"); you may not use this file except in compliance with
  8. // the License. You may obtain a copy of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. //
  18. #endregion
  19. // MONO 1.0 Beta mcs does not like #if !A && !B && !C syntax
  20. // .NET Compact Framework 1.0 has no support for EventLog
  21. #if !NETCF
  22. // SSCLI 1.0 has no support for EventLog
  23. #if !SSCLI
  24. using System;
  25. using System.Diagnostics;
  26. using System.Globalization;
  27. using log4net.Util;
  28. using log4net.Layout;
  29. using log4net.Core;
  30. namespace log4net.Appender
  31. {
  32. /// <summary>
  33. /// Writes events to the system event log.
  34. /// </summary>
  35. /// <remarks>
  36. /// <para>
  37. /// The appender will fail if you try to write using an event source that doesn't exist unless it is running with local administrator privileges.
  38. /// See also http://logging.apache.org/log4net/release/faq.html#trouble-EventLog
  39. /// </para>
  40. /// <para>
  41. /// The <c>EventID</c> of the event log entry can be
  42. /// set using the <c>EventID</c> property (<see cref="LoggingEvent.Properties"/>)
  43. /// on the <see cref="LoggingEvent"/>.
  44. /// </para>
  45. /// <para>
  46. /// The <c>Category</c> of the event log entry can be
  47. /// set using the <c>Category</c> property (<see cref="LoggingEvent.Properties"/>)
  48. /// on the <see cref="LoggingEvent"/>.
  49. /// </para>
  50. /// <para>
  51. /// There is a limit of 32K characters for an event log message
  52. /// </para>
  53. /// <para>
  54. /// When configuring the EventLogAppender a mapping can be
  55. /// specified to map a logging level to an event log entry type. For example:
  56. /// </para>
  57. /// <code lang="XML">
  58. /// &lt;mapping&gt;
  59. /// &lt;level value="ERROR" /&gt;
  60. /// &lt;eventLogEntryType value="Error" /&gt;
  61. /// &lt;/mapping&gt;
  62. /// &lt;mapping&gt;
  63. /// &lt;level value="DEBUG" /&gt;
  64. /// &lt;eventLogEntryType value="Information" /&gt;
  65. /// &lt;/mapping&gt;
  66. /// </code>
  67. /// <para>
  68. /// The Level is the standard log4net logging level and eventLogEntryType can be any value
  69. /// from the <see cref="EventLogEntryType"/> enum, i.e.:
  70. /// <list type="bullet">
  71. /// <item><term>Error</term><description>an error event</description></item>
  72. /// <item><term>Warning</term><description>a warning event</description></item>
  73. /// <item><term>Information</term><description>an informational event</description></item>
  74. /// </list>
  75. /// </para>
  76. /// </remarks>
  77. /// <author>Aspi Havewala</author>
  78. /// <author>Douglas de la Torre</author>
  79. /// <author>Nicko Cadell</author>
  80. /// <author>Gert Driesen</author>
  81. /// <author>Thomas Voss</author>
  82. public class EventLogAppender : AppenderSkeleton
  83. {
  84. #region Public Instance Constructors
  85. /// <summary>
  86. /// Initializes a new instance of the <see cref="EventLogAppender" /> class.
  87. /// </summary>
  88. /// <remarks>
  89. /// <para>
  90. /// Default constructor.
  91. /// </para>
  92. /// </remarks>
  93. public EventLogAppender()
  94. {
  95. m_applicationName = System.Threading.Thread.GetDomain().FriendlyName;
  96. m_logName = "Application"; // Defaults to application log
  97. m_machineName = "."; // Only log on the local machine
  98. }
  99. /// <summary>
  100. /// Initializes a new instance of the <see cref="EventLogAppender" /> class
  101. /// with the specified <see cref="ILayout" />.
  102. /// </summary>
  103. /// <param name="layout">The <see cref="ILayout" /> to use with this appender.</param>
  104. /// <remarks>
  105. /// <para>
  106. /// Obsolete constructor.
  107. /// </para>
  108. /// </remarks>
  109. [Obsolete("Instead use the default constructor and set the Layout property")]
  110. public EventLogAppender(ILayout layout) : this()
  111. {
  112. Layout = layout;
  113. }
  114. #endregion // Public Instance Constructors
  115. #region Public Instance Properties
  116. /// <summary>
  117. /// The name of the log where messages will be stored.
  118. /// </summary>
  119. /// <value>
  120. /// The string name of the log where messages will be stored.
  121. /// </value>
  122. /// <remarks>
  123. /// <para>This is the name of the log as it appears in the Event Viewer
  124. /// tree. The default value is to log into the <c>Application</c>
  125. /// log, this is where most applications write their events. However
  126. /// if you need a separate log for your application (or applications)
  127. /// then you should set the <see cref="LogName"/> appropriately.</para>
  128. /// <para>This should not be used to distinguish your event log messages
  129. /// from those of other applications, the <see cref="ApplicationName"/>
  130. /// property should be used to distinguish events. This property should be
  131. /// used to group together events into a single log.
  132. /// </para>
  133. /// </remarks>
  134. public string LogName
  135. {
  136. get { return m_logName; }
  137. set { m_logName = value; }
  138. }
  139. /// <summary>
  140. /// Property used to set the Application name. This appears in the
  141. /// event logs when logging.
  142. /// </summary>
  143. /// <value>
  144. /// The string used to distinguish events from different sources.
  145. /// </value>
  146. /// <remarks>
  147. /// Sets the event log source property.
  148. /// </remarks>
  149. public string ApplicationName
  150. {
  151. get { return m_applicationName; }
  152. set { m_applicationName = value; }
  153. }
  154. /// <summary>
  155. /// This property is used to return the name of the computer to use
  156. /// when accessing the event logs. Currently, this is the current
  157. /// computer, denoted by a dot "."
  158. /// </summary>
  159. /// <value>
  160. /// The string name of the machine holding the event log that
  161. /// will be logged into.
  162. /// </value>
  163. /// <remarks>
  164. /// This property cannot be changed. It is currently set to '.'
  165. /// i.e. the local machine. This may be changed in future.
  166. /// </remarks>
  167. public string MachineName
  168. {
  169. get { return m_machineName; }
  170. set { /* Currently we do not allow the machine name to be changed */; }
  171. }
  172. /// <summary>
  173. /// Add a mapping of level to <see cref="EventLogEntryType"/> - done by the config file
  174. /// </summary>
  175. /// <param name="mapping">The mapping to add</param>
  176. /// <remarks>
  177. /// <para>
  178. /// Add a <see cref="Level2EventLogEntryType"/> mapping to this appender.
  179. /// Each mapping defines the event log entry type for a level.
  180. /// </para>
  181. /// </remarks>
  182. public void AddMapping(Level2EventLogEntryType mapping)
  183. {
  184. m_levelMapping.Add(mapping);
  185. }
  186. /// <summary>
  187. /// Gets or sets the <see cref="SecurityContext"/> used to write to the EventLog.
  188. /// </summary>
  189. /// <value>
  190. /// The <see cref="SecurityContext"/> used to write to the EventLog.
  191. /// </value>
  192. /// <remarks>
  193. /// <para>
  194. /// The system security context used to write to the EventLog.
  195. /// </para>
  196. /// <para>
  197. /// Unless a <see cref="SecurityContext"/> specified here for this appender
  198. /// the <see cref="SecurityContextProvider.DefaultProvider"/> is queried for the
  199. /// security context to use. The default behavior is to use the security context
  200. /// of the current thread.
  201. /// </para>
  202. /// </remarks>
  203. public SecurityContext SecurityContext
  204. {
  205. get { return m_securityContext; }
  206. set { m_securityContext = value; }
  207. }
  208. /// <summary>
  209. /// Gets or sets the <c>EventId</c> to use unless one is explicitly specified via the <c>LoggingEvent</c>'s properties.
  210. /// </summary>
  211. /// <remarks>
  212. /// <para>
  213. /// The <c>EventID</c> of the event log entry will normally be
  214. /// set using the <c>EventID</c> property (<see cref="LoggingEvent.Properties"/>)
  215. /// on the <see cref="LoggingEvent"/>.
  216. /// This property provides the fallback value which defaults to 0.
  217. /// </para>
  218. /// </remarks>
  219. public int EventId {
  220. get { return m_eventId; }
  221. set { m_eventId = value; }
  222. }
  223. /// <summary>
  224. /// Gets or sets the <c>Category</c> to use unless one is explicitly specified via the <c>LoggingEvent</c>'s properties.
  225. /// </summary>
  226. /// <remarks>
  227. /// <para>
  228. /// The <c>Category</c> of the event log entry will normally be
  229. /// set using the <c>Category</c> property (<see cref="LoggingEvent.Properties"/>)
  230. /// on the <see cref="LoggingEvent"/>.
  231. /// This property provides the fallback value which defaults to 0.
  232. /// </para>
  233. /// </remarks>
  234. public short Category
  235. {
  236. get { return m_category; }
  237. set { m_category = value; }
  238. }
  239. #endregion // Public Instance Properties
  240. #region Implementation of IOptionHandler
  241. /// <summary>
  242. /// Initialize the appender based on the options set
  243. /// </summary>
  244. /// <remarks>
  245. /// <para>
  246. /// This is part of the <see cref="IOptionHandler"/> delayed object
  247. /// activation scheme. The <see cref="ActivateOptions"/> method must
  248. /// be called on this object after the configuration properties have
  249. /// been set. Until <see cref="ActivateOptions"/> is called this
  250. /// object is in an undefined state and must not be used.
  251. /// </para>
  252. /// <para>
  253. /// If any of the configuration properties are modified then
  254. /// <see cref="ActivateOptions"/> must be called again.
  255. /// </para>
  256. /// </remarks>
  257. override public void ActivateOptions()
  258. {
  259. try
  260. {
  261. base.ActivateOptions();
  262. if (m_securityContext == null)
  263. {
  264. m_securityContext = SecurityContextProvider.DefaultProvider.CreateSecurityContext(this);
  265. }
  266. bool sourceAlreadyExists = false;
  267. string currentLogName = null;
  268. using (SecurityContext.Impersonate(this))
  269. {
  270. sourceAlreadyExists = EventLog.SourceExists(m_applicationName);
  271. if (sourceAlreadyExists) {
  272. currentLogName = EventLog.LogNameFromSourceName(m_applicationName, m_machineName);
  273. }
  274. }
  275. if (sourceAlreadyExists && currentLogName != m_logName)
  276. {
  277. LogLog.Debug(declaringType, "Changing event source [" + m_applicationName + "] from log [" + currentLogName + "] to log [" + m_logName + "]");
  278. }
  279. else if (!sourceAlreadyExists)
  280. {
  281. LogLog.Debug(declaringType, "Creating event source Source [" + m_applicationName + "] in log " + m_logName + "]");
  282. }
  283. string registeredLogName = null;
  284. using (SecurityContext.Impersonate(this))
  285. {
  286. if (sourceAlreadyExists && currentLogName != m_logName)
  287. {
  288. //
  289. // Re-register this to the current application if the user has changed
  290. // the application / logfile association
  291. //
  292. EventLog.DeleteEventSource(m_applicationName, m_machineName);
  293. CreateEventSource(m_applicationName, m_logName, m_machineName);
  294. registeredLogName = EventLog.LogNameFromSourceName(m_applicationName, m_machineName);
  295. }
  296. else if (!sourceAlreadyExists)
  297. {
  298. CreateEventSource(m_applicationName, m_logName, m_machineName);
  299. registeredLogName = EventLog.LogNameFromSourceName(m_applicationName, m_machineName);
  300. }
  301. }
  302. m_levelMapping.ActivateOptions();
  303. LogLog.Debug(declaringType, "Source [" + m_applicationName + "] is registered to log [" + registeredLogName + "]");
  304. }
  305. catch (System.Security.SecurityException ex)
  306. {
  307. ErrorHandler.Error("Caught a SecurityException trying to access the EventLog. Most likely the event source "
  308. + m_applicationName
  309. + " doesn't exist and must be created by a local administrator. Will disable EventLogAppender."
  310. + " See http://logging.apache.org/log4net/release/faq.html#trouble-EventLog",
  311. ex);
  312. Threshold = Level.Off;
  313. }
  314. }
  315. #endregion // Implementation of IOptionHandler
  316. /// <summary>
  317. /// Create an event log source
  318. /// </summary>
  319. /// <remarks>
  320. /// Uses different API calls under NET_2_0
  321. /// </remarks>
  322. private static void CreateEventSource(string source, string logName, string machineName)
  323. {
  324. #if NET_2_0
  325. EventSourceCreationData eventSourceCreationData = new EventSourceCreationData(source, logName);
  326. eventSourceCreationData.MachineName = machineName;
  327. EventLog.CreateEventSource(eventSourceCreationData);
  328. #else
  329. EventLog.CreateEventSource(source, logName, machineName);
  330. #endif
  331. }
  332. #region Override implementation of AppenderSkeleton
  333. /// <summary>
  334. /// This method is called by the <see cref="M:AppenderSkeleton.DoAppend(LoggingEvent)"/>
  335. /// method.
  336. /// </summary>
  337. /// <param name="loggingEvent">the event to log</param>
  338. /// <remarks>
  339. /// <para>Writes the event to the system event log using the
  340. /// <see cref="ApplicationName"/>.</para>
  341. ///
  342. /// <para>If the event has an <c>EventID</c> property (see <see cref="LoggingEvent.Properties"/>)
  343. /// set then this integer will be used as the event log event id.</para>
  344. ///
  345. /// <para>
  346. /// There is a limit of 32K characters for an event log message
  347. /// </para>
  348. /// </remarks>
  349. override protected void Append(LoggingEvent loggingEvent)
  350. {
  351. //
  352. // Write the resulting string to the event log system
  353. //
  354. int eventID = m_eventId;
  355. // Look for the EventID property
  356. object eventIDPropertyObj = loggingEvent.LookupProperty("EventID");
  357. if (eventIDPropertyObj != null)
  358. {
  359. if (eventIDPropertyObj is int)
  360. {
  361. eventID = (int)eventIDPropertyObj;
  362. }
  363. else
  364. {
  365. string eventIDPropertyString = eventIDPropertyObj as string;
  366. if (eventIDPropertyString == null)
  367. {
  368. eventIDPropertyString = eventIDPropertyObj.ToString();
  369. }
  370. if (eventIDPropertyString != null && eventIDPropertyString.Length > 0)
  371. {
  372. // Read the string property into a number
  373. int intVal;
  374. if (SystemInfo.TryParse(eventIDPropertyString, out intVal))
  375. {
  376. eventID = intVal;
  377. }
  378. else
  379. {
  380. ErrorHandler.Error("Unable to parse event ID property [" + eventIDPropertyString + "].");
  381. }
  382. }
  383. }
  384. }
  385. short category = m_category;
  386. // Look for the Category property
  387. object categoryPropertyObj = loggingEvent.LookupProperty("Category");
  388. if (categoryPropertyObj != null)
  389. {
  390. if (categoryPropertyObj is short)
  391. {
  392. category = (short) categoryPropertyObj;
  393. }
  394. else
  395. {
  396. string categoryPropertyString = categoryPropertyObj as string;
  397. if (categoryPropertyString == null)
  398. {
  399. categoryPropertyString = categoryPropertyObj.ToString();
  400. }
  401. if (categoryPropertyString != null && categoryPropertyString.Length > 0)
  402. {
  403. // Read the string property into a number
  404. short shortVal;
  405. if (SystemInfo.TryParse(categoryPropertyString, out shortVal))
  406. {
  407. category = shortVal;
  408. }
  409. else
  410. {
  411. ErrorHandler.Error("Unable to parse event category property [" + categoryPropertyString + "].");
  412. }
  413. }
  414. }
  415. }
  416. // Write to the event log
  417. try
  418. {
  419. string eventTxt = RenderLoggingEvent(loggingEvent);
  420. // There is a limit of about 32K characters for an event log message
  421. if (eventTxt.Length > MAX_EVENTLOG_MESSAGE_SIZE)
  422. {
  423. eventTxt = eventTxt.Substring(0, MAX_EVENTLOG_MESSAGE_SIZE);
  424. }
  425. EventLogEntryType entryType = GetEntryType(loggingEvent.Level);
  426. using(SecurityContext.Impersonate(this))
  427. {
  428. EventLog.WriteEntry(m_applicationName, eventTxt, entryType, eventID, category);
  429. }
  430. }
  431. catch(Exception ex)
  432. {
  433. ErrorHandler.Error("Unable to write to event log [" + m_logName + "] using source [" + m_applicationName + "]", ex);
  434. }
  435. }
  436. /// <summary>
  437. /// This appender requires a <see cref="Layout"/> to be set.
  438. /// </summary>
  439. /// <value><c>true</c></value>
  440. /// <remarks>
  441. /// <para>
  442. /// This appender requires a <see cref="Layout"/> to be set.
  443. /// </para>
  444. /// </remarks>
  445. override protected bool RequiresLayout
  446. {
  447. get { return true; }
  448. }
  449. #endregion // Override implementation of AppenderSkeleton
  450. #region Protected Instance Methods
  451. /// <summary>
  452. /// Get the equivalent <see cref="EventLogEntryType"/> for a <see cref="Level"/> <paramref name="level"/>
  453. /// </summary>
  454. /// <param name="level">the Level to convert to an EventLogEntryType</param>
  455. /// <returns>The equivalent <see cref="EventLogEntryType"/> for a <see cref="Level"/> <paramref name="level"/></returns>
  456. /// <remarks>
  457. /// Because there are fewer applicable <see cref="EventLogEntryType"/>
  458. /// values to use in logging levels than there are in the
  459. /// <see cref="Level"/> this is a one way mapping. There is
  460. /// a loss of information during the conversion.
  461. /// </remarks>
  462. virtual protected EventLogEntryType GetEntryType(Level level)
  463. {
  464. // see if there is a specified lookup.
  465. Level2EventLogEntryType entryType = m_levelMapping.Lookup(level) as Level2EventLogEntryType;
  466. if (entryType != null)
  467. {
  468. return entryType.EventLogEntryType;
  469. }
  470. // Use default behavior
  471. if (level >= Level.Error)
  472. {
  473. return EventLogEntryType.Error;
  474. }
  475. else if (level == Level.Warn)
  476. {
  477. return EventLogEntryType.Warning;
  478. }
  479. // Default setting
  480. return EventLogEntryType.Information;
  481. }
  482. #endregion // Protected Instance Methods
  483. #region Private Instance Fields
  484. /// <summary>
  485. /// The log name is the section in the event logs where the messages
  486. /// are stored.
  487. /// </summary>
  488. private string m_logName;
  489. /// <summary>
  490. /// Name of the application to use when logging. This appears in the
  491. /// application column of the event log named by <see cref="m_logName"/>.
  492. /// </summary>
  493. private string m_applicationName;
  494. /// <summary>
  495. /// The name of the machine which holds the event log. This is
  496. /// currently only allowed to be '.' i.e. the current machine.
  497. /// </summary>
  498. private string m_machineName;
  499. /// <summary>
  500. /// Mapping from level object to EventLogEntryType
  501. /// </summary>
  502. private LevelMapping m_levelMapping = new LevelMapping();
  503. /// <summary>
  504. /// The security context to use for privileged calls
  505. /// </summary>
  506. private SecurityContext m_securityContext;
  507. /// <summary>
  508. /// The event ID to use unless one is explicitly specified via the <c>LoggingEvent</c>'s properties.
  509. /// </summary>
  510. private int m_eventId = 0;
  511. /// <summary>
  512. /// The event category to use unless one is explicitly specified via the <c>LoggingEvent</c>'s properties.
  513. /// </summary>
  514. private short m_category = 0;
  515. #endregion // Private Instance Fields
  516. #region Level2EventLogEntryType LevelMapping Entry
  517. /// <summary>
  518. /// A class to act as a mapping between the level that a logging call is made at and
  519. /// the color it should be displayed as.
  520. /// </summary>
  521. /// <remarks>
  522. /// <para>
  523. /// Defines the mapping between a level and its event log entry type.
  524. /// </para>
  525. /// </remarks>
  526. public class Level2EventLogEntryType : LevelMappingEntry
  527. {
  528. private EventLogEntryType m_entryType;
  529. /// <summary>
  530. /// The <see cref="EventLogEntryType"/> for this entry
  531. /// </summary>
  532. /// <remarks>
  533. /// <para>
  534. /// Required property.
  535. /// The <see cref="EventLogEntryType"/> for this entry
  536. /// </para>
  537. /// </remarks>
  538. public EventLogEntryType EventLogEntryType
  539. {
  540. get { return m_entryType; }
  541. set { m_entryType = value; }
  542. }
  543. }
  544. #endregion // LevelColors LevelMapping Entry
  545. #region Private Static Fields
  546. /// <summary>
  547. /// The fully qualified type of the EventLogAppender class.
  548. /// </summary>
  549. /// <remarks>
  550. /// Used by the internal logger to record the Type of the
  551. /// log message.
  552. /// </remarks>
  553. private readonly static Type declaringType = typeof(EventLogAppender);
  554. /// <summary>
  555. /// The maximum size supported by default.
  556. /// </summary>
  557. /// <remarks>
  558. /// http://msdn.microsoft.com/en-us/library/xzwc042w(v=vs.100).aspx
  559. /// The 32766 documented max size is two bytes shy of 32K (I'm assuming 32766
  560. /// may leave space for a two byte null terminator of #0#0). The 32766 max
  561. /// length is what the .NET 4.0 source code checks for, but this is WRONG!
  562. /// Strings with a length > 31839 on Windows Vista or higher can CORRUPT
  563. /// the event log! See: System.Diagnostics.EventLogInternal.InternalWriteEvent()
  564. /// for the use of the 32766 max size.
  565. /// </remarks>
  566. private readonly static int MAX_EVENTLOG_MESSAGE_SIZE_DEFAULT = 32766;
  567. /// <summary>
  568. /// The maximum size supported by a windows operating system that is vista
  569. /// or newer.
  570. /// </summary>
  571. /// <remarks>
  572. /// See ReportEvent API:
  573. /// http://msdn.microsoft.com/en-us/library/aa363679(VS.85).aspx
  574. /// ReportEvent's lpStrings parameter:
  575. /// "A pointer to a buffer containing an array of
  576. /// null-terminated strings that are merged into the message before Event Viewer
  577. /// displays the string to the user. This parameter must be a valid pointer
  578. /// (or NULL), even if wNumStrings is zero. Each string is limited to 31,839 characters."
  579. ///
  580. /// Going beyond the size of 31839 will (at some point) corrupt the event log on Windows
  581. /// Vista or higher! It may succeed for a while...but you will eventually run into the
  582. /// error: "System.ComponentModel.Win32Exception : A device attached to the system is
  583. /// not functioning", and the event log will then be corrupt (I was able to corrupt
  584. /// an event log using a length of 31877 on Windows 7).
  585. ///
  586. /// The max size for Windows Vista or higher is documented here:
  587. /// http://msdn.microsoft.com/en-us/library/xzwc042w(v=vs.100).aspx.
  588. /// Going over this size may succeed a few times but the buffer will overrun and
  589. /// eventually corrupt the log (based on testing).
  590. ///
  591. /// The maxEventMsgSize size is based on the max buffer size of the lpStrings parameter of the ReportEvent API.
  592. /// The documented max size for EventLog.WriteEntry for Windows Vista and higher is 31839, but I'm leaving room for a
  593. /// terminator of #0#0, as we cannot see the source of ReportEvent (though we could use an API monitor to examine the
  594. /// buffer, given enough time).
  595. /// </remarks>
  596. private readonly static int MAX_EVENTLOG_MESSAGE_SIZE_VISTA_OR_NEWER = 31839 - 2;
  597. /// <summary>
  598. /// The maximum size that the operating system supports for
  599. /// a event log message.
  600. /// </summary>
  601. /// <remarks>
  602. /// Used to determine the maximum string length that can be written
  603. /// to the operating system event log and eventually truncate a string
  604. /// that exceeds the limits.
  605. /// </remarks>
  606. private readonly static int MAX_EVENTLOG_MESSAGE_SIZE = GetMaxEventLogMessageSize();
  607. /// <summary>
  608. /// This method determines the maximum event log message size allowed for
  609. /// the current environment.
  610. /// </summary>
  611. /// <returns></returns>
  612. private static int GetMaxEventLogMessageSize()
  613. {
  614. if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major >= 6)
  615. return MAX_EVENTLOG_MESSAGE_SIZE_VISTA_OR_NEWER;
  616. return MAX_EVENTLOG_MESSAGE_SIZE_DEFAULT;
  617. }
  618. #endregion Private Static Fields
  619. }
  620. }
  621. #endif // !SSCLI
  622. #endif // !NETCF