ConsoleCommStruct.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. using System.Runtime.InteropServices;
  2. using System;
  3. using Microsoft.Win32.SafeHandles;
  4. namespace WinSCP
  5. {
  6. public enum ConsoleEvent { None, Print, Input, Choice, Title, Init, Progress }
  7. [StructLayout(LayoutKind.Sequential)]
  8. internal class ConsoleInitEventStruct
  9. {
  10. public uint InputType;
  11. public uint OutputType;
  12. public bool WantsProgress; // since version 6
  13. public bool UseStdErr; // since version 10
  14. public bool BinaryOutput; // since version 10
  15. public bool BinaryInput; // since version 10
  16. }
  17. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  18. internal class ConsolePrintEventStruct
  19. {
  20. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10240)]
  21. public string Message; // wide since version 4
  22. [MarshalAs(UnmanagedType.I1)]
  23. public bool FromBeginning;
  24. [MarshalAs(UnmanagedType.I1)]
  25. public bool Error; // since version 7
  26. }
  27. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  28. internal class ConsoleInputEventStruct
  29. {
  30. [MarshalAs(UnmanagedType.I1)]
  31. public bool Echo;
  32. [MarshalAs(UnmanagedType.I1)]
  33. public bool Result;
  34. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10240)]
  35. public string Str; // wide since version 4
  36. public uint Timer; // since version 2
  37. }
  38. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  39. internal class ConsoleChoiceEventStruct
  40. {
  41. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
  42. public string Options; // wide since version 4
  43. public int Cancel;
  44. public int Break;
  45. public int Result;
  46. public int Timeouted; // since version 2
  47. public uint Timer; // since version 2
  48. [MarshalAs(UnmanagedType.I1)]
  49. public bool Timeouting; // since version 4
  50. public int Continue; // since version 9
  51. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5120)]
  52. public string Message; // since version 9
  53. }
  54. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  55. internal class ConsoleTitleEventStruct
  56. {
  57. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10240)]
  58. public string Title; // wide since version 4
  59. }
  60. // Since version 6
  61. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  62. internal class ConsoleProgressEventStruct
  63. {
  64. public enum ProgressOperation { Copy }
  65. public enum ProgressSide { Local, Remote }
  66. public ProgressOperation Operation;
  67. public ProgressSide Side;
  68. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
  69. public string FileName;
  70. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
  71. public string Directory;
  72. public uint OverallProgress;
  73. public uint FileProgress;
  74. public uint CPS;
  75. public bool Cancel; // since version 8
  76. }
  77. [StructLayout(LayoutKind.Sequential)]
  78. internal class ConsoleCommHeader
  79. {
  80. public uint Size;
  81. public int Version;
  82. public ConsoleEvent Event;
  83. }
  84. internal class ConsoleCommStruct : IDisposable
  85. {
  86. public const int CurrentVersion = 0x000A;
  87. public ConsoleCommStruct(Session session, SafeFileHandle fileMapping)
  88. {
  89. _session = session;
  90. _fileMapping = fileMapping;
  91. _session.Logger.WriteLineLevel(1, "Acquiring communication structure");
  92. _ptr = UnsafeNativeMethods.MapViewOfFile(_fileMapping, FileMapAccess.FileMapAllAccess, 0, 0, UIntPtr.Zero);
  93. _session.Logger.WriteLineLevel(1, "Acquired communication structure");
  94. _payloadPtr = new IntPtr(_ptr.ToInt64() + 12);
  95. _header = (ConsoleCommHeader)Marshal.PtrToStructure(_ptr, typeof(ConsoleCommHeader));
  96. }
  97. ~ConsoleCommStruct()
  98. {
  99. Dispose();
  100. }
  101. public void Dispose()
  102. {
  103. if (_ptr != IntPtr.Zero)
  104. {
  105. if (_headerInvalidated)
  106. {
  107. Marshal.StructureToPtr(_header, _ptr, false);
  108. }
  109. if (_payload != null)
  110. {
  111. if ((Event != ConsoleEvent.Print) && (Event != ConsoleEvent.Title))
  112. {
  113. Marshal.StructureToPtr(_payload, _payloadPtr, false);
  114. }
  115. }
  116. _session.Logger.WriteLineLevel(1, "Releasing communication structure");
  117. if (!UnsafeNativeMethods.UnmapViewOfFile(_ptr))
  118. {
  119. throw _session.Logger.WriteException(new SessionLocalException(_session, "Cannot release file mapping"));
  120. }
  121. _session.Logger.WriteLineLevel(1, "Released communication structure");
  122. _ptr = IntPtr.Zero;
  123. }
  124. GC.SuppressFinalize(this);
  125. }
  126. public ConsoleEvent Event
  127. {
  128. get
  129. {
  130. return _header.Event;
  131. }
  132. }
  133. public ConsolePrintEventStruct PrintEvent { get { return UnmarshalPayload<ConsolePrintEventStruct>(ConsoleEvent.Print); } }
  134. public ConsoleInitEventStruct InitEvent { get { return UnmarshalPayload<ConsoleInitEventStruct>(ConsoleEvent.Init); } }
  135. public ConsoleInputEventStruct InputEvent { get { return UnmarshalPayload<ConsoleInputEventStruct>(ConsoleEvent.Input); } }
  136. public ConsoleChoiceEventStruct ChoiceEvent { get { return UnmarshalPayload<ConsoleChoiceEventStruct>(ConsoleEvent.Choice); } }
  137. public ConsoleTitleEventStruct TitleEvent { get { return UnmarshalPayload<ConsoleTitleEventStruct>(ConsoleEvent.Title); } }
  138. public ConsoleProgressEventStruct ProgressEvent { get { return UnmarshalPayload<ConsoleProgressEventStruct>(ConsoleEvent.Progress); } }
  139. private T UnmarshalPayload<T>(ConsoleEvent e)
  140. {
  141. CheckNotDisposed();
  142. if (e != Event)
  143. {
  144. throw _session.Logger.WriteException(new InvalidOperationException("Payload type does not match with event"));
  145. }
  146. if (_payload == null)
  147. {
  148. _payload = Marshal.PtrToStructure(_payloadPtr, typeof(T));
  149. }
  150. return (T)_payload;
  151. }
  152. private void CheckNotDisposed()
  153. {
  154. if (_ptr == IntPtr.Zero)
  155. {
  156. throw _session.Logger.WriteException(new InvalidOperationException("Object is disposed"));
  157. }
  158. }
  159. public static int Size
  160. {
  161. get
  162. {
  163. Type[] types =
  164. new[] {
  165. typeof(ConsolePrintEventStruct), typeof(ConsoleInitEventStruct), typeof(ConsoleInputEventStruct),
  166. typeof(ConsoleChoiceEventStruct), typeof(ConsoleTitleEventStruct), typeof(ConsoleProgressEventStruct) };
  167. int maxSize = 0;
  168. foreach (Type type in types)
  169. {
  170. maxSize = Math.Max(maxSize, Marshal.SizeOf(type));
  171. }
  172. return (Marshal.SizeOf(typeof(ConsoleCommHeader)) + maxSize);
  173. }
  174. }
  175. public void InitHeader()
  176. {
  177. _headerInvalidated = true;
  178. _header.Size = (uint) Size;
  179. _header.Version = CurrentVersion;
  180. _header.Event = ConsoleEvent.None;
  181. }
  182. private IntPtr _ptr;
  183. private readonly ConsoleCommHeader _header;
  184. private bool _headerInvalidated;
  185. private readonly IntPtr _payloadPtr;
  186. private object _payload;
  187. private readonly SafeFileHandle _fileMapping;
  188. private readonly Session _session;
  189. }
  190. }