@@ -239,15 +239,9 @@ func RenderEvent(
239
239
240
240
// Only a single string is returned when rendering XML.
241
241
err = FormatEventString (EvtFormatMessageXml ,
242
- eventHandle , providerName , EvtHandle (publisherHandle ), lang , out )
242
+ eventHandle , providerName , EvtHandle (publisherHandle ), lang , renderBuf , out )
243
243
// Recover by rendering the XML without the RenderingInfo (message string).
244
244
if err != nil {
245
- // Do not try to recover from InsufficientBufferErrors because these
246
- // can be retried with a larger buffer.
247
- if errors .Is (err , sys.InsufficientBufferError {}) {
248
- return err
249
- }
250
-
251
245
err = RenderEventXML (eventHandle , renderBuf , out )
252
246
}
253
247
@@ -256,8 +250,8 @@ func RenderEvent(
256
250
257
251
// Message reads the event data associated with the EvtHandle and renders
258
252
// and returns the message only.
259
- func Message (h EvtHandle , buf []byte , pubHandleProvider func (string ) sys.MessageFiles ) (message string , err error ) {
260
- providerName , err := evtRenderProviderName (buf , h )
253
+ func Message (h EvtHandle , renderBuf []byte , pubHandleProvider func (string ) sys.MessageFiles ) (message string , err error ) {
254
+ providerName , err := evtRenderProviderName (renderBuf , h )
261
255
if err != nil {
262
256
return "" , err
263
257
}
@@ -386,12 +380,15 @@ func Close(h EvtHandle) error {
386
380
// publisherHandle is a handle to the publisher's metadata as provided by
387
381
// EvtOpenPublisherMetadata.
388
382
// lang is the language ID.
383
+ // renderBuf is a scratch buffer to render the message, if not provided or of
384
+ // insufficient size then a buffer from a system pool will be used
389
385
func FormatEventString (
390
386
messageFlag EvtFormatMessageFlag ,
391
387
eventHandle EvtHandle ,
392
388
publisher string ,
393
389
publisherHandle EvtHandle ,
394
390
lang uint32 ,
391
+ renderBuf []byte ,
395
392
out io.Writer ,
396
393
) error {
397
394
// Open a publisher handle if one was not provided.
@@ -405,29 +402,42 @@ func FormatEventString(
405
402
defer _EvtClose (ph ) //nolint:errcheck // This is just a resource release.
406
403
}
407
404
408
- // Determine the buffer size needed (given in WCHARs).
409
- var bufferUsed uint32
410
- err := _EvtFormatMessage (ph , eventHandle , 0 , 0 , nil , messageFlag , 0 , nil , & bufferUsed )
411
- if err != windows .ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // This is an errno.
405
+ var bufferPtr * byte
406
+ if renderBuf != nil {
407
+ bufferPtr = & renderBuf [0 ]
408
+ }
409
+
410
+ // EvtFormatMessage operates with WCHAR buffer, assuming the size of the buffer in characters.
411
+ // https://docs.microsoft.com/en-us/windows/win32/api/winevt/nf-winevt-evtformatmessage
412
+ var bufferNeeded uint32
413
+ bufferSize := uint32 (len (renderBuf ) / 2 )
414
+
415
+ err := _EvtFormatMessage (ph , eventHandle , 0 , 0 , nil , messageFlag , bufferSize , bufferPtr , & bufferNeeded )
416
+ if err != nil && err != windows .ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // This is an errno.
412
417
return fmt .Errorf ("failed in EvtFormatMessage: %w" , err )
418
+ } else if err == nil {
419
+ // Windows API returns a null terminated WCHAR C-style string in the buffer. bufferNeeded applies
420
+ // only when ERROR_INSUFFICIENT_BUFFER is returned. Luckily the UTF16ToUTF8Bytes/UTF16ToString
421
+ // functions stop at null termination. Note, as signaled in a comment at the end of this function,
422
+ // this behavior is bad for EvtFormatMessageKeyword as then the API returns a list of null terminated
423
+ // strings in the buffer (it's fine for now as we don't use this parameter value).
424
+ return common .UTF16ToUTF8Bytes (renderBuf , out )
413
425
}
414
426
415
427
// Get a buffer from the pool and adjust its length.
416
428
bb := sys .NewPooledByteBuffer ()
417
429
defer bb .Free ()
418
- // The documentation for EvtFormatMessage specifies that the buffer is
419
- // requested "in characters", and the buffer itself is LPWSTR, meaning the
420
- // characters are WCHAR so double the value.
421
- // https://docs.microsoft.com/en-us/windows/win32/api/winevt/nf-winevt-evtformatmessage
422
- bb .Reserve (int (bufferUsed * 2 ))
423
430
424
- err = _EvtFormatMessage (ph , eventHandle , 0 , 0 , nil , messageFlag , bufferUsed , bb .PtrAt (0 ), & bufferUsed )
431
+ bb .Reserve (int (bufferNeeded * 2 ))
432
+ bufferSize = bufferNeeded
433
+
434
+ err = _EvtFormatMessage (ph , eventHandle , 0 , 0 , nil , messageFlag , bufferSize , bb .PtrAt (0 ), & bufferNeeded )
425
435
if err != nil {
426
436
return fmt .Errorf ("failed in EvtFormatMessage: %w" , err )
427
437
}
428
438
429
439
// This assumes there is only a single string value to read. This will
430
- // not work to read keys (when messageFlag == EvtFormatMessageKeyword).
440
+ // not work to read keys (when messageFlag == EvtFormatMessageKeyword)
431
441
return common .UTF16ToUTF8Bytes (bb .Bytes (), out )
432
442
}
433
443
0 commit comments