ConsoleCommStruct.cs 8.8 KB

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