static NAN_METHOD()

in src/win/conpty.cc [266:384]


static NAN_METHOD(PtyConnect) {
  Nan::HandleScope scope;

  // If we're working with conpty's we need to call ConnectNamedPipe here AFTER
  //    the Socket has attempted to connect to the other end, then actually
  //    spawn the process here.

  std::stringstream errorText;
  BOOL fSuccess = FALSE;

  if (info.Length() != 5 ||
      !info[0]->IsNumber() ||
      !info[1]->IsString() ||
      !info[2]->IsString() ||
      !info[3]->IsArray() ||
      !info[4]->IsFunction()) {
    Nan::ThrowError("Usage: pty.connect(id, cmdline, cwd, env, exitCallback)");
    return;
  }

  const int id = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust();
  const std::wstring cmdline(path_util::to_wstring(Nan::Utf8String(info[1])));
  const std::wstring cwd(path_util::to_wstring(Nan::Utf8String(info[2])));
  const v8::Local<v8::Array> envValues = info[3].As<v8::Array>();
  const v8::Local<v8::Function> exitCallback = v8::Local<v8::Function>::Cast(info[4]);

  // Fetch pty handle from ID and start process
  pty_baton* handle = get_pty_baton(id);
  if (!handle) {
    Nan::ThrowError("Invalid pty handle");
    return;
  }

  // Prepare command line
  std::unique_ptr<wchar_t[]> mutableCommandline = std::make_unique<wchar_t[]>(cmdline.length() + 1);
  HRESULT hr = StringCchCopyW(mutableCommandline.get(), cmdline.length() + 1, cmdline.c_str());

  // Prepare cwd
  std::unique_ptr<wchar_t[]> mutableCwd = std::make_unique<wchar_t[]>(cwd.length() + 1);
  hr = StringCchCopyW(mutableCwd.get(), cwd.length() + 1, cwd.c_str());

  // Prepare environment
  std::wstring env;
  if (!envValues.IsEmpty()) {
    std::wstringstream envBlock;
    for(uint32_t i = 0; i < envValues->Length(); i++) {
      std::wstring envValue(path_util::to_wstring(Nan::Utf8String(Nan::Get(envValues, i).ToLocalChecked())));
      envBlock << envValue << L'\0';
    }
    envBlock << L'\0';
    env = envBlock.str();
  }
  auto envV = vectorFromString(env);
  LPWSTR envArg = envV.empty() ? nullptr : envV.data();

  ConnectNamedPipe(handle->hIn, nullptr);
  ConnectNamedPipe(handle->hOut, nullptr);

  // Attach the pseudoconsole to the client application we're creating
  STARTUPINFOEXW siEx{0};
  siEx.StartupInfo.cb = sizeof(STARTUPINFOEXW);
  siEx.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
  siEx.StartupInfo.hStdError = nullptr;
  siEx.StartupInfo.hStdInput = nullptr;
  siEx.StartupInfo.hStdOutput = nullptr;

  SIZE_T size = 0;
  InitializeProcThreadAttributeList(NULL, 1, 0, &size);
  BYTE *attrList = new BYTE[size];
  siEx.lpAttributeList = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(attrList);

  fSuccess = InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &size);
  if (!fSuccess) {
    return throwNanError(&info, "InitializeProcThreadAttributeList failed", true);
  }
  fSuccess = UpdateProcThreadAttribute(siEx.lpAttributeList,
                                       0,
                                       PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
                                       handle->hpc,
                                       sizeof(HPCON),
                                       NULL,
                                       NULL);
  if (!fSuccess) {
    return throwNanError(&info, "UpdateProcThreadAttribute failed", true);
  }

  PROCESS_INFORMATION piClient{};
  fSuccess = !!CreateProcessW(
      nullptr,
      mutableCommandline.get(),
      nullptr,                      // lpProcessAttributes
      nullptr,                      // lpThreadAttributes
      false,                        // bInheritHandles VERY IMPORTANT that this is false
      EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
      envArg,                       // lpEnvironment
      mutableCwd.get(),             // lpCurrentDirectory
      &siEx.StartupInfo,            // lpStartupInfo
      &piClient                     // lpProcessInformation
  );
  if (!fSuccess) {
    return throwNanError(&info, "Cannot create process", true);
  }

  // Update handle
  handle->hShell = piClient.hProcess;
  handle->cb.Reset(exitCallback);
  handle->async.data = handle;

  // Setup OnProcessExit callback
  uv_async_init(uv_default_loop(), &handle->async, OnProcessExit);

  // Setup Windows wait for process exit event
  RegisterWaitForSingleObject(&handle->hWait, piClient.hProcess, OnProcessExitWinEvent, (PVOID)handle, INFINITE, WT_EXECUTEONLYONCE);

  // Return
  v8::Local<v8::Object> marshal = Nan::New<v8::Object>();
  Nan::Set(marshal, Nan::New<v8::String>("pid").ToLocalChecked(), Nan::New<v8::Number>(piClient.dwProcessId));
  info.GetReturnValue().Set(marshal);
}