using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; namespace Yaulw.Win32 { /// /// Quick and Easy Atom Window Messages Communication between windows of different /// Processes. /// public class AtomMessenger : IDisposable { #region Private Members private const string _AtomMsgIDFormat = "AtomMsgID-[{0}]"; private string _AtomMsgStr = ""; private uint _AtomMsg = 0; private bool _disposed = false; // For Posted Atom Messages private const int MAGIC_NUMBER_POSTED_ATOM_KEYS = 7; private List _postedAtomKeys = new List(); #endregion #region Construction /// /// Create an Atom Messaging Component with a Unique Identifier. /// The same Identifier should be used by all applications that want to pass /// Messages back and forth. /// ~You need to dispose of this Object only if you plan on using PostAtomMessage() /// /// An identifier for the Messaging public AtomMessenger(Guid UniqueWindowMessageIdentifier) { if (UniqueWindowMessageIdentifier == null) throw new ArgumentNullException(); // Register the Unique Window Message _AtomMsgStr = String.Format(_AtomMsgIDFormat, UniqueWindowMessageIdentifier); _AtomMsg = User32.RegisterWindowMessage(_AtomMsgStr); if (_AtomMsg == 0) throw new Exception("RegisterWindowMessage Failed"); } /// /// Finalizer /// ~AtomMessenger() { Dispose(true); } #endregion #region Atom Message Construction Helpers /// /// Retrieve ';' seperated values from an Atom Message /// /// an atom message that contains multiple args /// multiple args public string[] RetrieveMultipleValuesFromAtomMessage(string AtomMessage) { if (!String.IsNullOrEmpty(AtomMessage)) return AtomMessage.Split(';'); else return null; } /// /// Create ';' seperated Atom message from multiple values /// /// an Atom Message containing multiple args public string SetMultipleValueInAtomMessage(params object[] args) { if (args != null) { StringBuilder sb = new StringBuilder(); foreach (object a in args) { sb.Append(a.ToString()); sb.Append(";"); } // Remove Trailing ";" sb = sb.Remove(sb.Length - 1, 1); return sb.ToString(); } return String.Empty; } #endregion #region Atom Message Identify / Retrieval /// /// Use this to check if the passed in Message is an Atom Message. /// The identifier of the Message must match the Message registered with the UniqueIdentifier /// /// a Windows.Forms Message /// true if the Messages matches the one created by the UniqueIdentifier public bool IsAtomMessage(ref Message m) { return (m.Msg == _AtomMsg); } /// /// Retrieves the values passed in by an Atom Message /// /// a Windows.Forms Message /// the Message Retrieved from the Atom /// the source window who send it, if valid public void GetAtomMessageValues(ref Message m, out string AtomMessage, out IntPtr hWndSrc) { AtomMessage = String.Empty; hWndSrc = IntPtr.Zero; // Check to make sure this is an Atom Message if (!IsAtomMessage(ref m)) return; // Source is passed to us by wParam if(m.WParam != IntPtr.Zero && User32.IsWindow(m.WParam)) hWndSrc = m.WParam; // Now retrieve the Atom Message StringBuilder sb = new StringBuilder( 254 ); uint AtomKey = (uint) m.LParam; uint slen = Kernel32.GlobalGetAtomName(AtomKey, sb, 254); if (slen == 0) return; // Write out Retrieved Message AtomMessage = sb.ToString(); } /// /// Retrieves the values passed in by an Atom Message /// /// a Windows.Forms Message /// the Message with multiple values retrieved from the Atom /// the source window who send it, if valid public void GetAtomMessageValues(ref Message m, out string[] AtomMessage, out IntPtr hWndSrc) { AtomMessage = null; hWndSrc = IntPtr.Zero; // Check to make sure this is an Atom Message if (!IsAtomMessage(ref m)) return; // Source is passed to us by wParam if (m.WParam != IntPtr.Zero && User32.IsWindow(m.WParam)) hWndSrc = m.WParam; // Now retrieve the Atom Message StringBuilder sb = new StringBuilder(254); uint AtomKey = (uint)m.LParam; uint slen = Kernel32.GlobalGetAtomName(AtomKey, sb, 254); if (slen == 0) return; // Write out Retrieved Message AtomMessage = RetrieveMultipleValuesFromAtomMessage(sb.ToString()); } #endregion #region Send/Post Atom Message /// /// Sends an Atom Message To the Specified Window or All Windows /// /// Sender Windows (Nice to have), in case Receiver Needs it /// Can be IntPtr.Zero to BroadCast the Message, otherwise specify window to send to /// The Message to Send * Can not be longer than 254 chars * public void SendAtomMessage(IntPtr hWndSrc, IntPtr hWndDst, string AtomMessage) { // Is Broadcast? bool bBroadcast = (hWndDst==IntPtr.Zero); // Check to make sure Atom Message is proper if(String.IsNullOrEmpty(AtomMessage) || AtomMessage.Length > 254) throw new ArgumentException("AtomMessage Invalid"); // Register Atom uint AtomKey = Kernel32.GlobalAddAtom(AtomMessage); if (AtomKey == 0) throw new Exception("GlobalAddAtom Failed"); // Send the Message if(bBroadcast) User32.SendMessage(Definitions.HWND_BROADCAST, (int)_AtomMsg, hWndSrc, (IntPtr)AtomKey); else User32.SendMessage(hWndDst, (int)_AtomMsg, hWndSrc, (IntPtr)AtomKey); // We are done with the Atom Kernel32.GlobalDeleteAtom(AtomKey); } /// /// Sends an Atom Message To the Specified Window or All Windows /// /// Sender Windows (Nice to have), in case Receiver Needs it /// Can be IntPtr.Zero to BroadCast the Message, otherwise specify window to send to /// The Message to Send that has multiple values * Total string can not be longer than 254 chars * public void SendAtomMessage(IntPtr hWndSrc, IntPtr hWndDst, string[] AtomMessage) { SendAtomMessage(hWndSrc, hWndDst, SetMultipleValueInAtomMessage(AtomMessage)); } /// /// Post an Atom Message To the Specified Window or All Windows /// /// Sender Windows (Nice to have), in case Receiver Needs it /// Can be IntPtr.Zero to BroadCast the Message, otherwise specify window to send to /// The Message to Send * Can not be longer than 254 chars * public void PostAtomMessage(IntPtr hWndSrc, IntPtr hWndDst, string AtomMessage) { // Is Broadcast? bool bBroadcast = (hWndDst == IntPtr.Zero); // Check to make sure Atom Message is proper if (String.IsNullOrEmpty(AtomMessage) || AtomMessage.Length > 254) throw new ArgumentException("AtomMessage Invalid"); // Register a new Atom uint nAtomKey = Kernel32.GlobalAddAtom(AtomMessage); if (nAtomKey == 0) throw new Exception("GlobalAddAtom Failed"); // Send the Message if (bBroadcast) User32.PostMessage(Definitions.HWND_BROADCAST, (int)_AtomMsg, hWndSrc, (IntPtr)nAtomKey); else User32.PostMessage(hWndDst, (int)_AtomMsg, hWndSrc, (IntPtr)nAtomKey); // Imp! Atom still must get Deleted, that is why we add it to DS AddPostedAtomKey(nAtomKey); } /// /// Post an Atom Message To the Specified Window or All Windows /// /// Sender Windows (Nice to have), in case Receiver Needs it /// Can be IntPtr.Zero to BroadCast the Message, otherwise specify window to send to /// The Message to Send that has multiple values * Can not be longer than 254 chars * public void PostAtomMessage(IntPtr hWndSrc, IntPtr hWndDst, string[] AtomMessage) { PostAtomMessage(hWndSrc, hWndDst, SetMultipleValueInAtomMessage(AtomMessage)); } #endregion #region Posted Atom Keys handling /// /// Adds the Specified Posted Atom Key to the Posted Atoms DS. /// Clears the DS, if MAGIC_NUMBER_POSTED_ATOM_KEY has been reached. /// /// a unique AtomKey private void AddPostedAtomKey(uint nKey) { if (_postedAtomKeys.Count >= MAGIC_NUMBER_POSTED_ATOM_KEYS) DeleteAllPostedAtomKeys(); _postedAtomKeys.Add(nKey); } /// /// Deletes all posted Atoms and Clears the PostedAtoms DS /// private void DeleteAllPostedAtomKeys() { foreach (uint Key in _postedAtomKeys) Kernel32.GlobalDeleteAtom(Key); _postedAtomKeys.Clear(); } #endregion #region IDisposable Members /// /// Dispose Posted Atom Strings /// public void Dispose() { Dispose(true); // Use SupressFinalize in case a subclass // of this type implements a finalizer GC.SuppressFinalize(this); } /// /// Dispose Posted Atom Strings /// /// true, if called from within protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { if (_postedAtomKeys.Count != 0) DeleteAllPostedAtomKeys(); } // Indicate that the instance has been disposed. _postedAtomKeys = null; _disposed = true; } } #endregion } }