How to open windows' service-specific event log - winapi

We need to read service-specific event log "Microsoft-Windows-TerminalServices-LocalSessionManager/Operational" programmatically. Following code
LPWSTR pwsLogName = L"Microsoft-Windows-TerminalServices-LocalSessionManager/Operational";
HANDLE hEventLog = OpenEventLog(nullptr, pwsLogName);
results opening "Application" log instead of required.
In the same time command line utility wevtutil can succesfuly read needed log:
wevtutil query-events Microsoft-Windows-TerminalServices-LocalSessionManager/Operational
What are we doing wrong?

Finally we've found OpenEventLog() is dead and we need to use Evt* functions (EvtQuery & company)

The behaviour is by design; see: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-openeventloga
Todo: check if registry key exists first, then use openEventLog() if succeeded.
_stprintf_s(szKeyName, _T("System\\CurrentControlSet\\Services\\EventLog\\%s"),szLogName);
dwRet = RegOpenKey(HKEY_LOCAL_MACHINE, szKeyName, &hKey);
if (ERROR_SUCCESS == dwRet)
{
RegCloseKey(hKey);
// continue with openEventLog()

Related

How can I delete a file and send it to the recycle bin in Vista/7 using IFileOperation?

According to the documentation for IFileOperation::SetOperationFlags, the FOFX_RECYCLEONDELETE flag was introduced in Windows 8.
I would like to delete files and send them to the recycle bin. How is it possible to do that using IFileOperation in Vista and Windows 7?
I know that SHFileOperation supports that functionality, but I don't want to use SHFileOperation as Microsoft are telling us to use IFileOperation in its place. Is this possible using IFileOperation, and if so, how is it to be done?
The documentation for SetOperationFlags says:
This member can be a combination of the following flags. FOF flags are defined in Shellapi.h and FOFX flags are defined in Shobjidl.h.
So you can use the exact same flag, FOF_ALLOWUNDO, that you use with SHFileOperation to direct a delete action to move to the recycle bin.
 FOFX_RECYCLEONDELETE flag was introduced in Win 8 - will it work in Vista/7?
Since FOFX_RECYCLEONDELETE was introduced in Windows 8, then it did not exist in Vista/7, so no, it will not work in those versions.
There's always SHFileOperation but I'd rather use a more up-to-date Win32 API method. Anything else to know? Any alternate ways of recycling files/folders?
SHFileOperation() is the only documented way to recycle files/folders:
When used to delete a file, SHFileOperation permanently deletes the file unless you set the FOF_ALLOWUNDO flag in the fFlags member of the SHFILEOPSTRUCT structure pointed to by lpFileOp. Setting that flag sends the file to the Recycle Bin. If you want to simply delete a file and guarantee that it is not placed in the Recycle Bin, use DeleteFile.
That same flag is available in IFileOperation, but its documented behavior is different:
Preserve undo information, if possible.
Prior to Windows Vista, operations could be undone only from the same process that performed the original operation.
In Windows Vista and later systems, the scope of the undo is a user session. Any process running in the user session can undo another operation. The undo state is held in the Explorer.exe process, and as long as that process is running, it can coordinate the undo functions.
That is why FOFX_RECYCLEONDELETE had to be introduced - to re-add the old Recycle Bin behavior that had been lost when IFileOperation was first introduced.
I have verified David Heffernan's assessment of the FOF_ALLOWUNDO flag's use with IFileOperation to send items to the recycle bin. Here's the code. Apparently SHCreateItemFromParsingName is MS's way of saying create an item from a string. This code is catered to C++ with Qt. You'll have to initialize COM first of course.
void Worker::deleteItem(QString item)
{
HRESULT hr;
IFileOperation *pfo;
wchar_t *itemWChar = new wchar_t[item.length()+1];
item.toWCharArray(itemWChar);
itemWChar[item.length()] = 0;
PCWSTR itemPCWSTR = itemWChar;
hr = CoCreateInstance(CLSID_FileOperation,
NULL,
CLSCTX_ALL,
//IID_IFileOperation,
IID_PPV_ARGS(&pfo));
if (!SUCCEEDED(hr))
{
//error handling here
return;
}
hr = pfo->SetOperationFlags(FOF_ALLOWUNDO | FOF_NOCONFIRMATION);
if (!SUCCEEDED(hr))
{
//error handling here
return;
}
IShellItem *deleteItem = NULL;
hr = SHCreateItemFromParsingName(itemPCWSTR,
NULL,
IID_PPV_ARGS(&deleteItem));
if (!SUCCEEDED(hr))
{
//error handling here
return;
}
hr = pfo->DeleteItem(deleteItem,NULL);
if (deleteItem != NULL)
{
deleteItem->Release();
}
if (!SUCCEEDED(hr))
{
//error handling here
return;
}
hr = pfo->PerformOperations();
if (!SUCCEEDED(hr))
{
//error handling here
return;
}
pfo->Release();
delete[] itemWChar;
}

Boost event logger

http://boost-log.sourceforge.net/libs/log/doc/html/log/detailed/sink_backends.html
In this page there is sample code to initialize boost windows event backends,
but when I run it it gives memory error at first line.
void init_logging()
{
// Create an event log sink
boost::shared_ptr< sink_t > sink(new sink_t());
sink->set_formatter
(
expr::format("%1%: [%2%] - %3%")
% expr::attr< unsigned int >("LineID")
% expr::attr< boost::posix_time::ptime >("TimeStamp")
% expr::smessage
);
// We'll have to map our custom levels to the event log event types
sinks::event_log::custom_event_type_mapping< severity_level > mapping("Severity");
mapping[normal] = sinks::event_log::info;
mapping[warning] = sinks::event_log::warning;
mapping[error] = sinks::event_log::error;
sink->locked_backend()->set_event_type_mapper(mapping);
// Add the sink to the core
logging::core::get()->add_sink(sink);
}
Here it fails to create sink_t object.
boost::shared_ptr< sink_t > sink(new sink_t());
Any idea what is the problem and how can I solve this?
Also If you know any other source that I can learn using boost event logging please write.
No answer yet...
But I have found a solution in a blog by Timo Geusch.
http://www.lonecpluspluscoder.com/2011/01/boost-log-preventing-the-unhandled-exception-in-windows-7-when-attempting-to-log-to-the-event-log/
The reason for this problem was that the registry key in HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\EventLog\ that the application needs access to both has to be present (if you’re not administrator, you don’t have the privileges to create it) and the user who runs the application also needs to be able to both read and write to it.

InitiateShutdown fails with RPC_S_SERVER_UNAVAILABLE error for a remote computer

I'm trying to implement rebooting of a remote computer with InitiateShutdown API using the following code, but it fails with RPC_S_SERVER_UNAVAILABLE or 1722 error code:
//Process is running as administrator
//Select a remote machine to reboot:
//INFO: Tried it with and w/o two opening slashes.
LPCTSTR pServerName = L"192.168.42.105";
//Or use 127.0.0.1 if you don't have access to another machine on your network.
//This will attempt to reboot your local machine.
//In that case make sure to call shutdown /a /m \\127.0.0.1 to cancel it.
if(AdjustPrivilege(NULL, L"SeShutdownPrivilege", TRUE) &&
AdjustPrivilege(pServerName, L"SeRemoteShutdownPrivilege", TRUE))
{
int nErrorCode = ::InitiateShutdown(pServerName, NULL, 30,
SHUTDOWN_INSTALL_UPDATES | SHUTDOWN_RESTART, 0);
//Receive nErrorCode == 1722, or RPC_S_SERVER_UNAVAILABLE
}
BOOL AdjustPrivilege(LPCTSTR pStrMachine, LPCTSTR pPrivilegeName, BOOL bEnable)
{
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
BOOL bRes = FALSE;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return FALSE;
if(LookupPrivilegeValue(pStrMachine, pPrivilegeName, &tkp.Privileges[0].Luid))
{
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : SE_PRIVILEGE_REMOVED;
bRes = AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
int nOSError = GetLastError();
if(bRes)
{
if(nOSError != ERROR_SUCCESS)
bRes = FALSE;
}
}
CloseHandle(hToken);
return bRes;
}
So to prepare for this code to run I do the following on this computer, which is Windows 7 Pro (as I would do for the Microsoft's shutdown tool):
Run the following "as administrator" to allow SMB access to the logged in user D1 on the 192.168.42.105 computer (per this answer):
NET USE \\192.168.42.105\IPC$ 1234 /USER:D1
Run the process with my code above "as administrator".
And then do the following on remote computer, or 192.168.42.105, that has Windows 7 Pro (per answer here with most upvotes):
Control Panel, Network and Sharing Center, Change Advanced Sharing settings
"Private" enable "Turn on File and Printer sharing"
Set the following key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
LocalAccountTokenFilterPolicy=dword:1
RUN secpol.msc, then go to Local Security Policy, Security Settings, Local Policies, User Rights Assignment. Add "Everyone" to "Force shutdown from a remote system". (Just remember to remove it after you're done testing!)
Note that the following shutdown command seems to work just fine to reboot the remote computer:
shutdown /r /m \\192.168.42.105 /t 30
What am I missing with my code?
EDIT:
OK. I will admit that I was merely interested in why InitiateShutdown doesn't seem to "want" to work with a remote server connection, while InitiateSystemShutdownEx or InitiateSystemShutdown had no issues at all. (Unfortunately the latter two did not have the dwShutdownFlags parameter, which I needed to pass the SHUTDOWN_INSTALL_UPDATES flag to, which caused my persistence...)
At this point I had no other way of finding out than dusting out a copy of WinDbg... I'm still trying to dig into it, but so far this is what I found...
(A) It turns out that InitiateSystemShutdownEx internally uses a totally different RPC call. W/o too many details, it initiates RPC binding with RpcStringBindingComposeW using the following parameters:
ObjUuid = NULL
ProtSeq = ncacn_np
NetworkAddr = \\192.168.42.105
EndPoint = \\PIPE\\InitShutdown
Options = NULL
or the following binding string:
ncacn_np:\\\\192.168.42.105[\\PIPE\\InitShutdown]
(B) While InitiateShutdown on the other hand uses the following binding parameters:
ObjUuid = 765294ba-60bc-48b8-92e9-89fd77769d91
ProtSeq = ncacn_ip_tcp
NetworkAddr = 192.168.42.105
EndPoint = NULL
Options = NULL
which it later translates into the following binding string:
ncacn_np:\\\\192.168.42.105[\\PIPE\\lsarpc]
that it uses to obtain the RPC handle that it passes to WsdrInitiateShutdown (that seems to have its own specification):
So as you see, the InitiateShutdown call is technically treated as Unknown RPC service (for the UUID {765294ba-60bc-48b8-92e9-89fd77769d91}), which later causes a whole bunch of credential checks between the server and the client:
which, honestly, I'm not sure I want to step into with a low-level debugger :)
At this stage I will say that I am not very well versed on "Local Security Authority" interface (or the \PIPE\lsarpc named pipe configuration.) So if anyone knows what configuration is missing on the server side to allow this RPC call to go through, I would appreciate if you could post your take on it?

Windows: ReportEvent function

As far as I understood, the ReportEvent function requires Message Text Files associated through the registry to receive properly formatted messages. Is there any common Event Ids or any simple way to report an event with no Message Text Files associated?
Or may be, is there special common Event Source which I can use in my application? Something like RegisterEventSource(NULL, "Application")?
You don't have to register your messages in HKLM. (Which is a good thing, because you can't register messages if you're not an administrator).
But that doesn't stop you from writing events to the Windows Application event log. The only downside is that starting with Windows Vista you'll just get some ugly text along with it.
HRESULT LogToEventLog(String Source, String EventText, int EventType, DWORD EventID)
{
/*
EventType is one of:
EVENTLOG_ERROR_TYPE = $0001;
EVENTLOG_WARNING_TYPE = $0002;
EVENTLOG_INFORMATION_TYPE = $0004;
EVENTLOG_AUDIT_SUCCESS = $0008;
EVENTLOG_AUDIT_FAILURE = $0010;
Source is your name for your app or feature, e.g.:
"My Cool App"
"Outlook"
"ESENT"
"Chrome"
*/
HANDLE h = RegisterEventSource(null, Source); //null --> local computer
if (h == 0)
return HResultFromWin32(GetLastError);
try
{
PChar[1] ss;
ss[0] = PChar(EventText);
if (!ReportEvent(
h, // event log handle
EventType, // event type
0, // category zero
EventID, // event identifier
null, // no user security identifier
1, // one substitution string
0, // no data
#ss, // pointer to string array
null // pointer to data
))
{
return HResultFromWin32(GetLastError);
}
}
finally
{
DeregisterEventSource(h);
}
return S_OK;
}
And so now you can log events to the Application event log:
LogToEventLog("Stackoverflow", "Question 5399066 was answered by Ian Boyd",
EVENTLOG_INFORMATION_TYPE, 0x45);
Steal someone else's registration
Unfortunately, starting with Windows Vista, Windows will give ugly complaints that you didn't register the event beforehand:
The description for Event ID 69 from source Stackoverflow 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.
If the event originated on another computer, the display information
had to be saved with the event.
The following information was included with the event:
Question 5399066 was answered by Ian Boyd
But you don't have to live with it. Just because you didn't register an message source file in HKLM, doesn't mean nobody else did.
Notice, for example, a message from the Outlook source in the Event log:
Source: Outlook
EventID: 0x40000020
Event Data: D:\win32app\Exchange\Outlook2003.pst
Message: The store D:\win32app\Exchange\Outlook2003.pst has detected a catalog checkpoint.
You can check registration information for Outlook in:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application\Outlook
And see:
MessageEventFile: REG_SZ = "D:\Programs\MICROS~4\Office14\1033\MAPIR.DLL"
If you peek into the resources of MAPIR.dll binary, you'll see its Message Table:
1 MESSAGETABLE
{
0x12, "Connection stats for server (%1). Rpcs Attempted (%2), Rpcs Succeeded (%3), Rpcs Failed (%4), Rpcs Canceled (%5), Rpc UI shown (%6), Avg request time (%7) ms, Min request time (%8) ms, Max request time (%9) ms.\r\n"
0x14, "Cancelable RPC started.\r\n"
0x15, "Cancelable RPC shutdown.\r\n"
0x40000010, "Cancelable RPC dialog shown for server (%1), total wait time was (%2) ms, result was (%3).\r\n"
0x40000011, "User canceled request against server (%1) after waiting (%2) ms.\r\n"
0x40000013, "Rpc call (%1) on transport (%2) to server (%3) failed with error code (%4) after waiting (%5) ms; eeInfo (%6).\r\n"
0x40000016, "There was a problem reading one or more of your reminders. Some reminders may not appear.\r\n"
0x40000017, "Unable to update public free/busy data.\r\n"
0x4000001A, "%1\r\n"
0x4000001B, "%1\r\n"
0x4000001D, "The store %1 is being re-pushed to the indexer for the following reason: %2.\r\n"
0x4000001E, "Starting reconciliation for the store %1 for the following reason: %2.\r\n"
0x4000001F, "The store %1 has detected a catalog rebuild.\r\n"
0x40000020, "The store %1 has detected a catalog checkpoint.\r\n"
...
}
You can see that eventid 0x40000020 is assocated with a formatting string:
"The store %1 has detected a catalog checkpoint.\r\n"
You can hijack Outlook's registration:
LogToEventLog("Outlook", "Your mom", EVENTLOG_INFORMATION_TYPE, $40000020);
and you'll get your event added to the event log without all the ugly warnings:
No, you just have to follow the rules and define your message text files, build them into resources, link them to your app etc.
The example provided at MSDN leads you through everything you need to do.
Try this out, it's worked for me before..
http://www.codeproject.com/KB/system/xeventlog.aspx

Windows Services query

Using the method described in the MSDN for registering a Windows Service (ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.WIN32COM.v10.en/dllproc/base/createservice.htm) and using similar code to the supplied example:
schService = CreateService(
schSCManager, // SCManager database
TEXT("Sample_Srv"), // name of service
lpszDisplayName, // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
SERVICE_DEMAND_START, // start type
SERVICE_ERROR_NORMAL, // error control type
szPath, // path to service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password
My issue is that, although the service is registered and works perfectly, in msconfig.msc the service has 'Take No Action' in the recovery options. Is there a way I can programatically change this so that upon failure it restarts?
Take a look at ChangeServiceConfig2 for setting those types of service options.
You might be able to set this using the sc command.
sc failure "servicename" reset= 0 actions= restart/30000////
This will tell it to reset the failure counter after 0 days (never), and restart after 30 seconds on the first failure with no action for the second and later failures.
Performed further digging in the MSDN - it wasn't particularly easy to find but it appears
ChangeServiceConfig2 (http://msdn.microsoft.com/en-us/library/ms681988(VS.85).aspx)
BOOL WINAPI ChangeServiceConfig2(
__in SC_HANDLE hService,
__in DWORD dwInfoLevel,
__in_opt LPVOID lpInfo
);
When param dwInfoLevel is SERVICE_CONFIG_FAILURE_ACTIONS (2) then the lpInfo parameter is a pointer to a SERVICE_FAILURE_ACTIONS structure.
SERVICE_FAILURE_ACTIONS Structure
http://msdn.microsoft.com/en-us/library/ms685939(VS.85).aspx
Where you can configure the 'optional' service settings as you wish.

Resources