Mobile Development – Start apps with another using injectDLL

Print This Post Print This Post

I already posted one article about using injectDLL, one about subclassing a foreign window. This time injectDLL is used to start and stop an application when another app is started. Remember that every DLL listed inside the MULTI_SZ registry key HKLM\System\Kernel\injectDLL is loaded into every process.

I would like to add some battery and wifi indicator to a full screen Remote Desktop Mobile (RDM) session. There are two specialized applications that display a small bar graph on the right of the screen, one for the battery level and one for the WiFi RSSI.

tsshellwnd_normal_with_wifi_and_batt_monitor_bars

The upper right one is for the WLAN RSSI and the lower bar shows the battery charge level.

As these apps should only add there display when RDM ist started, I added them to an injectDLL.

The injectDLL is loaded into every process that is started on the Windows Mobile device. Inside the DLLMain function, that every DLL has to implement, the code checks if the loading process is “\windows\wpctsc.exe”:

BOOL APIENTRY DllMain( HANDLE hModule, 
 DWORD ul_reason_for_call, 
 LPVOID lpReserved
 )
{
 if (ul_reason_for_call == DLL_PROCESS_ATTACH)
 {
 // Disable the DLL_THREAD_ATTACH and DLL_THREAD_DETACH notification calls
 DisableThreadLibraryCalls ((HMODULE)hModule);

 TCHAR szModuleFileName[MAX_PATH+1] = {0};
 TCHAR szRecord[MAX_PATH] = {0};
 if ((GetModuleFileName(NULL, szModuleFileName, MAX_PATH)) == NULL){
   wsprintf(szRecord, L"GetModuleFileName failed!");
   WriteRecordToTextFile(szRecord);
   return TRUE;
 }

 if (_wcsicmp(szModuleFileName, MODULE_FILENAME) != 0){
   wsprintf(szRecord, L"Compare ModuleFileName failed: %s", szModuleFileName);
   WriteRecordToTextFile(szRecord);
   return TRUE;
 }
...
 if ((hThread = CreateThread(NULL, 0, WaitForProcessToBeUpAndReadyThread, 0, 0, NULL)) == NULL)

MODULE_FILENAME holds the full name of the process we want to use. The above code simply returns without further action if the loader does not match the process we are looking for. If the process matches, the code creates a thread that will do the further processing. This avoids that the further actions block the watched process from being blocked or slow down.

The created thread just waits for the windows being ‘visible’ to the system and then starts the external processes:

  while ((hWndRDM = FindWindow(L"TSSHELLWND", NULL)) == NULL)
  {
...
  do{
    hChildWin=FindChildWindowByParent(hWndRDM, L"UIMainClass");
  }while(hChildWin==NULL);
...
  if(runProcess(L"\\Windows\\RdmAddonBatt2.exe", L"")==0)
    DEBUGMSG(1, (L"\\Windows\\RdmAddonBatt2.exe started\n"));
  else
    DEBUGMSG(1, (L"\\Windows\\RdmAddonBatt2.exe not started?"));
  if(runProcess(L"\\Windows\\RdmAddonWiFi.exe", L"")==0)
    DEBUGMSG(1, (L"\\Windows\\RdmAddonWiFi.exe started\n"));
  else
    DEBUGMSG(1, (L"\\Windows\\RdmAddonWiFi.exe not started?"));
...

In the above code the external processes are hard coded, but it would be easy to use the registry or a configuration file to replace the hard coded file names.

There are much more useful scenarios for using injectDLL.

Code at Github: RdmInject, RdmAddonBatt2, RdmAddonWiFi

 

Leave a Reply