_FX BOOLEAN Ipc_StartServer(const WCHAR *TruePath, BOOLEAN Async)
{
    static const WCHAR *_format    = L"%S (%d)";
    static const WCHAR *_formatHex = L"%S (%08X)";
    static const WCHAR *_rpcss = L"RpcSs";
    static const WCHAR *_dcomlaunch = L"DcomLaunch";
    const WCHAR *service;
    HANDLE hServerEvent, hServerProcess;
    BOOLEAN event_created = FALSE;
    BOOLEAN bRet = TRUE;

    //
    // in async mode, several args are passed through the TruePath pointer
    //

    if (Async == '*') {

        ULONG_PTR *args = (ULONG_PTR *)TruePath;
        TruePath = (const WCHAR *)args[0];
        service = (const WCHAR *)args[1];
        hServerEvent = (HANDLE)args[2];
        hServerProcess = (HANDLE)args[3];
        Dll_Free(args);

        Async = FALSE;

    } else {

    //
    // -    SandboxieDcomLaunch is started when SandboxieRpcSs asks
    //      for the 'actkernel' port.
    //
    // -    SandboxieRpcSs is started when any process that is not
    //      SandboxieDcomLaunch asks for the 'epmapper' port
    //

        if (Dll_ImageType == DLL_IMAGE_SANDBOXIE_RPCSS) {

            if (_wcsicmp(TruePath, Ipc_actkernel) == 0)
                service = _dcomlaunch;
            else
                return FALSE;

        } else if (Dll_ImageType != DLL_IMAGE_SANDBOXIE_DCOMLAUNCH) {

            if (_wcsicmp(TruePath, Ipc_epmapper) == 0)
                service = _rpcss;
            else
                return FALSE;

        } else
            return FALSE;

        //
        // open server event
        //

        hServerEvent = Ipc_GetServerEvent(service, &event_created);
        if (! hServerEvent)
            return FALSE;

        if (! event_created)
            hServerProcess = NULL;
        else {

            //
            // launch the process
            //
            // note that we launch the process from the calling thread even
            // when called in async mode.  this is because CreateProcess
            // is a complicated call that can end up attempting to acquire
            // the loader lock.  while at the same time, some other thread
            // may already have taken the loader lock, and might be waiting
            // for SandboxieRpcSs to start
            //
            // v4 note:  CreateProcess uses LoadLibrary and because we are
            // invoked during the DLL init phase, this may cause problems
            // with DLL initialization.  this is in addition to the problem
            // described in the paragraph above, and other problems such as
            // an SRP or AppLocker rule preventing us from starting the
            // SandboxieRpcSs.  requesting SbieSvc ProcessServevr to run
            // SandboxieRpcSs for us can fix all of these problems.
            //

            ULONG errnum;
            STARTUPINFO si;
            PROCESS_INFORMATION pi;

            WCHAR program[64];
            wcscpy(program, SANDBOXIE);
            wcscat(program, service);
            wcscat(program, L".exe");

            memzero(&si, sizeof(STARTUPINFO));
            si.cb = sizeof(STARTUPINFO);
            si.dwFlags = STARTF_FORCEOFFFEEDBACK;
            si.dwXCountChars = si.dwYCountChars = tzuk;

            if (service == _rpcss) {

                //
                // starting rpcss: go thrugh SbieSvc ProcessServer
                //

                WCHAR homedir[MAX_PATH];
                SbieApi_GetHomePath(NULL, 0, homedir, MAX_PATH);

                if (! Proc_ImpersonateSelf(TRUE))
                    errnum = ERROR_NO_IMPERSONATION_TOKEN;
                else {

                    WCHAR *fullpath = Dll_AllocTemp(512 * sizeof(WCHAR));
                    Sbie_snwprintf(fullpath, 512, L"\"%s\\%s\"", homedir, program);
                    OutputDebugStringW(fullpath);
                    OutputDebugStringW(L"YYCRpcSs:\n");

                    if (! SbieDll_RunSandboxed(
                            L"*THREAD*", fullpath, homedir, 0, &si, &pi))
                        errnum = GetLastError();
                    else
                        errnum = -1;
                    Proc_ImpersonateSelf(FALSE);

                    Dll_Free(fullpath);
                }

            } else {

                //
                // rpcss starting dcomlaunch:  normal CreateProcess
                // (SandboxieRpcSs has to be parent of SandboxieDcomLaunch)
                //

                if (! SbieDll_RunFromHome(program, NULL, &si, &pi))
                    errnum = GetLastError();
                else
                    errnum = -1;
            }

            if (errnum != -1) {

                SbieApi_Log(2204, _format, service, errnum);

                CloseHandle(hServerEvent);

                return FALSE;
            }

            hServerProcess = pi.hProcess;
            CloseHandle(pi.hThread);
        }
    }

    //
    // if async mode requested, restart ourselves in a separate thread
    //

    if (Async) {

        //
        // if async mode,
        //

        ULONG_PTR *args;
        ULONG idThread;
        HANDLE hThread;

        args = (ULONG_PTR *)Dll_AllocTemp(sizeof(ULONG_PTR) * 4);
        args[0] = (ULONG_PTR)TruePath;
        args[1] = (ULONG_PTR)service;
        args[2] = (ULONG_PTR)hServerEvent;
        args[3] = (ULONG_PTR)hServerProcess;

        hThread = CreateThread(
                    NULL, 0, Ipc_StartServer_Thread,
                    (void *)args, CREATE_SUSPENDED, &idThread);
        if (hThread) {

            SetThreadPriority(hThread, THREAD_PRIORITY_BELOW_NORMAL);
            ResumeThread(hThread);
            CloseHandle(hThread);
        }

    } else {

        //
        // continued processing in the main line or the separate thread
        //
        // here we wait for the server process to signal the event
        //

        ULONG NumWaitHandles = (hServerProcess) ? 2 : 1;
        HANDLE WaitHandles[2];
        WaitHandles[0] = hServerEvent;
        WaitHandles[1] = hServerProcess;

        while (1) {

            ULONG rc = WaitForMultipleObjects(
                NumWaitHandles, WaitHandles, FALSE, 30 * 1000);

            if (rc == WAIT_OBJECT_0)
                break;

            if (rc == (WAIT_OBJECT_0 + 1)) {

                if (GetExitCodeProcess(hServerProcess, &rc)
                        && rc != 0 && rc != STILL_ACTIVE) {

                    if (rc == STATUS_LICENSE_QUOTA_EXCEEDED) {

                        // duplicate copy of SandboxieRpcSs.exe,
                        // so don't indicate any error.
                        // see also apps/com/rpcss/rpcss.c

                    } else {

                        const WCHAR *FormatToUse =
                            (rc & 0x80000000) ? _formatHex : _format;
                        SbieApi_Log(2204, FormatToUse, service, rc, rc);
                    }

                } else
                    SbieApi_Log(2204, _format, service, -3);

                break;
            }

            SbieApi_Log(2204, _format, service, -1);
        }

        //
        // close handles
        //

        CloseHandle(hServerEvent);

        //
        // special case:  if we were waiting for the rpcss server to load,
        // we should now also try to wait for the dcomlaunch server to load.
        // this is because rpcss signals its event _before_ starting the
        // dcomlaunch server.
        //
        // exception to special case:  (1) if running with 'open COM', then
        // SandboxieDcomLaunch does not load.  (2) if the DcomLaunch service
        // is not available (i.e. Windows pre XP SP 2)
        //

        if (service == _rpcss && (! SbieDll_IsOpenCOM())) {

            HANDLE hKey = Scm_OpenKeyForService(_dcomlaunch, FALSE);
            if (hKey) {

                HANDLE hEvent = NULL;
                NtClose(hKey);

                while (1) {

                    if (! hEvent) {
                        
                        hEvent = Ipc_GetServerEvent(_dcomlaunch, NULL);

                        if (!hEvent) {

                            ULONG rc = 0;

                            // hServerProcess should stay running. If hServerProcess exits, probably a crash,
                            // we have no chance to open the ServiceInitComplete event. Break to loop.
                            if (GetExitCodeProcess(hServerProcess, &rc)
                                && rc != 0 && rc != STILL_ACTIVE) {

                                SbieApi_Log(2204, _format, _dcomlaunch, -4);
                                bRet = FALSE;
                                break;
                            }

                            Sleep(50);
                            continue;
                        }
                    }

                    if (WAIT_OBJECT_0 == WaitForSingleObject(
                                                hEvent, 30 * 1000))
                        break;

                    SbieApi_Log(2204, _format, _dcomlaunch, -2);
                }

                if (hEvent)
                    CloseHandle(hEvent);
            }
        }

        if (hServerProcess)
            CloseHandle(hServerProcess);
    }

    //
    // finish
    //

    return bRet;
}

  

_FX BOOLEAN Ipc_StartServer(const WCHAR *TruePath, BOOLEAN Async)
SbieDll_StartCOM(TRUE);
_FX void Dll_InitExeEntry(void)
_ FX void Ldr_Inject_Entry(ULONG_PTR *pRetAddr)
Ldr_Inject_Init(bHostInject);
_FX BOOLEAN SbieDll_InjectLow_CopyData(
_FX ULONG SbieDll_InjectLow(HANDLE hProcess, ULONG init_flags, BOOLEAN dup_drv_handle)
errlvl = SbieDll_InjectLow(hProcess, sbieLow.init_flags, TRUE);