ETW Logging From Service and Driver

A recent feature addition to my product at work is logging to a custom event log channel using ETW. I have used technology built on top of ETW before, such as WPP tracing, and while it is a bit cumbersome, the advantages are great, and once it’s set up, it’s not hard at all to use. I have also done classic Event Logging before.

With this recent feature work, I wanted to use the current technology, and I wanted to be able to log from my service and driver. I thought I had everything working. I created a manifest .xml file that defined my custom channels, and the events I wanted to log. I wrote some code to register/unregister with ETW, and to log various messages with their accompanying data.

But in my testing, I was able to log events from my service (running as system), but not from my driver. The calls to EtwRegister and EtwWrite looked like they were working perfectly. They returned a success code, but nothing every appeared in the log. The documentation says they will behave this way (returning success but not logging anything) if the event is not enabled.

I spend the next few hours trying to figure out why my events were not enabled. The logging level (Error, Warning, Information) was fine. The keywords were fine. The manifest file and the message resources it pointed to were okay. I even went so far as to write a notification callback so I could see what the system was enabling. It all looked fine.

I finally decided to try using a built-in channel instead of my own, and I selected the SYSTEM channel. Bam! Everything started working. Then I started to think that this might be a security issue. This was a bit counter-intuitive because the APIs weren’t failing. I would think that if you tried to write an event but didn’t have rights, you should get an access denied error, but apparently the system just happily eats your event and gives you a thumbs up.

The key turned out to be the isolation attribute on my custom channel. Setting the isolation=”system” attribute on the channel made everything start working.

I don’t understand why the logging worked from my service, but not from my driver, since both were running in the system context. But there it is. A very simple solution to a maddening problem (at least for a day).

Windows Installer SQUIDs

Windows Installer stores information about components, products, etc. that are installed on the system in the registry. Most of these elements are identified by a GUID, but the Installer doesn’t always store the GUID directly in the registry. Instead it uses what I call SQUIDs, or Squished GUIDs. Basically they just reformat the GUID a bit and remove some of the extra characters that don’t add anything (hyphens and braces). You can read more about this here: A Brief Note on Installer GUIDs.

Periodically when I am working on some installer code I can end up needing to search the registry for stuff that is related to a component I am working on. It is very helpful to be able to convert between SQUIDs and GUIDs. I usually just do this by hand, but today I decided I needed a bit more help doing this and wrote a tiny vbscript piece of code that does the conversion. On the chance that it might provide value for someone else, I have decided to post it here.

Function SquidToGuid(s)
  SquidToGuid = _
    StrReverse(Mid(s, 1, 8)) & "-" & _
    StrReverse(Mid(s, 9, 4)) & "-" & _
    StrReverse(Mid(s, 13, 4)) & "-" & _
    StrReverse(Mid(s, 17, 2)) & StrReverse(Mid(s, 19, 2)) & "-" & _
    StrReverse(Mid(s, 21, 2)) & StrReverse(Mid(s, 23, 2)) & StrReverse(Mid(s, 25, 2)) & _
    StrReverse(Mid(s, 27, 2)) & StrReverse(Mid(s, 29, 2)) & StrReverse(Mid(s, 31, 2))
End Function

Function GuidToSquid(g)
  Replace g, "{", ""
  Replace g, "}", ""
  GuidToSquid = _
    StrReverse(Mid(g, 1, 8)) & _
    StrReverse(Mid(g, 10, 4)) & _
    StrReverse(Mid(g, 15, 4)) & _
    StrReverse(Mid(g, 20, 2)) & StrReverse(Mid(g, 22, 2)) & _
    StrReverse(Mid(g, 25, 2)) & StrReverse(Mid(g, 27, 2)) & StrReverse(Mid(g, 29, 2)) & _
    StrReverse(Mid(g, 31, 2)) & StrReverse(Mid(g, 33, 2)) & StrReverse(Mid(g, 35, 2))
End Function