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
}
}