ConsoleCommStruct.cs 8.4 KB

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