static bool IsProcessRunning(int pid)
{
    std::vector<ProcessInfo> processes;
    if (!GetProcesses(processes))
        return false;
    for (size_t i = 0; i < processes.size(); ++i)
    {
        if (processes[i].pid == pid)
            return true;
    }
    return false;
}
static bool IsExplorerRunning()
{
    std::vector<ProcessInfo> processes;
    if (!GetProcesses(processes))
        return false;
    for (size_t i = 0; i < processes.size(); ++i)
    {
        if (!_tcsicmp(processes[i].image.c_str(), wxT("explorer.exe")))
            return true;
    }
    return false;
}
int
ListApplications(std::string& plist, bool opt_runningApps, bool opt_debuggable)
{
    int result = -1;
    
    CFAllocatorRef alloc = kCFAllocatorDefault;
    
    // Create a mutable array that we can populate. Specify zero so it can be of any size.
    CFReleaser<CFMutableArrayRef> plistMutableArray (::CFArrayCreateMutable (alloc, 0, &kCFTypeArrayCallBacks));

    const uid_t our_uid = getuid();

#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
    
    if (our_uid == 0)
    {
        bool all_users = true;
        result = GetProcesses (plistMutableArray.get(), all_users);
    }
    else
    {
        CFReleaser<CFStringRef> sbsFrontAppID (::SBSCopyFrontmostApplicationDisplayIdentifier ());
        CFReleaser<CFArrayRef> sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable));

        // Need to check the return value from SBSCopyApplicationDisplayIdentifiers.
        CFIndex count = sbsAppIDs.get() ? ::CFArrayGetCount (sbsAppIDs.get()) : 0;
        CFIndex i = 0;
        for (i = 0; i < count; i++)
        {
            CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i);

            // Create a new mutable dictionary for each application
            CFReleaser<CFMutableDictionaryRef> appInfoDict (::CFDictionaryCreateMutable (alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));

            // Get the process id for the app (if there is one)
            pid_t pid = INVALID_NUB_PROCESS;
            if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &pid) == true)
            {
                CFReleaser<CFNumberRef> pidCFNumber (::CFNumberCreate (alloc,  kCFNumberSInt32Type, &pid));
                ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PID_KEY, pidCFNumber.get());
            }

            // Set the a boolean to indicate if this is the front most
            if (sbsFrontAppID.get() && displayIdentifier && (::CFStringCompare (sbsFrontAppID.get(), displayIdentifier, 0) == kCFCompareEqualTo))
                ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, kCFBooleanTrue);
            else
                ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, kCFBooleanFalse);


            CFReleaser<CFStringRef> executablePath (::SBSCopyExecutablePathForDisplayIdentifier (displayIdentifier));
            if (executablePath.get() != NULL)
            {
                ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PATH_KEY, executablePath.get());
            }

            CFReleaser<CFStringRef> iconImagePath (::SBSCopyIconImagePathForDisplayIdentifier (displayIdentifier)) ;
            if (iconImagePath.get() != NULL)
            {
                ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY, iconImagePath.get());
            }

            CFReleaser<CFStringRef> localizedDisplayName (::SBSCopyLocalizedApplicationNameForDisplayIdentifier (displayIdentifier));
            if (localizedDisplayName.get() != NULL)
            {
                ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_DISPLAY_NAME_KEY, localizedDisplayName.get());
            }

            // Append the application info to the plist array
            ::CFArrayAppendValue (plistMutableArray.get(), appInfoDict.get());
        }
    }
#else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
    // When root, show all processes
    bool all_users = (our_uid == 0);
    GetProcesses (plistMutableArray.get(), all_users);
#endif
    
    CFReleaser<CFDataRef> plistData (::CFPropertyListCreateXMLData (alloc, plistMutableArray.get()));
    
    // write plist to service port
    if (plistData.get() != NULL)
    {
        CFIndex size = ::CFDataGetLength (plistData.get());
        const UInt8 *bytes = ::CFDataGetBytePtr (plistData.get());
        if (bytes != NULL && size > 0)
        {
            plist.assign((char *)bytes, size);
            return 0;   // Success
        }
        else
        {
            DNBLogError("empty application property list.");
            result = -2;
        }
    }
    else
    {
        DNBLogError("serializing task list.");
        result = -3;
    }
    
    return result;

}
int SetupHelperApp::OnRun()
{
#ifdef _WIN64
    DBG("--- x64 SetupHelper");
#else
    DBG("--- x86 SetupHelper");
#endif
    
    if (__argc < 3)
        return -1;

    std::vector<ProcessInfo> processes;
    DBG("Get processes (1)"); 
    if (!GetProcesses(processes))
        return -1;
    DBG("Got processes (1):");
    for (size_t i = 0; i < processes.size(); ++i)
    {
        DBG(i << ": " << processes[i].pid);
    }

    // Kill all Explorer and cvslock processes
    
    for (size_t i = 0; i < processes.size(); ++i)
    {
        if (!_tcsicmp(processes[i].image.c_str(), wxT("explorer.exe")))
        {
            DBG("Killing Explorer");
            // Show dialog
            wxProgressDialog* dlg = new wxProgressDialog(wxT("TortoiseCVS Setup"), wxText(__argv[1]));
            dlg->Show();
            // Kill Explorer
            TerminateProcess(processes[i]);
            int i = 0;
            while (IsProcessRunning(processes[i].pid) && (i < 50))
            {
                dlg->Update((i+1)*100/50);
                Sleep(50);
            }
            
            // Avoid the Active Desktop error message
            HKEY hKey;
            if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
                              0, KEY_WRITE, &hKey))
            {
                DWORD data = 0;
                RegSetValueExA(hKey, "FaultKey", 0, REG_DWORD, reinterpret_cast<BYTE*>(&data), sizeof(DWORD));
            }

            dlg->Destroy();
        }
        else if (!_tcsicmp(processes[i].image.c_str(), wxT("cvslock.exe")))
        {
            // Kill CVSNT lock service
            TerminateProcess(processes[i]);
        }
    }

    // Show dialog
    wxProgressDialog* dlg = new wxProgressDialog(wxT("TortoiseCVS Setup"), wxText(__argv[1]));
    dlg->Show();

    // Wait for Explorer restart
    for (int i = 0; i < 50; ++i)
    {
       if (IsExplorerRunning())
          break;
       dlg->Update((i+1)*100/50);
       Sleep(50);
    }
    if (!IsExplorerRunning())
    {
       dlg->Update(0);
       STARTUPINFOA startupinfo;
       startupinfo.cb = sizeof(STARTUPINFOA);
       startupinfo.lpReserved = 0;
       startupinfo.lpDesktop = 0;
       startupinfo.lpTitle = 0;
       startupinfo.dwFlags = 0;
       startupinfo.cbReserved2 = 0;
       startupinfo.lpReserved2 = 0;
       PROCESS_INFORMATION processinfo;
       CreateProcessA(0, "explorer.exe", 0, 0, FALSE, 0, 0, 0, &startupinfo, &processinfo);
       CloseHandle(processinfo.hProcess);
       CloseHandle(processinfo.hThread);
       for (int i = 0; i < 10; ++i)
       {
          Sleep(50);
          dlg->Update((i+1)*100/10);
       }
    }
    dlg->Destroy();
    
    DBG("Check remaining processes");
    bool anyLeft = true;
    while (anyLeft)
    {
        processes.clear();
        DBG("Get processes (2)");
        if (!GetProcesses(processes))
            return -1;
        DBG("Got processes (2):");
        for (size_t i = 0; i < processes.size(); ++i)
        {
            DBG(i << ": " << processes[i].pid);
        }
        
        anyLeft = false;
        for (size_t i = 0; i < processes.size(); ++i)
        {
            if (_tcsicmp(processes[i].image.c_str(), wxT("explorer.exe")))
            {
                anyLeft = true;
                // Ask user to close application
                wxString msg;
                msg << wxT("The application ");
                if (processes[i].title.empty())
                {
                    // No title found (may be minimized to tray etc.)
                    msg << processes[i].image;
                }
                else
                    msg << wxT("'") << processes[i].title << wxT("' (") << processes[i].image << wxT(")");
                msg << wxT("\nneeds to be closed before TortoiseCVS setup can continue.\n")
                    << wxT("Please close the application and click OK, or click Cancel to leave the process ")
                    << wxT("running.\nIn the latter case, you will be required to reboot Windows after installation.");
                if (MessageBox(0, msg.c_str(), wxT("TortoiseCVS Setup"), MB_OKCANCEL | MB_ICONINFORMATION) == IDCANCEL)
                    return 1;
            }
        }
    }

    DBG("Loop done");

    if (__argv[2][0] == 'b')
        return 0;

    // Delete any renamed DLL's
    // TODO: Make this work even when installing to a different directory
    std::string rootDir(GetRootDir());
    std::string src(rootDir);
    src += "TortoiseShell.dll_renamed";
    DBG("Deleting " << src);
    DeleteFileA(src.c_str());
    src = rootDir;
    src += "TortoiseShell64.dll_renamed";
    DBG("Deleting " << src);
    DeleteFileA(src.c_str());
    src = rootDir;
    src += "TortoiseAct.exe_renamed";
    DBG("Deleting " << src);
    DeleteFileA(src.c_str());

    DBG("Exit: 0");
    return 0;
}