public bool WaitAndRead(WaitHandle hProcess, Action <string> results) { bool R = false; char *b = null; const int bLen = 7900; var ev = new ManualResetEvent(false); try { var ha = new WaitHandle[2] { ev, hProcess }; for (bool useSB = false; ; useSB = results == null) { var o = new Api.OVERLAPPED { hEvent = ev.SafeWaitHandle.DangerousGetHandle() }; if (!Api.ConnectNamedPipe(_hPipe, &o)) { int e = lastError.code; if (e != Api.ERROR_PIPE_CONNECTED) { if (e != Api.ERROR_IO_PENDING) { break; } int wr = WaitHandle.WaitAny(ha); if (wr != 0) { Api.CancelIo(_hPipe); R = true; break; } //task ended if (!Api.GetOverlappedResult(_hPipe, ref o, out _, false)) { Api.DisconnectNamedPipe(_hPipe); break; } } } if (b == null) { b = (char *)MemoryUtil.Alloc(bLen); } bool readOK; while (((readOK = Api.ReadFile(_hPipe, b, bLen, out int n, null)) || (lastError.code == Api.ERROR_MORE_DATA)) && n > 0) { n /= 2; if (!readOK) { useSB = true; } if (useSB) //rare { _sb ??= new StringBuilder(bLen); if (results == null && _s != null) { _sb.Append(_s); } _s = null; _sb.Append(b, n); } else { _s = new string(b, 0, n); } if (readOK) { if (results != null) { results(ResultString); _sb?.Clear(); } break; } //note: MSDN says must use OVERLAPPED with ReadFile too, but works without it. } Api.DisconnectNamedPipe(_hPipe); if (!readOK) { break; } } } finally { ev.Dispose(); MemoryUtil.Free(b); } return(R); }
/// <summary> /// Executes the compiled assembly in new process. /// Returns: process id if started now, 0 if failed, (int)ATask.ERunResult.deferred if scheduled to run later. /// </summary> /// <param name="f"></param> /// <param name="r"></param> /// <param name="args"></param> /// <param name="noDefer">Don't schedule to run later. If cannot run now, just return 0.</param> /// <param name="wrPipeName">Pipe name for ATask.WriteResult.</param> /// <param name="ignoreLimits">Don't check whether the task can run now.</param> /// <param name="runFromEditor">Starting from the Run button or menu Run command.</param> public unsafe int RunCompiled(FileNode f, Compiler.CompResults r, string[] args, bool noDefer = false, string wrPipeName = null, bool ignoreLimits = false, bool runFromEditor = false) { g1: if (!ignoreLimits && !_CanRunNow(f, r, out var running, runFromEditor)) { var ifRunning = r.ifRunning; bool same = running.f == f; if (!same) { ifRunning = r.ifRunning2 switch { EIfRunning2.cancel => EIfRunning.cancel, EIfRunning2.wait => EIfRunning.wait, EIfRunning2.warn => EIfRunning.warn, _ => (ifRunning & ~EIfRunning._restartFlag) switch { EIfRunning.cancel => EIfRunning.cancel, EIfRunning.wait => EIfRunning.wait, _ => EIfRunning.warn } }; } else if (ifRunning.Has(EIfRunning._restartFlag)) { if (runFromEditor) { ifRunning = EIfRunning.restart; } else { ifRunning &= ~EIfRunning._restartFlag; } } //AOutput.Write(same, ifRunning); switch (ifRunning) { case EIfRunning.cancel: break; case EIfRunning.wait when !noDefer: _q.Insert(0, new _WaitingTask(f, r, args)); return((int)ATask.ERunResult.deferred); //-1 case EIfRunning.restart when _EndTask(running): goto g1; default: //warn string s1 = same ? "it" : $"{running.f.SciLink}"; AOutput.Write($"<>Cannot start {f.SciLink} because {s1} is running. You may want to <+properties \"{f.IdStringWithWorkspace}\">change<> <c green>ifRunning<>, <c green>ifRunning2<>, <c green>runMode<>."); break; } return(0); } _SpUac uac = _SpUac.normal; int preIndex = 0; if (!AUac.IsUacDisabled) { //info: to completely disable UAC on Win7: gpedit.msc/Computer configuration/Windows settings/Security settings/Local policies/Security options/User Account Control:Run all administrators in Admin Approval Mode/Disabled. Reboot. //note: when UAC disabled, if our uac is System, IsUacDisabled returns false (we probably run as SYSTEM user). It's OK. var IL = AUac.OfThisProcess.IntegrityLevel; if (r.uac == EUac.inherit) { switch (IL) { case UacIL.High: preIndex = 1; break; case UacIL.UIAccess: uac = _SpUac.uiAccess; preIndex = 2; break; } } else { switch (IL) { case UacIL.Medium: case UacIL.UIAccess: if (r.uac == EUac.admin) { uac = _SpUac.admin; } break; case UacIL.High: if (r.uac == EUac.user) { uac = _SpUac.userFromAdmin; } break; case UacIL.Low: case UacIL.Untrusted: case UacIL.Unknown: //break; case UacIL.System: case UacIL.Protected: AOutput.Write($"<>Cannot run {f.SciLink}. Meta comment option <c green>uac {r.uac}<> cannot be used when the UAC integrity level of this process is {IL}. Supported levels are Medium, High and uiAccess."); return(0); //info: cannot start Medium IL process from System process. Would need another function. Never mind. } if (r.uac == EUac.admin) { preIndex = 1; } } } string exeFile, argsString; _Preloaded pre = null; byte[] taskParams = null; bool bit32 = r.prefer32bit || AVersion.Is32BitOS; if (r.notInCache) //meta role exeProgram { exeFile = Compiler.DllNameToAppHostExeName(r.file, bit32); argsString = args == null ? null : Au.Util.AStringUtil.CommandLineFromArray(args); } else { exeFile = AFolders.ThisAppBS + (bit32 ? "Au.Task32.exe" : "Au.Task.exe"); //int iFlags = r.hasConfig ? 1 : 0; int iFlags = 0; if (r.mtaThread) { iFlags |= 2; } if (r.console) { iFlags |= 4; } taskParams = Au.Util.Serializer_.SerializeWithSize(r.name, r.file, r.pdbOffset, iFlags, args, r.fullPathRefs, wrPipeName, (string)AFolders.Workspace); wrPipeName = null; if (bit32 && !AVersion.Is32BitOS) { preIndex += 3; } pre = s_preloaded[preIndex] ??= new _Preloaded(preIndex); argsString = pre.pipeName; } int pid; WaitHandle hProcess = null; bool disconnectPipe = false; try { //APerf.First(); var pp = pre?.hProcess; if (pp != null && 0 != Api.WaitForSingleObject(pp.SafeWaitHandle.DangerousGetHandle(), 0)) //preloaded process exists { hProcess = pp; pid = pre.pid; pre.hProcess = null; pre.pid = 0; } else { if (pp != null) { pp.Dispose(); pre.hProcess = null; pre.pid = 0; } //preloaded process existed but somehow ended (pid, hProcess) = _StartProcess(uac, exeFile, argsString, wrPipeName); } Api.AllowSetForegroundWindow(pid); if (pre != null) { //APerf.First(); var o = new Api.OVERLAPPED { hEvent = pre.overlappedEvent }; if (!Api.ConnectNamedPipe(pre.hPipe, &o)) { int e = ALastError.Code; if (e != Api.ERROR_PIPE_CONNECTED) { if (e != Api.ERROR_IO_PENDING) { throw new AuException(e); } var ha = stackalloc IntPtr[2] { pre.overlappedEvent, hProcess.SafeWaitHandle.DangerousGetHandle() }; int wr = Api.WaitForMultipleObjectsEx(2, ha, false, -1, false); if (wr != 0) { Api.CancelIo(pre.hPipe); throw new AuException("*start task. Preloaded task process ended"); } //note: if fails when 32-bit process, rebuild solution with platform x86 disconnectPipe = true; if (!Api.GetOverlappedResult(pre.hPipe, ref o, out _, false)) { throw new AuException(0); } } } //APerf.Next(); if (!Api.WriteFileArr(pre.hPipe, taskParams, out _)) { throw new AuException(0); } //APerf.Next(); Api.DisconnectNamedPipe(pre.hPipe); disconnectPipe = false; //APerf.NW('e'); //start preloaded process for next task. Let it wait for pipe connection. if (uac != _SpUac.admin) //we don't want second UAC consent { try { (pre.pid, pre.hProcess) = _StartProcess(uac, exeFile, argsString, null); } catch (Exception ex) { ADebug.Print(ex); } } } } catch (Exception ex) { AOutput.Write(ex); if (disconnectPipe) { Api.DisconnectNamedPipe(pre.hPipe); } hProcess?.Dispose(); return(0); } var rt = new RunningTask(f, hProcess, r.runMode != ERunMode.green); _Add(rt); return(pid); }