Add MSUserNotifications to Source/win32 directory to support notifications on windows

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/back/branches/gnustep_testplant_branch@39739 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Marcian Lytwyn 2016-05-11 20:46:04 +00:00
parent e67d8442e9
commit 58ca6b5c8e
35 changed files with 2974 additions and 0 deletions

View file

@ -43,6 +43,7 @@ GNUSTEP_LOCAL_ADDITIONAL_MAKEFILES=back.make
include $(GNUSTEP_MAKEFILES)/common.make
include ./Version
-include ../back/Version
#
# The list of subproject directories

View file

@ -33,6 +33,9 @@ include ../../config.make
# The library to be compiled, as a library or as a bundle
SUBPROJECT_NAME=win32
#win32_SUBPROJECTS = MSUserNotifications
SUBPROJECTS = MSUserNotifications
win32_LOCALIZED_RESOURCE_FILES = \
# The C source files to be compiled
@ -53,5 +56,6 @@ w32_GLcontext.m \
-include GNUmakefile.preamble
include $(GNUSTEP_MAKEFILES)/subproject.make
include $(GNUSTEP_MAKEFILES)/aggregate.make
-include GNUmakefile.postamble

View file

@ -0,0 +1,59 @@
ifeq ($(GNUSTEP_MAKEFILES),)
GNUSTEP_MAKEFILES := $(shell gnustep-config --variable=GNUSTEP_MAKEFILES 2>/dev/null)
ifeq ($(GNUSTEP_MAKEFILES),)
$(warning )
$(warning Unable to obtain GNUSTEP_MAKEFILES setting from gnustep-config!)
$(warning Perhaps gnustep-make is not properly installed,)
$(warning so gnustep-config is not in your PATH.)
$(warning )
$(warning Your PATH is currently $(PATH))
$(warning )
endif
endif
ifeq ($(GNUSTEP_MAKEFILES),)
$(error You need to set GNUSTEP_MAKEFILES before compiling!)
endif
BUNDLE_NAME = NSUserNotification
NSUserNotification_NEEDS_GUI = NO
NSUserNotification_CFLAGS += -DWINVER=0x0600 -D_WIN32_IE=0x0600 -DBUILD_DLL -fms-extensions
NSUserNotification_OBJCFLAGS += -DWINVER=0x0600 -D_WIN32_IE=0x0600 -DBUILD_DLL -fms-extensions
NSUserNotification_OBJCCFLAGS += -DWINVER=0x0600 -D_WIN32_IE=0x0600 -DBUILD_DLL -fms-extensions
NSUserNotification_PRINCIPAL_CLASS = MSUserNotificationCenter
NSUserNotification_OBJC_FILES =
NSUserNotification_OBJCC_FILES = MSUserNotification.mm
NSUserNotification_BUNDLE_LIBS = -lgnustep-gui -lgdi32
#NSUserNotification_LIBS = stdc++
#NSUserNotification_INCLUDE_DIRS += \
# -I/mingw/lib/gcc/mingw32/4.8.1/include/c++/mingw32 \
# -I/mingw/lib/gcc/mingw32/4.8.1/include/c++
NSUserNotification_RESOURCE_FILES = \
ToastNotifications/obj/ToastNotifications-0.dll \
TaskbarNotifications/obj/TaskbarNotifications-0.dll
#NSUserNotification_SUBPROJECTS = ToastNotifications TaskbarNotifications
SUBPROJECTS = ToastNotifications TaskbarNotifications
# Set this to the ROOT DIRECTORY of your GNUstep build that contains the 'core' directory...
BASE_VERSION_DEFINED =
ifneq ($(MAJOR_VERSION),)
ifneq ($(MINOR_VERSION),)
BASE_VERSION_DEFINED=1
endif
endif
ifdef ($(BASE_VERSION_DEFINED),)
ifeq ($(GNUSTEP_BUILD_ROOT),)
$(error You need to checkout core/base and/or set GNUSTEP_BUILD_ROOT to your gnustep root containing 'core/base' before compiling!)
endif
include ${GNUSTEP_BUILD_ROOT}/core/base/Version
BASE_VERSION_DEFINED=1
endif
libgnustep-base_INTERFACE_VERSION=$(MAJOR_VERSION).$(MINOR_VERSION)
NSUserNotification_INSTALL_DIR = $(GNUSTEP_LIBRARY)/Libraries/gnustep-base/Versions/$(libgnustep-base_INTERFACE_VERSION)/Resources/
#include GNUmakefile.postamble
include $(GNUSTEP_MAKEFILES)/common.make
include $(GNUSTEP_MAKEFILES)/aggregate.make
include $(GNUSTEP_MAKEFILES)/bundle.make

View file

@ -0,0 +1,12 @@
#
# Make list of class names for DLL exports. Uses the actual classes from
# the .o files, so it should really have everything needed.
#
libNSUserNotifications.def: $(OBJ_FILES_TO_LINK)
rm -f $@
rm -f _tmp.def
cat win32-def.top > $@
nm $^ | grep '^........ [TR] _' | sed 's/[^_]*_//' > _tmp.def
cat _tmp.def | grep "_class_name_" >> $@
rm -rf _tmp.def

View file

View file

@ -0,0 +1,75 @@
/* Interface for DKUserNotification for GNUstep
Copyright (C) 2014 Free Software Foundation, Inc.
Written by: Marcus Mueller <znek@mulle-kybernetik.com>
Date: 2014
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
// NOTE: for the time being, NSUserNotificationCenter needs this feature.
// Whenever this restriction is lifted, we can get rid of it here as well.
#if __has_feature(objc_default_synthesize_properties)
#include <windows.h>
#include <GNUstepBase/GSConfig.h>
// C++ header includes...
#include <string>
#include "MSUserNotificationAPI.h"
#import <GNUstepBase/GSVersionMacros.h>
#if OS_API_VERSION(MAC_OS_X_VERSION_10_8,GS_API_LATEST)
#include <Foundation/NSUUID.h>
#import <Foundation/NSUserNotification.h>
#if defined(__cplusplus)
extern "C" {
#endif
// DLL API Function prototypes...
typedef BOOL __cdecl (*SendNotificationFunctionPtr)(HWND, HICON, SEND_NOTE_INFO_PTR);
typedef BOOL __cdecl (*RemoveNotificationFunctionPtr)(HICON, REMOVE_NOTE_INFO_PTR);
typedef BOOL __cdecl (*ActivationCallbackFunctionPtr)(void);
typedef BOOL __cdecl (*SetActivationCallbackPtr)(ActivationCallbackFunctionPtr);
@class NSConnection;
@class NSArray;
@class NSMutableDictionary;
@protocol Notifications;
@interface MSUserNotificationCenter : NSUserNotificationCenter
{
HICON appIcon;
NSString *appIconPath;
NSMutableDictionary *imageToIcon;
NSArray *caps;
NSUInteger uniqueID;
NSMutableDictionary *idToNotes;
}
@end
#if defined(__cplusplus)
}
#endif
#endif /* OS_API_VERSION(MAC_OS_X_VERSION_10_8,GS_API_LATEST) */
#endif /* __has_feature(objc_default_synthesize_properties) */

View file

@ -0,0 +1,633 @@
/* Implementation for NSUserNotification for GNUstep/Windows
Copyright (C) 2014 Free Software Foundation, Inc.
Written by: Marcus Mueller <znek@mulle-kybernetik.com>
Date: 2014
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
// NOTE: for the time being, NSUserNotificationCenter needs this feature.
// Whenever this restriction is lifted, we can get rid of it here as well.
#if __has_feature(objc_default_synthesize_properties)
#define EXPOSE_NSUserNotification_IVARS 1
#define EXPOSE_NSUserNotificationCenter_IVARS 1
#import "MSUserNotification.h"
#import <GNUstepBase/GNUstep.h>
#import "GNUstepBase/NSObject+GNUstepBase.h"
#import "GNUstepBase/NSDebug+GNUstepBase.h"
#import <AppKit/NSGraphics.h>
#import <AppKit/NSImage.h>
#import <AppKit/NSWindow.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSBundle.h>
#import <Foundation/NSDictionary.h>
#import "Foundation/NSException.h"
#import <Foundation/NSProcessInfo.h>
#import <Foundation/NSScanner.h>
#import <Foundation/NSString.h>
#import <Foundation/NSURL.h>
#import <Foundation/NSUUID.h>
#import <Foundation/NSValue.h>
#include <stdio.h>
#include <windows.h>
#include <ShellAPI.h>
#include <shlwapi.h>
#if defined(__cplusplus)
extern "C" {
#endif
static SendNotificationFunctionPtr pSendNotification = NULL;
static RemoveNotificationFunctionPtr pRemoveNotification = NULL;
static HMODULE hNotificationLib = NULL;
static NSString * const kButtonActionKey = @"show";
@interface NSImage (Private)
- (NSString*)_filename;
@end
@implementation NSImage (Private)
- (NSString*)_filename
{
return _fileName;
}
@end
@interface NSUserNotification ()
@property (readwrite, retain) NSDate *actualDeliveryDate;
@property (readwrite, getter=isPresented) BOOL presented;
@property (readwrite) NSUserNotificationActivationType activationType;
@end
@interface NSUserNotificationCenter (Private)
- (NSUserNotification *) deliveredNotificationWithUniqueId: (id)uniqueId;
@end
@interface MSUserNotificationCenter (Private)
- (NSString *) cleanupTextIfNecessary: (NSString *)rawText;
@end
@implementation MSUserNotificationCenter
- (id) init
{
NSDebugLLog(@"NSUserNotification", @"initializing...");
self = [super init];
if (self)
{
NS_DURING
{
// Initialize instance variables...
imageToIcon = [NSMutableDictionary new];
uniqueID = 1;
NSBundle *classBundle = [NSBundle bundleForClass: [self class]];
NSBundle *mainBundle = [NSBundle mainBundle];
NSDictionary *infoDict = [mainBundle infoDictionary];
NSString *imageName = [[infoDict objectForKey:@"CFBundleIconFiles"] objectAtIndex:0];
NSString *imageType = @"";
NSString *path = nil;
NSImage *image = [self _imageForBundleInfo:infoDict];
appIcon = [self _iconFromImage:image];
appIconPath = [image _filename];
NSLog(@"%s:bundle: %@ image: %@ icon: %p path: %@", __PRETTY_FUNCTION__, classBundle, image, appIcon, appIconPath);
if (appIcon == NULL)
{
NSLog(@"%s:unable to load icon for bundle: %@ GetLastError: %ld", __PRETTY_FUNCTION__, mainBundle, GetLastError());
}
OSVERSIONINFO osvi = { 0 };
WINBOOL bIsWindows81orLater = false;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if (GetVersionEx(&osvi)== 0)
{
NSLog(@"%s:failed getting OS version with error id: %ld", __PRETTY_FUNCTION__, GetLastError());
}
else
{
#if defined(DEBUG)
NSLog(@"%s:version number is: %d", __PRETTY_FUNCTION__, osvi.dwMajorVersion);
#endif
if (osvi.dwMajorVersion >= 6)
{
if (osvi.dwMinorVersion == 1)
{
bIsWindows81orLater = 0;
}
else
{
bIsWindows81orLater = 1;
}
if(bIsWindows81orLater)
path = [classBundle pathForResource: @"ToastNotifications-0" ofType: @"dll"];
else
path = [classBundle pathForResource: @"TaskbarNotifications-0" ofType: @"dll"];
// Try loading the corresponding DLL required for notifications...
hNotificationLib = LoadLibrary([path UTF8String]);
// For for not good conditions...
if (hNotificationLib == NULL)
{
NSLog(@"%s:DLL load error: %ld", __PRETTY_FUNCTION__, GetLastError());
}
else
{
pSendNotification = (SendNotificationFunctionPtr)GetProcAddress(hNotificationLib, "sendNotification");
pRemoveNotification = (RemoveNotificationFunctionPtr)GetProcAddress(hNotificationLib, "removeNotification");
#if 1 //defined(DEBUG)
NSLog(@"%s:DLL ptr: %p send notification ptr: %p remove ptr: %p", __PRETTY_FUNCTION__,
hNotificationLib, pSendNotification, pRemoveNotification);
#endif
}
}
}
}
NS_HANDLER
{
}
NS_ENDHANDLER
}
return self;
}
- (void) dealloc
{
// Cleanup any icons we generated...
NSEnumerator *iter = [imageToIcon objectEnumerator];
HICON icon = NULL;
while ((icon = (HICON)[[iter nextObject] pointerValue]) != NULL)
{
DestroyIcon(icon);
}
[imageToIcon release];
[super dealloc];
}
- (NSImage*) _imageForBundleInfo:(NSDictionary*)infoDict
{
NSImage *image = nil;
NSString *appIconFile = [infoDict objectForKey: @"NSIcon"];
if (appIconFile && ![appIconFile isEqual: @""])
{
image = [NSImage imageNamed: appIconFile];
}
// Try to look up the icns file.
appIconFile = [infoDict objectForKey: @"CFBundleIconFile"];
if (appIconFile && ![appIconFile isEqual: @""])
{
image = [NSImage imageNamed: appIconFile];
}
if (image == nil)
{
image = [NSImage imageNamed: @"GNUstep"];
}
else
{
/* Set the new image to be named 'NSApplicationIcon' ... to do that we
* must first check that any existing image of the same name has its
* name removed.
*/
[(NSImage*)[NSImage imageNamed: @"NSApplicationIcon"] setName: nil];
// We need to copy the image as we may have a proxy here
image = AUTORELEASE([image copy]);
[image setName: @"NSApplicationIcon"];
}
return image;
}
- (HICON) _iconFromRep: (NSBitmapImageRep*)rep
{
HICON result = NULL;
if (rep)
{
int w = [rep pixelsWide];
int h = [rep pixelsHigh];
// Create a windows bitmap from the image representation's bitmap...
if ((w > 0) && (h > 0))
{
BITMAP bm;
HDC hDC = GetDC(NULL);
HDC hMainDC = CreateCompatibleDC(hDC);
HDC hAndMaskDC = CreateCompatibleDC(hDC);
HDC hXorMaskDC = CreateCompatibleDC(hDC);
HBITMAP hAndMaskBitmap = NULL;
HBITMAP hXorMaskBitmap = NULL;
// Create the source bitmap...
HBITMAP hSourceBitmap = CreateBitmap(w, h, [rep numberOfPlanes], [rep bitsPerPixel], [rep bitmapData]);
// Get the dimensions of the source bitmap
GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
// Create compatible bitmaps for the device context...
hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight);
hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight);
// Select the bitmaps to DC
HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap);
HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap);
HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap);
/* On windows, to calculate the color for a pixel, first an AND is done
* with the background and the "and" bitmap, then an XOR with the "xor"
* bitmap. This means that when the data in the "and" bitmap is 0, the
* pixel will get the color as specified in the "xor" bitmap.
* However, if the data in the "and" bitmap is 1, the result will be the
* background XOR'ed with the value in the "xor" bitmap. In case the "xor"
* data is completely black (0x000000) the pixel will become transparent,
* in case it's white (0xffffff) the pixel will become the inverse of the
* background color.
*/
// Scan each pixel of the souce bitmap and create the masks
int y;
int *pixel = (int*)[rep bitmapData];
for(y = 0; y < bm.bmHeight; ++y)
{
int x;
for (x = 0; x < bm.bmWidth; ++x)
{
if (*pixel++ == 0x00000000)
{
SetPixel(hAndMaskDC, x, y, RGB(255, 255, 255));
SetPixel(hXorMaskDC, x, y, RGB(0, 0, 0));
}
else
{
SetPixel(hAndMaskDC, x, y, RGB(0, 0, 0));
SetPixel(hXorMaskDC, x, y, GetPixel(hMainDC, x, y));
}
}
}
// Reselect the old bitmap objects...
SelectObject(hMainDC, hOldMainBitmap);
SelectObject(hAndMaskDC, hOldAndMaskBitmap);
SelectObject(hXorMaskDC, hOldXorMaskBitmap);
// Create the cursor from the generated and/xor data...
ICONINFO iconinfo = { 0 };
iconinfo.fIcon = FALSE;
iconinfo.xHotspot = 0;
iconinfo.yHotspot = 0;
iconinfo.hbmMask = hAndMaskBitmap;
iconinfo.hbmColor = hXorMaskBitmap;
// Finally, try to create the cursor...
result = CreateIconIndirect(&iconinfo);
// Cleanup the DC's...
DeleteDC(hXorMaskDC);
DeleteDC(hAndMaskDC);
DeleteDC(hMainDC);
// Cleanup the bitmaps...
DeleteObject(hXorMaskBitmap);
DeleteObject(hAndMaskBitmap);
DeleteObject(hSourceBitmap);
// Release the screen HDC...
ReleaseDC(NULL,hDC);
}
}
return(result);
}
- (NSBitmapImageRep*) _getStandardBitmap:(NSImage *)image
{
NSBitmapImageRep *rep;
if (image == nil)
{
return nil;
}
rep = (NSBitmapImageRep *)[image bestRepresentationForDevice: nil];
if (!rep || ![rep respondsToSelector: @selector(samplesPerPixel)])
{
/* FIXME: We might create a blank cursor here? */
//NSLog(@"%s:could not convert cursor bitmap data for image: %@", __PRETTY_FUNCTION__, image);
return nil;
}
else
{
// Convert into something usable by the backend
return [rep _convertToFormatBitsPerSample: 8
samplesPerPixel: [rep hasAlpha] ? 4 : 3
hasAlpha: [rep hasAlpha]
isPlanar: NO
colorSpaceName: NSCalibratedRGBColorSpace
bitmapFormat: 0
bytesPerRow: 0
bitsPerPixel: 0];
}
}
- (HICON) _iconFromImage: (NSImage *)image
{
// Default the return cursur ID to NULL...
HICON result = NULL;
#if defined(DEBUG)
NSLog(@"%s:image: %@ imageToIcon dict: %@", __PRETTY_FUNCTION__, image, imageToIcon);
#endif
if ([image name] == nil)
{
NSLog(@"%s:cannot create/store image icon for NIL image names: %@", __PRETTY_FUNCTION__, result, [image name]);
}
else if ([imageToIcon objectForKey:[image name]])
{
result = (HICON)[[imageToIcon objectForKey: image] pointerValue];
#if defined(DEBUG)
NSLog(@"%s:reusing icon: %p for imageName: %@", __PRETTY_FUNCTION__, result, [image name]);
#endif
}
else
{
NSBitmapImageRep *rep = [self _getStandardBitmap:image];
if (rep == NULL)
{
NSLog(@"%s:error creating standard bitmap for image: %@", __PRETTY_FUNCTION__, image);
}
else
{
// Try to create the icon from the image...
result = [self _iconFromRep:rep];
// Need to save these created cursors to remove later...
if (result != NULL)
{
[imageToIcon setObject:[NSValue valueWithPointer:result] forKey:[image name]];
#if defined(DEBUG)
NSLog(@"%s:saving icon: %p for imageName: %@", __PRETTY_FUNCTION__, result, [image name]);
#endif
}
}
}
// Return whatever we were able to generate...
return(result);
}
- (GUID)guidFromUUIDString:(NSString*)uuidString
{
NSLog(@"%s:UUIDString: %@", __PRETTY_FUNCTION__, uuidString);
GUID theGUID = { 0 };
NSUInteger value = 0;
NSArray *components = [uuidString componentsSeparatedByString: @"-"];
NSScanner *scanner1 = [NSScanner scannerWithString: [components objectAtIndex: 0]];
NSScanner *scanner2 = [NSScanner scannerWithString: [components objectAtIndex: 1]];
NSScanner *scanner3 = [NSScanner scannerWithString: [components objectAtIndex: 2]];
NSString *data4 = [[components objectAtIndex: 3] stringByAppendingString: [components objectAtIndex: 4]];
NSScanner *scanner4 = [NSScanner scannerWithString: data4];
[scanner1 scanHexInt: (NSUInteger*)&theGUID.Data1];
[scanner2 scanHexInt: (NSUInteger*)&value];
theGUID.Data2 = (WORD) value;
[scanner3 scanHexInt: (NSUInteger*)&value];
theGUID.Data3 = (WORD) value;
return theGUID;
}
- (GUID)guidFromUUID:(NSUUID*)uuid
{
// Note: This is an example GUID only and should not be used.
// Normally, you should use a GUID-generating tool to provide the value to
// assign to guidItem.
return([self guidFromUUIDString:[uuid UUIDString]]);
}
- (NSUUID*)generateUUID
{
return [NSUUID UUID];
}
- (GUID)generateGUID
{
return [self guidFromUUID:[self generateUUID]];
}
- (NSNumber*)_showNotification:(NSUserNotification*)note forWindow:(NSWindow*)forWindow
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
#if defined(DEBUG)
NSLog(@"%s:title: %@ informativeText: %@ contentImage: %@ (%@)", __PRETTY_FUNCTION__, note.title, note.informativeText, note.contentImage, [note.contentImage _filename]);
#endif
NSNumber *result = nil;
if (pSendNotification != NULL)
{
NSUUID *uuid = [self generateUUID];
NSString *UUIDString = [uuid UUIDString];
std::string uuidString = std::string([UUIDString UTF8String]);
// Try to send the notification...
SEND_NOTE_INFO_T noteInfo = { 0 };
noteInfo.uuidString = [UUIDString UTF8String];
noteInfo.title = [note.title UTF8String];
noteInfo.informativeText = [note.informativeText UTF8String];
noteInfo.appIconPath = [appIconPath UTF8String];
// Content image is for displaying within the notification...
// i.e. Shell notes - within the balloon somwhere...
// Toast notes - with the toast window somewhere
// Check for content image and generate a windows icon from it...
if (note.contentImage != nil)
{
// Attempt to create a window icon from image...
noteInfo.contentIcon = [self _iconFromImage:note.contentImage];
#if defined(DEBUG)
NSLog(@"%s:image: %@ icon: %p", __PRETTY_FUNCTION__, note.contentImage, noteInfo.contentIcon);
#endif
}
BOOL status = pSendNotification((HWND)GetModuleHandle(NULL), appIcon, &noteInfo);
if (status)
{
note.identifier = [[UUIDString copy] autorelease];
note.presented = YES;
#if defined(DEBUG)
NSLog(@"%s:status: %d uniqueID: %d", __PRETTY_FUNCTION__, status, uniqueID);
#endif
return [NSNumber numberWithBool:uniqueID++];
}
}
// Cleanup...
[pool drain];
// TODO: Should we return something else here????
return [NSNumber numberWithBool:0];
}
- (void) _deliverNotification: (NSUserNotification *)un
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSString *appName = nil;
NSString *imageName = nil;
NSURL *imageURL = nil;
NSURL *soundFileURL = nil;
NSBundle *bundle = [NSBundle mainBundle];
if (bundle)
{
NSDictionary *info = [bundle localizedInfoDictionary];
if (info)
{
appName = [info objectForKey: @"NSBundleName"];
imageName = [info objectForKey: @"NSIcon"];
if (imageName)
imageURL = [bundle URLForResource: imageName withExtension: nil];
}
if (un.soundName && [caps containsObject:@"sound"])
{
soundFileURL = [bundle URLForResource: un.soundName
withExtension: nil];
}
}
// fallback
if (!appName)
appName = [[NSProcessInfo processInfo] processName];
NSMutableArray *actions = [NSMutableArray array];
#if 0
if ([un hasActionButton])
{
NSString *actionButtonTitle = un.actionButtonTitle;
if (!actionButtonTitle)
actionButtonTitle = _(@"Show");
// NOTE: don't use "default", as it's used by convention and seems
// to remove the actionButton entirely
// (tested with Notification Daemon (0.3.7))
[actions addObject: kButtonActionKey];
[actions addObject: [self cleanupTextIfNecessary: actionButtonTitle]];
}
#endif
NSDebugMLLog(@"NSUserNotification",
@"appName: %@ imageName: %@ imageURL: %@ soundFileURL: %@",
appName, imageName, imageURL, soundFileURL);
NSMutableDictionary *hints = [NSMutableDictionary dictionary];
if (un.userInfo)
[hints addEntriesFromDictionary: un.userInfo];
if (imageURL)
[hints setObject: [imageURL absoluteString] forKey: @"image-path"];
if (soundFileURL)
[hints setObject: [soundFileURL path] forKey: @"sound-file"];
NSString *summary = [self cleanupTextIfNecessary: un.title];
NSString *body = [self cleanupTextIfNecessary: un.informativeText];
NSNumber *uniqueId = [self _showNotification:un forWindow: nil];
ASSIGN(un->_uniqueId, uniqueId);
un.presented = ([uniqueId integerValue] != 0) ? YES : NO;
[pool drain];
}
- (void)_removeDeliveredNotification:(NSUserNotification *)theNote
{
#if defined(DEBUG)
NSLog(@"%s:note: %@ ID: %@", __PRETTY_FUNCTION__, theNote, theNote->_uniqueId);
#endif
if (pRemoveNotification != NULL)
{
REMOVE_NOTE_INFO_T noteInfo = { 0 };
noteInfo.uniqueID = [theNote->_uniqueId integerValue];
pRemoveNotification(appIcon, &noteInfo);
}
}
- (NSString *)cleanupTextIfNecessary:(NSString *)rawText
{
if (!rawText || ![caps containsObject:@"body-markup"])
return nil;
NSMutableString *t = (NSMutableString *)[rawText mutableCopy];
[t replaceOccurrencesOfString: @"&" withString: @"&amp;" options: 0 range: NSMakeRange(0, [t length])]; // must be first!
[t replaceOccurrencesOfString: @"<" withString: @"&lt;" options: 0 range: NSMakeRange(0, [t length])];
[t replaceOccurrencesOfString: @">" withString: @"&gt;" options: 0 range: NSMakeRange(0, [t length])];
[t replaceOccurrencesOfString: @"\"" withString: @"&quot;" options: 0 range: NSMakeRange(0, [t length])];
[t replaceOccurrencesOfString: @"'" withString: @"&apos;" options: 0 range: NSMakeRange(0, [t length])];
return t;
}
// SIGNALS
- (void)receiveNotificationClosedNotification:(NSNotification *)n
{
id nId = [[n userInfo] objectForKey: @"arg0"];
NSUserNotification *un = [self deliveredNotificationWithUniqueId: nId];
NSDebugMLLog(@"NSUserNotification", @"%@", un);
}
- (void)receiveActionInvokedNotification:(NSNotification *)n
{
id nId = [[n userInfo] objectForKey: @"arg0"];
NSUserNotification *un = [self deliveredNotificationWithUniqueId: nId];
NSString *action = [[n userInfo] objectForKey: @"arg1"];
NSDebugMLLog(@"NSUserNotification", @"%@ -- action: %@", un, action);
if ([action isEqual:kButtonActionKey])
un.activationType = NSUserNotificationActivationTypeActionButtonClicked;
else
un.activationType = NSUserNotificationActivationTypeContentsClicked;
if (self.delegate && [self.delegate respondsToSelector:@selector(userNotificationCenter:didActivateNotification:)])
[self.delegate userNotificationCenter: self didActivateNotification: un];
}
@end
#if defined(__cplusplus)
}
#endif
#endif /* __has_feature(objc_default_synthesize_properties) */

View file

@ -0,0 +1,25 @@
#pragma once
#include <string>
#if defined(__cplusplus)
extern "C" { // Only if you are using C++ rather than C
#endif
typedef struct _SEND_NOTE_INFO
{
const char *uuidString;
const char *title;
const char *informativeText;
HICON contentIcon;
const char *appIconPath;
} SEND_NOTE_INFO_T, *SEND_NOTE_INFO_PTR;
typedef struct _REMOVE_NOTE_INFO
{
UINT uniqueID;
} REMOVE_NOTE_INFO_T, *REMOVE_NOTE_INFO_PTR;
#if defined(__cplusplus)
}
#endif

View file

@ -0,0 +1,30 @@
ifeq ($(GNUSTEP_MAKEFILES),)
GNUSTEP_MAKEFILES := $(shell gnustep-config --variable=GNUSTEP_MAKEFILES 2>/dev/null)
ifeq ($(GNUSTEP_MAKEFILES),)
$(warning )
$(warning Unable to obtain GNUSTEP_MAKEFILES setting from gnustep-config!)
$(warning Perhaps gnustep-make is not properly installed,)
$(warning so gnustep-config is not in your PATH.)
$(warning )
$(warning Your PATH is currently $(PATH))
$(warning )
endif
endif
ifeq ($(GNUSTEP_MAKEFILES),)
$(error You need to set GNUSTEP_MAKEFILES before compiling!)
endif
LIBRARY_NAME = TaskbarNotifications
TaskbarNotifications_NEEDS_GUI = NO
TaskbarNotifications_CFLAGS += -DWINVER=0x0600 -D_WIN32_IE=0x0600 -DBUILD_DLL -fms-extensions
TaskbarNotifications_OBJCFLAGS += -DWINVER=0x0600 -D_WIN32_IE=0x0600 -DBUILD_DLL -fms-extensions
TaskbarNotifications_OBJCCFLAGS += -DWINVER=0x0600 -D_WIN32_IE=0x0600 -DBUILD_DLL -fms-extensions
TaskbarNotifications_OBJC_FILES =
TaskbarNotifications_OBJCC_FILES = TaskbarNotifications.mm
TaskbarNotifications_LIBRARIES_DEPEND_UPON += -lgnustep-gui -lgdi32
TaskbarNotifications_RESOURCE_FILES +=
include $(GNUSTEP_MAKEFILES)/common.make
include $(GNUSTEP_MAKEFILES)/library.make

View file

@ -0,0 +1,863 @@
// Windows documentation - see: https://msdn.microsoft.com/en-us/library/windows/desktop/bb773352(v=vs.85).aspx
// Several members of this structure are only supported for Windows 2000 and later. To enable these members,
// include one of the following lines in your header:
// // Windows Vista and later:
// #define NTDDI_VERSION NTDDI_WIN2K
// #define NTDDI_VERSION NTDDI_WINXP
// #define NTDDI_VERSION NTDDI_VISTA
//
// // Windows XP and earlier:
// #define _WIN32_IE 0x0500
//
// We've defined these in the GNUmakefile!!!!
// MUST BE FIRST!!!
#include <../MSUserNotification.h>
#include <AppKit/NSGraphics.h>
#include <AppKit/NSImage.h>
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSString.h>
#include <Foundation/NSScanner.h>
#include <Foundation/NSUserNotification.h>
#include <Foundation/NSUUID.h>
#include <Foundation/NSValue.h>
#include <ShellAPI.h>
#include <shlwapi.h>
//#include <Winuser.h>
#include <rpcdce.h>
#if !defined(NIIF_USER)
#define NIIF_USER 0x00000004
#endif
#if !defined(NIN_SELECT)
#define NIN_SELECT (WM_USER + 0)
#endif
#if !defined(NIN_BALLOONSHOW)
#define NIN_BALLOONSHOW (WM_USER + 2)
#endif
#if !defined(NIN_BALLOONHIDE)
#define NIN_BALLOONHIDE (WM_USER + 3)
#endif
#if !defined(NIN_BALLOONTIMEOUT)
#define NIN_BALLOONTIMEOUT (WM_USER + 4)
#endif
#if !defined(NIN_BALLOONUSERCLICK)
#define NIN_BALLOONUSERCLICK (WM_USER + 5)
#endif
#ifdef BUILD_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif
#define WIN_EXTRABYTES 0
#define WINDOW_CLASS_NAME TEXT("NSUserNotificationTaskbar")
#define WINDOW_TITLE_NAME TEXT("TaskBarNotifierWin")
#define NOTIFY_MESSAGE_NAME TEXT("NSUserNotificationWindowsMessage")
#if defined(__cplusplus)
extern "C" {
#endif
void _registerWindowsClass();
void _unregisterWindowsClass();
void _initWin32Context();
void _destroyWin32Context();
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HICON _iconForBundle();
void _setupNotifyDataIcon(NOTIFYICONDATA&, HICON);
void _setupNotifyDataUUID(NOTIFYICONDATA &, const char *);
void _setupNotifyData(NOTIFYICONDATA&);
UINT _addApplicationIcon(DWORD, const char *, HICON);
void _removeApplicationIcon(DWORD, HICON);
void _removeProcessInfo();
void _removeApplicationIconForID(UINT appIconID);
static HANDLE gHandleDLL = NULL;
static HWND gHandleWin = NULL;
static UINT gNotifyMsg = 0;
static UINT gNotifyCnt = 0;
// Objective-C/GNUstep references...
static NSString *gUuidString = nil;
static NSMutableDictionary *gProcessInfo = nil;
// Need to capture the DLL instance handle....
BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
DWORD processID = GetCurrentProcessId();
#if defined(DEBUG)
NSLog(@"%s:hinstDLL: %p dwReason: %d lpvReserved: %p procID: %p", __PRETTY_FUNCTION__, hinstDLL, dwReason, lpvReserved, processID);
#endif
switch (dwReason)
{
case DLL_PROCESS_ATTACH: // Do process attach specific stuff here...
// TODO: DO we need to worry about OTHER PROCESSES ATTACHING???
// Save the DLL instance handle...
// FIXME: Need process specific code here to setup for capturing the generated icons
gHandleDLL = hinstDLL;
#if 0 // DOES NOT WORK - error code 126 - "The specified module could not be found"
// even though the instance handle is the same as the modile handle...
// Disable thread attache/detach invocations...
if (DisableThreadLibraryCalls((HMODULE)hinstDLL) == 0)
{
NSLog(@"%s:PROCESS_ATTACH:disable thread library calls error: %d", __PRETTY_FUNCTION__, GetLastError());
return FALSE;
}
#endif
// General intialization...
_initWin32Context();
// If no window...
if (gHandleWin == NULL)
return FALSE; // This is bad...
// Log our info...
#if 0 //defined(DEBUG)
NSLog(@"%s:PROCESS_ATTACH:gHandleDLL: %p gHandleWin: %p", __PRETTY_FUNCTION__, gHandleDLL, gHandleWin);
#endif
break;
case DLL_PROCESS_DETACH: // Do process attach specific stuff here...
// FIXME: Need process specific code here to remove any generated icons
#if 0 //defined(DEBUG)
NSLog(@"%s:PROCESS_DETACH:gHandleDLL: %p gHandleWin: %p", __PRETTY_FUNCTION__, gHandleDLL, gHandleWin);
#endif
// Cleanup window stuff...
_destroyWin32Context();
break;
case DLL_THREAD_ATTACH: // Do thread attach specific stuff here...
// FIXME: Do we need thread specific code here...
#if 0 //defined(DEBUG)
NSLog(@"%s:THREAD_ATTACH:gHandleDLL: %p gHandleWin: %p", __PRETTY_FUNCTION__, gHandleDLL, gHandleWin);
#endif
break;
case DLL_THREAD_DETACH: // Do thread detach specific stuff here...
// FIXME: Do we need thread specific code here...
#if 0 //defined(DEBUG)
NSLog(@"%s:THREAD_DETACH:gHandleDLL: %p gHandleWin: %p", __PRETTY_FUNCTION__, gHandleDLL, gHandleWin);
#endif
break;
}
return TRUE;
}
void _initWin32Context()
{
NSString *gHandleString = nil;
NSNumber *value = [NSNumber numberWithInteger:GetCurrentProcessId()];
// Register our message window type......
_registerWindowsClass();
// Register the windows notify message we want...
gNotifyMsg = RegisterWindowMessage(NOTIFY_MESSAGE_NAME);
if (gNotifyMsg == 0)
{
NSLog(@"%s:error registering windos message - error: %d", __PRETTY_FUNCTION__, GetLastError());
return;
}
// Create a message only window...if it hasn't been created yet...
gHandleWin = CreateWindowEx( 0, WINDOW_CLASS_NAME, WINDOW_TITLE_NAME, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL );
if (gHandleWin == NULL)
{
NSLog(@"%s:PROCESS_ATTACH:create window error: %d", __PRETTY_FUNCTION__, GetLastError());
return;
}
// Initialize necessary data structures...
gProcessInfo = [NSMutableDictionary new];
[gProcessInfo setObject:[NSMutableDictionary dictionary] forKey:@"AppIcons"];
[gProcessInfo setObject:[NSMutableDictionary dictionary] forKey:@"AppNotes"];
[gProcessInfo setObject:value forKey:@"ProcessID"];
}
void _destroyWin32Context()
{
// Remove the process' info dictionary...
_removeProcessInfo();
// Destroy the message window...
if (gHandleWin != NULL)
DestroyWindow(gHandleWin);
// Unregister the window class...
_unregisterWindowsClass();
}
NSImage *_imageForBundleInfo(NSDictionary*infoDict)
{
NSImage *image = nil;
NSString *appIconFile = [infoDict objectForKey: @"NSIcon"];
if (appIconFile && ![appIconFile isEqual: @""])
{
image = [NSImage imageNamed: appIconFile];
}
// Try to look up the icns file.
appIconFile = [infoDict objectForKey: @"CFBundleIconFile"];
if (appIconFile && ![appIconFile isEqual: @""])
{
image = [NSImage imageNamed: appIconFile];
}
if (image == nil)
{
image = [NSImage imageNamed: @"GNUstep"];
}
else
{
/* Set the new image to be named 'NSApplicationIcon' ... to do that we
* must first check that any existing image of the same name has its
* name removed.
*/
[(NSImage*)[NSImage imageNamed: @"NSApplicationIcon"] setName: nil];
// We need to copy the image as we may have a proxy here
image = AUTORELEASE([image copy]);
[image setName: @"NSApplicationIcon"];
}
return image;
}
HICON _iconFromRep(NSBitmapImageRep* rep)
{
HICON result = NULL;
if (rep)
{
int w = [rep pixelsWide];
int h = [rep pixelsHigh];
// Create a windows bitmap from the image representation's bitmap...
if ((w > 0) && (h > 0))
{
BITMAP bm;
HDC hDC = GetDC(NULL);
HDC hMainDC = CreateCompatibleDC(hDC);
HDC hAndMaskDC = CreateCompatibleDC(hDC);
HDC hXorMaskDC = CreateCompatibleDC(hDC);
HBITMAP hAndMaskBitmap = NULL;
HBITMAP hXorMaskBitmap = NULL;
// Create the source bitmap...
HBITMAP hSourceBitmap = CreateBitmap(w, h, [rep numberOfPlanes], [rep bitsPerPixel], [rep bitmapData]);
// Get the dimensions of the source bitmap
GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
// Create compatible bitmaps for the device context...
hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight);
hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight);
// Select the bitmaps to DC
HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap);
HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap);
HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap);
/* On windows, to calculate the color for a pixel, first an AND is done
* with the background and the "and" bitmap, then an XOR with the "xor"
* bitmap. This means that when the data in the "and" bitmap is 0, the
* pixel will get the color as specified in the "xor" bitmap.
* However, if the data in the "and" bitmap is 1, the result will be the
* background XOR'ed with the value in the "xor" bitmap. In case the "xor"
* data is completely black (0x000000) the pixel will become transparent,
* in case it's white (0xffffff) the pixel will become the inverse of the
* background color.
*/
// Scan each pixel of the souce bitmap and create the masks
int y;
int *pixel = (int*)[rep bitmapData];
for(y = 0; y < bm.bmHeight; ++y)
{
int x;
for (x = 0; x < bm.bmWidth; ++x)
{
if (*pixel++ == 0x00000000)
{
SetPixel(hAndMaskDC, x, y, RGB(255, 255, 255));
SetPixel(hXorMaskDC, x, y, RGB(0, 0, 0));
}
else
{
SetPixel(hAndMaskDC, x, y, RGB(0, 0, 0));
SetPixel(hXorMaskDC, x, y, GetPixel(hMainDC, x, y));
}
}
}
// Reselect the old bitmap objects...
SelectObject(hMainDC, hOldMainBitmap);
SelectObject(hAndMaskDC, hOldAndMaskBitmap);
SelectObject(hXorMaskDC, hOldXorMaskBitmap);
// Create the cursor from the generated and/xor data...
ICONINFO iconinfo = { 0 };
iconinfo.fIcon = FALSE;
iconinfo.xHotspot = 0;
iconinfo.yHotspot = 0;
iconinfo.hbmMask = hAndMaskBitmap;
iconinfo.hbmColor = hXorMaskBitmap;
// Finally, try to create the cursor...
result = CreateIconIndirect(&iconinfo);
// Cleanup the DC's...
DeleteDC(hXorMaskDC);
DeleteDC(hAndMaskDC);
DeleteDC(hMainDC);
// Cleanup the bitmaps...
DeleteObject(hXorMaskBitmap);
DeleteObject(hAndMaskBitmap);
DeleteObject(hSourceBitmap);
// Release the screen HDC...
ReleaseDC(NULL,hDC);
}
}
return(result);
}
NSBitmapImageRep *_getStandardBitmap(NSImage *image)
{
NSBitmapImageRep *rep;
if (image == nil)
{
return nil;
}
rep = (NSBitmapImageRep *)[image bestRepresentationForDevice: nil];
if (!rep || ![rep respondsToSelector: @selector(samplesPerPixel)])
{
/* FIXME: We might create a blank cursor here? */
#if defined(DEBUG)
NSLog(@"%s:could not convert cursor bitmap data for image: %@", __PRETTY_FUNCTION__, image);
#endif
return nil;
}
else
{
// Convert into something usable by the backend
return [rep _convertToFormatBitsPerSample: 8
samplesPerPixel: [rep hasAlpha] ? 4 : 3
hasAlpha: [rep hasAlpha]
isPlanar: NO
colorSpaceName: NSCalibratedRGBColorSpace
bitmapFormat: 0
bytesPerRow: 0
bitsPerPixel: 0];
}
}
HICON _iconFromImage(NSImage *image)
{
// Default the return cursur ID to NULL...
HICON result = NULL;
NSBitmapImageRep *rep = _getStandardBitmap(image);
if (rep == NULL)
{
NSLog(@"%s:error creating standard bitmap for image: %@", __PRETTY_FUNCTION__, image);
}
else
{
// Try to create the icon from the image...
result = _iconFromRep(rep);
}
// Return whatever we were able to generate...
return(result);
}
HICON _iconForBundle()
{
NSBundle *mainBundle = [NSBundle mainBundle];
NSDictionary *infoDict = [mainBundle infoDictionary];
NSLog(@"%s:infoDict: %@", __PRETTY_FUNCTION__, infoDict);
NSString *imageName = [[infoDict objectForKey:@"CFBundleIconFiles"] objectAtIndex:0];
NSString *imageType = @"";
NSString *path = [mainBundle pathForResource:imageName ofType:imageType];
NSImage *image = _imageForBundleInfo(infoDict);
return _iconFromImage(image);
}
void _registerWindowsClass()
{
WNDCLASSEX wc = { 0 };
// Register the main window class.
wc.cbSize = sizeof(wc);
wc.style = 0;
wc.lpfnWndProc = (WNDPROC)MainWndProc;
wc.cbClsExtra = 0;
// Keep extra space for each window, for OFF_LEVEL and OFF_ORDERED
wc.cbWndExtra = WIN_EXTRABYTES;
wc.hInstance = (HINSTANCE)gHandleDLL;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = WINDOW_CLASS_NAME;
wc.hIconSm = NULL;
if (!RegisterClassEx(&wc))
{
NSLog(@"%s:error registering windows class - error: %d", __PRETTY_FUNCTION__, GetLastError());
return;
}
// FIXME We should use GetSysColor to get standard colours from MS Window and
// use them in NSColor
// Should we create a message only window here, so we can get events, even when
// no windows are created?
}
void _unregisterWindowsClass()
{
UnregisterClass(WINDOW_CLASS_NAME, (HINSTANCE)gHandleDLL);
}
/* Windows documentation.....
See: https://msdn.microsoft.com/en-us/library/windows/desktop/bb773352(v=vs.85).aspx
An application-defined message identifier. The system uses this identifier to send
notification messages to the window identified in hWnd. These notification messages
are sent when a mouse event or hover occurs in the bounding rectangle of the icon,
when the icon is selected or activated with the keyboard, or when those actions occur
in the balloon notification.
When the uVersion member is either 0 or NOTIFYICON_VERSION, the wParam parameter of the
message contains the identifier of the taskbar icon in which the event occurred. This
identifier can be 32 bits in length. The lParam parameter holds the mouse or keyboard
message associated with the event. For example, when the pointer moves over a taskbar
icon, lParam is set to WM_MOUSEMOVE.
When the uVersion member is NOTIFYICON_VERSION_4, applications continue to receive
notification events in the form of application-defined messages through the uCallbackMessage
member, but the interpretation of the lParam and wParam parameters of that message is changed
as follows:
o LOWORD(lParam) contains notification events, such as NIN_BALLOONSHOW, NIN_POPUPOPEN, or
WM_CONTEXTMENU.
o HIWORD(lParam) contains the icon ID. Icon IDs are restricted to a length of 16 bits.
o GET_X_LPARAM(wParam) returns the X anchor coordinate for notification events NIN_POPUPOPEN,
NIN_SELECT, NIN_KEYSELECT, and all mouse messages between WM_MOUSEFIRST and WM_MOUSELAST.
If any of those messages are generated by the keyboard, wParam is set to the upper-left corner
of the target icon. For all other messages, wParam is undefined.
o GET_Y_LPARAM(wParam) returns the Y anchor coordinate for notification events and messages
as defined for the X anchor.
*/
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
LRESULT status = 0;
#if defined(DEBUG)
NSLog(@"%s:hwnd: %p uMsg %d wParam: %p lParam: %p", __PRETTY_FUNCTION__, hwnd, uMsg, wParam, lParam);
#endif
// If it's not our notify event message ID then...
if (uMsg != gNotifyMsg)
{
// invoke default windows procedure to handle the message...
status = DefWindowProc(hwnd, uMsg, wParam, lParam);
}
else // Otherwise we'll process it...
{
UINT loword = LOWORD(lParam);
switch (loword)
{
case NIN_SELECT:
#if defined(DEBUG)
NSLog(@"%s:NIN_SELECT:", __PRETTY_FUNCTION__);
#endif
break;
case NIN_BALLOONSHOW:
#if defined(DEBUG)
NSLog(@"%s:NIN_BALLOONSHOW:", __PRETTY_FUNCTION__);
#endif
break;
case NIN_BALLOONHIDE:
#if defined(DEBUG)
NSLog(@"%s:NIN_BALLOONHIDE:", __PRETTY_FUNCTION__);
#endif
break;
case NIN_BALLOONTIMEOUT:
#if defined(DEBUG)
NSLog(@"%s:NIN_BALLOONTIMEOUT:", __PRETTY_FUNCTION__);
#endif
break;
case NIN_BALLOONUSERCLICK:
#if defined(DEBUG)
NSLog(@"%s:NIN_BALLOONUSERCLICK:", __PRETTY_FUNCTION__);
#endif
break;
case NIN_POPUPOPEN:
#if defined(DEBUG)
NSLog(@"%s:NIN_POPUPOPEN:", __PRETTY_FUNCTION__);
#endif
break;
case NIN_POPUPCLOSE:
#if defined(DEBUG)
NSLog(@"%s:NIN_POPUPCLOSE:", __PRETTY_FUNCTION__);
#endif
break;
case WM_MOUSEMOVE:
#if defined(DEBUG)
NSLog(@"%s:WM_MOUSEMOVE:", __PRETTY_FUNCTION__);
#endif
break;
case WM_LBUTTONUP:
#if defined(DEBUG)
NSLog(@"%s:WM_LBUTTONUP:", __PRETTY_FUNCTION__);
#endif
break;
case WM_LBUTTONDOWN:
#if defined(DEBUG)
NSLog(@"%s:WM_LBUTTONDOWN:", __PRETTY_FUNCTION__);
#endif
break;
case WM_LBUTTONDBLCLK:
#if defined(DEBUG)
NSLog(@"%s:WM_LBUTTONDBLCLK:", __PRETTY_FUNCTION__);
#endif
break;
case WM_RBUTTONUP:
#if defined(DEBUG)
NSLog(@"%s:WM_RBUTTONUP:", __PRETTY_FUNCTION__);
#endif
break;
case WM_RBUTTONDOWN:
#if defined(DEBUG)
NSLog(@"%s:WM_RBUTTONDOWN:", __PRETTY_FUNCTION__);
#endif
break;
case WM_RBUTTONDBLCLK:
#if defined(DEBUG)
NSLog(@"%s:WM_RBUTTONDBLCLK:", __PRETTY_FUNCTION__);
#endif
break;
case WM_CONTEXTMENU:
#if defined(DEBUG)
NSLog(@"%s:WM_CONTEXTMENU:", __PRETTY_FUNCTION__);
#endif
break;
default:
NSLog(@"%s:default - unhandled notification event: %d", __PRETTY_FUNCTION__, loword);
status = 1;
break;
}
}
// Cleanup...
[pool drain];
return status;
}
NSMutableDictionary *appIconsForProcess()
{
return [gProcessInfo objectForKey:@"AppIcons"];
}
NSMutableDictionary *appIconInfoForID(UINT appIconID)
{
NSNumber *appIDValue = [NSNumber numberWithInteger:appIconID];
NSDictionary *appIcons = appIconsForProcess();
NSDictionary *appIcon = [appIcons objectForKey:appIDValue];
NSLog(@"Appicon for ID: %d value: %@", __PRETTY_FUNCTION__, appIconID, appIcon);
return appIcon;
}
void removeAppIconInfoForID(UINT appIconID)
{
NSNumber *appIDValue = [NSNumber numberWithInteger:appIconID];
NSMutableDictionary *appIcons = appIconsForProcess();
[appIcons removeObjectForKey:appIDValue];
}
void removeAppIconsForProcess()
{
// Clean up applications icons on task bar...
NSDictionary *appicons = appIconsForProcess();
NSEnumerator *iconsIter = [appicons objectEnumerator];
NSDictionary *iconInfo = nil;
while ((iconInfo = [iconsIter nextObject]))
{
UINT appIconID = [[iconInfo objectForKey:@"AppIconID"] integerValue];
#if defined(DEBUG)
NSLog(@"%s:removing proc ID: %d iconID: %d", __PRETTY_FUNCTION__, GetCurrentProcessId(), appIconID);
#endif
// Remove from task bar...
_removeApplicationIconForID(appIconID);
// And destroy the icon memory...
DestroyIcon((HICON)[[iconInfo objectForKey:@"AppIcon"] pointerValue]);
}
}
void _removeProcessInfo()
{
removeAppIconsForProcess();
[gProcessInfo release];
}
UINT _addApplicationIcon(DWORD processID, const char *uuidString, HICON icon)
{
NSValue *iconValue = [NSValue valueWithPointer:icon];
NSMutableDictionary *appIcons = appIconsForProcess();
UINT appID = -1;
#if defined(DEBUG)
NSLog(@"%s:icon: %p iconValue: %@ appIcons: %@", __PRETTY_FUNCTION__, icon, iconValue, appIcons);
#endif
if ([appIcons objectForKey:iconValue] != nil)
{
NSDictionary *appIcon = [appIcons objectForKey:iconValue];
appID = [[appIcon objectForKey:@"AppIconID"] integerValue];
#if defined(DEBUG)
NSLog(@"%s:re-using icon: %p with uID: %d", __PRETTY_FUNCTION__, icon, appID);
#endif
}
else
{
#if defined(DEBUG)
NSLog(@"%s:adding app icon for UUID: %p icon: %p", __PRETTY_FUNCTION__, uuidString, icon);
#endif
NOTIFYICONDATA notifyData = { 0 };
// Initialize basic structure...
_setupNotifyData(notifyData);
_setupNotifyDataIcon(notifyData, icon);
notifyData.uID = gNotifyCnt++;
// Adding...
if (Shell_NotifyIcon(NIM_ADD, &notifyData) == 0)
{
NSLog(@"%s:adding windows notification icon failed - error: %ld", __PRETTY_FUNCTION__, GetLastError());
return FALSE;
}
// Set version......
if (Shell_NotifyIcon(NIM_SETVERSION, &notifyData) == 0)
{
NSLog(@"%s:setting windows notification version failed - error: %ld", __PRETTY_FUNCTION__, GetLastError());
return FALSE;
}
// Add the application ID for this icon...
appID = notifyData.uID;
// Remember this information...
#if defined(DEBUG)
NSLog(@"%s:setting up icon: %p with uID: %d", __PRETTY_FUNCTION__, icon, appID);
#endif
NSMutableDictionary *appIcon = [NSMutableDictionary dictionary];
NSNumber *appIDValue = [NSNumber numberWithInteger:appID];
[appIcon setObject:appIDValue forKey:@"AppIconID"];
[appIcon setObject:iconValue forKey:@"AppIcon"];
[appIcons setObject:appIcon forKey:iconValue];
}
return appID;
}
void _removeApplicationIcon(DWORD processID, HICON icon)
{
}
void _removeApplicationIconForID(UINT appIconID)
{
NOTIFYICONDATA notifyData = { 0 };
_setupNotifyData(notifyData);
notifyData.uID = appIconID;
// Deleting...
if (Shell_NotifyIcon(NIM_DELETE, &notifyData) == 0)
{
NSLog(@"%s:deleting windows notification icon failed - error: %ld", __PRETTY_FUNCTION__, GetLastError());
}
}
GUID guidFromUUIDString(NSString *uuidString)
{
GUID theGUID;
NSArray *components = [uuidString componentsSeparatedByString: @"-"];
NSScanner *scanner1 = [NSScanner scannerWithString: [components objectAtIndex: 0]];
NSScanner *scanner2 = [NSScanner scannerWithString: [components objectAtIndex: 1]];
NSScanner *scanner3 = [NSScanner scannerWithString: [components objectAtIndex: 2]];
NSString *data4 = [[components objectAtIndex: 3] stringByAppendingString: [components objectAtIndex: 4]];
NSScanner *scanner4 = [NSScanner scannerWithString: data4];
NSUInteger value;
[scanner1 scanHexInt: (NSUInteger*)&theGUID.Data1];
[scanner2 scanHexInt: (NSUInteger*)&value];
theGUID.Data2 = (WORD) value;
[scanner3 scanHexInt: (NSUInteger*)&value];
theGUID.Data3 = (WORD) value;
return theGUID;
}
void _setupNotifyDataIcon(NOTIFYICONDATA &notifyData, HICON icon)
{
// If we were not able to load the icon image...
if (icon == NULL)
{
notifyData.dwInfoFlags |= NIIF_INFO;
}
else
{
// otherwise use it in the notification...
notifyData.uFlags |= NIF_ICON;
notifyData.hIcon = icon;
}
}
void _setupNotifyDataBalloonIcon(NOTIFYICONDATA &notifyData, HICON contentIcon)
{
#if _WIN32_WINNT >= 0x600
// If we were given a content icon image...
if (contentIcon != NULL)
{
// otherwise use it in the notification...
notifyData.dwInfoFlags |= NIIF_USER;
notifyData.hBalloonIcon = contentIcon;
}
#endif
}
void _setupNotifyDataUUID(NOTIFYICONDATA &notifyData, const char *uuidString)
{
#if USE_GUID
if (uuidString != NULL)
{
// Setup the flags and GUID...
notifyData.uFlags |= NIF_GUID;
notifyData.guidItem = guidFromUUIDString([NSString stringWithFormat:@"%s",uuidString]);
}
#else
// Otherwise using uID field...
notifyData.uID = 0;
#endif
}
void _setupNotifyDataTextInfo(NOTIFYICONDATA &notifyData, const char *title, const char *informativeText)
{
// Setup the flags...
notifyData.uFlags = NIF_TIP | NIF_INFO | NIF_MESSAGE;
// This text will be shown as the icon's tooltip.
StrCpy(notifyData.szTip, informativeText);
StrCpy(notifyData.szInfo, title);
StrCpy(notifyData.szInfoTitle, informativeText);
}
void _setupNotifyData(NOTIFYICONDATA &notifyData)
{
notifyData.cbSize = sizeof(NOTIFYICONDATA);
notifyData.uCallbackMessage = gNotifyMsg;
notifyData.hWnd = gHandleWin;
notifyData.dwState |= NIS_SHAREDICON;
// Load the /timeout/version???
notifyData.uVersion = NOTIFYICON_VERSION;
}
EXPORT BOOL __cdecl sendNotification(HWND hWnd, HICON icon, SEND_NOTE_INFO_T *note)
{
#if 0 //defined(DEBUG)
NSLog(@"%s:hWnd: %p icon: %p GUID: %p UUID: %s", __PRETTY_FUNCTION__, hWnd, icon, note->uuidString);
NSLog(@"%s:note title: %s informativeText: %s", __PRETTY_FUNCTION__, note->title, note->informativeText);
#endif
NOTIFYICONDATA notifyData = { 0 };
_setupNotifyData(notifyData);
_setupNotifyDataUUID(notifyData, note->uuidString);
_setupNotifyDataIcon(notifyData, icon);
#if 0
_setupNotifyDataBalloonIcon(notifyData, note->contentIcon);
#endif
_setupNotifyDataTextInfo(notifyData, note->title, note->informativeText);
// Need the uID for this icon...
notifyData.uID = _addApplicationIcon(GetCurrentProcessId(), note->uuidString, icon);
// Show the notification.
// Modifying...
if (Shell_NotifyIcon(NIM_MODIFY, &notifyData) == 0)
{
NSLog(@"%s:windows notification update failed for note title %s error: %ld", __PRETTY_FUNCTION__, note->title, GetLastError());
return FALSE;
}
// TODO: Should we instead return a NSString HERE instead???
return TRUE;
}
EXPORT BOOL __cdecl removeNotification(HICON icon, REMOVE_NOTE_INFO_T *noteinfo)
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NOTIFYICONDATA notifyData = { 0 };
BOOL status = TRUE;
NSLog(@"%s:%d:ID %d", __PRETTY_FUNCTION__, __LINE__, noteinfo->uniqueID);
_setupNotifyData(notifyData);
notifyData.uID = _addApplicationIcon(GetCurrentProcessId(), NULL, icon);
// Show the notification.
// Modifying...
if (Shell_NotifyIcon(NIM_MODIFY, &notifyData) == 0)
{
NSLog(@"%s:windows notification update failed for note ID %d error: %ld", __PRETTY_FUNCTION__, noteinfo->uniqueID, GetLastError());
return FALSE;
}
[pool drain];
return status;
}
#if defined(__cplusplus)
}
#endif

View file

@ -0,0 +1,23 @@
all: ToastNotifications-0.dll
clean:
rm -rf obj
(cd ToastNotifications/Debug && (find . -type f | grep -v '.dll' | xargs rm -rf))
(cd ToastNotifications/Release && (find . -type f | grep -v '.dll' | xargs rm -rf))
(rm -rf ToastNotifications/ToastNotifications/Debug)
(rm -rf ToastNotifications/ToastNotifications/Release)
(rm -rf ToastNotifications/ipch ToastNotifications/*.sdf)
obj:
mkdir -p obj
install: ToastNotifications-0.dll
ifeq ($(debug),yes)
ToastNotifications-0.dll: obj ToastNotifications/Debug/ToastNotifications.dll
cp -p ToastNotifications/Debug/ToastNotifications.dll obj/ToastNotifications-0.dll
else
ToastNotifications-0.dll: obj ToastNotifications/Release/ToastNotifications.dll
cp -p ToastNotifications/Release/ToastNotifications.dll obj/ToastNotifications-0.dll
endif

View file

@ -0,0 +1,31 @@
ifeq ($(GNUSTEP_MAKEFILES),)
GNUSTEP_MAKEFILES := $(shell gnustep-config --variable=GNUSTEP_MAKEFILES 2>/dev/null)
ifeq ($(GNUSTEP_MAKEFILES),)
$(warning )
$(warning Unable to obtain GNUSTEP_MAKEFILES setting from gnustep-config!)
$(warning Perhaps gnustep-make is not properly installed,)
$(warning so gnustep-config is not in your PATH.)
$(warning )
$(warning Your PATH is currently $(PATH))
$(warning )
endif
endif
ifeq ($(GNUSTEP_MAKEFILES),)
$(error You need to set GNUSTEP_MAKEFILES before compiling!)
endif
LIBRARY_NAME = ToastNotifications
ToastNotifications_NEEDS_GUI = NO
ToastNotification_CFLAGS += -DBUILD_DLL
ToastNotification_OBJCFLAGS += -DBUILD_DLL
ToastNotifications_LDFLAGS +=
ToastNotifications_OBJC_FILES =
ToastNotifications_OBJCC_FILES = ToastNotifications.mm
ToastNotifications_RESOURCE_FILES +=
ADDITIONAL_OBJCCFLAGS += -DBUILD_DLL
include $(GNUSTEP_MAKEFILES)/common.make
include $(GNUSTEP_MAKEFILES)/library.make

View file

@ -0,0 +1,18 @@
all: ToastNotifications-0.dll
clean:
rm -rf obj
obj:
mkdir -p obj
install: ToastNotifications-0.dll
ifeq ($(debug),yes)
ToastNotifications-0.dll: obj ToastNotifications/Debug/ToastNotifications.dll
cp -p ToastNotifications/Debug/ToastNotifications.dll obj/ToastNotifications-0.dll
else
ToastNotifications-0.dll: obj ToastNotifications/Release/ToastNotifications.dll
cp -p ToastNotifications/Release/ToastNotifications.dll obj/ToastNotifications-0.dll
endif

View file

@ -0,0 +1,70 @@
#include <Foundation/NSString.h>
#include <Foundation/NSScanner.h>
#include <Foundation/NSUserNotification.h>
#include <Foundation/NSUUID.h>
#include <Foundation/NSValue.h>
#include <../MSUserNotification.h>
#include <windows.h>
#ifdef BUILD_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif
#if defined(__cplusplus)
extern "C" { // Only if you are using C++ rather than C
#endif
static HANDLE gHandleDLL = NULL;
// Need to capture the DLL instance handle....
BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH: // Do process attach specific stuff here...
// Save the DLL instance handle...
gHandleDLL = hinstDLL;
break;
case DLL_PROCESS_DETACH: // Do process attach specific stuff here...
break;
case DLL_THREAD_ATTACH: // Do thread attach specific stuff here...
break;
case DLL_THREAD_DETACH: // Do thread detach specific stuff here...
break;
}
return TRUE;
}
EXPORT NSString * __cdecl sendNotification(HWND hWnd, NSMutableString *theGUID, HICON icon, NSUserNotification *note)
{
NSString *uuid = [[NSUUID UUID] UUIDString];
NSLog(@"%s:UUID: %@", __PRETTY_FUNCTION__, uuid);
NSArray *components = [uuid componentsSeparatedByString: @"-"];
NSLog(@"%s:components: %@", __PRETTY_FUNCTION__, components);
GUID myGUID;
NSString *data4 = [[components objectAtIndex: 3] stringByAppendingString: [components objectAtIndex: 4]];
myGUID.Data1 = [[components objectAtIndex: 0] longLongValue];
myGUID.Data2 = [[components objectAtIndex: 1] integerValue];
myGUID.Data3 = [[components objectAtIndex: 2] integerValue];
int index;
for (index = 0; index < 8; index++)
myGUID.Data4[index] = [data4 characterAtIndex: index];
// TODO: Add your Toast code here...
// IF ERROR DO NOT RETURN UUID...
if (true)
return nil;
// TOD: Return status...
return uuid;
}
#if defined(__cplusplus)
}
#endif

View file

@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ToastNotifications", "ToastNotifications\ToastNotifications.vcxproj", "{5158BA99-59BA-4F4C-8D1D-8E8886EDF0BA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5158BA99-59BA-4F4C-8D1D-8E8886EDF0BA}.Debug|Win32.ActiveCfg = Debug|Win32
{5158BA99-59BA-4F4C-8D1D-8E8886EDF0BA}.Debug|Win32.Build.0 = Debug|Win32
{5158BA99-59BA-4F4C-8D1D-8E8886EDF0BA}.Release|Win32.ActiveCfg = Release|Win32
{5158BA99-59BA-4F4C-8D1D-8E8886EDF0BA}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,67 @@
========================================================================
MICROSOFT FOUNDATION CLASS LIBRARY : ToastNotifications Project Overview
========================================================================
AppWizard has created this ToastNotifications DLL for you. This DLL not only
demonstrates the basics of using the Microsoft Foundation classes but
is also a starting point for writing your DLL.
This file contains a summary of what you will find in each of the files that
make up your ToastNotifications DLL.
ToastNotifications.vcxproj
This is the main project file for VC++ projects generated using an Application Wizard.
It contains information about the version of Visual C++ that generated the file, and
information about the platforms, configurations, and project features selected with the
Application Wizard.
ToastNotifications.vcxproj.filters
This is the filters file for VC++ projects generated using an Application Wizard.
It contains information about the association between the files in your project
and the filters. This association is used in the IDE to show grouping of files with
similar extensions under a specific node (for e.g. ".cpp" files are associated with the
"Source Files" filter).
ToastNotifications.h
This is the main header file for the DLL. It declares the
CToastNotificationsApp class.
ToastNotifications.cpp
This is the main DLL source file. It contains the class CToastNotificationsApp.
ToastNotifications.rc
This is a listing of all of the Microsoft Windows resources that the
program uses. It includes the icons, bitmaps, and cursors that are stored
in the RES subdirectory. This file can be directly edited in Microsoft
Visual C++.
res\ToastNotifications.rc2
This file contains resources that are not edited by Microsoft
Visual C++. You should place all resources not editable by
the resource editor in this file.
ToastNotifications.def
This file contains information about the DLL that must be
provided to run with Microsoft Windows. It defines parameters
such as the name and description of the DLL. It also exports
functions from the DLL.
/////////////////////////////////////////////////////////////////////////////
Other standard files:
StdAfx.h, StdAfx.cpp
These files are used to build a precompiled header (PCH) file
named ToastNotifications.pch and a precompiled types file named StdAfx.obj.
Resource.h
This is the standard header file, which defines new resource IDs.
Microsoft Visual C++ reads and updates this file.
/////////////////////////////////////////////////////////////////////////////
Other notes:
AppWizard uses "TODO:" to indicate parts of the source code you
should add to or customize.
/////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,16 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by ToastNotifications.rc
//
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 2000
#define _APS_NEXT_CONTROL_VALUE 2000
#define _APS_NEXT_SYMED_VALUE 2000
#define _APS_NEXT_COMMAND_VALUE 32771
#endif
#endif

View file

@ -0,0 +1,60 @@
class StringReferenceWrapper
{
public:
// Constructor which takes an existing string buffer and its length as the parameters.
// It fills an HSTRING_HEADER struct with the parameter.
// Warning: The caller must ensure the lifetime of the buffer outlives this
// object as it does not make a copy of the wide string memory.
StringReferenceWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw()
{
HRESULT hr = WindowsCreateStringReference(stringRef, length, &_header, &_hstring);
if (FAILED(hr))
{
RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr);
}
}
~StringReferenceWrapper()
{
WindowsDeleteString(_hstring);
}
template <size_t N>
StringReferenceWrapper(_In_reads_(N) wchar_t const (&stringRef)[N]) throw()
{
UINT32 length = N-1;
HRESULT hr = WindowsCreateStringReference(stringRef, length, &_header, &_hstring);
if (FAILED(hr))
{
RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr);
}
}
template <size_t _>
StringReferenceWrapper(_In_reads_(_) wchar_t (&stringRef)[_]) throw()
{
UINT32 length;
HRESULT hr = SizeTToUInt32(wcslen(stringRef), &length);
if (FAILED(hr))
{
RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr);
}
WindowsCreateStringReference(stringRef, length, &_header, &_hstring);
}
HSTRING Get() const throw()
{
return _hstring;
}
private:
HSTRING _hstring;
HSTRING_HEADER _header;
};

View file

@ -0,0 +1,71 @@
#include "stdafx.h"
#include "ToastEventHandler.h"
using namespace ABI::Windows::UI::Notifications;
ToastEventHandler::ToastEventHandler(_In_ HWND hToActivate, _In_ HWND hEdit) : _ref(1), _hToActivate(hToActivate), _hEdit(hEdit)
{
}
ToastEventHandler::~ToastEventHandler()
{
}
// DesktopToastActivatedEventHandler
IFACEMETHODIMP ToastEventHandler::Invoke(_In_ IToastNotification* sender, _In_ IInspectable* /* args */)
{
static char str[512];
sprintf_s(str, "%s:%d: IToastNotePtr: %p msg: The user clicked on the toast", __FUNCTION__, __LINE__, sender);
OutputDebugStringA(str);
BOOL succeeded = SetForegroundWindow(_hToActivate);
if (succeeded)
{
LRESULT result = SendMessage(_hEdit, WM_SETTEXT, reinterpret_cast<WPARAM>(nullptr), reinterpret_cast<LPARAM>(L"The user clicked on the toast."));
succeeded = result ? TRUE : FALSE;
}
return succeeded ? S_OK : E_FAIL;
}
// DesktopToastDismissedEventHandler
IFACEMETHODIMP ToastEventHandler::Invoke(_In_ IToastNotification* sender, _In_ IToastDismissedEventArgs* e)
{
ToastDismissalReason tdr;
HRESULT hr = e->get_Reason(&tdr);
if (SUCCEEDED(hr))
{
wchar_t *outputText;
switch (tdr)
{
case ToastDismissalReason_ApplicationHidden:
outputText = L"The application hid the toast using ToastNotifier.hide()";
break;
case ToastDismissalReason_UserCanceled:
outputText = L"The user dismissed this toast";
break;
case ToastDismissalReason_TimedOut:
outputText = L"The toast has timed out";
break;
default:
outputText = L"Toast not activated";
break;
}
static wchar_t str[512];
swprintf_s(str, L"%s:%d: IToastNotePtr: %p msg: %s", TEXT(__FUNCTION__), __LINE__, sender, outputText);
OutputDebugStringW(str);
LRESULT succeeded = SendMessage(_hEdit, WM_SETTEXT, reinterpret_cast<WPARAM>(nullptr), reinterpret_cast<LPARAM>(outputText));
hr = succeeded ? S_OK : E_FAIL;
}
return hr;
}
// DesktopToastFailedEventHandler
IFACEMETHODIMP ToastEventHandler::Invoke(_In_ IToastNotification* /* sender */, _In_ IToastFailedEventArgs* /* e */)
{
LRESULT succeeded = SendMessage(_hEdit, WM_SETTEXT, reinterpret_cast<WPARAM>(nullptr), reinterpret_cast<LPARAM>(L"The toast encountered an error."));
return succeeded ? S_OK : E_FAIL;
}

View file

@ -0,0 +1,55 @@
#pragma once
typedef ABI::Windows::Foundation::ITypedEventHandler<ABI::Windows::UI::Notifications::ToastNotification *, ::IInspectable *> DesktopToastActivatedEventHandler;
typedef ABI::Windows::Foundation::ITypedEventHandler<ABI::Windows::UI::Notifications::ToastNotification *, ABI::Windows::UI::Notifications::ToastDismissedEventArgs *> DesktopToastDismissedEventHandler;
typedef ABI::Windows::Foundation::ITypedEventHandler<ABI::Windows::UI::Notifications::ToastNotification *, ABI::Windows::UI::Notifications::ToastFailedEventArgs *> DesktopToastFailedEventHandler;
class ToastEventHandler :
public Microsoft::WRL::Implements<DesktopToastActivatedEventHandler, DesktopToastDismissedEventHandler, DesktopToastFailedEventHandler>
{
public:
ToastEventHandler::ToastEventHandler(_In_ HWND hToActivate, _In_ HWND hEdit);
~ToastEventHandler();
// DesktopToastActivatedEventHandler
IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender, _In_ IInspectable* args);
// DesktopToastDismissedEventHandler
IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender, _In_ ABI::Windows::UI::Notifications::IToastDismissedEventArgs *e);
// DesktopToastFailedEventHandler
IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender, _In_ ABI::Windows::UI::Notifications::IToastFailedEventArgs *e);
// IUnknown
IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_ref); }
IFACEMETHODIMP_(ULONG) Release() {
ULONG l = InterlockedDecrement(&_ref);
if (l == 0) delete this;
return l;
}
IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _COM_Outptr_ void **ppv) {
if (IsEqualIID(riid, IID_IUnknown))
*ppv = static_cast<IUnknown*>(static_cast<DesktopToastActivatedEventHandler*>(this));
else if (IsEqualIID(riid, __uuidof(DesktopToastActivatedEventHandler)))
*ppv = static_cast<DesktopToastActivatedEventHandler*>(this);
else if (IsEqualIID(riid, __uuidof(DesktopToastDismissedEventHandler)))
*ppv = static_cast<DesktopToastDismissedEventHandler*>(this);
else if (IsEqualIID(riid, __uuidof(DesktopToastFailedEventHandler)))
*ppv = static_cast<DesktopToastFailedEventHandler*>(this);
else *ppv = nullptr;
if (*ppv) {
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
private:
ULONG _ref;
HWND _hToActivate;
HWND _hEdit;
};

View file

@ -0,0 +1,489 @@
// ToastNotifications.cpp : Defines the initialization routines for the DLL.
//
#include "stdafx.h"
#include <iostream>
#include <Windows.Foundation.h>
#include <wrl\implements.h>
#include <wrl\client.h>
#include <wrl\wrappers\corewrappers.h>
#include <Windows.ui.notifications.h>
#include <strsafe.h>
#include "ToastNotifications.h"
#include "ToastEventHandler.h"
#include "../../../MSUserNotificationAPI.h"
#include <locale>
#include <codecvt>
#include <string>
using namespace Microsoft::WRL;
using namespace ABI::Windows::UI::Notifications;
using namespace ABI::Windows::Data::Xml::Dom;
using namespace Windows::Foundation;
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
//
//TODO: If this DLL is dynamically linked against the MFC DLLs,
// any functions exported from this DLL which call into
// MFC must have the AFX_MANAGE_STATE macro added at the
// very beginning of the function.
//
// For example:
//
// extern "C" BOOL PASCAL EXPORT ExportedFunction()
// {
// AFX_MANAGE_STATE(AfxGetStaticModuleState());
// // normal function body here
// }
//
// It is very important that this macro appear in each
// function, prior to any calls into MFC. This means that
// it must appear as the first statement within the
// function, even before any object variable declarations
// as their constructors may generate calls into the MFC
// DLL.
//
// Please see MFC Technical Notes 33 and 58 for additional
// details.
//
// CToastNotificationsApp
BEGIN_MESSAGE_MAP(CToastNotificationsApp, CWinApp)
END_MESSAGE_MAP()
// CToastNotificationsApp construction
CToastNotificationsApp::CToastNotificationsApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
CToastNotificationsApp::~CToastNotificationsApp()
{
#if defined(DEBUG)
static char str[512];
sprintf_s(str, "%s:%d: DONE", __FUNCTION__, __LINE__);
OutputDebugStringA(str);
#endif
}
// The one and only CToastNotificationsApp object
CToastNotificationsApp theApp;
// CToastNotificationsApp initialization
BOOL CToastNotificationsApp::InitInstance()
{
CWinApp::InitInstance();
return TRUE;
}
// Create the toast XML from a template
HRESULT CToastNotificationsApp::CreateToastXml(_In_ IToastNotificationManagerStatics *toastManager, _Outptr_ IXmlDocument** inputXml, wchar_t* notificationTitle, wchar_t* notificationDescription, wchar_t* imagePath)
{
#if defined(DEBUG)
int number = 600;
char str[256];
sprintf_s(str, "inside create toast xml and calling GetTemplateContent %d\n", number);
OutputDebugStringA(str);
#endif
HRESULT hr = toastManager->GetTemplateContent(ToastTemplateType_ToastImageAndText04, inputXml);
#if defined(DEBUG)
sprintf_s(str, "done with GetTemplateContent %d\n", number);
OutputDebugStringA(str);
#endif
if (SUCCEEDED(hr))
{
#if defined(DEBUG)
sprintf_s(str, "OK inside\n");
OutputDebugStringA(str);
#endif
//wchar_t *imagePath = _wfullpath(nullptr, L"toastImageAndText.png", MAX_PATH);
#if defined(DEBUG)
static wchar_t wstr[512];
swprintf_s(wstr, TEXT("%s:%d:imagePath: %s"), TEXT(__FUNCTION__), __LINE__, imagePath);
OutputDebugStringW(wstr);
#endif
hr = imagePath != nullptr ? S_OK : HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
if (SUCCEEDED(hr))
{
#if defined(DEBUG)
sprintf_s(str, "got hte image path and now setting its source");
OutputDebugStringA(str);
#endif
hr = SetImageSrc(imagePath, *inputXml);
if (SUCCEEDED(hr))
{
#if defined(DEBUG)
sprintf_s(str, "done setting the source");
OutputDebugStringA(str);
#endif
wchar_t* textValues[] = {
notificationTitle,
notificationDescription,
L" "
};
UINT32 textLengths[] = { wcslen(notificationTitle), wcslen(notificationDescription), 6 };
hr = SetTextValues(textValues, 3, textLengths, *inputXml);
}
}
else {
#if defined(DEBUG)
sprintf_s(str, "AHh!with imagepath %ld\n", GetLastError());
OutputDebugStringA(str);
#endif
}
}
else {
#if defined(DEBUG)
sprintf_s(str, "AHh! Shoot, done with GetTemplateContent %ld\n", GetLastError());
OutputDebugStringA(str);
#endif
}
return hr;
}
HRESULT CToastNotificationsApp::SetTextValues(_In_reads_(textValuesCount) wchar_t **textValues, _In_ UINT32 textValuesCount, _In_reads_(textValuesCount) UINT32 *textValuesLengths, _In_ IXmlDocument *toastXml)
{
#if defined(DEBUG)
int number = 600;
char str[256];
sprintf_s(str, "inside set text values %d\n", number);
OutputDebugStringA(str);
#endif
HRESULT hr = textValues != nullptr && textValuesCount > 0 ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hr))
{
ComPtr<IXmlNodeList> nodeList;
#if defined(DEBUG)
sprintf_s(str, "before calling get tag names %d\n", number);
OutputDebugStringA(str);
#endif
hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"text").Get(), &nodeList);
if (SUCCEEDED(hr))
{
#if defined(DEBUG)
sprintf_s(str, "inside succeeded %d\n", number);
OutputDebugStringA(str);
#endif
UINT32 nodeListLength;
hr = nodeList->get_Length(&nodeListLength);
if (SUCCEEDED(hr))
{
#if defined(DEBUG)
sprintf_s(str, "inside got length %d\n", number);
OutputDebugStringA(str);
#endif
hr = textValuesCount <= nodeListLength ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hr))
{
#if defined(DEBUG)
sprintf_s(str, "insidenode list length %d\n", number);
OutputDebugStringA(str);
#endif
for (UINT32 i = 0; i < textValuesCount; i++)
{
ComPtr<IXmlNode> textNode;
hr = nodeList->Item(i, &textNode);
if (SUCCEEDED(hr))
{
#if defined(DEBUG)
sprintf_s(str, "before calling node value string %d\n", number);
OutputDebugStringA(str);
#endif
hr = SetNodeValueString(StringReferenceWrapper(textValues[i], textValuesLengths[i]).Get(), textNode.Get(), toastXml);
}
}
}
}
}
}
//int number = 600;
//char str[256];
#if defined(DEBUG)
sprintf_s(str, "returning from set text values %d\n", number);
OutputDebugStringA(str);
#endif
return hr;
}
HRESULT CToastNotificationsApp::SetImageSrc(_In_z_ wchar_t *imagePath, _In_ IXmlDocument *toastXml)
{
wchar_t imageSrc[MAX_PATH] = L"file:///";
HRESULT hr = StringCchCat(imageSrc, ARRAYSIZE(imageSrc), imagePath);
if (SUCCEEDED(hr))
{
ComPtr<IXmlNodeList> nodeList;
hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"image").Get(), &nodeList);
if (SUCCEEDED(hr))
{
ComPtr<IXmlNode> imageNode;
hr = nodeList->Item(0, &imageNode);
if (SUCCEEDED(hr))
{
ComPtr<IXmlNamedNodeMap> attributes;
hr = imageNode->get_Attributes(&attributes);
if (SUCCEEDED(hr))
{
ComPtr<IXmlNode> srcAttribute;
hr = attributes->GetNamedItem(StringReferenceWrapper(L"src").Get(), &srcAttribute);
if (SUCCEEDED(hr))
{
#if defined(DEBUG)
static char str[256];
sprintf_s(str, "setting image source %d", hr);
OutputDebugStringA(str);
#endif
hr = SetNodeValueString(StringReferenceWrapper(imageSrc).Get(), srcAttribute.Get(), toastXml);
}
}
}
}
}
#if defined(DEBUG)
int number = 600;
char str[256];
sprintf_s(str, "returning from set image source %d\n", number);
OutputDebugStringA(str);
#endif
return hr;
}
HRESULT CToastNotificationsApp::SetNodeValueString(_In_ HSTRING inputString, _In_ IXmlNode *node, _In_ IXmlDocument *xml)
{
#if defined(DEBUG)
int number = 600;
char str[256];
sprintf_s(str, "inside node value string %d\n", number);
OutputDebugStringA(str);
#endif
ComPtr<IXmlText> inputText;
HRESULT hr = xml->CreateTextNode(inputString, &inputText);
if (SUCCEEDED(hr))
{
#if defined(DEBUG)
sprintf_s(str, "done create node text %d\n", number);
OutputDebugStringA(str);
#endif
ComPtr<IXmlNode> inputTextNode;
hr = inputText.As(&inputTextNode);
if (SUCCEEDED(hr))
{
ComPtr<IXmlNode> pAppendedChild;
hr = node->AppendChild(inputTextNode.Get(), &pAppendedChild);
}
}
#if defined(DEBUG)
number = 600;
sprintf_s(str, "returning from set node value string %d\n", number);
OutputDebugStringA(str);
#endif
return hr;
}
HRESULT CToastNotificationsApp::CreateToast(_In_ IToastNotificationManagerStatics *toastManager, _In_ IXmlDocument *xml, HWND hWnd)
{
_hwnd = hWnd;
ComPtr<IToastNotifier> notifier;
HRESULT hr = toastManager->CreateToastNotifierWithId(StringReferenceWrapper(AppId).Get(), &notifier);
if (SUCCEEDED(hr))
{
ComPtr<IToastNotificationFactory> factory;
hr = GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &factory);
if (SUCCEEDED(hr))
{
ComPtr<IToastNotification> toast;
hr = factory->CreateToastNotification(xml, &toast);
if (SUCCEEDED(hr))
{
// Register the event handlers
EventRegistrationToken activatedToken, dismissedToken, failedToken;
ComPtr<ToastEventHandler> eventHandler(new ToastEventHandler(_hwnd, _hwnd));
toast->add_Activated(eventHandler.Get(), &activatedToken);
if (SUCCEEDED(hr))
{
hr = toast->add_Dismissed(eventHandler.Get(), &dismissedToken);
if (SUCCEEDED(hr))
{
hr = toast->add_Failed(eventHandler.Get(), &failedToken);
if (SUCCEEDED(hr))
{
hr = notifier->Show(toast.Get());
}
}
}
}
}
}
#if defined(DEBUG)
static wchar_t str[256];
swprintf_s(str, TEXT("returning from create toast"));
OutputDebugString(str);
#endif
return hr;
}
HRESULT CToastNotificationsApp::DisplayToast(HWND hWnd, wchar_t* notificationTitle, wchar_t* notificationDescription, wchar_t* imagePath)
{
#if defined(DEBUG)
static wchar_t str[512];
swprintf_s(str, L"%s:%d: note title: %su infoText: %su", TEXT(__FUNCTION__), __LINE__, notificationTitle, notificationTitle, imagePath);
OutputDebugString(str);
#endif
ComPtr<IToastNotificationManagerStatics> toastStatics;
HRESULT hr = GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &toastStatics);
if (SUCCEEDED(hr))
{
ComPtr<IXmlDocument> toastXml;
hr = CreateToastXml(toastStatics.Get(), &toastXml, notificationTitle, notificationDescription, imagePath);
if (SUCCEEDED(hr))
{
#if defined(DEBUG)
char str[256];
sprintf_s(str, "done with toast xml and calling the toast method %d\n");
OutputDebugStringA(str);
#endif
hr = CreateToast(toastStatics.Get(), toastXml.Get(), hWnd);
}
return hr;
}
else
return 1;
}
extern "C" EXPORT BOOL __cdecl sendNotification(HWND hWnd, HICON icon, SEND_NOTE_INFO_T *noteInfo)
{
//NSLog(@"%s:hWnd: %p icon: %p GUID: %p note: %p", __PRETTY_FUNCTION__, hWnd, icon, note);
#if defined(DEBUG)
static char str[512];
sprintf_s(str, "%s:%d: note %p", __FUNCTION__, __LINE__, noteInfo);
OutputDebugStringA(str);
sprintf_s(str, "%s:%d: noteInfo ptrs: title: %p infoText: %p imagePath: %p", __FUNCTION__, __LINE__, noteInfo->title, noteInfo->informativeText, noteInfo->appIconPath);
OutputDebugStringA(str);
#endif
#if defined(DEBUG)
sprintf_s(str, "%s:%d: note title: %s", __FUNCTION__, __LINE__, noteInfo->title);
OutputDebugStringA(str);
sprintf_s(str, "%s:%d: note infoText: %s", __FUNCTION__, __LINE__, noteInfo->informativeText);
OutputDebugStringA(str);
sprintf_s(str, "%s:%d: note contentPath: %s", __FUNCTION__, __LINE__, noteInfo->appIconPath);
OutputDebugStringA(str);
sprintf_s(str, "%s:%d: note UUID: %s", __FUNCTION__, __LINE__, noteInfo->uuidString);
OutputDebugStringA(str);
#endif
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::wstring title = converter.from_bytes(noteInfo->title);
std::wstring description = converter.from_bytes(noteInfo->informativeText);
std::wstring imagePath = TEXT("");
// Convert content image path if available...
if (noteInfo->appIconPath != NULL)
imagePath = converter.from_bytes(noteInfo->appIconPath);
#if defined(DEBUG)
static wchar_t wstr[512];
swprintf_s(wstr, TEXT("%s:%d: note title: %s infoText: %s imagePath: %s"), TEXT(__FUNCTION__), __LINE__, title.c_str(), description.c_str(), imagePath.c_str());
OutputDebugString(wstr);
#endif
#if 0
CToastNotificationsApp *app = new CToastNotificationsApp();
HRESULT hr = app->DisplayToast(hWnd, const_cast<wchar_t*>(title.c_str()), const_cast<wchar_t*>(description.c_str()));
#else
HRESULT hr = theApp.DisplayToast(hWnd, const_cast<wchar_t*>(title.c_str()), const_cast<wchar_t*>(description.c_str()), const_cast<wchar_t*>(imagePath.c_str()));
#endif
#if defined(DEBUG)
sprintf_s(str, "%s:%d: HR %d", __FUNCTION__, __LINE__, hr);
OutputDebugStringA(str);
#endif
if (SUCCEEDED(hr))
{
return TRUE;
}
return FALSE;
}
EXPORT BOOL __cdecl removeNotification(HICON icon, REMOVE_NOTE_INFO_T *noteinfo)
{
return FALSE;
}

View file

@ -0,0 +1,6 @@
; ToastNotifications.def : Declares the module parameters for the DLL.
LIBRARY
EXPORTS
; Explicit exports can go here

View file

@ -0,0 +1,64 @@
// ToastNotifications.h : main header file for the ToastNotifications DLL
//
#pragma once
#ifndef __AFXWIN_H__
#error "include 'stdafx.h' before including this file for PCH"
#endif
#include "resource.h" // main symbols
const wchar_t AppId[] = L"Microsoft.Samples.DesktopToasts";
// CToastNotificationsApp
// See ToastNotifications.cpp for the implementation of this class
//
class CToastNotificationsApp : public CWinApp
{
public:
CToastNotificationsApp();
~CToastNotificationsApp();
// Overrides
public:
virtual BOOL InitInstance();
DECLARE_MESSAGE_MAP()
friend class ToastEventHandler;
public:
//HRESULT DisplayToast(HWND hWnd, wchar_t* notificationTitle, wchar_t* notificationDescription);
HRESULT DisplayToast(HWND hWnd, wchar_t* notificationTitle, wchar_t* notificationDescription, wchar_t* imagePath);
HRESULT CreateToastXml(
_In_ ABI::Windows::UI::Notifications::IToastNotificationManagerStatics *toastManager,
_Outptr_ ABI::Windows::Data::Xml::Dom::IXmlDocument **xml, wchar_t* notificationTitle, wchar_t* notificationDescription, wchar_t* imagePath);
HRESULT CreateToast(
_In_ ABI::Windows::UI::Notifications::IToastNotificationManagerStatics *toastManager,
_In_ ABI::Windows::Data::Xml::Dom::IXmlDocument *xml, HWND hWnd
);
HRESULT SetImageSrc(
_In_z_ wchar_t *imagePath,
_In_ ABI::Windows::Data::Xml::Dom::IXmlDocument *toastXml
);
HRESULT SetTextValues(
_In_reads_(textValuesCount) wchar_t **textValues,
_In_ UINT32 textValuesCount,
_In_reads_(textValuesCount) UINT32 *textValuesLengths,
_In_ ABI::Windows::Data::Xml::Dom::IXmlDocument *toastXml
);
HRESULT SetNodeValueString(
_In_ HSTRING onputString,
_In_ ABI::Windows::Data::Xml::Dom::IXmlNode *node,
_In_ ABI::Windows::Data::Xml::Dom::IXmlDocument *xml
);
private:
HWND _hwnd;
HWND _hEdit;
};

View file

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{5158BA99-59BA-4F4C-8D1D-8E8886EDF0BA}</ProjectGuid>
<RootNamespace>ToastNotifications</RootNamespace>
<Keyword>MFCDLLProj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<UseOfMfc>Dynamic</UseOfMfc>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<UseOfMfc>Dynamic</UseOfMfc>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>BUILD_DLL;WIN32;_WINDOWS;_DEBUG;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ModuleDefinitionFile>.\ToastNotifications.def</ModuleDefinitionFile>
<AdditionalDependencies>runtimeobject.lib;shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Midl>
<MkTypLibCompatible>false</MkTypLibCompatible>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</Midl>
<ResourceCompile>
<Culture>0x0409</Culture>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>BUILD_DLL;WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<ModuleDefinitionFile>.\ToastNotifications.def</ModuleDefinitionFile>
<AdditionalDependencies>runtimeobject.lib;shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Midl>
<MkTypLibCompatible>false</MkTypLibCompatible>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</Midl>
<ResourceCompile>
<Culture>0x0409</Culture>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="ToastEventHandler.cpp" />
<ClCompile Include="ToastNotifications.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Resource.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="StringReferenceWrapper.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="ToastEventHandler.h" />
<ClInclude Include="ToastNotifications.h" />
</ItemGroup>
<ItemGroup>
<None Include="res\ToastNotifications.rc2" />
<None Include="ToastNotifications.def" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ToastNotifications.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ToastNotifications.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ToastEventHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ToastNotifications.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="targetver.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="StringReferenceWrapper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ToastEventHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="ToastNotifications.def">
<Filter>Source Files</Filter>
</None>
<None Include="res\ToastNotifications.rc2">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ToastNotifications.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

View file

@ -0,0 +1,7 @@
// stdafx.cpp : source file that includes just the standard includes
// ToastNotifications.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"

View file

@ -0,0 +1,66 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
#pragma once
#ifdef BUILD_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif
#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
#endif
#include "targetver.h"
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
#include <afxwin.h> // MFC core and standard components
#include <afxext.h> // MFC extensions
#ifndef _AFX_NO_OLE_SUPPORT
#include <afxole.h> // MFC OLE classes
#include <afxodlgs.h> // MFC OLE dialog classes
#include <afxdisp.h> // MFC Automation classes
#endif // _AFX_NO_OLE_SUPPORT
#ifndef _AFX_NO_DB_SUPPORT
#include <afxdb.h> // MFC ODBC database classes
#endif // _AFX_NO_DB_SUPPORT
#ifndef _AFX_NO_DAO_SUPPORT
#include <afxdao.h> // MFC DAO database classes
#endif // _AFX_NO_DAO_SUPPORT
#ifndef _AFX_NO_OLE_SUPPORT
#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
#endif
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h> // MFC support for Windows Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT
#pragma once
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#include <SDKDDKVer.h>
// Windows Header Files:
#include <Windows.h>
#include <sal.h>
#include <Psapi.h>
#include <strsafe.h>
#include <ObjBase.h>
#include <ShObjIdl.h>
#include <propvarutil.h>
#include <functiondiscoverykeys.h>
#include <intsafe.h>
#include <guiddef.h>
#include <roapi.h>
#include <wrl\client.h>
#include <wrl\implements.h>
#include <windows.ui.notifications.h>
#include "StringReferenceWrapper.h"

View file

@ -0,0 +1,8 @@
#pragma once
// Including SDKDDKVer.h defines the highest available Windows platform.
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
#include <SDKDDKVer.h>