summaryrefslogtreecommitdiffstats
blob: b78014037b8a0923b8024f57c72ddf0e24e9097c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
// About:
//   Snarl C++ interface implementation
//   To understand what the different functions do and what they return, please
//   have a look at the API on http://www.fullphat.net/dev/api.htm.
//   Also have a look at mSnarl_i.bas found in the CVS/SVN repository for Snarl.
//
//   The functions in SnarlInterface both have ANSI(UTF8) and UNICODE versions.
//   If the LPCWSTR (unicode) version of the functions are called, the strings
//   are converted to UTF8 by SnarlInterface before sent to Snarl. So using the
//   ANSI/UTF8/LPCSTR versions of the functions are faster!
//   
//
// Difference to VB implementation:
//   Please note that string functions return NULL when they fail and not an
//   empty string. So check for NULL...
//   Function names doesn't have the pre "sn".
//
// 
// Authors:
//   Written and maintained by Toke Noer Nøttrup
//   Original C++ version by "Whitman"
//
// License etc. :
//   Feel free to use this code and class as you see fit.
//   If you improve the code, it would be nice of you to take contact to the
//   authors, so all can get the benifit.
//
//   There is no guarantee that the code is correct or functional.
//   USE AT YOUR OWN RISK
//-----------------------------------------------------------------------------

// History
//  2008/12/31 : Implemented V39 API
//             : Moved SnarlInterface into new Snarl namespace and moved enums etc. out of class
//             : Added WCHAR overloads for all functions
//  2008/08/27 : Fixed return value of IsMessageVisible and HideMessage (returns false on failure now)
//             : Fixed critical error in the new overloaded UpdateMessage() function
//  2008/08/27 : x64 compiler fix
//  2008/08/24 : Renamed all functions to not have prepended "sn".
//             : Memory allocation functions added. (Use FreeString to free strings returned by Snarl)
//             : Added m_nLastMessageId to the class. (Use GetLastMessageId() to get it)
//             : Overloaded a few functions, so one don't have include the message id. (UpdateMessage f.i.)

//  2008/06/20 : Fixed snShowMessageEx so it actually sends the extended version - oops
//             : Fixed compiler warning C4800: forcing value to bool 'true' or 'false' (performance warning)

//  2008/05/19 : uSend and uSendEx would always set return value to M_OK on successfull call
//  2008/04/14 : Updated to follow (what should be) the final Snarl 2.0 API
//  2008/03/28 : Few fixes for Snarl 2.0
//  2007/05/23 : snGetGlobalMsg & snGetSnarlWindow made static
//  2007/03/25 : 1.6 RC1 fixup
//  2007/03/04 : Added - snGetAppPath, snGetIconsPath, snGetVersionEx, 
//                       snSetTimeout, uSendEx


#include "SnarlInterface.h"

using namespace Snarl;


//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
SnarlInterface::SnarlInterface()
: m_hwndFrom(NULL), m_nLastMessageId(0)
{

}

SnarlInterface::~SnarlInterface()
{

}


//-----------------------------------------------------------------------------
// snShowMessage()

/// Displays a message with Title and Text. Timeout controls how long the
/// message is displayed for (in seconds) (omitting this value means the message
/// is displayed indefinately). IconPath specifies the location of a PNG image
/// which will be displayed alongside the message text.
/// <returns>Message Id on success or M_RESULT on failure</returns>

LONG32 SnarlInterface::ShowMessage(LPCSTR szTitle, LPCSTR szText, LONG32 timeout, LPCSTR szIconPath, HWND hWndReply, WPARAM uReplyMsg)
{
	SNARLSTRUCT ss;
	ZeroMemory((void*)&ss, sizeof(ss));

	ss.Cmd = SNARL_SHOW;
	StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, szTitle);
	StringCbCopyA((LPSTR)&ss.Text,  SNARL_STRING_LENGTH, szText);
	StringCbCopyA((LPSTR)&ss.Icon,  SNARL_STRING_LENGTH, szIconPath);
	ss.Timeout = timeout;

	ss.LngData2 = reinterpret_cast<LONG32>(hWndReply);
	ss.Id = static_cast<LONG32>(uReplyMsg);

	m_nLastMessageId = Send(ss);
	return m_nLastMessageId;
}

LONG32 SnarlInterface::ShowMessage(LPCWSTR szTitle, LPCWSTR szText, LONG32 timeout, LPCWSTR szIconPath, HWND hWndReply, WPARAM uReplyMsg)
{
	LPSTR szUTF8Title = WideToUTF8(szTitle);
	LPSTR szUTF8Text  = WideToUTF8(szText);
	LPSTR szUFT8IconPath = WideToUTF8(szIconPath);
	
	LONG32 result = ShowMessage(szUTF8Title, szUTF8Text, timeout, szUFT8IconPath, hWndReply, uReplyMsg);
	
	delete [] szUTF8Title;
	delete [] szUTF8Text;
	delete [] szUFT8IconPath;
	
	return result;
}
	
//-----------------------------------------------------------------------------
// snShowMessageEx()

/// Displays a notification. This function is identical to snShowMessage()
/// except that Class specifies an alert previously registered with
/// snRegisterAlert() and SoundFile can optionally specify a WAV sound to play
/// when the notification is displayed on screen.

/// <returns>Message Id on success or M_RESULT on failure</returns>

LONG32 SnarlInterface::ShowMessageEx(LPCSTR szClass, LPCSTR szTitle, LPCSTR szText, LONG32 timeout, LPCSTR szIconPath, HWND hWndReply, WPARAM uReplyMsg, LPCSTR szSoundFile)
{
	SNARLSTRUCTEX ssex;
	ZeroMemory((void*)&ssex, sizeof(ssex));

	ssex.Cmd = SNARL_EX_SHOW;
	ssex.Timeout = timeout;
	ssex.LngData2 = reinterpret_cast<LONG32>(hWndReply);
	ssex.Id = static_cast<LONG32>(uReplyMsg);

	StringCbCopyA((LPSTR)&ssex.Class, SNARL_STRING_LENGTH, szClass);
	StringCbCopyA((LPSTR)&ssex.Title, SNARL_STRING_LENGTH, szTitle);
	StringCbCopyA((LPSTR)&ssex.Text,  SNARL_STRING_LENGTH, szText);
	StringCbCopyA((LPSTR)&ssex.Icon,  SNARL_STRING_LENGTH, szIconPath);
	StringCbCopyA((LPSTR)&ssex.Extra, SNARL_STRING_LENGTH, szSoundFile);

	m_nLastMessageId = Send(ssex);
	return m_nLastMessageId;
}

LONG32 SnarlInterface::ShowMessageEx(LPCWSTR szClass, LPCWSTR szTitle, LPCWSTR szText, LONG32 timeout, LPCWSTR szIconPath, HWND hWndReply, WPARAM uReplyMsg, LPCWSTR szSoundFile)
{
	LPSTR szUTF8Class = WideToUTF8(szClass);
	LPSTR szUTF8Title = WideToUTF8(szTitle);
	LPSTR szUTF8Text  = WideToUTF8(szText);
	LPSTR szUFT8IconPath = WideToUTF8(szIconPath);
	LPSTR szUFT8SoundFile = WideToUTF8(szSoundFile);
	
	LONG32 result = ShowMessageEx(szUTF8Class, szUTF8Title, szUTF8Text, timeout, szUFT8IconPath, hWndReply, uReplyMsg, szUFT8SoundFile);
	
	delete [] szUTF8Class;
	delete [] szUTF8Title;
	delete [] szUTF8Text;
	delete [] szUFT8IconPath;
	delete [] szUFT8SoundFile;
	
	return result;
}

//-----------------------------------------------------------------------------
// snHideMessage()

/// Hides the notification specified by Id. Id is the value returned by
/// snShowMessage() or snShowMessageEx() when the notification was initially
/// created. This function returns True if the notification was successfully
/// hidden or False otherwise (for example, the notification may no longer exist).

BOOL SnarlInterface::HideMessage(LONG32 Id)
{
	SNARLSTRUCT ss;
	ss.Cmd = SNARL_HIDE;
	ss.Id = Id;

	LONG32 n = Send(ss);
	return (n == -1 || n == 1) ? TRUE : FALSE;
}

BOOL SnarlInterface::HideMessage()
{
	return HideMessage(m_nLastMessageId);
}

//-----------------------------------------------------------------------------
// snIsMessageVisible()

/// Returns True if the notification specified by Id is still visible, or
/// False if not. Id is the value returned by snShowMessage() or
/// snShowMessageEx() when the notification was initially created.

BOOL SnarlInterface::IsMessageVisible(LONG32 Id)
{
	SNARLSTRUCT ss;
	ss.Cmd = SNARL_IS_VISIBLE;
	ss.Id = Id;

	// We are getting -1 when true, checking for 1 just in case. We don't want to return true for the other M_RESULT returns
	LONG32 n = Send(ss);	
	return (n == -1 || n == 1) ? TRUE : FALSE;
}

BOOL SnarlInterface::IsMessageVisible()
{
	if (m_nLastMessageId == 0)
		return FALSE;

	return IsMessageVisible(m_nLastMessageId);
}

//-----------------------------------------------------------------------------
// snUpdateMessage()

/// Changes the title and text in the message specified by Id to the values
/// specified by Title and Text respectively. Id is the value returned by 
/// snShowMessage() or snShowMessageEx() when the notification was originally
/// created. To change the timeout parameter of a notification, use snSetTimeout()

M_RESULT SnarlInterface::UpdateMessage(LONG32 id, LPCSTR szTitle, LPCSTR szText, LPCSTR szIconPath)
{
	SNARLSTRUCT ss;
	ZeroMemory((void*)&ss, sizeof(ss));

	ss.Cmd = SNARL_UPDATE;
	ss.Id = id;
	
	StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, szTitle);
	StringCbCopyA((LPSTR)&ss.Text,  SNARL_STRING_LENGTH, szText);
	StringCbCopyA((LPSTR)&ss.Icon,  SNARL_STRING_LENGTH, szIconPath);

	return static_cast<M_RESULT>(Send(ss));
}

M_RESULT SnarlInterface::UpdateMessage(LONG32 id, LPCWSTR szTitle, LPCWSTR szText, LPCWSTR szIconPath)
{
	LPSTR szParam1 = WideToUTF8(szTitle);
	LPSTR szParam2 = WideToUTF8(szText);
	LPSTR szParam3 = WideToUTF8(szIconPath);
	
	M_RESULT result = UpdateMessage(id, szParam1, szParam2, szParam3);
	
	delete [] szParam1;
	delete [] szParam2;
	delete [] szParam3;
	
	return result;
}

M_RESULT SnarlInterface::UpdateMessage(LPCSTR szTitle, LPCSTR szText, LPCSTR szIconPath)
{
	return UpdateMessage(m_nLastMessageId, szTitle, szText, szIconPath);
}

M_RESULT SnarlInterface::UpdateMessage(LPCWSTR szTitle, LPCWSTR szText, LPCWSTR szIconPath)
{
	return UpdateMessage(m_nLastMessageId, szTitle, szText, szIconPath);
}

//-----------------------------------------------------------------------------
// snRegisterConfig

/// Registers an application's configuration interface with Snarl.
/// AppName is the text that's displayed in the Applications list so it should
/// be people friendly ("My cool app" rather than "my_cool_app").

M_RESULT SnarlInterface::RegisterConfig(HWND hWnd, LPCSTR szAppName, LONG32 replyMsg)
{
	return RegisterConfig2(hWnd, szAppName, replyMsg, "");
}

M_RESULT SnarlInterface::RegisterConfig(HWND hWnd, LPCWSTR szAppName, LONG32 replyMsg)
{
	return RegisterConfig2(hWnd, szAppName, replyMsg, L"");
}

//-----------------------------------------------------------------------------
// snRegisterConfig2

/// Registers an application's configuration interface with Snarl.
/// This function is identical to snRegisterConfig() except that Icon can be
/// used to specify a PNG image which will be displayed against the
/// application's entry in Snarl's Preferences panel.

M_RESULT SnarlInterface::RegisterConfig2(HWND hWnd, LPCSTR szAppName, LONG32 replyMsg, LPCSTR szIcon)
{
	if (!szAppName || !szIcon)
		return M_BAD_POINTER;

	SNARLSTRUCT ss;

	m_hwndFrom = hWnd;

	ss.Cmd = SNARL_REGISTER_CONFIG_WINDOW_2;
	ss.LngData2 = reinterpret_cast<LONG32>(hWnd);
	ss.Id = replyMsg;
	StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, szAppName);
	StringCbCopyA((LPSTR)&ss.Icon, SNARL_STRING_LENGTH, szIcon);

	return static_cast<M_RESULT>(Send(ss));
}

M_RESULT SnarlInterface::RegisterConfig2(HWND hWnd, LPCWSTR szAppName, LONG32 replyMsg, LPCWSTR szIcon)
{
	LPSTR szParam1 = WideToUTF8(szAppName);
	LPSTR szParam2 = WideToUTF8(szIcon);
	
	M_RESULT result = RegisterConfig2(hWnd, szParam1, replyMsg, szParam2);
	
	delete [] szParam1;
	delete [] szParam2;
	
	return result;
}


//-----------------------------------------------------------------------------
// snRevokeConfig

/// Removes the application previously registered using snRegisterConfig() or
/// snRegisterConfig2(). hWnd should be the same as that used during registration.

M_RESULT SnarlInterface::RevokeConfig(HWND hWnd)
{
	SNARLSTRUCT ss;
	
	m_hwndFrom = NULL;

	ss.Cmd = SNARL_REVOKE_CONFIG_WINDOW;
	ss.LngData2 = reinterpret_cast<LONG32>(hWnd);

	return static_cast<M_RESULT>(Send(ss));
}


//-----------------------------------------------------------------------------
// snGetVersion()

/// Checks if Snarl is currently running and, if it is, retrieves the major and
/// minor release version numbers in Major and Minor respectively.
/// Returns True if Snarl is running, False otherwise.

BOOL SnarlInterface::GetVersion(WORD* Major, WORD* Minor)
{
	SNARLSTRUCT ss;
	ss.Cmd = SNARL_GET_VERSION;
	LONG32 versionInfo = Send(ss);
	if (versionInfo > 0 && versionInfo != M_FAILED && versionInfo != M_TIMED_OUT) {
		*Major = HIWORD(versionInfo);
		*Minor = LOWORD(versionInfo);
		return TRUE;
	}
	return FALSE;
}


//-----------------------------------------------------------------------------
// snGetVersionEx

/// Returns the Snarl system version number. This is an integer value which
/// represents the system build number and can be used to identify the specific
/// version of Snarl running

LONG32 SnarlInterface::GetVersionEx()
{
	SNARLSTRUCT ss;
	ss.Cmd = SNARL_GET_VERSION_EX;
	return Send(ss);
}


//-----------------------------------------------------------------------------
// snSetTimeout()

/// Sets the timeout of existing notification Id to Timeout seconds. Id is the
/// value returned by snShowMessage() or snShowMessageEx() when the notification
/// was first created. 

M_RESULT SnarlInterface::SetTimeout(LONG32 Id, LONG32 Timeout)
{
	SNARLSTRUCT ss;
	ss.Cmd = SNARL_SET_TIMEOUT;
	ss.Id = Id;
	ss.LngData2 = Timeout;

	return static_cast<M_RESULT>(Send(ss));
}

M_RESULT SnarlInterface::SetTimeout(LONG32 Timeout)
{
	return SetTimeout(m_nLastMessageId, Timeout);
}

//-----------------------------------------------------------------------------
// snRegisterAlert()

/// Registers an alert of Class for application AppName which must have previously
/// been registered with either snRegisterConfig() or snRegisterConfig2().

M_RESULT SnarlInterface::RegisterAlert(LPCSTR szAppName, LPCSTR szClass)
{
	SNARLSTRUCT ss;
	ss.Cmd = SNARL_REGISTER_ALERT;
	StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, szAppName);
	StringCbCopyA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, szClass);

	return static_cast<M_RESULT>(Send(ss));
}

M_RESULT SnarlInterface::RegisterAlert(LPCWSTR szAppName, LPCWSTR szClass)
{
	LPSTR szParam1 = WideToUTF8(szAppName);
	LPSTR szParam2 = WideToUTF8(szClass);
	
	M_RESULT result = RegisterAlert(szParam1, szParam2);
	
	delete [] szParam1;
	delete [] szParam2;
	
	return result;
}

//-----------------------------------------------------------------------------
// snGetGlobalMsg()

/// Returns the atom that corresponds to the "SnarlGlobalEvent" registered
/// Windows message. This message is sent by Snarl when it is first starts and
/// when it shuts down.

LONG32 SnarlInterface::GetGlobalMsg()
{
	return RegisterWindowMessage(SNARL_GLOBAL_MSG);
}


//-----------------------------------------------------------------------------
// snGetSnarlWindow

HWND SnarlInterface::GetSnarlWindow()
{
	return FindWindow(NULL, _T("Snarl"));
}


//-----------------------------------------------------------------------------
// snGetAppPath()

/// Returns a pointer to the path.
/// ** Remember to call FreeString

LPCTSTR SnarlInterface::GetAppPath()
{
	HWND hWnd = GetSnarlWindow();
	if (hWnd)
	{
		HWND hWndPath = FindWindowEx(hWnd, 0, _T("static"), NULL);
		if (hWndPath)
		{
			TCHAR strTmp[MAX_PATH] = {0};
			int nReturn = GetWindowText(hWndPath, strTmp, MAX_PATH);
			if (nReturn > 0) {
				TCHAR* strReturn = AllocateString(nReturn + 1);
				StringCchCopy(strReturn, nReturn + 1, strTmp);
				return strReturn;
			}
		}
	}
	return NULL;
}


//-----------------------------------------------------------------------------
// snGetIconsPath()

/// Returns a pointer to the iconpath.
/// ** Remember to call FreeString when done with the string

LPCTSTR SnarlInterface::GetIconsPath()
{
	TCHAR* szIconPath = NULL;
	LPCTSTR szPath = GetAppPath();
	if (!szPath)
		return NULL;

	size_t nLen = 0;
	if (SUCCEEDED(StringCbLength(szPath, MAX_PATH, &nLen)))
	{
		nLen += 10 + 1; // etc\\icons\\ + NULL
		szIconPath = AllocateString(nLen);

		StringCbCopy(szIconPath, nLen * sizeof(TCHAR), szPath);
		StringCbCat(szIconPath, nLen * sizeof(TCHAR), _T("etc\\icons\\"));
	}
	
	FreeString(szPath);

	return szIconPath;
}


//-----------------------------------------------------------------------------
// snSetAsSnarlApp()

/// Identifies an application as a Snarl App.  (V39)

void SnarlInterface::SetAsSnarlApp(HWND hWndOwner, SNARL_APP_FLAGS Flags)
{
	if (IsWindow(hWndOwner)) {
		SetProp(hWndOwner, _T("snarl_app"), reinterpret_cast<HANDLE>(1));
		SetProp(hWndOwner, _T("snarl_app_flags"), reinterpret_cast<HANDLE>(Flags));
	}
}


//-----------------------------------------------------------------------------
// snGetAppMsg()

/// Returns the global Snarl Application message  (V39)

LONG32 SnarlInterface::GetAppMsg()
{
	return RegisterWindowMessage(SNARL_APP_MSG);
}


//-----------------------------------------------------------------------------
// snRegisterApp()

/// Registers an application with Snarl  (V39)

M_RESULT SnarlInterface::RegisterApp(LPCSTR Application, LPCSTR SmallIcon, LPCSTR LargeIcon, HWND hWnd, LONG32 ReplyMsg)
{
	m_hwndFrom = hWnd;
	  
    SNARLSTRUCT ss;
	ss.Cmd = SNARL_REGISTER_APP;
	
	StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, Application);
	StringCbCopyA((LPSTR)&ss.Icon,  SNARL_STRING_LENGTH, SmallIcon);
	StringCbCopyA((LPSTR)&ss.Text,  SNARL_STRING_LENGTH, LargeIcon);

	ss.LngData2 = reinterpret_cast<LONG32>(hWnd);
	ss.Id = ReplyMsg;
	ss.Timeout = GetCurrentProcessId();

	return static_cast<M_RESULT>(Send(ss));
}

M_RESULT SnarlInterface::RegisterApp(LPCWSTR Application, LPCWSTR SmallIcon, LPCWSTR LargeIcon, HWND hWnd, LONG32 ReplyMsg)
{
	LPSTR szParam1 = WideToUTF8(Application);
	LPSTR szParam2 = WideToUTF8(SmallIcon);
	LPSTR szParam3 = WideToUTF8(LargeIcon);
	
	M_RESULT result = RegisterApp(szParam1, szParam2, szParam3, hWnd, ReplyMsg);
	
	delete [] szParam1;
	delete [] szParam2;
	delete [] szParam3;
	
	return result;
}


//-----------------------------------------------------------------------------
// snUnregisterApp()

/// Unregisters an application with Snarl  (V39)

M_RESULT SnarlInterface::UnregisterApp()
{
	SNARLSTRUCT ss;
	ss.Cmd = SNARL_UNREGISTER_APP;
	ss.LngData2 = GetCurrentProcessId();
	
	m_hwndFrom = NULL;
	
	return static_cast<M_RESULT>(Send(ss));
}


//-----------------------------------------------------------------------------
// snShowNotification()

/// Displays a Snarl notification using registered class  (V39)
/// <returns>Message Id on success or M_RESULT on failure</returns>

LONG32 SnarlInterface::ShowNotification(LPCSTR Class, LPCSTR Title, LPCSTR Text, LONG32 Timeout, LPCSTR Icon, HWND hWndReply, LONG32 uReplyMsg, LPCSTR Sound)
{
	SNARLSTRUCTEX ssex;
	ssex.Cmd = SNARL_SHOW_NOTIFICATION;
	
	StringCbCopyExA((LPSTR)&ssex.Title, SNARL_STRING_LENGTH, Title, NULL, NULL, STRSAFE_IGNORE_NULLS);
	StringCbCopyExA((LPSTR)&ssex.Text,  SNARL_STRING_LENGTH, Text,  NULL, NULL, STRSAFE_IGNORE_NULLS);
	StringCbCopyExA((LPSTR)&ssex.Icon,  SNARL_STRING_LENGTH, Icon,  NULL, NULL, STRSAFE_IGNORE_NULLS);

	ssex.Timeout = Timeout;
	ssex.LngData2 = reinterpret_cast<LONG32>(hWndReply);
	ssex.Id = uReplyMsg;

	StringCbCopyExA((LPSTR)&ssex.Extra, SNARL_STRING_LENGTH, Sound, NULL, NULL, STRSAFE_IGNORE_NULLS);
	StringCbCopyA((LPSTR)&ssex.Class,  SNARL_STRING_LENGTH, Class);
    
	ssex.Reserved1 = GetCurrentProcessId();
	
	m_nLastMessageId = Send(ssex);
	return m_nLastMessageId;
}

LONG32 SnarlInterface::ShowNotification(LPCWSTR Class, LPCWSTR Title, LPCWSTR Text, LONG32 Timeout, LPCWSTR Icon, HWND hWndReply, LONG32 uReplyMsg, LPCWSTR Sound)
{
	LPSTR szParam1 = WideToUTF8(Class);
	LPSTR szParam2 = WideToUTF8(Title);
	LPSTR szParam3 = WideToUTF8(Text);
	LPSTR szParam4 = WideToUTF8(Icon);
	LPSTR szParam5 = WideToUTF8(Sound);
	
	LONG32 result = ShowNotification(szParam1, szParam2, szParam3, Timeout, szParam4, hWndReply, uReplyMsg, szParam5);
	
	delete [] szParam1;
	delete [] szParam2;
	delete [] szParam3;
	delete [] szParam4;
	delete [] szParam5;
	
	return result;
}


//-----------------------------------------------------------------------------
// snChangeAttribute()

/// (V39)

M_RESULT SnarlInterface::ChangeAttribute(LONG32 Id, SNARL_ATTRIBUTES Attr, LPCSTR Value)
{
	SNARLSTRUCT ss;
	ss.Cmd = SNARL_CHANGE_ATTR;
	ss.Id = Id;
	ss.LngData2 = Attr;
	
	StringCbCopyExA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, Value, NULL, NULL, STRSAFE_IGNORE_NULLS);
	
	return static_cast<M_RESULT>(Send(ss));
}

M_RESULT SnarlInterface::ChangeAttribute(LONG32 Id, SNARL_ATTRIBUTES Attr, LPCWSTR Value)
{
	LPSTR szParam1 = WideToUTF8(Value);
	
	M_RESULT result = ChangeAttribute(Id, Attr, szParam1);
	
	delete [] szParam1;
	
	return result;
}

M_RESULT SnarlInterface::ChangeAttribute(SNARL_ATTRIBUTES Attr, LPCSTR Value)
{
	return ChangeAttribute(m_nLastMessageId, Attr, Value);
}

M_RESULT SnarlInterface::ChangeAttribute(SNARL_ATTRIBUTES Attr, LPCWSTR Value)
{
	return ChangeAttribute(m_nLastMessageId, Attr, Value);
}


//-----------------------------------------------------------------------------
// snSetClassDefault()

/// Sets the default value for an alert class  (V39)

M_RESULT SnarlInterface::SetClassDefault(LPCSTR Class, SNARL_ATTRIBUTES Attr, LPCSTR Value)
{
	SNARLSTRUCT ss;
	ss.Cmd = SNARL_SET_CLASS_DEFAULT;
	ss.LngData2 = Attr;
	ss.Timeout = GetCurrentProcessId();
	
	StringCbCopyExA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, Class, NULL, NULL, STRSAFE_IGNORE_NULLS);
	StringCbCopyExA((LPSTR)&ss.Icon, SNARL_STRING_LENGTH, Value, NULL, NULL, STRSAFE_IGNORE_NULLS);

	return static_cast<M_RESULT>(Send(ss));
}

M_RESULT SnarlInterface::SetClassDefault(LPCWSTR Class, SNARL_ATTRIBUTES Attr, LPCWSTR Value)
{
	LPSTR szParam1 = WideToUTF8(Class);
	LPSTR szParam2 = WideToUTF8(Value);
	
	M_RESULT result = SetClassDefault(szParam1, Attr, szParam2);
	
	delete [] szParam1;
	delete [] szParam2;
	
	return result;
}


//-----------------------------------------------------------------------------
// snGetRevision()

/// Gets the current Snarl revision (build) number  (V39)
/// Returns the build version number, or M_RESULT on failure.

LONG32 SnarlInterface::GetRevision()
{
	SNARLSTRUCT ss;
	ss.Cmd = SNARL_GET_REVISION;
	ss.LngData2 = 0xFFFE;

	return Send(ss);
}


//-----------------------------------------------------------------------------
// snAddClass()

/// (V39)

M_RESULT SnarlInterface::AddClass(LPCSTR Class, LPCSTR Description, SNARL_CLASS_FLAGS Flags, LPCSTR DefaultTitle, LPCSTR DefaultIcon, LONG32 DefaultTimeout)
{
	SNARLSTRUCT ss;
	ss.Cmd = SNARL_ADD_CLASS;
	ss.LngData2 = Flags;
	ss.Timeout = GetCurrentProcessId();
	
	StringCbCopyExA((LPSTR)&ss.Text,  SNARL_STRING_LENGTH, Class, NULL, NULL, STRSAFE_IGNORE_NULLS);
	StringCbCopyExA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, Description, NULL, NULL, STRSAFE_IGNORE_NULLS);

	LONG32 result = Send(ss);

	if (static_cast<M_RESULT>(result) == M_OK)
	{
		SetClassDefault(Class, SNARL_ATTRIBUTE_TITLE, DefaultTitle);
		SetClassDefault(Class, SNARL_ATTRIBUTE_ICON, DefaultIcon);
		if (DefaultTimeout > 0) {
			char str[64] = {0};
			StringCbPrintfA((LPSTR)&str, sizeof(str), "%d", DefaultTimeout);
			SetClassDefault(Class, SNARL_ATTRIBUTE_TIMEOUT, str);
		}
		
		return M_OK;
	}
	else
		return M_FAILED;
}

M_RESULT SnarlInterface::AddClass(LPCWSTR Class, LPCWSTR Description, SNARL_CLASS_FLAGS Flags, LPCWSTR DefaultTitle, LPCWSTR DefaultIcon, LONG32 DefaultTimeout)
{
	LPCSTR szClass        = WideToUTF8(Class);
	LPCSTR szDescription  = WideToUTF8(Description);
	LPCSTR szDefaultTitle = WideToUTF8(DefaultTitle);
	LPCSTR szDefaultIcon  = WideToUTF8(DefaultIcon);
	
	M_RESULT result = AddClass(szClass, szDescription, Flags, szDefaultTitle, szDefaultIcon, DefaultTimeout);
	
	delete [] szClass;
	delete [] szDescription;
	delete [] szDefaultTitle;
	delete [] szDefaultIcon;
	
	return result;
}

//-----------------------------------------------------------------------------
// Private functions
//-----------------------------------------------------------------------------

template <class T>
LONG32 SnarlInterface::Send(T ss)
{
	DWORD_PTR nReturn = M_FAILED;

	HWND hWnd = GetSnarlWindow();
	if (IsWindow(hWnd))
	{
		COPYDATASTRUCT cds;
		cds.dwData = 2;
		cds.cbData = sizeof(ss);
		cds.lpData = &ss;

		if (SendMessageTimeout(hWnd, WM_COPYDATA, (WPARAM)m_hwndFrom, (LPARAM)&cds, SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 1000, &nReturn) == 0)
		{
			if (GetLastError() == ERROR_TIMEOUT)
				nReturn = M_TIMED_OUT;
		}
	}

	return static_cast<LONG32>(nReturn);
}

//-----------------------------------------------------------------------------

// Remember to : delete [] returned string

LPSTR SnarlInterface::WideToUTF8(LPCWSTR szWideStr)
{
	if (szWideStr == NULL)
		return NULL;

    int nSize = WideCharToMultiByte(CP_UTF8, 0, szWideStr, -1, NULL, 0, NULL, NULL);
    LPSTR szUTF8 = new char[nSize];
    WideCharToMultiByte(CP_UTF8, 0, szWideStr, -1, szUTF8, nSize, NULL, NULL);
    
    return szUTF8;
}