Files
Yaulw/Win32/AtomMessenger.cs
2016-02-15 12:32:26 -05:00

322 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Yaulw.Win32
{
/// <remarks>
/// Quick and Easy Atom Window Messages Communication between windows of different
/// Processes.
/// </remarks>
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<uint> _postedAtomKeys = new List<uint>();
#endregion
#region Construction
/// <summary>
/// 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()
/// </summary>
/// <param name="UniqueWindowMessageIdentifier">An identifier for the Messaging</param>
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");
}
/// <summary>
/// Finalizer
/// </summary>
~AtomMessenger()
{
Dispose(true);
}
#endregion
#region Atom Message Construction Helpers
/// <summary>
/// Retrieve ';' seperated values from an Atom Message
/// </summary>
/// <param name="AtomMessage">an atom message that contains multiple args</param>
/// <returns>multiple args</returns>
public string[] RetrieveMultipleValuesFromAtomMessage(string AtomMessage)
{
if (!String.IsNullOrEmpty(AtomMessage))
return AtomMessage.Split(';');
else
return null;
}
/// <summary>
/// Create ';' seperated Atom message from multiple values
/// </summary>
/// <returns>an Atom Message containing multiple args</returns>
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
/// <summary>
/// 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
/// </summary>
/// <param name="m">a Windows.Forms Message</param>
/// <returns>true if the Messages matches the one created by the UniqueIdentifier</returns>
public bool IsAtomMessage(ref Message m)
{
return (m.Msg == _AtomMsg);
}
/// <summary>
/// Retrieves the values passed in by an Atom Message
/// </summary>
/// <param name="m">a Windows.Forms Message</param>
/// <param name="AtomMessage">the Message Retrieved from the Atom</param>
/// <param name="hWndSrc">the source window who send it, if valid</param>
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();
}
/// <summary>
/// Retrieves the values passed in by an Atom Message
/// </summary>
/// <param name="m">a Windows.Forms Message</param>
/// <param name="AtomMessage">the Message with multiple values retrieved from the Atom</param>
/// <param name="hWndSrc">the source window who send it, if valid</param>
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
/// <summary>
/// Sends an Atom Message To the Specified Window or All Windows
/// </summary>
/// <param name="hWndSrc">Sender Windows (Nice to have), in case Receiver Needs it</param>
/// <param name="hWndDst">Can be IntPtr.Zero to BroadCast the Message, otherwise specify window to send to</param>
/// <param name="AtomMessage">The Message to Send * Can not be longer than 254 chars *</param>
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);
}
/// <summary>
/// Sends an Atom Message To the Specified Window or All Windows
/// </summary>
/// <param name="hWndSrc">Sender Windows (Nice to have), in case Receiver Needs it</param>
/// <param name="hWndDst">Can be IntPtr.Zero to BroadCast the Message, otherwise specify window to send to</param>
/// <param name="AtomMessage">The Message to Send that has multiple values * Total string can not be longer than 254 chars *</param>
public void SendAtomMessage(IntPtr hWndSrc, IntPtr hWndDst, string[] AtomMessage)
{
SendAtomMessage(hWndSrc, hWndDst, SetMultipleValueInAtomMessage(AtomMessage));
}
/// <summary>
/// Post an Atom Message To the Specified Window or All Windows
/// </summary>
/// <param name="hWndSrc">Sender Windows (Nice to have), in case Receiver Needs it</param>
/// <param name="hWndDst">Can be IntPtr.Zero to BroadCast the Message, otherwise specify window to send to</param>
/// <param name="AtomMessage">The Message to Send * Can not be longer than 254 chars *</param>
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);
}
/// <summary>
/// Post an Atom Message To the Specified Window or All Windows
/// </summary>
/// <param name="hWndSrc">Sender Windows (Nice to have), in case Receiver Needs it</param>
/// <param name="hWndDst">Can be IntPtr.Zero to BroadCast the Message, otherwise specify window to send to</param>
/// <param name="AtomMessage">The Message to Send that has multiple values * Can not be longer than 254 chars *</param>
public void PostAtomMessage(IntPtr hWndSrc, IntPtr hWndDst, string[] AtomMessage)
{
PostAtomMessage(hWndSrc, hWndDst, SetMultipleValueInAtomMessage(AtomMessage));
}
#endregion
#region Posted Atom Keys handling
/// <summary>
/// Adds the Specified Posted Atom Key to the Posted Atoms DS.
/// Clears the DS, if MAGIC_NUMBER_POSTED_ATOM_KEY has been reached.
/// </summary>
/// <param name="nKey">a unique AtomKey</param>
private void AddPostedAtomKey(uint nKey)
{
if (_postedAtomKeys.Count >= MAGIC_NUMBER_POSTED_ATOM_KEYS)
DeleteAllPostedAtomKeys();
_postedAtomKeys.Add(nKey);
}
/// <summary>
/// Deletes all posted Atoms and Clears the PostedAtoms DS
/// </summary>
private void DeleteAllPostedAtomKeys()
{
foreach (uint Key in _postedAtomKeys)
Kernel32.GlobalDeleteAtom(Key);
_postedAtomKeys.Clear();
}
#endregion
#region IDisposable Members
/// <summary>
/// Dispose Posted Atom Strings
/// </summary>
public void Dispose()
{
Dispose(true);
// Use SupressFinalize in case a subclass
// of this type implements a finalizer
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose Posted Atom Strings
/// </summary>
/// <param name="disposing">true, if called from within</param>
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
}
}