PowerShellEditorServices icon indicating copy to clipboard operation
PowerShellEditorServices copied to clipboard

Integrating PSES into Visual Studio

Open Xrumet opened this issue 4 years ago • 20 comments

Hi, Not sure is an issue or I doing something wrong... I am trying to kickoff POC project to integrate PSES into Visual Studio. Before starting to work on extension itself I would like to check how to deal with PSES in a simple console application (with some hardcoded settings).

First step is to start Server - is done. I guess it is done, at least I see running process, log.txt, StartEditorServices.log and sessionDetails.json files with proper data inside.

Second step is to create and initialize Client - and here is an issue... I have find out that PSES itself using OmniShart under the hood to startup Server so I decided to go same way by using "OmniSharp.Extensions.LanguageServer.Client" and "var PsesLanguageClient = LanguageClient.Create(options => ..." to get an instance of Client. I have stuck on "await PsesLanguageClient.Initialize(new CancellationToken()).ConfigureAwait(false);". Playing with that in a different ways - I guess the issue is, request is sent but no response because of "Access deny" issue in internal "System.IO" implementation for named pipes.

Environment details:

  • OS Windows 10 Enterprise, Version 21H1, Build 19043.1165
  • Net Framework runtime 4.7.1
  • PowerShell Version 5.1.19041.1151
  • LSP Transport is duplex named pipe

startup.ps1 file looks like that:

$PSES_BUNDLE_PATH = "D:\PowerShellEditorServices"
$SESSION_TEMP_PATH  = "D:\pslog"

. $PSES_BUNDLE_PATH/PowerShellEditorServices/Start-EditorServices.ps1 `
	-BundledModulesPath $PSES_BUNDLE_PATH `
	-LogPath "$SESSION_TEMP_PATH/logs.log" `
	-SessionDetailsPath "$SESSION_TEMP_PATH/session.json" `
	-FeatureFlags @() `
	-AdditionalModules @() `
	-HostName 'My Client' `
	-HostProfileId 'myclient' `
	-HostVersion 1.0.0 `
	-LogLevel Verbose

Maybe you have or may provide simple sample? I have found some based on NodeJs but nothing for C#. And maybe my main question is - do you officially support to work on Windows with combination of PS v5 and Net 4.7.1?

Please let me know if I may provide any additional information, and thanks a lot for any help...

Xrumet avatar Dec 08 '21 14:12 Xrumet

Playing with that in a different ways - I guess the issue is, request is sent but no response because of "Access deny" issue in internal "System.IO" implementation for named pipes.

Hmm could you elaborate on this? A full repro example would also be very helpful

do you officially support to work on Windows with combination of PS v5 and Net 4.7.1?

More specifically PS v5.1 but yeah definitely

SeeminglyScience avatar Dec 08 '21 14:12 SeeminglyScience

Thanks for so quick response, Here is solution file - https://github.com/Xrumet/PowerShellTools/tree/main/Draft Startup.ps1 - https://github.com/Xrumet/PowerShellTools/blob/main/Draft/ConsoleApp2/startup.ps1 Program.cs ->Main - https://github.com/Xrumet/PowerShellTools/blob/main/Draft/ConsoleApp2/Program.cs

It is dirty a bit, but still the issue is on line # 195 of program.cs I guess

Xrumet avatar Dec 08 '21 14:12 Xrumet

Hmm could you elaborate on this?

That's what I see if pause application: image

And also when I switched pipe from duplex mode to separate in and out pipes, I got an exception about permission deny when try to connect to output pipe - "//await outPipeClient.ConnectAsync(4000);"

Xrumet avatar Dec 08 '21 15:12 Xrumet

I only guessing, maybe I do something wrong there by passing -HostProfileId 'myclient' ?...

Xrumet avatar Dec 08 '21 15:12 Xrumet

I think this line here might be the problem:

https://github.com/Xrumet/PowerShellTools/blob/e577ed9ee77ef3b4e5d53acabf6d034416bb6fff/Draft/ConsoleApp2/Program.cs#L240

Starting the language server in an elevated process is going to make is so non-elevated processes are unable to access the named pipe it creates.

SeeminglyScience avatar Dec 08 '21 15:12 SeeminglyScience

So, should I try to run Start-EditorServices.ps1 directly? And I guess still with "startInfo.Verb = "runAs";" ?

Xrumet avatar Dec 08 '21 15:12 Xrumet

I'd just comment out that line and give it ago however you've been doin' it.

The verb RunAs attempts to elevate the process, probably triggering a UAC prompt. By default for security reasons, the named pipe created by an elevated process cannot be connected to by one that isn't. That's why you get an access denied error, so you should be able to ditch setting the verb and at the very least get further than before.

SeeminglyScience avatar Dec 08 '21 15:12 SeeminglyScience

Noup, didn't help, Will try to switch back to separate in\out pipes to see exact error and share it.

Xrumet avatar Dec 08 '21 15:12 Xrumet

Yeap, that's it: image

May it be because of:

 - Profile paths:
   + AllUsersAllHosts:       C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1
   + AllUsersCurrentHost:    C:\Windows\System32\WindowsPowerShell\v1.0\myclient_profile.ps1
   + CurrentUserAllHosts:    C:\Users\xrumet\Documents\WindowsPowerShell\profile.ps1
   + CurrentUserCurrentHost: C:\Users\xrumet\Documents\WindowsPowerShell\myclient_profile.ps1

I guess System32 folder is protected too much :) I do not have any experience working with pipes so have no even idea where to looking to solve this issue...

Xrumet avatar Dec 08 '21 15:12 Xrumet

From other hand input pipe connected successfully... CanRead and CanWrite properties have correct values, Maybe the issue that "ConnectAsync" is used but "IsAsync = false"?

Have pushed all changes.

Xrumet avatar Dec 08 '21 15:12 Xrumet

Added System.IO.Pipes.PipeOptions.Asynchronous to make it async - didn't help :(

Xrumet avatar Dec 08 '21 15:12 Xrumet

afaik there's only one pipe, not one for read and one for write.

an example session details file looks like this:

{
	"status":"started",
	"languageServiceTransport":"NamedPipe",
	"languageServicePipeName":"\\\\.\\pipe\\PSES_2wbpjblh.pnn",
	"debugServiceTransport":"NamedPipe",
	"debugServicePipeName":"\\\\.\\pipe\\PSES_ndg4d4tp.2pr"
}

So it's unclear what exactly you're getting back in sessionInfoObj.

SeeminglyScience avatar Dec 08 '21 15:12 SeeminglyScience

I have added this line: -SplitInOutPipes to startup.ps1, so session looks like that for me:

{
	"status": "started",
	"languageServiceTransport": "NamedPipeSimplex",
	"languageServiceReadPipeName": "\\\\.\\pipe\\in_PSES_xqriwnay.d1o",
	"languageServiceWritePipeName": "\\\\.\\pipe\\out_PSES_xqriwnay.d1o",
	"debugServiceTransport": "NamedPipeSimplex",
	"debugServiceReadPipeName": "\\\\.\\pipe\\in_PSES_w2ufkaig.dfj",
	"debugServiceWritePipeName": "\\\\.\\pipe\\out_PSES_w2ufkaig.dfj"
}

and in program.cs reading it from sessionInfoObj.languageServiceReadPipeName and languageServiceWritePipeName properties, in runtime/debug values looks pretty correct...

Xrumet avatar Dec 08 '21 15:12 Xrumet

Wonder if Visual Studio debugging process have limited permissions so tried to run app manually with admin console: image

no luck yet :(

Xrumet avatar Dec 08 '21 15:12 Xrumet

Oh okay I stand corrected, we support that apparently. I'd probably try to get it working without splitting them first as that's the most tested path.

You're getting the same error message while not splitting right? When you get the exception can you verify that the serverName and pipeName are correct?

SeeminglyScience avatar Dec 08 '21 15:12 SeeminglyScience

You're getting the same error message while not splitting right?

Noup, it is "successfully" connected and silently hangs when try to call client.initialize

When you get the exception can you verify that the serverName and pipeName are correct?

I see next values:

?sessionInfoObj.languageServiceWritePipeName
      "\\\\.\\pipe\\out_PSES_fjriplcj.3xt"      (this is raw value from session)
?outserverName
      "."          (this is what I have extracted and try to pass as input parameter into NamedPipeClientStream)
?outpipeName
      "out_PSES_fjriplcj.3xt"

Not sure, but guess that's what should be there

Xrumet avatar Dec 08 '21 16:12 Xrumet

Just in case, I have looked around a bit (a lot :) ), and found some suggestions like that: https://stackoverflow.com/questions/3478166/named-pipe-server-throws-unauthorizedaccessexception-when-creating-a-second-inst

Unfortunately for me most of them to be applied on server-side...

Xrumet avatar Dec 08 '21 16:12 Xrumet

@SeeminglyScience, one more Thanks for your help and attention, at least not I am not alone there now.

Xrumet avatar Dec 08 '21 16:12 Xrumet

Noup, it is "successfully" connected and silently hangs when try to call client.initialize

Ah okay that's very different and more likely to be the exact thing we need to troubleshoot. That's also a little harder to direct you into places to check, so I'll need to build it myself and check some things a bit later. Can you push the latest changes?

Just in case, I have looked around a bit (a lot :) ), and found some suggestions like that: stackoverflow.com/questions/3478166/named-pipe-server-throws-unauthorizedaccessexception-when-creating-a-second-inst

Unfortunately for me most of them to be applied on server-side...

I don't think this is related FYI

SeeminglyScience avatar Dec 08 '21 16:12 SeeminglyScience

Yes, I have pushed all I have.

Xrumet avatar Dec 08 '21 16:12 Xrumet

Finally I tried to compile PSES binaries locally and somehow it works now! So, this issue may be closed.

Xrumet avatar Aug 17 '22 04:08 Xrumet

That may be thanks to the awesome work @andschwa has done getting PSES to work in different clients ❤️

SeeminglyScience avatar Aug 17 '22 15:08 SeeminglyScience