diff --git a/Growl Extras/PhonyBalloony/AppContext.cs b/Growl Extras/PhonyBalloony/AppContext.cs
new file mode 100644
index 0000000..9ac45b4
--- /dev/null
+++ b/Growl Extras/PhonyBalloony/AppContext.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Forms;
+
+namespace GrowlExtras.PhonyBalloony
+{
+ class AppContext : ApplicationContext, IDisposable
+ {
+ private WndProcReader wpr;
+ private SystemBalloonIntercepter sbi;
+
+ public AppContext()
+ {
+ this.wpr = new WndProcReader(WndProc);
+ Microsoft.Win32.SystemEvents.SessionEnding += new Microsoft.Win32.SessionEndingEventHandler(SystemEvents_SessionEnding);
+
+ // this is just a useful addition for debugging. this allows us to stop the process without logging of or shutting down the computer
+#if DEBUG
+ Microsoft.Win32.SystemEvents.SessionSwitch += new Microsoft.Win32.SessionSwitchEventHandler(SystemEvents_SessionSwitch);
+#endif
+ }
+
+ public void Start()
+ {
+ Stop();
+
+ this.sbi = new SystemBalloonIntercepter(this.wpr.Handle);
+ sbi.Start();
+ }
+
+ public void Stop()
+ {
+ if (sbi != null)
+ {
+ this.sbi.Stop();
+ this.sbi = null;
+ }
+ }
+
+ private void WndProc(ref Message m)
+ {
+ /* this is for handling intercepted system balloon messages */
+ if (this.sbi != null)
+ sbi.ProcessWindowMessage(ref m);
+ }
+
+ void SystemEvents_SessionEnding(object sender, Microsoft.Win32.SessionEndingEventArgs e)
+ {
+ Stop();
+ }
+
+ void SystemEvents_SessionSwitch(object sender, Microsoft.Win32.SessionSwitchEventArgs e)
+ {
+ if(e.Reason == Microsoft.Win32.SessionSwitchReason.SessionUnlock)
+ Stop();
+ }
+
+ #region IDisposable Members
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ try
+ {
+ Microsoft.Win32.SystemEvents.SessionEnding -= new Microsoft.Win32.SessionEndingEventHandler(SystemEvents_SessionEnding);
+#if DEBUG
+ Microsoft.Win32.SystemEvents.SessionSwitch -= new Microsoft.Win32.SessionSwitchEventHandler(SystemEvents_SessionSwitch);
+#endif
+ this.Stop();
+ }
+ catch
+ {
+ // suppress
+ }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Growl Extras/PhonyBalloony/PhonyBalloony.csproj b/Growl Extras/PhonyBalloony/PhonyBalloony.csproj
new file mode 100644
index 0000000..b8d5a9b
--- /dev/null
+++ b/Growl Extras/PhonyBalloony/PhonyBalloony.csproj
@@ -0,0 +1,95 @@
+
+
+ Debug
+ AnyCPU
+ 8.0.50727
+ 2.0
+ {34A423AD-4D5F-42AB-9339-DFB5FD717642}
+ WinExe
+ Properties
+ GrowlExtras.PhonyBalloony
+ PhonyBalloony
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ AnyCPU
+
+
+
+ False
+ ..\..\Growl Connectors\NET\libraries\Growl.Connector.dll
+
+
+ False
+ ..\..\Growl Connectors\NET\libraries\Growl.CoreLibrary.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+ True
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+
\ No newline at end of file
diff --git a/Growl Extras/PhonyBalloony/PhonyBalloony.sln b/Growl Extras/PhonyBalloony/PhonyBalloony.sln
new file mode 100644
index 0000000..2b29a4e
--- /dev/null
+++ b/Growl Extras/PhonyBalloony/PhonyBalloony.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhonyBalloony", "PhonyBalloony.csproj", "{34A423AD-4D5F-42AB-9339-DFB5FD717642}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {34A423AD-4D5F-42AB-9339-DFB5FD717642}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {34A423AD-4D5F-42AB-9339-DFB5FD717642}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {34A423AD-4D5F-42AB-9339-DFB5FD717642}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {34A423AD-4D5F-42AB-9339-DFB5FD717642}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Growl Extras/PhonyBalloony/Program.cs b/Growl Extras/PhonyBalloony/Program.cs
new file mode 100644
index 0000000..9b7a623
--- /dev/null
+++ b/Growl Extras/PhonyBalloony/Program.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace GrowlExtras.PhonyBalloony
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ AppContext context = new AppContext();
+ using (context)
+ {
+ context.Start();
+ Application.Run(context);
+ context.Stop();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Growl Extras/PhonyBalloony/Properties/AssemblyInfo.cs b/Growl Extras/PhonyBalloony/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..1e58f85
--- /dev/null
+++ b/Growl Extras/PhonyBalloony/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("PhonyBalloony")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("element code project")]
+[assembly: AssemblyProduct("PhonyBalloony")]
+[assembly: AssemblyCopyright("Copyright © element code project 2010")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("d6ec9a72-c9a2-45c4-8960-2c0401d48677")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Growl Extras/PhonyBalloony/Properties/Resources.Designer.cs b/Growl Extras/PhonyBalloony/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..ec7c4b1
--- /dev/null
+++ b/Growl Extras/PhonyBalloony/Properties/Resources.Designer.cs
@@ -0,0 +1,84 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.3082
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace GrowlExtras.PhonyBalloony.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GrowlExtras.PhonyBalloony.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ internal static System.Drawing.Bitmap error {
+ get {
+ object obj = ResourceManager.GetObject("error", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ internal static System.Drawing.Bitmap info {
+ get {
+ object obj = ResourceManager.GetObject("info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ internal static System.Drawing.Bitmap warning {
+ get {
+ object obj = ResourceManager.GetObject("warning", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+ }
+}
diff --git a/Growl Extras/PhonyBalloony/Properties/Resources.resx b/Growl Extras/PhonyBalloony/Properties/Resources.resx
new file mode 100644
index 0000000..80c5cce
--- /dev/null
+++ b/Growl Extras/PhonyBalloony/Properties/Resources.resx
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ..\Resources\error.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Resources\info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Resources\warning.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
\ No newline at end of file
diff --git a/Growl Extras/PhonyBalloony/Properties/Settings.Designer.cs b/Growl Extras/PhonyBalloony/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..fce8b59
--- /dev/null
+++ b/Growl Extras/PhonyBalloony/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.3082
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace GrowlExtras.PhonyBalloony.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "8.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/Growl Extras/PhonyBalloony/Properties/Settings.settings b/Growl Extras/PhonyBalloony/Properties/Settings.settings
new file mode 100644
index 0000000..abf36c5
--- /dev/null
+++ b/Growl Extras/PhonyBalloony/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/Growl Extras/PhonyBalloony/README.txt b/Growl Extras/PhonyBalloony/README.txt
new file mode 100644
index 0000000..896ab45
--- /dev/null
+++ b/Growl Extras/PhonyBalloony/README.txt
@@ -0,0 +1,29 @@
+PhonyBalloony
+------------------------------
+
+PhonyBalloony is a process that runs in the background and intercepts Windows
+system balloon messages and re-routes them through Growl for Windows.
+
+Since PhonyBalloony intercepts system-level messages and has no UI, the best
+way to use PhonyBalloony is to have it automatically start when you log on to
+Windows.
+
+1. Unzip the following files into a folder:
+ PhonyBalloony.exe
+ Growl.Connector.dll
+ Growl.CoreLibrary.dll
+ SystemBalloonIntercepter.dll
+
+2. Drag PhonyBalloony.exe to your 'Startup' folder
+ Start Button > Programs > Startup
+
+If you want to start PhonyBalloony for your current session manually, just
+double-click the PhonyBalloony.exe file. There is no UI or tray icon, but the
+PhonyBalloony process will show up in Task Manager.
+
+PhonyBalloony automatically shuts itself down when you log off or shutdown
+your computer. If Growl is not running or is closed while PhonyBalloony is
+running, it will revert to using the system balloons.
+
+NOTE: PhonyBalloony has not been tested on 64-bit systems and may not work
+properly.
\ No newline at end of file
diff --git a/Growl Extras/PhonyBalloony/Resources/error.png b/Growl Extras/PhonyBalloony/Resources/error.png
new file mode 100644
index 0000000..d7301e5
Binary files /dev/null and b/Growl Extras/PhonyBalloony/Resources/error.png differ
diff --git a/Growl Extras/PhonyBalloony/Resources/info.png b/Growl Extras/PhonyBalloony/Resources/info.png
new file mode 100644
index 0000000..cde124d
Binary files /dev/null and b/Growl Extras/PhonyBalloony/Resources/info.png differ
diff --git a/Growl Extras/PhonyBalloony/Resources/warning.png b/Growl Extras/PhonyBalloony/Resources/warning.png
new file mode 100644
index 0000000..303e809
Binary files /dev/null and b/Growl Extras/PhonyBalloony/Resources/warning.png differ
diff --git a/Growl Extras/PhonyBalloony/SystemBalloonIntercepter.cs b/Growl Extras/PhonyBalloony/SystemBalloonIntercepter.cs
new file mode 100644
index 0000000..45bfd74
--- /dev/null
+++ b/Growl Extras/PhonyBalloony/SystemBalloonIntercepter.cs
@@ -0,0 +1,292 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using Growl.Connector;
+
+namespace GrowlExtras.PhonyBalloony
+{
+ public delegate void HookReplacedEventHandler();
+
+ public class SystemBalloonIntercepter : Hook
+ {
+ public const int WM_COPYDATA = 74;
+
+ private IntPtr MSG_REPLACED; // value will come from RegisterWindowMessage
+ private const string MSG_NAME_REPLACED = "GFW_HOOK_CALLWNDPROC_REPLACED";
+
+ // icon flags are mutually exclusive and take only the lowest 2 bits
+ public const int NIIF_NONE = 0x00000000;
+ public const int NIIF_INFO = 0x00000001;
+ public const int NIIF_WARNING = 0x00000002;
+ public const int NIIF_ERROR = 0x00000003;
+ public const int NIIF_USER = 0x00000004;
+
+ private static System.Drawing.Image IMAGE_INFO = Properties.Resources.info;
+ private static System.Drawing.Image IMAGE_ERROR = Properties.Resources.error;
+ private static System.Drawing.Image IMAGE_WARNING = Properties.Resources.warning;
+
+ public event HookReplacedEventHandler HookReplaced;
+
+ private bool registered;
+ private bool hooked;
+ private IntPtr handle;
+ private GrowlConnector growl;
+ private Growl.Connector.Application app;
+ private Growl.Connector.NotificationType ntBalloon;
+ private System.Timers.Timer timer;
+
+ public SystemBalloonIntercepter(IntPtr handle) : base(handle)
+ {
+ this.handle = handle;
+
+ this.app = new Growl.Connector.Application("Windows");
+ this.app.Icon = IMAGE_INFO;
+ this.ntBalloon = new NotificationType("balloon", "System Balloons");
+ this.growl = new GrowlConnector();
+ this.growl.EncryptionAlgorithm = Cryptography.SymmetricAlgorithmType.PlainText;
+
+ this.timer = new System.Timers.Timer(10 * 1000); // ten seconds
+ this.timer.AutoReset = false;
+ this.timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
+ }
+
+ void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
+ {
+ TrySetHook();
+ }
+
+ ~SystemBalloonIntercepter()
+ {
+ this.Stop();
+ }
+
+ protected override void OnStart()
+ {
+ // Retreive the message IDs that we'll look for in WndProc
+ MSG_REPLACED = RegisterWindowMessage(MSG_NAME_REPLACED);
+
+ TrySetHook();
+ }
+
+ protected override void OnStop()
+ {
+ UninitializeCallWndProcHook();
+ }
+
+ private bool Register()
+ {
+ if (growl.IsGrowlRunning())
+ {
+ this.growl.Register(this.app, new NotificationType[] { this.ntBalloon });
+ this.registered = true;
+ }
+ return this.registered;
+ }
+
+ private void TrySetHook()
+ {
+ bool ok = Register();
+
+ // Start the hook
+ if (ok && !this.hooked)
+ {
+ this.hooked = true;
+ InitializeCallWndProcHook(IntPtr.Zero, _Handle);
+ }
+
+ // try again later if Growl is not yet running
+ if (!ok)
+ {
+ this.timer.Start();
+ }
+ }
+
+ public override void ProcessWindowMessage(ref Message m)
+ {
+ if (m.Msg == WM_COPYDATA)
+ {
+ if ((int)m.WParam == 97)
+ {
+ if (growl.IsGrowlRunning())
+ {
+ COPYDATASTRUCT cds = new COPYDATASTRUCT();
+ cds = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT));
+ //Utility.WriteLine(cds.cbData);
+ //Utility.WriteLine(cds.dwData);
+ //Utility.WriteLine(cds.lpData);
+
+ TRAYINFO ti = new TRAYINFO();
+ ti = (TRAYINFO)Marshal.PtrToStructure(cds.lpData, typeof(TRAYINFO));
+ //Console.WriteLine("_title: " + ti.nid.szInfoTitle);
+ //Console.WriteLine("_text: " + ti.nid.szInfo);
+
+ // get the icon
+ // it is important to check these in descending order since the 'flags' are not mutually exclusive
+ System.Drawing.Image image = IMAGE_INFO; // default to INFO
+ if (IsFlagSet(ti.nid.dwInfoFlags, NIIF_USER) && ti.nid.hIcon != IntPtr.Zero)
+ {
+ System.Drawing.Icon icon = System.Drawing.Icon.FromHandle(ti.nid.hIcon);
+ using (icon)
+ {
+ image = icon.ToBitmap();
+ }
+ }
+ else if (IsFlagSet(ti.nid.dwInfoFlags, NIIF_ERROR))
+ image = IMAGE_ERROR;
+ else if (IsFlagSet(ti.nid.dwInfoFlags, NIIF_WARNING))
+ image = IMAGE_WARNING;
+ // flag 1 == IMAGE_INFO anyway
+
+ if (!String.IsNullOrEmpty(ti.nid.szInfo))
+ OnSystemBalloonIntercepted(ti.nid.szInfoTitle, ti.nid.szInfo, image);
+
+ // zero == success
+ m.Result = IntPtr.Zero;
+ }
+ else
+ {
+ // non-zero == growl not running
+ // the hook will show the standard system balloon instead
+ m.Result = new IntPtr(int.MaxValue);
+ }
+ }
+ }
+ else if (m.Msg == (int) MSG_REPLACED)
+ {
+ if (HookReplaced != null)
+ HookReplaced();
+ }
+ }
+
+ private void OnSystemBalloonIntercepted(string title, string text, System.Drawing.Image icon)
+ {
+ if (String.IsNullOrEmpty(title)) title = "[no title]";
+ if (String.IsNullOrEmpty(text)) text = "[no text]";
+
+ Notification notification = new Notification(this.app.Name, this.ntBalloon.Name, String.Empty, title, text);
+ notification.Icon = icon;
+ this.growl.Notify(notification);
+ }
+
+ private bool IsFlagSet(int val, int flag)
+ {
+ int result = (val & flag);
+ return (result == flag);
+ }
+
+ private static void InitializeCallWndProcHook(IntPtr threadID, IntPtr DestWindow)
+ {
+ if (IntPtr.Size == 8)
+ InitializeCallWndProcHook64(threadID, DestWindow);
+ else
+ InitializeCallWndProcHook32(threadID, DestWindow);
+ }
+
+ private static void UninitializeCallWndProcHook()
+ {
+ if (IntPtr.Size == 8)
+ UninitializeCallWndProcHook64();
+ else
+ UninitializeCallWndProcHook32();
+ }
+
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ struct TRAYINFO
+ {
+ public IntPtr x;
+ public IntPtr y;
+ public NOTIFYICONDATA nid;
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ struct NOTIFYICONDATA
+ {
+ public System.Int32 cbSize; // DWORD
+ public System.IntPtr hWnd; // HWND
+ public System.Int32 uID; // UINT
+ public System.Int32 uFlags; // UINT
+ public System.Int32 uCallbackMessage; // UINT
+ public System.IntPtr hIcon; // HICON
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
+ public System.String szTip; // char[128]
+ public System.Int32 dwState; // DWORD
+ public System.Int32 dwStateMask; // DWORD
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
+ public System.String szInfo; // char[256]
+ public System.Int32 uTimeoutOrVersion; // UINT
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
+ public System.String szInfoTitle; // char[64]
+ public System.Int32 dwInfoFlags; // DWORD
+ //GUID guidItem; > IE 6
+ }
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct COPYDATASTRUCT
+ {
+ public IntPtr dwData;
+ public IntPtr cbData;
+ public IntPtr lpData;
+ }
+
+ // Functions imported from our unmanaged DLL (32-bit)
+ [DllImport("SystemBalloonIntercepter.dll", EntryPoint = "InitializeCallWndProcHook")]
+ private static extern void InitializeCallWndProcHook32(IntPtr threadID, IntPtr DestWindow);
+ [DllImport("SystemBalloonIntercepter.dll", EntryPoint = "UninitializeCallWndProcHook")]
+ private static extern void UninitializeCallWndProcHook32();
+
+ // Functions imported from our unmanaged DLL (64-bit)
+ [DllImport("SystemBalloonIntercepter64.dll", EntryPoint = "InitializeCallWndProcHook")]
+ private static extern void InitializeCallWndProcHook64(IntPtr threadID, IntPtr DestWindow);
+ [DllImport("SystemBalloonIntercepter64.dll", EntryPoint = "UninitializeCallWndProcHook")]
+ private static extern void UninitializeCallWndProcHook64();
+
+ // Win32 API methods
+ [DllImport("user32.dll")]
+ private static extern IntPtr RegisterWindowMessage(string lpString);
+ }
+
+ public abstract class Hook
+ {
+ protected bool _IsActive = false;
+ protected IntPtr _Handle;
+
+ protected Hook(IntPtr Handle)
+ {
+ _Handle = Handle;
+ }
+
+ public void Start()
+ {
+ if (!_IsActive)
+ {
+ _IsActive = true;
+ OnStart();
+ }
+ }
+
+ public void Stop()
+ {
+ if (_IsActive)
+ {
+ OnStop();
+ _IsActive = false;
+ }
+ }
+
+ ~Hook()
+ {
+ Stop();
+ }
+
+ public bool IsActive
+ {
+ get { return _IsActive; }
+ }
+
+ protected abstract void OnStart();
+ protected abstract void OnStop();
+ public abstract void ProcessWindowMessage(ref System.Windows.Forms.Message m);
+ }
+}
\ No newline at end of file
diff --git a/Growl Extras/PhonyBalloony/SystemBalloonIntercepter.dll b/Growl Extras/PhonyBalloony/SystemBalloonIntercepter.dll
new file mode 100644
index 0000000..3adc20d
Binary files /dev/null and b/Growl Extras/PhonyBalloony/SystemBalloonIntercepter.dll differ
diff --git a/Growl Extras/PhonyBalloony/SystemBalloonIntercepter64.dll b/Growl Extras/PhonyBalloony/SystemBalloonIntercepter64.dll
new file mode 100644
index 0000000..cd4c299
Binary files /dev/null and b/Growl Extras/PhonyBalloony/SystemBalloonIntercepter64.dll differ
diff --git a/Growl Extras/PhonyBalloony/WndProcReader.cs b/Growl Extras/PhonyBalloony/WndProcReader.cs
new file mode 100644
index 0000000..20e2f29
--- /dev/null
+++ b/Growl Extras/PhonyBalloony/WndProcReader.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Windows.Forms;
+
+namespace GrowlExtras.PhonyBalloony
+{
+ class WndProcReader : NativeWindow
+ {
+ public delegate void WndProcDelegate(ref Message m);
+
+ CreateParams cp = new CreateParams();
+ WndProcDelegate del;
+
+ public WndProcReader(WndProcDelegate del)
+ {
+ this.del = del;
+
+ // Create the actual window
+ this.CreateHandle(cp);
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ // let the delegate process the message
+ if (this.del != null) this.del(ref m);
+
+ // if the delegate didnt set the result, let the message pass through to the base method.
+ // otherwise, we assume the delegate set the result to an error value and return that instead.
+ if(m.Result == IntPtr.Zero)
+ base.WndProc(ref m);
+ }
+ }
+}
diff --git a/Growl Extras/SystemBalloonIntercepter/DllMain.cpp b/Growl Extras/SystemBalloonIntercepter/DllMain.cpp
new file mode 100644
index 0000000..8cd3d1b
--- /dev/null
+++ b/Growl Extras/SystemBalloonIntercepter/DllMain.cpp
@@ -0,0 +1,41 @@
+#include "stdafx.h"
+#include
+#include
+
+//
+// Capture the application instance of this module to pass to
+// hook initialization.
+//
+extern HINSTANCE g_appInstance;
+
+BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ //
+ // Capture the application instance of this module to pass to
+ // hook initialization.
+ //
+ if (g_appInstance == NULL)
+ {
+ g_appInstance = hinstDLL;
+ }
+ break;
+
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_THREAD_DETACH:
+ break;
+
+ case DLL_PROCESS_DETACH:
+ break;
+
+ default:
+ OutputDebugString("That's weird.\n");
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/Growl Extras/SystemBalloonIntercepter/GlobalCbtHook.vcproj b/Growl Extras/SystemBalloonIntercepter/GlobalCbtHook.vcproj
new file mode 100644
index 0000000..58d446d
--- /dev/null
+++ b/Growl Extras/SystemBalloonIntercepter/GlobalCbtHook.vcproj
@@ -0,0 +1,598 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.cpp b/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.cpp
new file mode 100644
index 0000000..277c8c8
--- /dev/null
+++ b/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.cpp
@@ -0,0 +1,109 @@
+#include "stdafx.h"
+#include
+#include "SystemBalloonIntercepter.h"
+
+// Store the application instance of this module to pass to hook initialization. This is set in DLLMain().
+HINSTANCE g_appInstance = NULL;
+
+UINT msg_unsubclass = 0;
+UINT msg_replaced = 0;
+HHOOK hookCallWndProc = NULL;
+LRESULT CALLBACK CallWndProcHookCallback(UINT code, WPARAM wparam, LPARAM lparam);
+LRESULT CALLBACK SubclassWndProc(HWND hwnd, UINT code, WPARAM wparam, LPARAM lparam);
+LRESULT oldWndProc;
+UINT WM_UNSUBCLASS = 0;
+BOOL isSubclassed = false;
+
+bool InitializeCallWndProcHook(DWORD threadID, HWND destination)
+{
+ if (g_appInstance == NULL)
+ {
+ return false;
+ }
+
+ msg_unsubclass = RegisterWindowMessage("GFW_HOOK_UNSUBCLASS");
+ msg_replaced = RegisterWindowMessage("GFW_HOOK_CALLWNDPROC_REPLACED");
+ HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), "GFW_HOOK_HWND_CALLWNDPROC");
+ if (dstWnd != NULL)
+ {
+ PostMessage(dstWnd, msg_replaced, 0, 0);
+ }
+ dstWnd = destination;
+ SetProp(GetDesktopWindow(), "GFW_HOOK_HWND_CALLWNDPROC", dstWnd);
+
+ hookCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)CallWndProcHookCallback, g_appInstance, threadID);
+ return hookCallWndProc != NULL;
+}
+
+void UninitializeCallWndProcHook()
+{
+ // stop subclassing
+ HWND trayHwnd = FindWindow("Shell_TrayWnd", NULL);
+ SendMessageW(trayHwnd, msg_unsubclass, 0, 0);
+ RemoveProp(GetDesktopWindow(), "GFW_HOOK_HWND_CALLWNDPROC");
+
+ // unhook
+ if (hookCallWndProc != NULL)
+ {
+ UnhookWindowsHookEx(hookCallWndProc);
+ hookCallWndProc = NULL;
+ }
+}
+
+
+LRESULT CALLBACK CallWndProcHookCallback(UINT code, WPARAM wparam, LPARAM lparam)
+{
+ if(WM_UNSUBCLASS == 0) WM_UNSUBCLASS = RegisterWindowMessage("GFW_HOOK_UNSUBCLASS");
+
+ if (code >= 0)
+ {
+ PCWPSTRUCT pCwpStruct = (PCWPSTRUCT)lparam;
+ HWND hwnd = pCwpStruct->hwnd;
+ HWND trayHwnd = FindWindow("Shell_TrayWnd", NULL);
+
+ if((LONG_PTR) hwnd == (LONG_PTR) trayHwnd)
+ {
+ if(!isSubclassed)
+ {
+ isSubclassed = true;
+ oldWndProc = (LRESULT) SetWindowLongPtr(trayHwnd, GWLP_WNDPROC, (LONG_PTR) SubclassWndProc);
+ }
+ }
+ }
+
+ return CallNextHookEx(hookCallWndProc, code, wparam, lparam);
+}
+
+LRESULT CALLBACK SubclassWndProc(HWND hwnd, UINT code, WPARAM wparam, LPARAM lparam)
+{
+ LRESULT r = 0;
+ BOOL handled = false;
+
+ if(code == WM_COPYDATA)
+ {
+ PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT) lparam;
+
+ if (cpData->dwData == 1)
+ {
+ DWORD trayCommand = *(DWORD *) (((BYTE *)cpData->lpData) + 4);
+ PNOTIFYICONDATA iconData = (PNOTIFYICONDATA) (((BYTE *)cpData->lpData) + 8);
+
+ BOOL isBalloon = (iconData->uFlags & NIF_INFO);
+ if(isBalloon)
+ {
+ HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), "GFW_HOOK_HWND_CALLWNDPROC");
+ r = SendMessageW(dstWnd, WM_COPYDATA, 97, lparam);
+ if(r == 0) handled = true;
+ }
+ }
+ }
+ else if(code == WM_UNSUBCLASS)
+ {
+ SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG) oldWndProc);
+ isSubclassed = false;
+ handled = true;
+ }
+
+ if(!handled) r = CallWindowProc((WNDPROC)oldWndProc, hwnd, code, wparam, lparam);
+ return r;
+}
\ No newline at end of file
diff --git a/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.def b/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.def
new file mode 100644
index 0000000..30cf456
--- /dev/null
+++ b/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.def
@@ -0,0 +1,5 @@
+LIBRARY SystemHookCore
+
+EXPORTS
+ InitializeCallWndProcHook
+ UninitializeCallWndProcHook
diff --git a/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.h b/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.h
new file mode 100644
index 0000000..c16bdd4
--- /dev/null
+++ b/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.h
@@ -0,0 +1,19 @@
+#pragma once
+
+namespace HookCoreErrors
+{
+ namespace SetCallBack
+ {
+ const int SUCCESS = 1;
+ const int ALREADY_SET = -2;
+ const int NOT_IMPLEMENTED = -3;
+ const int ARGUMENT_ERROR = -4;
+ }
+ namespace FilterMessage
+ {
+ const int SUCCESS = 1;
+ const int FAILED = -2;
+ const int NOT_IMPLEMENTED = -3;
+ }
+}
+
diff --git a/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.rc b/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.rc
new file mode 100644
index 0000000..88b2b98
--- /dev/null
+++ b/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.rc
@@ -0,0 +1,102 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "element code project"
+ VALUE "FileDescription", "SystemBalloonIntercepter"
+ VALUE "FileVersion", "1, 0, 0, 0"
+ VALUE "InternalName", "SystemBalloonIntercepter"
+ VALUE "LegalCopyright", ""
+ VALUE "OriginalFilename", "SystemBalloonIntercepter.dll"
+ VALUE "ProductName", "Growl for Windows System Balloon Intercepter"
+ VALUE "ProductVersion", "1, 0, 0, 0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.sln b/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.sln
new file mode 100644
index 0000000..6fc8b9e
--- /dev/null
+++ b/Growl Extras/SystemBalloonIntercepter/SystemBalloonIntercepter.sln
@@ -0,0 +1,37 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SystemBalloonIntercepter", "GlobalCbtHook.vcproj", "{A558485A-58F7-4748-A0BE-EE1BB01732F5}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EA1A9905-09FF-4FF4-9F74-3C6D4FF61C3D}"
+ ProjectSection(SolutionItems) = preProject
+ save.txt = save.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Documentation|Win32 = Documentation|Win32
+ Documentation|x64 = Documentation|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A558485A-58F7-4748-A0BE-EE1BB01732F5}.Debug|Win32.ActiveCfg = Debug|Win32
+ {A558485A-58F7-4748-A0BE-EE1BB01732F5}.Debug|Win32.Build.0 = Debug|Win32
+ {A558485A-58F7-4748-A0BE-EE1BB01732F5}.Debug|x64.ActiveCfg = Debug|x64
+ {A558485A-58F7-4748-A0BE-EE1BB01732F5}.Debug|x64.Build.0 = Debug|x64
+ {A558485A-58F7-4748-A0BE-EE1BB01732F5}.Documentation|Win32.ActiveCfg = Documentation|Win32
+ {A558485A-58F7-4748-A0BE-EE1BB01732F5}.Documentation|Win32.Build.0 = Documentation|Win32
+ {A558485A-58F7-4748-A0BE-EE1BB01732F5}.Documentation|x64.ActiveCfg = Documentation|x64
+ {A558485A-58F7-4748-A0BE-EE1BB01732F5}.Documentation|x64.Build.0 = Documentation|x64
+ {A558485A-58F7-4748-A0BE-EE1BB01732F5}.Release|Win32.ActiveCfg = Release|Win32
+ {A558485A-58F7-4748-A0BE-EE1BB01732F5}.Release|Win32.Build.0 = Release|Win32
+ {A558485A-58F7-4748-A0BE-EE1BB01732F5}.Release|x64.ActiveCfg = Release|x64
+ {A558485A-58F7-4748-A0BE-EE1BB01732F5}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Growl Extras/SystemBalloonIntercepter/resource.h b/Growl Extras/SystemBalloonIntercepter/resource.h
new file mode 100644
index 0000000..4666cd8
--- /dev/null
+++ b/Growl Extras/SystemBalloonIntercepter/resource.h
@@ -0,0 +1,15 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by SystemBalloonIntercepter.rc
+//
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Growl Extras/SystemBalloonIntercepter/save.txt b/Growl Extras/SystemBalloonIntercepter/save.txt
new file mode 100644
index 0000000..5f3829a
--- /dev/null
+++ b/Growl Extras/SystemBalloonIntercepter/save.txt
@@ -0,0 +1,196 @@
+#include "stdafx.h"
+#include
+#include "SystemBalloonIntercepter.h"
+
+HHOOK hookCallWndProc = NULL;
+HHOOK hookCBT = NULL;
+HHOOK hookShell = NULL;
+
+// Store the application instance of this module to pass to hook initialization. This is set in DLLMain().
+HINSTANCE g_appInstance = NULL;
+
+
+typedef void (CALLBACK *HookProc)(int code, WPARAM w, LPARAM l);
+
+static LRESULT CALLBACK CallWndProcHookCallback(int code, WPARAM wparam, LPARAM lparam);
+static LRESULT CALLBACK CBTHookCallback(int code, WPARAM wparam, LPARAM lparam);
+static LRESULT CALLBACK ShellHookCallback(int code, WPARAM wparam, LPARAM lparam);
+
+
+
+/*
+static LRESULT FAR PASCAL SubClassFunc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);
+static FARPROC lpfnOldWndProc;
+LONG x = SetWindowLongW(trayHwnd, GWL_WNDPROC, (DWORD) func);
+*/
+
+
+
+
+
+
+bool InitializeCallWndProcHook(int threadID, HWND destination)
+{
+ if (g_appInstance == NULL)
+ {
+ return false;
+ }
+
+ UINT msg_sysnot = RegisterWindowMessage("GFW_HOOK_INTERCEPT_SYSNOT");
+ UINT msg_replaced = RegisterWindowMessage("GFW_HOOK_CALLWNDPROC_REPLACED");
+ HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), "GFW_HOOK_HWND_CALLWNDPROC");
+ if (dstWnd != NULL)
+ {
+ PostMessage(dstWnd, msg_replaced, 0, 0);
+ }
+ dstWnd = destination;
+ SetProp(GetDesktopWindow(), "GFW_HOOK_HWND_CALLWNDPROC", dstWnd);
+
+ PostMessage(dstWnd, msg_sysnot, 0, 0);
+
+ // Uncomment these if you want to play with them
+ //hookCBT = SetWindowsHookEx(WH_CBT, (HOOKPROC)CBTHookCallback, g_appInstance, threadID);
+ //hookShell = SetWindowsHookEx(WH_SHELL, (HOOKPROC)ShellHookCallback, g_appInstance, threadID);
+
+ hookCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)CallWndProcHookCallback, g_appInstance, threadID);
+ return hookCallWndProc != NULL;
+}
+
+void UninitializeCallWndProcHook()
+{
+ if (hookCallWndProc != NULL)
+ UnhookWindowsHookEx(hookCallWndProc);
+ hookCallWndProc = NULL;
+
+ if (hookCBT != NULL)
+ UnhookWindowsHookEx(hookCBT);
+ hookCBT = NULL;
+
+ if (hookShell != NULL)
+ UnhookWindowsHookEx(hookShell);
+ hookShell = NULL;
+}
+
+static LRESULT CALLBACK CallWndProcHookCallback(int code, WPARAM wparam, LPARAM lparam)
+{
+ if (code >= 0)
+ {
+ UINT msg_sysnot = RegisterWindowMessage("GFW_HOOK_INTERCEPT_SYSNOT");
+ UINT msg_replaced = RegisterWindowMessage("GFW_HOOK_CALLWNDPROC_REPLACED");
+ HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), "GFW_HOOK_HWND_CALLWNDPROC");
+
+ PCWPSTRUCT pCwpStruct = (PCWPSTRUCT)lparam;
+ HWND hwnd = pCwpStruct->hwnd;
+ HWND trayHwnd = FindWindow("Shell_TrayWnd", NULL);
+
+ if((UINT) hwnd == (UINT) trayHwnd && pCwpStruct->message == WM_COPYDATA)
+ {
+ PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT)pCwpStruct->lParam;
+
+ if (cpData->dwData == 1)
+ {
+ DWORD trayCommand = *(DWORD *) (((BYTE *)cpData->lpData) + 4);
+ PNOTIFYICONDATA iconData = (PNOTIFYICONDATA) (((BYTE *)cpData->lpData) + 8);
+
+ BOOL isBalloon = (iconData->uFlags & NIF_INFO);
+ if(isBalloon)
+ {
+ /* Attempt #1 to hide the system balloon - but it is not yet visible at this point, so no dice
+ NOTIFYICONDATA nid;
+ ZeroMemory(&nid, NOTIFYICONDATA_V2_SIZE);
+ nid.cbSize = NOTIFYICONDATA_V2_SIZE;
+ nid.hWnd = iconData->hWnd;
+ nid.uID = iconData->uID;
+ nid.hIcon = iconData->hIcon;
+ nid.uFlags = NIF_INFO;
+ nid.szInfo[0] = 0;
+ Shell_NotifyIcon(NIM_MODIFY, &nid);
+ */
+
+ /* Attempt #2 to hide the system balloon - same result as above for the same reason
+ iconData->szInfo[0] = 0;
+ Shell_NotifyIcon(NIM_MODIFY, iconData);
+ */
+
+ // THIS IS THE LINE THAT SENDS A MESSAGE TO GROWL TO TRIGGER THE NOTIFICATION
+ // IT ONLY WORKS ON GROWL BUILDS THAT SUPPORT SYSTEM BALLOONS (WHICH IS JUST MY LOCAL BUILD AT THE MOMENT)
+ pCwpStruct->message = WM_USER + 44;
+
+ return SendMessageW(dstWnd, WM_COPYDATA, 97, (LPARAM)cpData);
+ }
+ }
+ }
+ }
+
+ return CallNextHookEx(hookCallWndProc, code, wparam, lparam);
+}
+
+static LRESULT CALLBACK CBTHookCallback(int code, WPARAM wparam, LPARAM lparam)
+{
+ if(code > 0)
+ {
+ HWND newHwnd = (HWND) wparam;
+ CHAR szClassName[255];
+ GetClassName(newHwnd, szClassName, 255);
+
+ if(code == HCBT_CREATEWND)
+ {
+ OutputDebugString(szClassName);
+
+ if((lstrcmpi(szClassName, TEXT("tooltips_class32")) == 0)
+ || (lstrcmpi(szClassName, TEXT("tooltips_class")) == 0))
+ {
+ WINDOWINFO pwi;
+ pwi.cbSize = sizeof(PWINDOWINFO);
+ GetWindowInfo(newHwnd, &pwi);
+
+ TCHAR szBuffer[256];
+
+ OutputDebugString("style:");
+ wsprintf(szBuffer, "%lu", pwi.dwStyle);
+ OutputDebugString(szBuffer);
+ OutputDebugString(szClassName);
+ if((pwi.dwStyle & 0x40)) OutputDebugString("THIS WINDOW HAS TTS_BALLOON STYLE");
+ OutputDebugString("----------------------");
+ }
+ }
+ }
+
+ return CallNextHookEx(hookCBT, code, wparam, lparam);
+}
+
+static LRESULT CALLBACK ShellHookCallback(int code, WPARAM wparam, LPARAM lparam)
+{
+ if(code == HSHELL_WINDOWACTIVATED
+ || code == HSHELL_WINDOWCREATED
+ || code == HSHELL_WINDOWREPLACED
+ || code == HSHELL_RUDEAPPACTIVATED)
+ {
+ HWND newHwnd = (HWND) wparam;
+ CHAR szClassName[255];
+ GetClassName(newHwnd, szClassName, 255);
+
+ OutputDebugString(szClassName);
+
+ /*
+ //if(!lstrcmp(szClassName, "tooltips_class32"))
+ //if(!lstrcmp(szClassName, "UserEventWindow"))
+ //{
+ WINDOWINFO pwi;
+ pwi.cbSize = sizeof(PWINDOWINFO);
+ GetWindowInfo(newHwnd, &pwi);
+
+ TCHAR szBuffer[256];
+
+ OutputDebugString("style:");
+ wsprintf(szBuffer, "%lu", pwi.dwStyle);
+ OutputDebugString(szBuffer);
+ OutputDebugString(szClassName);
+ if((pwi.dwStyle & 0x40)) OutputDebugString("THIS WINDOW HAS TTS_BALLOON STYLE");
+ OutputDebugString("----------------------");
+ //}
+ */
+ }
+
+ return CallNextHookEx(hookShell, code, wparam, lparam);
+}
\ No newline at end of file
diff --git a/Growl Extras/SystemBalloonIntercepter/stdafx.cpp b/Growl Extras/SystemBalloonIntercepter/stdafx.cpp
new file mode 100644
index 0000000..a59630b
--- /dev/null
+++ b/Growl Extras/SystemBalloonIntercepter/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// SystemHookCore.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/Growl Extras/SystemBalloonIntercepter/stdafx.h b/Growl Extras/SystemBalloonIntercepter/stdafx.h
new file mode 100644
index 0000000..7c255ab
--- /dev/null
+++ b/Growl Extras/SystemBalloonIntercepter/stdafx.h
@@ -0,0 +1,20 @@
+//
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+
+#define WINVER 0x0501
+#define _WIN32_WINNT 0x0501
+
+#define _WIN32_IE 0x0600
+
+#include
+#include
+#include
+
+// TODO: reference additional headers your program requires here