ActiveXMessageFormatter.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. //------------------------------------------------------------------------------
  2. // <copyright file="ActiveXMessageFormatter.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. //------------------------------------------------------------------------------
  6. using Experimental.System.Messaging.Interop;
  7. using System;
  8. using System.IO;
  9. using System.Runtime.InteropServices;
  10. using System.Text;
  11. namespace Experimental.System.Messaging
  12. {
  13. /// <include file='doc\ActiveXMessageFormatter.uex' path='docs/doc[@for="ActiveXMessageFormatter"]/*' />
  14. /// <devdoc>
  15. /// <para>
  16. /// Formatter class that serializes and deserializes
  17. /// primitives, classes, enumeration, and other objects into and from <see cref='System.Messaging.MessageQueue'/>
  18. /// messages using binary format.
  19. /// </para>
  20. /// </devdoc>
  21. public class ActiveXMessageFormatter : IMessageFormatter
  22. {
  23. internal const short VT_ARRAY = 0x2000;
  24. internal const short VT_BOOL = 11;
  25. internal const short VT_BSTR = 8;
  26. internal const short VT_CLSID = 72;
  27. internal const short VT_CY = 6;
  28. internal const short VT_DATE = 7;
  29. internal const short VT_I1 = 16;
  30. internal const short VT_I2 = 2;
  31. internal const short VT_I4 = 3;
  32. internal const short VT_I8 = 20;
  33. internal const short VT_LPSTR = 30;
  34. internal const short VT_LPWSTR = 31;
  35. internal const short VT_NULL = 1;
  36. internal const short VT_R4 = 4;
  37. internal const short VT_R8 = 5;
  38. internal const short VT_STREAMED_OBJECT = 68;
  39. internal const short VT_STORED_OBJECT = 69;
  40. internal const short VT_UI1 = 17;
  41. internal const short VT_UI2 = 18;
  42. internal const short VT_UI4 = 19;
  43. internal const short VT_UI8 = 21;
  44. internal const short VT_VECTOR = 0x1000;
  45. private byte[] internalBuffer;
  46. private UnicodeEncoding unicodeEncoding;
  47. private ASCIIEncoding asciiEncoding;
  48. private char[] internalCharBuffer;
  49. /// <include file='doc\ActiveXMessageFormatter.uex' path='docs/doc[@for="ActiveXMessageFormatter.CanRead"]/*' />
  50. /// <devdoc>
  51. /// When this method is called, the formatter will attempt to determine
  52. /// if the contents of the message are something the formatter can deal with.
  53. /// </devdoc>
  54. public bool CanRead(Message message)
  55. {
  56. if (message == null)
  57. throw new ArgumentNullException("message");
  58. int variantType = message.BodyType;
  59. if (variantType != VT_BOOL && variantType != VT_CLSID &&
  60. variantType != VT_CY && variantType != VT_DATE &&
  61. variantType != VT_I1 && variantType != VT_UI1 &&
  62. variantType != VT_I2 && variantType != VT_UI2 &&
  63. variantType != VT_I4 && variantType != VT_UI4 &&
  64. variantType != VT_I8 && variantType != VT_UI8 &&
  65. variantType != VT_NULL && variantType != VT_R4 &&
  66. variantType != VT_I8 && variantType != VT_STREAMED_OBJECT &&
  67. variantType != VT_STORED_OBJECT &&
  68. variantType != (VT_VECTOR | VT_UI1) &&
  69. variantType != VT_LPSTR && variantType != VT_LPWSTR &&
  70. variantType != VT_BSTR && variantType != VT_R8)
  71. return false;
  72. return true;
  73. }
  74. /// <include file='doc\ActiveXMessageFormatter.uex' path='docs/doc[@for="ActiveXMessageFormatter.Clone"]/*' />
  75. /// <devdoc>
  76. /// This method is needed to improve scalability on Receive and ReceiveAsync scenarios. Not requiring
  77. /// thread safety on read and write.
  78. /// </devdoc>
  79. public object Clone()
  80. {
  81. return new ActiveXMessageFormatter();
  82. }
  83. /// <include file='doc\ActiveXMessageFormatter.uex' path='docs/doc[@for="ActiveXMessageFormatter.InitStreamedObject"]/*' />
  84. /// <devdoc>
  85. /// <para>[To be supplied.]</para>
  86. /// </devdoc>
  87. public static void InitStreamedObject(object streamedObject)
  88. {
  89. IPersistStreamInit persistStreamInit = streamedObject as IPersistStreamInit;
  90. if (persistStreamInit != null)
  91. persistStreamInit.InitNew();
  92. }
  93. /// <include file='doc\ActiveXMessageFormatter.uex' path='docs/doc[@for="ActiveXMessageFormatter.Read"]/*' />
  94. /// <devdoc>
  95. /// This method is used to read the contents from the given message
  96. /// and create an object.
  97. /// </devdoc>
  98. public object Read(Message message)
  99. {
  100. if (message == null)
  101. throw new ArgumentNullException("message");
  102. Stream stream;
  103. byte[] bytes;
  104. byte[] newBytes;
  105. int size;
  106. int variantType = message.BodyType;
  107. switch (variantType)
  108. {
  109. case VT_LPSTR:
  110. bytes = message.properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY);
  111. size = message.properties.GetUI4(NativeMethods.MESSAGE_PROPID_BODY_SIZE);
  112. if (this.internalCharBuffer == null || this.internalCharBuffer.Length < size)
  113. this.internalCharBuffer = new char[size];
  114. if (asciiEncoding == null)
  115. this.asciiEncoding = new ASCIIEncoding();
  116. this.asciiEncoding.GetChars(bytes, 0, size, this.internalCharBuffer, 0);
  117. return new String(this.internalCharBuffer, 0, size);
  118. case VT_BSTR:
  119. case VT_LPWSTR:
  120. bytes = message.properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY);
  121. size = message.properties.GetUI4(NativeMethods.MESSAGE_PROPID_BODY_SIZE) / 2;
  122. if (this.internalCharBuffer == null || this.internalCharBuffer.Length < size)
  123. this.internalCharBuffer = new char[size];
  124. if (unicodeEncoding == null)
  125. this.unicodeEncoding = new UnicodeEncoding();
  126. this.unicodeEncoding.GetChars(bytes, 0, size * 2, this.internalCharBuffer, 0);
  127. return new String(this.internalCharBuffer, 0, size);
  128. case VT_VECTOR | VT_UI1:
  129. bytes = message.properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY);
  130. size = message.properties.GetUI4(NativeMethods.MESSAGE_PROPID_BODY_SIZE);
  131. newBytes = new byte[size];
  132. Array.Copy(bytes, newBytes, size);
  133. return newBytes;
  134. case VT_BOOL:
  135. bytes = message.properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY);
  136. newBytes = new byte[1];
  137. Array.Copy(bytes, newBytes, 1);
  138. if (bytes[0] != 0)
  139. return true;
  140. return false;
  141. case VT_CLSID:
  142. bytes = message.properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY);
  143. newBytes = new byte[16];
  144. Array.Copy(bytes, newBytes, 16);
  145. return new Guid(newBytes);
  146. case VT_CY:
  147. bytes = message.properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY);
  148. newBytes = new byte[8];
  149. Array.Copy(bytes, newBytes, 8);
  150. return Decimal.FromOACurrency(BitConverter.ToInt64(newBytes, 0));
  151. case VT_DATE:
  152. bytes = message.properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY);
  153. newBytes = new byte[8];
  154. Array.Copy(bytes, newBytes, 8);
  155. return new DateTime(BitConverter.ToInt64(newBytes, 0));
  156. case VT_I1:
  157. case VT_UI1:
  158. stream = message.BodyStream;
  159. bytes = new byte[1];
  160. stream.Read(bytes, 0, 1);
  161. return bytes[0];
  162. case VT_I2:
  163. stream = message.BodyStream;
  164. bytes = new byte[2];
  165. stream.Read(bytes, 0, 2);
  166. return BitConverter.ToInt16(bytes, 0);
  167. case VT_UI2:
  168. stream = message.BodyStream;
  169. bytes = new byte[2];
  170. stream.Read(bytes, 0, 2);
  171. return BitConverter.ToUInt16(bytes, 0);
  172. case VT_I4:
  173. stream = message.BodyStream;
  174. bytes = new byte[4];
  175. stream.Read(bytes, 0, 4);
  176. return BitConverter.ToInt32(bytes, 0);
  177. case VT_UI4:
  178. stream = message.BodyStream;
  179. bytes = new byte[4];
  180. stream.Read(bytes, 0, 4);
  181. return BitConverter.ToUInt32(bytes, 0);
  182. case VT_I8:
  183. stream = message.BodyStream;
  184. bytes = new byte[8];
  185. stream.Read(bytes, 0, 8);
  186. return BitConverter.ToInt64(bytes, 0);
  187. case VT_UI8:
  188. stream = message.BodyStream;
  189. bytes = new byte[8];
  190. stream.Read(bytes, 0, 8);
  191. return BitConverter.ToUInt64(bytes, 0);
  192. case VT_R4:
  193. stream = message.BodyStream;
  194. bytes = new byte[4];
  195. stream.Read(bytes, 0, 4);
  196. return BitConverter.ToSingle(bytes, 0);
  197. case VT_R8:
  198. stream = message.BodyStream;
  199. bytes = new byte[8];
  200. stream.Read(bytes, 0, 8);
  201. return BitConverter.ToDouble(bytes, 0);
  202. case VT_NULL:
  203. return null;
  204. case VT_STREAMED_OBJECT:
  205. stream = message.BodyStream;
  206. ComStreamFromDataStream comStream = new ComStreamFromDataStream(stream);
  207. return NativeMethods.OleLoadFromStream(comStream, ref NativeMethods.IID_IUnknown);
  208. case VT_STORED_OBJECT:
  209. throw new NotSupportedException(Res.GetString(Res.StoredObjectsNotSupported));
  210. default:
  211. throw new InvalidOperationException(Res.GetString(Res.InvalidTypeDeserialization));
  212. }
  213. }
  214. /// <include file='doc\ActiveXMessageFormatter.uex' path='docs/doc[@for="ActiveXMessageFormatter.Write"]/*' />
  215. /// <devdoc>
  216. /// This method is used to write the given object into the given message.
  217. /// If the formatter cannot understand the given object, an exception is thrown.
  218. /// </devdoc>
  219. public void Write(Message message, object obj)
  220. {
  221. if (message == null)
  222. throw new ArgumentNullException("message");
  223. Stream stream;
  224. int variantType;
  225. if (obj is string)
  226. {
  227. int size = ((string)obj).Length * 2;
  228. if (this.internalBuffer == null || this.internalBuffer.Length < size)
  229. this.internalBuffer = new byte[size];
  230. if (unicodeEncoding == null)
  231. this.unicodeEncoding = new UnicodeEncoding();
  232. this.unicodeEncoding.GetBytes(((string)obj).ToCharArray(), 0, size / 2, this.internalBuffer, 0);
  233. message.properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY, this.internalBuffer);
  234. message.properties.AdjustSize(NativeMethods.MESSAGE_PROPID_BODY, size);
  235. message.properties.SetUI4(NativeMethods.MESSAGE_PROPID_BODY_SIZE, size);
  236. message.properties.SetUI4(NativeMethods.MESSAGE_PROPID_BODY_TYPE, VT_LPWSTR);
  237. return;
  238. }
  239. else if (obj is byte[])
  240. {
  241. byte[] bytes = (byte[])obj;
  242. if (this.internalBuffer == null || this.internalBuffer.Length < bytes.Length)
  243. this.internalBuffer = new byte[bytes.Length];
  244. Array.Copy(bytes, this.internalBuffer, bytes.Length);
  245. message.properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY, this.internalBuffer);
  246. message.properties.AdjustSize(NativeMethods.MESSAGE_PROPID_BODY, bytes.Length);
  247. message.properties.SetUI4(NativeMethods.MESSAGE_PROPID_BODY_SIZE, bytes.Length);
  248. message.properties.SetUI4(NativeMethods.MESSAGE_PROPID_BODY_TYPE, VT_UI1 | VT_VECTOR);
  249. return;
  250. }
  251. else if (obj is char[])
  252. {
  253. char[] chars = (char[])obj;
  254. int size = chars.Length * 2;
  255. if (this.internalBuffer == null || this.internalBuffer.Length < size)
  256. this.internalBuffer = new byte[size];
  257. if (unicodeEncoding == null)
  258. this.unicodeEncoding = new UnicodeEncoding();
  259. this.unicodeEncoding.GetBytes(chars, 0, size / 2, this.internalBuffer, 0);
  260. message.properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY, this.internalBuffer);
  261. message.properties.SetUI4(NativeMethods.MESSAGE_PROPID_BODY_SIZE, size);
  262. message.properties.SetUI4(NativeMethods.MESSAGE_PROPID_BODY_TYPE, VT_LPWSTR);
  263. return;
  264. }
  265. else if (obj is byte)
  266. {
  267. stream = new MemoryStream(1);
  268. stream.Write(new byte[] { (byte)obj }, 0, 1);
  269. variantType = VT_UI1;
  270. }
  271. else if (obj is bool)
  272. {
  273. stream = new MemoryStream(1);
  274. if ((bool)obj)
  275. stream.Write(new byte[] { 0xff }, 0, 1);
  276. else
  277. stream.Write(new byte[] { 0x00 }, 0, 1);
  278. variantType = VT_BOOL;
  279. }
  280. else if (obj is char)
  281. {
  282. stream = new MemoryStream(2);
  283. byte[] bytes = BitConverter.GetBytes((Char)obj);
  284. stream.Write(bytes, 0, 2);
  285. variantType = VT_UI2;
  286. }
  287. else if (obj is Decimal)
  288. {
  289. stream = new MemoryStream(8);
  290. byte[] bytes = BitConverter.GetBytes(Decimal.ToOACurrency((Decimal)obj));
  291. stream.Write(bytes, 0, 8);
  292. variantType = VT_CY;
  293. }
  294. else if (obj is DateTime)
  295. {
  296. stream = new MemoryStream(8);
  297. byte[] bytes = BitConverter.GetBytes(((DateTime)obj).Ticks);
  298. stream.Write(bytes, 0, 8);
  299. variantType = VT_DATE;
  300. }
  301. else if (obj is Double)
  302. {
  303. stream = new MemoryStream(8);
  304. byte[] bytes = BitConverter.GetBytes((Double)obj);
  305. stream.Write(bytes, 0, 8);
  306. variantType = VT_R8;
  307. }
  308. else if (obj is Int16)
  309. {
  310. stream = new MemoryStream(2);
  311. byte[] bytes = BitConverter.GetBytes((short)obj);
  312. stream.Write(bytes, 0, 2);
  313. variantType = VT_I2;
  314. }
  315. else if (obj is UInt16)
  316. {
  317. stream = new MemoryStream(2);
  318. byte[] bytes = BitConverter.GetBytes((UInt16)obj);
  319. stream.Write(bytes, 0, 2);
  320. variantType = VT_UI2;
  321. }
  322. else if (obj is Int32)
  323. {
  324. stream = new MemoryStream(4);
  325. byte[] bytes = BitConverter.GetBytes((int)obj);
  326. stream.Write(bytes, 0, 4);
  327. variantType = VT_I4;
  328. }
  329. else if (obj is UInt32)
  330. {
  331. stream = new MemoryStream(4);
  332. byte[] bytes = BitConverter.GetBytes((UInt32)obj);
  333. stream.Write(bytes, 0, 4);
  334. variantType = VT_UI4;
  335. }
  336. else if (obj is Int64)
  337. {
  338. stream = new MemoryStream(8);
  339. byte[] bytes = BitConverter.GetBytes((Int64)obj);
  340. stream.Write(bytes, 0, 8);
  341. variantType = VT_I8;
  342. }
  343. else if (obj is UInt64)
  344. {
  345. stream = new MemoryStream(8);
  346. byte[] bytes = BitConverter.GetBytes((UInt64)obj);
  347. stream.Write(bytes, 0, 8);
  348. variantType = VT_UI8;
  349. }
  350. else if (obj is Single)
  351. {
  352. stream = new MemoryStream(4);
  353. byte[] bytes = BitConverter.GetBytes((float)obj);
  354. stream.Write(bytes, 0, 4);
  355. variantType = VT_R4;
  356. }
  357. else if (obj is IPersistStream)
  358. {
  359. IPersistStream pstream = (IPersistStream)obj;
  360. ComStreamFromDataStream comStream = new ComStreamFromDataStream(new MemoryStream());
  361. NativeMethods.OleSaveToStream(pstream, comStream);
  362. stream = comStream.GetDataStream();
  363. variantType = VT_STREAMED_OBJECT;
  364. }
  365. else if (obj == null)
  366. {
  367. stream = new MemoryStream();
  368. variantType = VT_NULL;
  369. }
  370. else
  371. {
  372. throw new InvalidOperationException(Res.GetString(Res.InvalidTypeSerialization));
  373. }
  374. message.BodyStream = stream;
  375. message.BodyType = variantType;
  376. }
  377. [ComVisible(false)]
  378. private class ComStreamFromDataStream : IStream
  379. {
  380. private Stream dataStream;
  381. // to support seeking ahead of the stream length...
  382. private long virtualPosition = -1;
  383. public ComStreamFromDataStream(Stream dataStream)
  384. {
  385. if (dataStream == null) throw new ArgumentNullException("dataStream");
  386. this.dataStream = dataStream;
  387. }
  388. private void ActualizeVirtualPosition()
  389. {
  390. if (virtualPosition == -1) return;
  391. if (virtualPosition > dataStream.Length)
  392. dataStream.SetLength(virtualPosition);
  393. dataStream.Position = virtualPosition;
  394. virtualPosition = -1;
  395. }
  396. public IStream Clone()
  397. {
  398. NotImplemented();
  399. return null;
  400. }
  401. public void Commit(int grfCommitFlags)
  402. {
  403. dataStream.Flush();
  404. // Extend the length of the file if needed.
  405. ActualizeVirtualPosition();
  406. }
  407. public long CopyTo(IStream pstm, long cb, long[] pcbRead)
  408. {
  409. int bufSize = 4096;
  410. IntPtr buffer = Marshal.AllocHGlobal((IntPtr)bufSize);
  411. if (buffer == IntPtr.Zero) throw new OutOfMemoryException();
  412. long written = 0;
  413. try
  414. {
  415. while (written < cb)
  416. {
  417. int toRead = bufSize;
  418. if (written + toRead > cb) toRead = (int)(cb - written);
  419. int read = Read(buffer, toRead);
  420. if (read == 0) break;
  421. if (pstm.Write(buffer, read) != read)
  422. {
  423. throw EFail(Res.GetString(Res.IncorrectNumberOfBytes));
  424. }
  425. written += read;
  426. }
  427. }
  428. finally
  429. {
  430. Marshal.FreeHGlobal(buffer);
  431. }
  432. if (pcbRead != null && pcbRead.Length > 0)
  433. {
  434. pcbRead[0] = written;
  435. }
  436. return written;
  437. }
  438. public Stream GetDataStream()
  439. {
  440. return dataStream;
  441. }
  442. public void LockRegion(long libOffset, long cb, int dwLockType)
  443. {
  444. }
  445. protected static ExternalException EFail(string msg)
  446. {
  447. ExternalException e = new ExternalException(msg, NativeMethods.E_FAIL);
  448. throw e;
  449. }
  450. protected static void NotImplemented()
  451. {
  452. ExternalException e = new ExternalException(Res.GetString(Res.NotImplemented), NativeMethods.E_NOTIMPL);
  453. throw e;
  454. }
  455. public int Read(IntPtr buf, int length)
  456. {
  457. byte[] buffer = new byte[length];
  458. int count = Read(buffer, length);
  459. Marshal.Copy(buffer, 0, buf, length);
  460. return count;
  461. }
  462. public int Read(byte[] buffer, int length)
  463. {
  464. ActualizeVirtualPosition();
  465. return dataStream.Read(buffer, 0, length);
  466. }
  467. public void Revert()
  468. {
  469. NotImplemented();
  470. }
  471. public long Seek(long offset, int origin)
  472. {
  473. long pos = virtualPosition;
  474. if (virtualPosition == -1)
  475. {
  476. pos = dataStream.Position;
  477. }
  478. long len = dataStream.Length;
  479. switch (origin)
  480. {
  481. case NativeMethods.STREAM_SEEK_SET:
  482. if (offset <= len)
  483. {
  484. dataStream.Position = offset;
  485. virtualPosition = -1;
  486. }
  487. else
  488. {
  489. virtualPosition = offset;
  490. }
  491. break;
  492. case NativeMethods.STREAM_SEEK_END:
  493. if (offset <= 0)
  494. {
  495. dataStream.Position = len + offset;
  496. virtualPosition = -1;
  497. }
  498. else
  499. {
  500. virtualPosition = len + offset;
  501. }
  502. break;
  503. case NativeMethods.STREAM_SEEK_CUR:
  504. if (offset + pos <= len)
  505. {
  506. dataStream.Position = pos + offset;
  507. virtualPosition = -1;
  508. }
  509. else
  510. {
  511. virtualPosition = offset + pos;
  512. }
  513. break;
  514. }
  515. if (virtualPosition != -1)
  516. {
  517. return virtualPosition;
  518. }
  519. else
  520. {
  521. return dataStream.Position;
  522. }
  523. }
  524. public void SetSize(long value)
  525. {
  526. dataStream.SetLength(value);
  527. }
  528. public void Stat(IntPtr pstatstg, int grfStatFlag)
  529. {
  530. // GpStream has a partial implementation, but it's so partial rather
  531. // restrict it to use with GDI+
  532. NotImplemented();
  533. }
  534. public void UnlockRegion(long libOffset, long cb, int dwLockType)
  535. {
  536. }
  537. public int Write(IntPtr buf, int length)
  538. {
  539. byte[] buffer = new byte[length];
  540. Marshal.Copy(buf, buffer, 0, length);
  541. return Write(buffer, length);
  542. }
  543. public int Write(byte[] buffer, int length)
  544. {
  545. ActualizeVirtualPosition();
  546. dataStream.Write(buffer, 0, length);
  547. return length;
  548. }
  549. }
  550. }
  551. }