summaryrefslogtreecommitdiffstats
blob: 0ae0b378e9598b6e8b2c14a4d5a85d107e2d1c2c (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
/// <summary>
///  Snarl C++ interface implementation
///
///  Written and maintained by Toke Noer Nøttrup (toke@noer.it)
///
///  Please note the following changes compared to the VB6 (official API) dokumentation:
///  - Function names doesn't have the prefix "sn". Naming of constants and variables are
///    generally changed to follow Microsoft C# standard. This naming convention is kept for
///    the C++ version, to keep them alike.
///  - Grouped variables like SNARL_LAUNCHED, SNARL_QUIT is enums in SnarlEnums namespace.
///  - Message events like SNARL_NOTIFICATION_CLICKED, is found in SnarlEnums::MessageEvent.
///  - Please note that string functions return NULL when they fail and not an empty string.
///  - Some functions in the VB API takes an appToken as first parameter. This token is a
///    member variable in C++ version, so it is omitted from the functions.
///    (Always call RegisterApp as first function!)
///  - Functions manipulating messages (Update, Hide etc.) still takes a message token as
///    parameter, but you can get the last message token calling GetLastMsgToken();
///    Example: snarl.Hide(snarl.GetLastMsgToken());
///
///  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!
///
///  Funtions special to C++ V41 API compared to VB version:
///    GetLastMsgToken()
///    GetAppPath()
///    GetIconsPath()
/// </summary>
///----------------------------------------------------------------------------
/// <example>
/// SnarlInterface snarl;
/// snarl.RegisterApp(_T("CppTest"), _T("C++ test app"), NULL);
/// snarl.AddClass(_T("Class1"), _T("Class 1"));
/// snarl.EZNotify(_T("Class1"), _T("C++ example 1"), _T("Some text"), 10);
/// snarl.UnregisterApp();
///
/// Please see the SimpleTest.cpp and SnarlV41Test.cpp for more example code.
/// </example>
///----------------------------------------------------------------------------
/// <VersionHistory>
///  2010-08-13 : First release of V41 Snarl API implementation
/// </VersionHistory>

#define _CRT_SECURE_NO_WARNINGS

#include "SnarlInterface.h"


namespace Snarl {
namespace V41 {

//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
SnarlInterface::SnarlInterface()
	: appToken(0), lastMsgToken(0), localError(SnarlEnums::Success)
{
}

SnarlInterface::~SnarlInterface()
{
}

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

LONG32 SnarlInterface::RegisterApp(LPCSTR signature, LPCSTR title, LPCSTR icon, HWND hWndReply /* = NULL */, LONG32 msgReply /* = 0 */, SnarlEnums::AppFlags flags /* = SnarlEnums::AppDefault */)
{
	SnarlMessage msg;
	msg.Command = SnarlEnums::RegisterApp;
	msg.Token = 0;
	PackData(msg.PacketData, 
		"id::%s#?title::%s#?icon::%s#?hwnd::%d#?umsg::%d#?flags::%d", 
		signature, title, icon, hWndReply, msgReply, flags);

	appToken = Send(msg);
	lastMsgToken = 0;

	return appToken;
}

LONG32 SnarlInterface::RegisterApp(LPCWSTR signature, LPCWSTR title, LPCWSTR icon, HWND hWndReply /* = NULL */, LONG32 msgReply /* = 0 */, SnarlEnums::AppFlags flags /* = SnarlEnums::AppDefault */)
{
	LPCSTR szParam1 = WideToUTF8(signature);
	LPCSTR szParam2 = WideToUTF8(title);
	LPCSTR szParam3 = WideToUTF8(icon);
	
	LONG32 result = RegisterApp(szParam1, szParam2, szParam3, hWndReply, msgReply, flags);
	
	delete [] szParam1;
	delete [] szParam2;
	delete [] szParam3;

	return result;
}

LONG32 SnarlInterface::UnregisterApp()
{
	SnarlMessage msg;
	msg.Command = SnarlEnums::UnregisterApp;
	msg.Token = appToken;
	PackData(msg.PacketData, NULL);

	appToken = 0;
	lastMsgToken = 0;

	return Send(msg);
}

LONG32 SnarlInterface::UpdateApp(LPCSTR title /* = NULL */, LPCSTR icon /* = NULL */)
{
	if (title == NULL && icon == NULL)
		return 0;

	SnarlMessage msg;
	msg.Command = SnarlEnums::UpdateApp;
	msg.Token = appToken;
	
	// TODO: Uckly code ahead
	if (title != NULL && title[0] != 0 && icon != NULL && icon[0] != 0)
		PackData(msg.PacketData, "title::%s#?icon::%s", title, icon);
	else if (title != NULL && title[0] != 0)
		PackData(msg.PacketData, "title::%s", title);
	else if (icon != NULL && icon[0] != 0)
		PackData(msg.PacketData, "icon::%s", icon);
	
	return Send(msg);
}

LONG32 SnarlInterface::UpdateApp(LPCWSTR title /* = NULL */, LPCWSTR icon /* = NULL */)
{
	LPCSTR szParam1 = WideToUTF8(title);
	LPCSTR szParam2 = WideToUTF8(icon);
	
	LONG32 result = UpdateApp(szParam1, szParam2);
	
	delete [] szParam1;
	delete [] szParam2;

	return result;
}

LONG32 SnarlInterface::AddClass(LPCSTR className, LPCSTR description, bool enabled /* = true */)
{
	SnarlMessage msg;
	msg.Command = SnarlEnums::AddClass;
	msg.Token = appToken;
	PackData(msg.PacketData, "id::%s#?name::%s#?enabled::%d", className, description, (enabled ? 1 : 0));

	return Send(msg);
}

LONG32 SnarlInterface::AddClass(LPCWSTR className, LPCWSTR description, bool enabled /* = true */)
{
	LPCSTR szParam1 = WideToUTF8(className);
	LPCSTR szParam2 = WideToUTF8(description);
	
	LONG32 result = AddClass(szParam1, szParam2, enabled);
	
	delete [] szParam1;
	delete [] szParam2;

	return result;
}

LONG32 SnarlInterface::RemoveClass(LPCSTR className, bool forgetSettings /* = false */)
{
	SnarlMessage msg;
	msg.Command = SnarlEnums::RemoveClass;
	msg.Token = appToken;
	PackData(msg.PacketData, "id::%s#?forget::%d", className, (forgetSettings ? 1 : 0));

	return Send(msg);
}

LONG32 SnarlInterface::RemoveClass(LPCWSTR className, bool forgetSettings /* = false */)
{
	LPCSTR szParam1 = WideToUTF8(className);
	
	LONG32 result = RemoveClass(szParam1, forgetSettings);
	
	delete [] szParam1;

	return result;
}

LONG32 SnarlInterface::RemoveAllClasses(bool forgetSettings /* = false */)
{
	SnarlMessage msg;
	msg.Command = SnarlEnums::RemoveClass;
	msg.Token = appToken;
	PackData(msg.PacketData, "all::1#?forget::%d", (forgetSettings ? 1 : 0));

	return Send(msg);
}

LONG32 SnarlInterface::EZNotify(LPCSTR className, LPCSTR title, LPCSTR text, LONG32 timeout /* = -1 */, LPCSTR icon /* = NULL */, LONG32 priority /* = 0 */, LPCSTR acknowledge /* = NULL */, LPCSTR value /* = NULL */)
{
	SnarlMessage msg;
	msg.Command = SnarlEnums::Notify;
	msg.Token = appToken;
	PackData(msg.PacketData,
		"id::%s#?title::%s#?text::%s#?timeout::%d#?icon::%s#?priority::%d#?ack::%s#?value::%s",
		className, title, text, timeout, (icon ? icon : ""), priority, (acknowledge ? acknowledge : ""), (value ? value : ""));

	lastMsgToken = Send(msg);
	return lastMsgToken;
}

LONG32 SnarlInterface::EZNotify(LPCWSTR className, LPCWSTR title, LPCWSTR text, LONG32 timeout /* = -1 */, LPCWSTR icon /* = NULL */, LONG32 priority /* = 0 */, LPCWSTR acknowledge /* = NULL */, LPCWSTR value /* = NULL */)
{
	LPCSTR szParam1 = WideToUTF8(className);
	LPCSTR szParam2 = WideToUTF8(title);
	LPCSTR szParam3 = WideToUTF8(text);
	LPCSTR szParam4 = WideToUTF8(icon);
	LPCSTR szParam5 = WideToUTF8(acknowledge);
	LPCSTR szParam6 = WideToUTF8(value);
	
	LONG32 result = EZNotify(szParam1, szParam2, szParam3, timeout, szParam4, priority, szParam5, szParam6);
	
	delete [] szParam1; delete [] szParam2; delete [] szParam3;
	delete [] szParam4; delete [] szParam5; delete [] szParam6;

	return result;
}

LONG32 SnarlInterface::Notify(LPCSTR className, LPCSTR packetData)
{
	SnarlMessage msg;
	msg.Command = SnarlEnums::Notify;
	msg.Token = appToken;
	PackData(msg.PacketData, "id::%s#?%s", className, packetData);

	lastMsgToken = Send(msg);
	return lastMsgToken;
}

LONG32 SnarlInterface::Notify(LPCWSTR className, LPCWSTR packetData)
{
	LPCSTR szParam1 = WideToUTF8(className);
	LPCSTR szParam2 = WideToUTF8(packetData);

	LONG32 result = Notify(szParam1, szParam2);
	
	delete [] szParam1; delete [] szParam2;
	
	return result;
}

LONG32 SnarlInterface::EZUpdate(LONG32 msgToken, LPCSTR title /* = NULL */, LPCSTR text /* = NULL */, LONG32 timeout /* = -1 */, LPCSTR icon /* = NULL */)
{
	SnarlMessage msg;
	msg.Command = SnarlEnums::UpdateNotification;
	msg.Token = msgToken;
	
	// Create packed data
	errno_t err = 0;
	ZeroMemory(msg.PacketData, sizeof(msg.PacketData));
	char* pData = reinterpret_cast<char*>(msg.PacketData);

	if (title != NULL) {
		err |= strncat_s(pData, SnarlPacketDataSize, (pData[0] != NULL) ? "#?title::" : "title::", _TRUNCATE); //StringCbCat(tmp, SnarlPacketDataSize, "title::%s");
		err |= strncat_s(pData, SnarlPacketDataSize, title, _TRUNCATE);
	}
	if (text != NULL) {
		err |= strncat_s(pData, SnarlPacketDataSize, (pData[0] != NULL) ? "#?text::" : "text::", _TRUNCATE);
		err |= strncat_s(pData, SnarlPacketDataSize, text, _TRUNCATE);
	}
	if (icon != NULL) {
		err |= strncat_s(pData, SnarlPacketDataSize, (pData[0] != NULL) ? "#?icon::" : "icon::", _TRUNCATE);
		err |= strncat_s(pData, SnarlPacketDataSize, icon, _TRUNCATE);
	}
	if (timeout != -1) {
		char tmp[32];
		_itoa_s(timeout, tmp, 10);
		
		err |= strncat_s(pData, SnarlPacketDataSize, (pData[0] != NULL) ? "#?timeout::" : "timeout::", _TRUNCATE);
		err |= strncat_s(pData, SnarlPacketDataSize, tmp, _TRUNCATE);
	}
	
	// Check for strcat errors and exit on error
	if (err != 0) {
		localError = SnarlEnums::ErrorFailed;
		return 0;
	}

	return Send(msg);
}

LONG32 SnarlInterface::EZUpdate(LONG32 msgToken, LPCWSTR title /* = NULL */, LPCWSTR text /* = NULL */, LONG32 timeout /* = -1 */, LPCWSTR icon /* = NULL */)
{
	LPCSTR szParam1 = WideToUTF8(title);
	LPCSTR szParam2 = WideToUTF8(text);
	LPCSTR szParam3 = WideToUTF8(icon);

	LONG32 result = EZUpdate(msgToken, szParam1, szParam2, timeout, szParam3);
	
	delete [] szParam1; delete [] szParam2; delete [] szParam3;
	
	return result;
}

LONG32 SnarlInterface::Update(LONG32 msgToken, LPCSTR packetData)
{
	SnarlMessage msg;
	msg.Command = SnarlEnums::UpdateNotification;
	msg.Token = msgToken;
	PackData(msg.PacketData, packetData);

	return Send(msg);
}

LONG32 SnarlInterface::Update(LONG32 msgToken, LPCWSTR packetData)
{
	LPCSTR szParam1 = WideToUTF8(packetData);

	LONG32 result = Update(msgToken, szParam1);
	
	delete [] szParam1;
	
	return result;
}

LONG32 SnarlInterface::Hide(LONG32 msgToken)
{
	SnarlMessage msg;
	msg.Command = SnarlEnums::HideNotification;
	msg.Token = msgToken;
	PackData(msg.PacketData, NULL);

	return Send(msg);
}

LONG32 SnarlInterface::IsVisible(LONG32 msgToken)
{
	SnarlMessage msg;
	msg.Command = SnarlEnums::IsNotificationVisible;
	msg.Token = msgToken;
	PackData(msg.PacketData, NULL);

	return Send(msg);
}

SnarlEnums::SnarlStatus SnarlInterface::GetLastError()
{
	return localError;
}

// static
BOOL SnarlInterface::IsSnarlRunning()
{
	return IsWindow(GetSnarlWindow());
}

LONG32 SnarlInterface::GetVersion()
{
	localError = SnarlEnums::Success;

	HWND hWnd = GetSnarlWindow();
	if (!IsWindow(hWnd))
	{
		localError = SnarlEnums::ErrorNotRunning;
		return 0;
	}

	HANDLE hProp = GetProp(hWnd, _T("_version"));
	return reinterpret_cast<int>(hProp);
}

// static
UINT SnarlInterface::Broadcast()
{
	return RegisterWindowMessage(SnarlGlobalMsg);
}

// static
UINT SnarlInterface::AppMsg()
{
	return RegisterWindowMessage(SnarlAppMsg);
}

// static
HWND SnarlInterface::GetSnarlWindow()
{
	return FindWindow(SnarlWindowClass, SnarlWindowTitle);;
}

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

	return NULL;
}

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

	size_t nLen = 0;
	// TODO: _tcsnlen MAX_PATH
	if (nLen = _tcsnlen(szPath, MAX_PATH))
	{
		nLen += 10 + 1; // etc\\icons\\ + NULL
		szIconPath = AllocateString(nLen);

		_tcsncpy(szIconPath, szPath, nLen);
		_tcsncat(szIconPath, _T("etc\\icons\\"), nLen);
	}
	
	FreeString(szPath);

	return szIconPath;
}

LONG32 SnarlInterface::GetLastMsgToken() const
{
	return lastMsgToken;
}


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

LONG32 SnarlInterface::Send(SnarlMessage msg)
{
	DWORD_PTR nReturn = 0; // Failure

	HWND hWnd = GetSnarlWindow();
	if (!IsWindow(hWnd))
	{
		localError = SnarlEnums::ErrorNotRunning;
		return 0;
	}

	COPYDATASTRUCT cds;
	cds.dwData = 0x534E4C02; // "SNL",2;
	cds.cbData = sizeof(SnarlMessage);
	cds.lpData = &msg;

	if (SendMessageTimeout(hWnd, WM_COPYDATA, (WPARAM)GetCurrentProcessId(), (LPARAM)&cds, SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 500, &nReturn) == 0)
	{
		// return zero on failure
		if (GetLastError() == ERROR_TIMEOUT)
			localError = SnarlEnums::ErrorTimedOut;
		else
			localError = SnarlEnums::ErrorFailed;
		
		return 0;
	}

	// return result and cache LastError
	HANDLE hProp = GetProp(hWnd, _T("last_error"));
	localError = static_cast<SnarlEnums::SnarlStatus>(reinterpret_cast<int>(hProp));

	return nReturn;
}

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

// Remember to delete [] returned string
inline
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;
}

void SnarlInterface::PackData(BYTE* data, LPCSTR format, ...)
{
	// Always zero array - Used to clear the array in member functions
	ZeroMemory(data, SnarlPacketDataSize);

	// Return if format string is empty
	if (format == NULL || format[0] == 0)
		return;

	int cchStrTextLen = 0;
	va_list args;
	va_start(args, format);
	
	// Get size of buffer
	cchStrTextLen = _vscprintf(format, args) + 1; // + NULL
	if (cchStrTextLen <= 1)
		return;

	// Create formated string - _TRUNCATE will ensure zero terminated
	_vsnprintf_s((char*)data, SnarlPacketDataSize, _TRUNCATE, format, args);

	va_end(args);
}

}} // namespace Snarl::V41