6
6
using System ;
7
7
using System . Collections . Generic ;
8
8
using System . Diagnostics ;
9
+ using System . IO . Pipes ;
9
10
using System . Linq ;
10
11
using System . Management . Automation ;
11
12
using System . Management . Automation . Host ;
12
13
using System . Reflection ;
13
14
using System . Runtime . InteropServices ;
15
+ using System . Security . AccessControl ;
16
+ using System . Security . Principal ;
14
17
using System . Threading ;
15
18
using System . Threading . Tasks ;
16
19
using Microsoft . Extensions . Logging ;
@@ -58,6 +61,15 @@ public class EditorServicesHost
58
61
{
59
62
#region Private Fields
60
63
64
+ // This int will be casted to a PipeOptions enum that only exists in .NET Core 2.1 and up which is why it's not available to us in .NET Standard.
65
+ private const int CurrentUserOnly = 0x20000000 ;
66
+
67
+ // In .NET Framework, NamedPipeServerStream has a constructor that takes in a PipeSecurity object. We will use reflection to call the constructor,
68
+ // since .NET Framework doesn't have the `CurrentUserOnly` PipeOption.
69
+ // doc: https://docs.microsoft.com/en-us/dotnet/api/system.io.pipes.namedpipeserverstream.-ctor?view=netframework-4.7.2#System_IO_Pipes_NamedPipeServerStream__ctor_System_String_System_IO_Pipes_PipeDirection_System_Int32_System_IO_Pipes_PipeTransmissionMode_System_IO_Pipes_PipeOptions_System_Int32_System_Int32_System_IO_Pipes_PipeSecurity_
70
+ private static readonly ConstructorInfo s_netFrameworkPipeServerConstructor =
71
+ typeof ( NamedPipeServerStream ) . GetConstructor ( new [ ] { typeof ( string ) , typeof ( PipeDirection ) , typeof ( int ) , typeof ( PipeTransmissionMode ) , typeof ( PipeOptions ) , typeof ( int ) , typeof ( int ) , typeof ( PipeSecurity ) } ) ;
72
+
61
73
private readonly HostDetails _hostDetails ;
62
74
63
75
private readonly PSHost _internalHost ;
@@ -69,6 +81,7 @@ public class EditorServicesHost
69
81
private readonly string [ ] _additionalModules ;
70
82
71
83
private PsesLanguageServer _languageServer ;
84
+ private PsesDebugServer _debugServer ;
72
85
73
86
private Microsoft . Extensions . Logging . ILogger _logger ;
74
87
@@ -229,8 +242,6 @@ public void StartLanguageService(
229
242
230
243
_logger . LogInformation ( $ "LSP NamedPipe: { config . InOutPipeName } \n LSP OutPipe: { config . OutPipeName } ") ;
231
244
232
-
233
-
234
245
switch ( config . TransportType )
235
246
{
236
247
case EditorServiceTransportType . NamedPipe :
@@ -280,17 +291,42 @@ public void StartDebugService(
280
291
ProfilePaths profilePaths ,
281
292
bool useExistingSession )
282
293
{
283
- /*
284
- this.debugServiceListener = CreateServiceListener(MessageProtocolType.DebugAdapter, config);
285
- this.debugServiceListener.ClientConnect += OnDebugServiceClientConnect;
286
- this.debugServiceListener.Start();
294
+ while ( ! System . Diagnostics . Debugger . IsAttached )
295
+ {
296
+ System . Console . WriteLine ( $ "{ Process . GetCurrentProcess ( ) . Id } ") ;
297
+ Thread . Sleep ( 2000 ) ;
298
+ }
287
299
288
- this.logger.Write(
289
- LogLevel.Normal,
290
- string.Format(
291
- "Debug service started, type = {0}, endpoint = {1}",
292
- config.TransportType, config.Endpoint));
293
- */
300
+ _logger . LogInformation ( $ "Debug NamedPipe: { config . InOutPipeName } \n Debug OutPipe: { config . OutPipeName } ") ;
301
+
302
+ switch ( config . TransportType )
303
+ {
304
+ case EditorServiceTransportType . NamedPipe :
305
+ NamedPipeServerStream inNamedPipe = CreateNamedPipe (
306
+ config . InOutPipeName ?? config . InPipeName ,
307
+ config . OutPipeName ,
308
+ out NamedPipeServerStream outNamedPipe ) ;
309
+
310
+ _debugServer = new PsesDebugServer (
311
+ _factory ,
312
+ inNamedPipe ,
313
+ outNamedPipe ?? inNamedPipe ) ;
314
+
315
+ Task [ ] tasks = outNamedPipe != null
316
+ ? new [ ] { inNamedPipe . WaitForConnectionAsync ( ) , outNamedPipe . WaitForConnectionAsync ( ) }
317
+ : new [ ] { inNamedPipe . WaitForConnectionAsync ( ) } ;
318
+ Task . WhenAll ( tasks )
319
+ . ContinueWith ( async task => {
320
+ _logger . LogInformation ( "Starting debug server" ) ;
321
+ await _debugServer . StartAsync ( ) ;
322
+ _logger . LogInformation (
323
+ $ "Debug service started, type = { config . TransportType } , endpoint = { config . Endpoint } ") ;
324
+ } ) ;
325
+ break ;
326
+
327
+ default :
328
+ throw new NotSupportedException ( "not supported" ) ;
329
+ }
294
330
}
295
331
296
332
/// <summary>
@@ -350,6 +386,81 @@ private void CurrentDomain_UnhandledException(
350
386
_logger . LogError ( $ "FATAL UNHANDLED EXCEPTION: { e . ExceptionObject } ") ;
351
387
}
352
388
389
+ private static NamedPipeServerStream CreateNamedPipe (
390
+ string inOutPipeName ,
391
+ string outPipeName ,
392
+ out NamedPipeServerStream outPipe )
393
+ {
394
+ // .NET Core implementation is simplest so try that first
395
+ if ( VersionUtils . IsNetCore )
396
+ {
397
+ outPipe = outPipeName == null
398
+ ? null
399
+ : new NamedPipeServerStream (
400
+ pipeName : outPipeName ,
401
+ direction : PipeDirection . Out ,
402
+ maxNumberOfServerInstances : 1 ,
403
+ transmissionMode : PipeTransmissionMode . Byte ,
404
+ options : ( PipeOptions ) CurrentUserOnly ) ;
405
+
406
+ return new NamedPipeServerStream (
407
+ pipeName : inOutPipeName ,
408
+ direction : PipeDirection . InOut ,
409
+ maxNumberOfServerInstances : 1 ,
410
+ transmissionMode : PipeTransmissionMode . Byte ,
411
+ options : PipeOptions . Asynchronous | ( PipeOptions ) CurrentUserOnly ) ;
412
+ }
413
+
414
+ // Now deal with Windows PowerShell
415
+ // We need to use reflection to get a nice constructor
416
+
417
+ var pipeSecurity = new PipeSecurity ( ) ;
418
+
419
+ WindowsIdentity identity = WindowsIdentity . GetCurrent ( ) ;
420
+ WindowsPrincipal principal = new WindowsPrincipal ( identity ) ;
421
+
422
+ if ( principal . IsInRole ( WindowsBuiltInRole . Administrator ) )
423
+ {
424
+ // Allow the Administrators group full access to the pipe.
425
+ pipeSecurity . AddAccessRule ( new PipeAccessRule (
426
+ new SecurityIdentifier ( WellKnownSidType . BuiltinAdministratorsSid , null ) . Translate ( typeof ( NTAccount ) ) ,
427
+ PipeAccessRights . FullControl , AccessControlType . Allow ) ) ;
428
+ }
429
+ else
430
+ {
431
+ // Allow the current user read/write access to the pipe.
432
+ pipeSecurity . AddAccessRule ( new PipeAccessRule (
433
+ WindowsIdentity . GetCurrent ( ) . User ,
434
+ PipeAccessRights . ReadWrite , AccessControlType . Allow ) ) ;
435
+ }
436
+
437
+ outPipe = outPipeName == null
438
+ ? null
439
+ : ( NamedPipeServerStream ) s_netFrameworkPipeServerConstructor . Invoke (
440
+ new object [ ] {
441
+ outPipeName ,
442
+ PipeDirection . InOut ,
443
+ 1 , // maxNumberOfServerInstances
444
+ PipeTransmissionMode . Byte ,
445
+ PipeOptions . Asynchronous ,
446
+ 1024 , // inBufferSize
447
+ 1024 , // outBufferSize
448
+ pipeSecurity
449
+ } ) ;
450
+
451
+ return ( NamedPipeServerStream ) s_netFrameworkPipeServerConstructor . Invoke (
452
+ new object [ ] {
453
+ inOutPipeName ,
454
+ PipeDirection . InOut ,
455
+ 1 , // maxNumberOfServerInstances
456
+ PipeTransmissionMode . Byte ,
457
+ PipeOptions . Asynchronous ,
458
+ 1024 , // inBufferSize
459
+ 1024 , // outBufferSize
460
+ pipeSecurity
461
+ } ) ;
462
+ }
463
+
353
464
#endregion
354
465
}
355
466
}
0 commit comments