No categories in Windows Event Log - c++

I have created a category file to the Event Logger, but the category names do not show up in Event Logger.
If I however open the log from C:\Windows\System32\winevt\Logs, the category names show up. The category name also show up if I use following PowerShell.
$eventlog = New-Object System.Diagnostics.EventLog("MyLog")
Write-Host $eventlog.Entries[0].Category
The .mc file looks like this:
MessageIdTypedef = WORD
LanguageNames=(
English=0x0409:MSG00409
Swedish=0x041D:MSG0041D
)
MessageId=1
SymbolicName=CAT_1
Language=English
Category 1
.
Language=Swedish
Kategori 1
.
MessageId=2
SymbolicName=CAT_2
Language=English
Category 2
.
Language=Swedish
Kattegori 2
.
; // Up to 22 categorys
In the registry, I have following:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog\MyLog\MySource]
"CategoryCount"=dword:00000016
"TypesSupported"=dword:00000007
"CategoryMessageFile"="C:\\path\\Messages.dll"
I have found https://social.msdn.microsoft.com/forums/windowsdesktop/en-us/3fed3069-ce0f-4168-8132-4d19d66fdd7e/windows-7eventlog-creating-custom-categories with the same problem, but no answer to the problem.
I have tried this in both Windows 7, 8 and Windows Server 2008 R2
Edit
I have created a test project to show what I have done. download it here

Try adding the machine's Authenticated Users or Users group to the message folder's security level. Keep the default permissions. Then either reboot or try to restart the EventLog service.
At an administrator command prompt: net stop eventlog
You will likely be prompted to shutdown other services. You must enter Y to continue. The services being shutdown will normally restart on their own so you just need to wait a few seconds. The eventlog service may fail to shutdown because another service has restarted, it may take a couple of tries to get everything down. Watch the resulting text closely for status.
I ran into this same issue while pointing the source registry information to my message folder in my VS project. I found that it started to work after I created a C:\Test folder with an admin user and referenced that instead. The only difference between this and the project folder was the two groups. Adding either of the two groups made things work. Taking them both away from either folder made it stop.
I also found that if I had matching MessageId's in my CategoryMessageFile and my EventMessageFile it ignored the CategoryMessageFile.

Another possibility, though obviously not the OP's issue (or language), that exhibits similar symptoms is when the EventMessageFile and the CategoryMessageFile have overlapping MessageIds. I am assuming this is a bug in the event log viewer utility when looking at a 'live' source, as the symptoms do not exist when using powershell, .NET, or directly opening the event log file in Event Viewer to parse the event logs.
In my situation, I have a .NET application and am using .NET APIs to create the event source using EventSourceCreationData to specify the CategoryResourceFile and CategoryCount. By default, the MessageResourceFile will be set to C:\Windows\Microsoft.NET\Framework\v4.0.30319\EventLogMessages.dll. The effect of these APIs causes the creation of the appropriate registry keys CategoryMessageFile, CategoryCount, and EventMessageFile. Important to note here is the message file from .NET helpfully adds messages from 0x0 to 0x000FFFF that are all defined as %1\r\n\x00 - essentially meaning when you create an event message in the default way, using EventLog.WriteEntry you see the string message that was logged regardless of the event id. If instead you use EventLog.WriteEvent, you pretty much need to use a specialized messages file (see docs on the EventInstance class). More Info
The symptoms are when looking at the live Event Viewer the Category as defined with the MessageId that was passed into the event log shows up as the integer instead of the string from the resource file. Note this is different behavior from if you had misconfigured the category resource file, permission issues, etc where you see the 'Task Category' as the integer but in parenthesis.
So for example, if you specified category id 8 and are expecting the localized category name 'My Category' the Event Viewer can show one of three things: (8) meaning the CategoryMessageFile or CategoryCount registry keys are misconfigured or there is a problem with the dll. My Category if everything is working as expected. 8 if you hit the issue I am describing.
Thanks to Rich Shealer's answer for the pointer. The problem is rather than getting the Category string from the CategoryMessageFile as one would expect, it is getting it from the MessageResourceFile - and since the .NET resource file merely is a formatting string that returns the first element of source data the numeric category id is generated.
There is no 'good' workaround, but in my use case of using metadata provided to log messages via a logging framework, and some filtering, messages generally have category and event id of zero, except for where I want to specifically set one when logging for a small subset of log messages. This means the logging framework is what is calling the APIs to write the log entries. A workaround is to declare MessageId of 0x0 to be %1 in the .mc file, and only start your event ids at a number higher than the number of categories you expect to have. Then configure the EventMessageFile in the registry or via the property MessageResourceFile to first specify the category file, the semicolon delimiter, then the .NET default resource file. This will properly show and messages logged with 0's for event id and category id, and for event ids greater than your category file the same behavior you previously had. However, any eventid with a number defined that conflicts with the category - well, you get the category name as the log message. You could also remove the message resource file but then you get the annoying 'resource cannot be found' message. I confirmed the behavior here but refuse to do this - will live with the missing text, as I mostly use APIs for analysis and it is correct there.
Sadly, I can't locate any open issue with the Windows Event Viewer - I see it broken in Windows 10 build 1909.

Related

How to retrieve detailed result from msi installation

I have a .msi file created by Wix toolset, used to install 5 drivers. And I have a setup application to launch the .msi by CreateProcess with msiexec.exe command, and provide an UI. Currently, my requirement is get the detailed result of the installation – which drivers installed successfully, which failed. Since I can just get the result of CreateProcess, how can I retrieve the detailed result from the installation? Very appreciate if you can provide some information on this issue.
I created the .msi file with the difx:Driver flag like below:
<difx:Driver AddRemovePrograms="no" DeleteFiles="no" ForceInstall="no" Legacy="no" PlugAndPlayPrompt="no" />
An MSI-based setup is transactional. It either all works or all fails and rolls back the system to its previous state. It seems that you have made a choice to defeat this paradigm and have it partially succeed leaving some drivers installed and others not.
It also appears that you have suppressed the installer's UI so that error information cannot be found.
I have two recommendations:
Don't use CreateProcess() and the "fire and forget" model. Use MsiSetExternalUIRecord with this model:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb309215(v=vs.85).aspx
There are C# p/invoke equivalents out there too. If you don't want to show all the UI then just collect the error messages and show them to the user if that's the goal. That's the only reliable way to get the actual error messages. This is the supported way for you to own the UI and collect only the messages that you think are important.
Allow a failed driver install to fail the entire install and roll it all back. It might actually be like this already. If the install partially succeeds and four drivers are not installed, what's the plan? You can't run the MSI again because it will go into repair/maintenance mode. If the user needs to fix something and do the install again then the product needs to be uninstalled anyway.
You can retrieve the verbose installation log using the /L*V parameter:
msiexec /i "C:\MyPackage\Example.msi" /L*V "C:\log\example.log"
You can read more here.
The general structure is:
msiexec.exe [/i][/x] <path_to_package> [/L{i|w|e|a|r|u|c|m|o|p|v|x+|!|*}][/log]
/L - enable logging
i - include status messages
w - include non-fatal warnings
e - include all error messages
a - mention when an action is started
r - include action-specific records
u - include user requests
c - include the initial UI parameters
m - include out-of-memory or fatal exit information
o - include out-of-disk-space messages
p - include terminal properties
v - verbose output
x - include extra debugging information
+ - append to an existing log file
! - flush each line to the log
* - log all information, except for v and x options
Another simpler method, instead of parsing the log, would be to write a small C# custom action to check if the drivers are installed on the machine.
You need to schedule that custom action close the end of the installation process, as deferred (not immediate).
You can generate a log (as suggested by Harsh) or you can create a custom action (either deferred as suggested by Bogdan if you are using the method he suggests) or sequenced after InstallFinalize (if you have some other method that doesn't require elevation), but that custom action would probably need to use some sort of IPC to communicate what it finds back to your program.
One possibility for IPC might be the MsiProcessMessage function in your custom action with the INSTALLMESSAGE_INFO message type (what you send will also show up in the log) that you can receive in your application, but that will require using the MsiSetExternalUIRecord function which will require replacing your CreateProcess calling msiexec with something from the Installation and Configuration Functions section of that page.
Or if writing custom actions isn't where you need to go it may be easier for you to call MsiGetFeatureState or MsiGetComponentState with MsiOpenProduct, assuming that gives you the granularity of detail you're after.

Delete HKEY_CURRENT_USER value on user log off

A windows service creates a registry value (for an Excel add-in) under HKEY_CURRENT_USER registry key for each logged on user (by calling ImpersonateLoggedOnUser() and RegSetValueEx()).
I need to delete this registry value when a user logs off, including system shutdown.
If it is not deleted at log off, and the software is uninstalled by one user then the entry in the registry remains for any other user that logged on during the lifetime of the service which causes a message box error to be displayed each time Excel begins because it is attempting to load an add-in that no longer exists.
Considered but rejected the following:
SetConsoleCtrlHandler() because there is no indication of what user is logging off.
REG_OPTION_VOLATILE because it is effective only when creating keys and I am only creating a value (did not thoroughly investigate so may not have been solution even if I was creating a key).
Are there any other mechanisms that would provide a solution to this? Windows versions are XP, Vista and 7.
Since you are already on a service, your life (should be) is easy.
In fact you can register yourself to receive the SERVICE_CONTROL_SESSIONCHANGE event. In particular, you will want to look for the WTS_SESSION_LOGOFF reason.
You have to register for these events in your service control routine, at startup, adding SERVICE_ACCEPT_SESSIONCHANGE. When the event is SERVICE_CONTROL_SESSIONCHANGE, the lpEventData parameter is a pointer to a WTSSESSION_NOTIFICATION structure, with information about the session currently terminating (thus, about the user logging off).
Check out the details on MSDN1, MSDN2, MSDN3 - the data structure that contains the dwSessionId of the interesting session
Check out this related (but not duplicate) question too
That said, I find João Augusto solution cleaner; I would use that for a similar problem; however, I wanted to add this solution for having an answer to the wider question (for future reference readers)
EDIT: Another method is to use SENS, check this MSDN article
An easier approach would be to put a command removing the value in question into the user's RunOnce key, e.g.,
reg.exe delete HKCU\Software\xyzzy /v myvalue /f
so that the unwanted value will be removed when the user next logs in. Note, however, that this might interfere with the creation of the value depending on how you are handling that.

Writing to the Windows Security Log with C++

I have been tasked with writing entries to the Windows security log. The entire project is Win32 C++ code. I have already written (with help from various online resources) a logging class that handles registration, deregistration, and code for executing the ReportEvent() call. Also, I've done the mc.exe and rc.exe steps for my event logging, if that helps establish where I'm at in the project.
My question is a multi-parter:
I've noticed at Filling Windows XP Security Event Log that there are some who believe this is not allowed by Windows. Others ( How to write log to SECURITY event Log in C#? ) imply otherwise. Possible or not?
If it is possible, how to get it to write to the security log. Is it as simple as specifying "Security" as my source name when calling RegisterEventSource()?
As far as deregistration, when should that occur? When the app is uninstalled? When the app closes? When the log entry is written?
How do I look up my log entries? I look in the Windows Event Viewer, but I don't see the entries I add with my test app, despite all the appropriate return values from the system calls. Where would I look up the events that I specified with a source name of "yarp" when I made my call to RegisterEventSource()?
For the moment, I'll just deal with the first question, because the answer to that probably renders the rest irrelevant.
Only Local Security Authority (lsass.exe) can write to the security log. This isn't a matter that something else attempting to get the privilege will fail -- it's a matter of there not being a way for anything else to even request the privilege at all (and this is by design).
From there, about the only answer to your other questions is "Sorry!"

ETW Tracing in a Driver -- post-procedure

I have ETW tracing in a driver; the manifest file is created properly, the resources are all compiled in, etc. On the target machine, I run this command as administrator:
wevtutil im myManifest.xml
I get no errors. Then, I run (as administrator):
logman create trace myProviderName -o Log.etl -p "{myProviderGUID}" -f bincirc -max 1000
With no errors. Then I wait enough time for some traces to have been performed, and then I run these comands (as administrator):
logman stop myProviderName
tracerpt Log000001.etl
Now the problem I'm having is that the generated file, dumpfile.xml shows records of none of my traces at all. It shows a basic structure of setup details including the Provider GUID, etc.
So my question: Is there a step I'm missing in the above procedure, or must the problem be with my tracing code?
It turns out that there was a problem; though it wasn't with my code. I did not include opcodes for my events in the manifest, and as such, no events were being recorded.
If someone stumbles upon this post, it may help you to note that your events must have a channel, level, opcode, and template for even basic functionality. Additionally, the above procedure is missing one step. I needed to do the following:
Right click 'My Computer' and select 'Manage'
Click Performance > Data Collector Sets > User Defined > myProvider
Right click myProvider in right-hand pane, select 'Properties'
Set Keywords(Any), Keywords(All), and Level according to what I specified in the manifest
Restart my machine, and re-enable trace via logman.
The above procedure (in the question + the supplemental directly above) will create a log session and produce a basic readable log output from the generated ETL file.

Is a separate message file library for my native Win32 service necessary?

We've got an old legacy win32 service, developed with C++, and we've just recently noticed that when the service starts up and stops, there is an informational message in the event logs about our missing event descriptions. To be more precise, the message looks like this:
The description for Event ID 0 from source [application] cannot be
found. Either the component that raises this event is not installed on
your local computer or the installation is corrupted. You can install
or repair the component on the local computer.
So we understand what this means, basically we're missing a library which has a message table compiled into it. This way when the event ID for changing status (start/stop) arrives, it can look up the message and print it in the event logs.
The question is, for these universal messages (changing status etc) which pretty much every service is going to have, surely there are default message table that we can use, rather than having to go to the trouble of creating another project, just for this, adding registries and updating our installer.
Seems like a lot of hassle for something that should surely be a default somewhere? Like the standard win32 error messages?
I've created a number of managed services in the past, and I'm pretty sure we didn't need to do anything like this before!
So to wrap this up, I guess the answer is that the a new message table/file is always required, regardless (so no there are no default messages you can use), so I'll just have to chuck in a message table into my services resource file and add a registry entry to the installer.
Still find it baffling thought that every native service has it's own 'service has stopped/started' message...!
Thanks!