2018年8月10日 星期五

[C++] Detection login or unlogin session by service [1/2]

This article shows how to create a Windows Service and detection session which is login or unlogin status.
Use basic window MSDN API.
Basic on [C++] Build Services by window.
When the service is completed, the window message is received in the sub thread, so we create two thread.
DWORD WINAPI ServiceSubThread(LPVOID lpParam)
{
 OutputDebugString(_T("=================Service Thread Start========================");
 
 thread_class c_thread;
 HANDLE m_threads[THREAD_MAIN_ID] = {};
 DWORD threadIDs[THREAD_MAIN_ID] = {};
 LPTHREAD_START_ROUTINE threadProcs[THREAD_MAIN_ID] = { c_thread.threadMain0, c_thread.threadMain1 };
 DWORD_PTR mask = 0;

 for (int i = 0; i < THREAD_MAIN_ID; ++i)
 {
  m_threads[i] = CreateThread(NULL, 0, threadProcs[i], &c_thread, CREATE_SUSPENDED, &threadIDs[i]);
  mask = 1 << i;
  SetThreadAffinityMask(m_threads[i], mask);
 }

 for (int i = 0; i < THREAD_MAIN_ID; ++i)
 {
  ResumeThread(m_threads[i]);
 }

 WaitForMultipleObjects(THREAD_MAIN_ID, m_threads, TRUE, INFINITE);

 for (int i = 0; i < THREAD_MAIN_ID; ++i)
 {
  CloseHandle(m_threads[i]);
 }

 OutputDebugString(_T("=================Service Thread End========================");
 return ERROR_SUCCESS;
}

In the threadMain0, We want the do Listen Window Massage.
DWORD WINAPI thread_class::threadMain0(LPVOID param)
{
 thread_class* This = static_cast<thread_class>(param);
 This-&gt;ListenSsession();
 return 0;
}
The threadMain1 go to see [C++] Detection login or unlogin session by service [2/2].

By the ListenSsession, create contains window class by WNDCLASSEX, And creates an overlapped by CreateWindowEx, then registers the specified window to receive session to know which status by login or other.
VOID thread_class::ListenSsession()
{
 try
 {
  HWND hwndMain;
  //you need init WNDCLASSEX create MainWndProc
  InitWindowClass();
  //you need create CreateWindowEx
  CreateWindowHWND(w_hInstance, hwndMain);
  bool rv = false;
  rv = WTSRegisterSessionNotification(hwndMain, NOTIFY_FOR_ALL_SESSIONS);
  if (!rv)
  {
   OutputDebugString(_T("Registers the specified window to receive session Fail");
  }
  MSG msg = MSG();
  while (true)
  {
   ListenStart(msg);
  }
 }
 catch (std::exception ex)
 {
  wprintf(L"Exception : %s", ex.what());
 }
 return VOID();
}

By the MainWndProc, by the WM_WTSSESSION_CHANGE to notifies applications of changes in session state.
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
 switch (msg)
 {
 case WM_WTSSESSION_CHANGE:
 {
  switch (wParam)
  {
  case WTS_CONSOLE_CONNECT:
   OutputDebugString(_T("CONSOLE_CONNECT");
   break;
  case WTS_CONSOLE_DISCONNECT:
   OutputDebugString(_T("CONSOLE_DISCONNECT");
   break;
  case WTS_REMOTE_CONNECT:
   OutputDebugString(_T("REMOTE_CONNECT");
   break;
  case WTS_REMOTE_DISCONNECT:
   OutputDebugString(_T("REMOTE_DISCONNECT");
   break;
  case WTS_SESSION_LOGON:
   OutputDebugString(_T("SESSION_LOGON");
   break;
  case WTS_SESSION_LOGOFF:
   OutputDebugString(_T("SESSION_LOGOFF");
   break;
  case WTS_SESSION_LOCK:
   OutputDebugString(_T("SESSION_LOCK");
   break;
  case WTS_SESSION_UNLOCK:
   OutputDebugString(_T("SESSION_UNLOCK");
   break;
  case WTS_SESSION_REMOTE_CONTROL:
   OutputDebugString(_T("SESSION_REMOTE_CONTROL");
   break;
  default:
   break;
  }
 }
 default:
  // Call the default window handler
  return DefWindowProc(hwnd, msg, wParam, lParam);
 }
 return 0;
}

[C++] Get PdhAddCounter by different international languages

In PdhAddCounter need adds the specified counter to the query.

The response -1073738824 means PDH_CSTATUS_NO_OBJECT=0xC0000BB8, using a non-english OS.

The system uses counters to collect performance data. Each counter is uniquely identified through its name and its path, or location. The syntax of a counter path is.

	PDH_STATUS status = ERROR_SUCCESS;
	WCHAR * PDHCounterName = new WCHAR[MAX_PATH];
	std::wmemset(PDHCounterName, 0, sizeof(PDHCounterName));
	
	std::wstring ProcessExampleName = L"ExampleProcess";
	std::wstring ObjectName = GetPerformanceCounterNamebyIndex(PerformanceObjectName);
	std::wstring CounterName = GetPerformanceCounterNamebyIndex(PerformanceCounterName);
		
	HQUERY hQuery = NULL;
	status = PdhOpenQuery(NULL, 0, &hQuery);

	if (ERROR_SUCCESS != status)
	{
		wprintf(L"PdhOpenQuery failed with 0x%x", status);
	}
	//\Process(ExampleProcess)\% Processor Time (English)
	//\Prozess(ExampleProcess)\% Prozessor zeit (Deutsch Maybe this)
	wsprintf(PDHCounterName, TEXT("\\%s(%s)\\%s"), ObjectName.c_str(), ProcessExampleName.c_str(), CounterName.c_str());
	
	HCOUNTER hCounter = NULL;
	status = PdhAddCounter(hQuery, PDHCounterName, 0, &hCounter);

	if (ERROR_SUCCESS != status)
	{
		wprintf(L"PdhAddCounter failed with 0x%x\n", status);
		reval = false;
	}

The index value that you specify must match one of the index values associated with the objects or counters that were loaded on the computer. The index/name value pairs are stored in the Counters registry value in the following registry location.

In Register Key Path: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009

PerformanceObjectName 230 //is Process
PerformanceCounterName 6 //is Processor Time

By the GetPerformanceCounterNamebyIndex:
std::wstring CpuUsageInfo::GetPerformanceCounterNamebyIndex(int index)
{
	PDH_STATUS status = ERROR_SUCCESS;
	WCHAR szObjectName[MAX_PATH] = L"";
	DWORD dwSize = sizeof(szObjectName);
	OptationFunction_Class object;

	if (status = PdhLookupPerfNameByIndex(NULL, index, szObjectName, &dwSize))
	{
		wprintf(L"PdhLookupPerfNameByIndex failed with 0x%x, LastError %ld\n", status, GetLastError());
	}
	return szObjectName;
}

2018年8月9日 星期四

[C++] Issue by Get PdhCollectQueryData to 0xc0000bbc Fixed

In the Performance Data Helper (PDH) functions return.
Trying to use the Windows performance counters to get the virtual bytes usage of a specific process, then sometimes encountered 0xc0000bbc.

Check the %windir%\system32\perfmon.msc if the system cannot get performance counter, use command fixed, rebuild performance counter library value:
cmd.exe lodctr /r
cmd.exe lodctr /q
cmd.exe lodctr /e:Performance Counters
For the processing at that time, it will appear again, because the system program is damaged, need to be repaired or rebuilt.

[C++] Detection login or unlogin session by service [2/2]


In receiving window massage, it is necessary to detect the status before booting into the OS.Why do I need to detect it outside because the window massage does not receive any state before sending the message, so I want to get the status that has not been send out by event.

In the threadMain1, We want the do Listen Window Massage.
DWORD WINAPI thread_class::threadMain1(LPVOID param)
{
 thread_class* This = static_cast<thread_class*>(param);
 This->ListenStatusChange();
 return 0;
}

By the ListenStatusChange
VOID thread_class::ListenStatusChange()
{
 //If not login in window 30 sec.
 WTS_Class wts;
 //Read OS user account wts.w_UserName
 wts.ReadConfig(wts.w_UserName);
 while (true)
 {
  bool rv = false;
  rv = wts.IsLogonbyUser(wts.w_UserName, WTSActive);
  if (!rv)
  {
   OutputDebugString(_T("If you have not logged in for 30 seconds");
   Sleep(30000);
   rv = wts.IsLogonbyUser(wts.w_UserName, WTSActive);
   if (!rv)
   {
    OutputDebugString(_T("Which user...");
   }
  }
  else
  {
   break;
  }
  Sleep(30000);
 }
 return VOID();
}

Specified the connection state of Specified the connection state.
We want to do designated account is logged on to the WinStation(WTSActive).
You can select other WTS_CONNECTSTATE_CLASS items, Get the message you want, but before you enter the OS, some connection state is not available.
By the WTS_Class IsLogonbyUser
BOOL WTS_Class::IsLogonbyUser(std::wstring UserName ,WTS_CONNECTSTATE_CLASS WST_Tpye)
{
 if (UserName.empty())
 {
  OutputDebugString(_T("UserName is empty");
 }
 PWTS_SESSION_INFO ppSessionInfo = NULL;
 DWORD     pCount = 0;
 WTS_SESSION_INFO  wts;

 WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE,
  0,
  1,
  &ppSessionInfo,
  &pCount); // pCount - Pointer to the variable that receives
      // the number of WTS_SESSION_INFO structures
      // returned in the ppSessionInfo buffer

 struct SessionInfo
 {
  std::wstring SessionName;
  std::wstring UserName;
  WTS_CONNECTSTATE_CLASS SessionType;
 };
 
 std::vector  SessionDataList;
 
 for (DWORD i = 0; i < pCount; i++)
 {
  SessionInfo tamplateData;
  wts = ppSessionInfo[i];
  DWORD     TSSessionId = wts.SessionId;
  LPTSTR     TSpWinStationName = wts.pWinStationName;
  WTS_CONNECTSTATE_CLASS TSState = wts.State;

  //ByPass Services
  if (_wcsicmp(TSpWinStationName, L"Services") == 0)
  {
   continue;
  }
  tamplateData.SessionName = (std::wstring)TSpWinStationName;
  tamplateData.SessionType = TSState;
  tamplateData.UserName = GetWTSInfo(i, WTSUserName);
  SessionDataList.push_back(tamplateData);
 }

 for (size_t Number = 0; Number < SessionDataList.size(); Number++)
 {
  if (_wcsicmp(UserName.c_str(), SessionDataList[Number].UserName.c_str()) == 0 &&
   WST_Tpye == SessionDataList[Number].SessionType)
  {
   return true;
  }
 }
 return false;
}

[C++] Build Services by window

It can be started automatically at system boot, by a user through the Services control panel applet, or by an application that uses the service functions.Then Create the Service Control Manager begin.

Now we need calling process to run service.
int WINAPI WinMain(HINSTANCE hInstance,
 HINSTANCE hPrevInstance,
 PSTR szCmdLine,
 int iCmdShow)
{
 SERVICE_TABLE_ENTRY ServiceTables[] =
 {
  { (LPWSTR)SERVICE_NAME,
  ((LPSERVICE_MAIN_FUNCTION)ServiceMaines) },
 { NULL, NULL }
 };
 
 if (StartServiceCtrlDispatcher(ServiceTables) == FALSE)
 {
  return GetLastError();
 }
}

In the ServiceMaines process, and register our service control handler with the SCM.
VOID WINAPI ServiceMaines(DWORD argc, LPTSTR *argv)
{
 DWORD Status = E_FAIL;

 StatusHandles = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);
 //StatusHandles = RegisterServiceCtrlHandlerEx(SERVICE_NAME, ServiceCtrlHandler, this);

 if (StatusHandles == NULL)
 {
  return void();
 }

 ZeroMemory(&StatusService, sizeof(StatusService));
 StatusService.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
 StatusService.dwControlsAccepted = 0;
 StatusService.dwCurrentState = SERVICE_START_PENDING;
 StatusService.dwWin32ExitCode = 0;
 StatusService.dwServiceSpecificExitCode = 0;
 StatusService.dwCheckPoint = 0;

 if (SetServiceStatus(StatusHandles, &StatusService) == FALSE)
 {
  OutputDebugString(_T(
   "Service controller set Service Status fail"));
 }

 g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
 if (g_ServiceStopEvent == NULL)
 {
  StatusService.dwControlsAccepted = 0;
  StatusService.dwCurrentState = SERVICE_STOPPED;
  StatusService.dwWin32ExitCode = GetLastError();
  StatusService.dwCheckPoint = 1;

  if (SetServiceStatus(StatusHandles, &StatusService) == FALSE)
  {
   OutputDebugString(_T(
    "Create a service stop set Service Status fail"));
  }
  return void();
 }

 StatusService.dwControlsAccepted = SERVICE_ACCEPT_STOP;
 StatusService.dwCurrentState = SERVICE_RUNNING;
 StatusService.dwWin32ExitCode = 0;
 StatusService.dwCheckPoint = 0;

 if (SetServiceStatus(StatusHandles, &StatusService) == FALSE)
 {
  OutputDebugString(_T(
   "SetServiceStatus returned error"));
 }

 HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL);

 WaitForSingleObject(hThread, INFINITE);

 CloseHandle(g_ServiceStopEvent);

 StatusService.dwControlsAccepted = 0;
 StatusService.dwCurrentState = SERVICE_STOPPED;
 StatusService.dwWin32ExitCode = 0;
 StatusService.dwCheckPoint = 3;

 if (SetServiceStatus(StatusHandles, &StatusService) == FALSE)
 {
  OutputDebugString(_T(
   "SetServiceStatus returned error"));
 }

}

By the Handler
VOID WINAPI ServiceCtrlHandler (DWORD CtrlCode)
{
    switch (CtrlCode) 
 {
     case SERVICE_CONTROL_STOP :
 
        if (StatusService.dwCurrentState != SERVICE_RUNNING)
           break;

        StatusService.dwControlsAccepted = 0;
        StatusService.dwCurrentState = SERVICE_STOP_PENDING;
        StatusService.dwWin32ExitCode = 0;
        StatusService.dwCheckPoint = 4;
 
        if (SetServiceStatus (StatusHandles, &StatusService) == FALSE)
        {
            OutputDebugString(_T(
              "SetServiceStatus returned error"));
        }
 
        // This will signal the worker thread to start shutting down
        SetEvent (g_ServiceStopEvent);
 
        break;
 
     default:
         break;
    }
}

The Service Thread
DWORD WINAPI ServiceSubThread(LPVOID lpParam)
{
 OutputDebugString(_T("================= Service Thread Start========================");
 //Do something..
 OutputDebugString(_T("================= Service Thread End========================");
}

2018年8月8日 星期三

[C++] Calling erase in reverse iterator

There are some difficulties in using the loop, especially the need to filter out the message.
Share some examples for everyone.

Use multimap associative containers here.
std::multimap<int,std::wstring> List;
List.insert(std::make_pair(1, L"A"));
List.insert(std::make_pair(1, L"B"));
List.insert(std::make_pair(2, L"B"));
List.insert(std::make_pair(3, L"D"));
List.insert(std::make_pair(4, L"E"));
for (std::multimap<int,std::wstring>::reverse_iterator it = List.rbegin(); it != List.rend(); ++it)
{
 if (it->second.find(L"B") != std::wstring::npos) 
 {
  List.erase(std::next(it).base()); 
  it = List.rbegin();
  if (OuthMonitor.empty())
  {
   break;
  }
 }
}
The relationship between it.base() and List is:
List.erase(std::next(it).base());
Then because the iterator has removed one, so specify the original begin.
it = List.rbegin();

Output:
List { size=0x00000003 }
[0x00000001] L"A"
[0x00000003] L"D"
[0x00000004] L"E"