Various improvements to sound effects.
[ClearClipboard.git] / src / ClearClipboard.c
blobbc3cb9ed7daf6d4dd3f8a55048a9a69df7fc73e3
1 /* ---------------------------------------------------------------------------------------------- */
2 /* Clear Clipboard */
3 /* Copyright(c) 2019 LoRd_MuldeR <mulder2@gmx.de> */
4 /* */
5 /* Permission is hereby granted, free of charge, to any person obtaining a copy of this software */
6 /* and associated documentation files (the "Software"), to deal in the Software without */
7 /* restriction, including without limitation the rights to use, copy, modify, merge, publish, */
8 /* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the */
9 /* Software is furnished to do so, subject to the following conditions: */
10 /* */
11 /* The above copyright notice and this permission notice shall be included in all copies or */
12 /* substantial portions of the Software. */
13 /* */
14 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING */
15 /* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
16 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, */
17 /* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
18 /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
19 /* ---------------------------------------------------------------------------------------------- */
21 #define WIN32_LEAN_AND_MEAN 1
22 #include <windows.h>
23 #include <shellapi.h>
24 #include <MMSystem.h>
26 #include "Version.h"
28 // Options
29 #define ENABLE_DEBUG_OUTPOUT 1
30 #define DEFAULT_TIMEOUT 30000U
31 #define DEFAULT_SOUND_LEVEL 1U
33 // Const
34 #define MUTEX_NAME L"{E19E5CE1-5EF2-4C10-843D-E79460920A4A}"
35 #define CLASS_NAME L"{6D6CB8E6-BFEE-40A1-A6B2-2FF34C43F3F8}"
36 #define TIMER_ID 0x5281CC36
37 #define ID_NOTIFYICON 0x8EF73CE1
38 #define WM_NOTIFYICON (WM_APP+101U)
39 #define MENU1_ID 0x1A5C
40 #define MENU2_ID 0x6810
41 #define MENU3_ID 0x46C3
42 #define MENU4_ID 0x38D6
44 // Debug output
45 #if defined(ENABLE_DEBUG_OUTPOUT) && ENABLE_DEBUG_OUTPOUT
46 #define PRINT(TEXT) do \
47 { \
48 if (g_debug) \
49 OutputDebugStringA("ClearClipboard -- " TEXT "\n"); \
50 } \
51 while(0)
52 #else
53 #define PRINT(TEXT) __noop((X))
54 #endif
56 // Play sound
57 #define PLAY_SOUND(X) do \
58 { \
59 if(g_sound_enabled >= (X)) \
60 play_sound_effect(); \
61 } \
62 while(0)
64 // Helper macro
65 #define ERROR_EXIT(X) do \
66 { \
67 result = (X); goto clean_up; \
68 } \
69 while(0)
71 // Wide string wrapper macro
72 #define _WTEXT_(X) L##X
73 #define WTEXT(X) _WTEXT_(X)
75 // Global variables
76 static UINT g_taskbar_created = 0U;
77 static HICON g_app_icon = NULL;
78 static HMENU g_context_menu = NULL;
79 static UINT g_timeout = DEFAULT_TIMEOUT;
80 static UINT g_sound_enabled = DEFAULT_SOUND_LEVEL;
81 static BOOL g_suspended = FALSE;
82 static ULONGLONG g_tickCount = 0U;
83 #ifndef _DEBUG
84 static BOOL g_debug = FALSE;
85 #else
86 static const BOOL g_debug = TRUE;
87 #endif
89 // Forward declaration
90 static LRESULT CALLBACK my_wnd_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
91 static BOOL clear_clipboard(void);
92 static UINT parse_arguments(const WCHAR *const command_line);
93 static BOOL update_autorun_entry(const BOOL remove);
94 static BOOL create_shell_notify_icon(const HWND hwnd, const BOOL remove);
95 static BOOL update_shell_notify_icon(const HWND hwnd, const BOOL suspended);
96 static BOOL about_screen(const BOOL first_run);
97 static BOOL show_disclaimer(void);
98 static BOOL play_sound_effect(void);
99 static WCHAR *get_configuration_path(void);
100 static WCHAR *get_executable_path(void);
101 static UINT get_config_value(const WCHAR *const path, const WCHAR *const name, const UINT default_value, const UINT min_value, const UINT max_value);
102 static DWORD reg_read_value(const HKEY root, const WCHAR *const path, const WCHAR *const name, const DWORD default_value);
103 static WCHAR *reg_read_string(const HKEY root, const WCHAR *const path, const WCHAR *const name);
104 static BOOL reg_write_value(const HKEY root, const WCHAR *const path, const WCHAR *const name, const DWORD value);
105 static BOOL reg_write_string(const HKEY root, const WCHAR *const path, const WCHAR *const name, const WCHAR *const text);
106 static BOOL reg_delete_value(const HKEY root, const WCHAR *const path, const WCHAR *const name);
108 // ==========================================================================
109 // Entry point function
110 // ==========================================================================
112 #ifndef _DEBUG
114 extern IMAGE_DOS_HEADER __ImageBase;
115 int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow);
117 int startup(void)
119 const int exit_code = wWinMain((HINSTANCE)&__ImageBase, NULL, GetCommandLineW(), SW_SHOWDEFAULT);
120 ExitProcess((UINT)exit_code);
121 return exit_code;
124 #endif //_DEBUG
126 // ==========================================================================
127 // MAIN
128 // ==========================================================================
130 int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
132 int result = 0, status = -1;
133 UINT mode = 0U;
134 HANDLE mutex = NULL;
135 HWND hwnd = NULL;
136 BOOL have_listener = FALSE, have_timer = FALSE;
137 const WCHAR *config_path = NULL;
138 WNDCLASSW wcl;
139 MSG msg;
141 // Initialize variables
142 SecureZeroMemory(&wcl, sizeof(WNDCLASSW));
143 SecureZeroMemory(&msg, sizeof(MSG));
145 // Parse CLI arguments
146 mode = parse_arguments(lpCmdLine);
147 PRINT("ClearClipboard v" VERSION_STR " [" __DATE__ "]");
149 // Check argument
150 if(mode > 4U)
152 MessageBoxW(NULL, L"Invalid command-line argument(s). Exiting!", L"ClearClipboard v" WTEXT(VERSION_STR), MB_ICONERROR | MB_TOPMOST);
153 return -1;
156 // Close running instances, if it was requested
157 if((mode == 1U) || (mode == 2U))
159 PRINT("closing all running instances...");
160 while(hwnd = FindWindowExW(NULL, hwnd, CLASS_NAME, NULL))
162 PRINT("sending WM_CLOSE");
163 SendMessageW(hwnd, WM_CLOSE, 0U, 0U);
165 if(mode == 1U)
167 PRINT("goodbye.");
168 return 0;
172 // Add or remove autorun entry, if it was requested
173 if((mode == 3U) || (mode == 4U))
175 const BOOL success = update_autorun_entry(mode > 3U);
176 PRINT("goodbye.");
177 return success ? 0 : 1;
180 // Lock single instance mutex
181 if(mutex = CreateMutexW(NULL, FALSE, MUTEX_NAME))
183 const DWORD ret = WaitForSingleObject(mutex, (mode > 1U) ? 5000U : 250U);
184 if((ret != WAIT_OBJECT_0) && (ret != WAIT_ABANDONED))
186 PRINT("already running, exiting!");
187 ERROR_EXIT(1);
191 // Show disclaimer message
192 if(!show_disclaimer())
194 ERROR_EXIT(2);
197 // Print status
198 PRINT("starting up...");
200 // Register "TaskbarCreated" window message
201 if(g_taskbar_created = RegisterWindowMessageW(L"TaskbarCreated"))
203 ChangeWindowMessageFilter(g_taskbar_created, MSGFLT_ADD);
206 // Read config from INI file
207 if(config_path = get_configuration_path())
209 g_timeout = get_config_value(config_path, L"Timeout", DEFAULT_TIMEOUT, 1000U, USER_TIMER_MAXIMUM);
210 g_sound_enabled = get_config_value(config_path, L"SoundEnabled", DEFAULT_SOUND_LEVEL, 0U, 2U);
211 LocalFree((HLOCAL)config_path);
214 // Load icon resources
215 if(!(g_app_icon = LoadIconW(hInstance, MAKEINTRESOURCEW(101))))
217 PRINT("failed to load icon resource!");
218 ERROR_EXIT(3);
221 // Create context menu
222 if(g_context_menu = CreatePopupMenu())
224 AppendMenuW(g_context_menu, MF_STRING, MENU1_ID, L"ClearClipboard v" WTEXT(VERSION_STR));
225 AppendMenuW(g_context_menu, MF_SEPARATOR, 0, NULL);
226 AppendMenuW(g_context_menu, MF_STRING, MENU2_ID, L"Clear now!");
227 AppendMenuW(g_context_menu, MF_STRING, MENU3_ID, L"Suspend auto-clearing");
228 AppendMenuW(g_context_menu, MF_SEPARATOR, 0, NULL);
229 AppendMenuW(g_context_menu, MF_STRING, MENU4_ID, L"Quit");
230 SetMenuDefaultItem(g_context_menu, MENU1_ID, FALSE);
232 else
234 PRINT("failed to create context menu!");
235 ERROR_EXIT(4);
238 // Register window class
239 wcl.lpfnWndProc = my_wnd_proc;
240 wcl.hInstance = hInstance;
241 wcl.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
242 wcl.lpszClassName = CLASS_NAME;
243 if(!RegisterClassW(&wcl))
245 PRINT("failed to register window class!");
246 ERROR_EXIT(5);
249 // Create the message-only window
250 if(!(hwnd = CreateWindowExW(0L, CLASS_NAME, L"ClearClipboard window", WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 0, 0, 0, 0, NULL, 0, hInstance, NULL)))
252 PRINT("failed to create the window!");
253 ERROR_EXIT(6);
256 // Create notification icon
257 create_shell_notify_icon(hwnd, FALSE);
258 update_shell_notify_icon(hwnd, FALSE);
260 // Add clipboard listener
261 if(!(have_listener = AddClipboardFormatListener(hwnd)))
263 PRINT("failed to install clipboard listener!");
264 ERROR_EXIT(7);
267 // Set up window timer
268 g_tickCount = GetTickCount64();
269 if(!(have_timer = SetTimer(hwnd, TIMER_ID, min(max(g_timeout / 50U, USER_TIMER_MINIMUM), USER_TIMER_MAXIMUM), NULL)))
271 PRINT("failed to install the window timer!");
272 ERROR_EXIT(8);
275 PRINT("clipboard monitoring started.");
277 // Message loop
278 while(status = GetMessageW(&msg, NULL, 0, 0) != 0)
280 if(status == -1)
282 PRINT("failed to fetch next message!");
283 ERROR_EXIT(9);
285 TranslateMessage(&msg);
286 DispatchMessage(&msg);
289 PRINT("shutting down now...");
291 clean_up:
293 // Kill timer
294 if(hwnd && have_timer)
296 KillTimer(hwnd, TIMER_ID);
299 // Delete notification icon
300 if(hwnd)
302 create_shell_notify_icon(hwnd, TRUE);
305 // Remove clipboard listener
306 if(hwnd && have_listener)
308 RemoveClipboardFormatListener(hwnd);
311 // Free icon resource
312 if(g_context_menu)
314 DestroyMenu(g_context_menu);
317 // Free icon resource
318 if(g_app_icon)
320 DestroyIcon(g_app_icon);
323 // Close mutex
324 if(mutex)
326 CloseHandle(mutex);
329 PRINT("goodbye.");
330 return result;
333 // ==========================================================================
334 // Window Procedure
335 // ==========================================================================
337 static LRESULT CALLBACK my_wnd_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
339 switch(message)
341 case WM_CLOSE:
342 PostQuitMessage(0);
343 break;
344 case WM_CLIPBOARDUPDATE:
345 PRINT("WM_CLIPBOARDUPDATE");
346 g_tickCount = GetTickCount64();
347 break;
348 case WM_TIMER:
349 PRINT("WM_TIMER");
351 const ULONGLONG tickCount = GetTickCount64();
352 if((tickCount > g_tickCount) && ((tickCount - g_tickCount) > g_timeout))
354 if(!g_suspended)
356 if(clear_clipboard())
358 g_tickCount = tickCount;
359 PLAY_SOUND(2U);
362 else
364 PRINT("skipped.");
365 g_tickCount = tickCount;
369 break;
370 case WM_NOTIFYICON:
371 switch(LOWORD(lParam))
373 case WM_CONTEXTMENU:
374 PRINT("WM_NOTIFYICON --> WM_CONTEXTMENU");
375 if(g_context_menu)
377 SetForegroundWindow(hWnd);
378 TrackPopupMenu(g_context_menu, TPM_BOTTOMALIGN | TPM_LEFTALIGN, LOWORD(wParam), HIWORD(wParam), 0, hWnd, NULL);
380 break;
381 case WM_LBUTTONDBLCLK:
382 PRINT("WM_LBUTTONDBLCLK");
383 if(clear_clipboard())
385 g_tickCount = GetTickCount64();
386 PLAY_SOUND(1U);
388 break;
390 break;
391 case WM_COMMAND:
392 PRINT("WM_COMMAND");
393 if(HIWORD(wParam) == 0)
395 switch(LOWORD(wParam))
397 case MENU1_ID:
398 PRINT("menu item #1 triggered");
399 about_screen(FALSE);
400 break;
401 case MENU2_ID:
402 PRINT("menu item #2 triggered");
403 if(clear_clipboard())
405 g_tickCount = GetTickCount64();
406 PLAY_SOUND(1U);
408 break;
409 case MENU3_ID:
410 PRINT("menu item #3 triggered");
411 g_suspended = !g_suspended;
412 CheckMenuItem(g_context_menu, MENU3_ID, g_suspended ? MF_CHECKED : MF_UNCHECKED);
413 update_shell_notify_icon(hWnd, g_suspended);
414 if(!g_suspended)
416 g_tickCount = GetTickCount64();
418 break;
419 case MENU4_ID:
420 PRINT("menu item #4 triggered");
421 PostMessageW(hWnd, WM_CLOSE, 0, 0);
422 break;
425 break;
426 default:
427 if(message == g_taskbar_created)
429 PRINT("TaskbarCreated");
430 create_shell_notify_icon(hWnd, FALSE);
431 update_shell_notify_icon(hWnd, g_suspended);
433 else
435 return DefWindowProc(hWnd, message, wParam, lParam);
439 return 0;
442 // ==========================================================================
443 // Clear Clipboard
444 // ==========================================================================
446 static BOOL clear_clipboard(void)
448 int retry;
449 BOOL success = FALSE;
451 PRINT("clearing clipboard...");
453 for(retry = 0; retry < 32; ++retry)
455 if(retry > 0)
457 PRINT("retry!");
458 Sleep(1); /*yield*/
460 if(OpenClipboard(NULL))
462 success = EmptyClipboard();
463 CloseClipboard();
465 if(success)
467 break; /*successful*/
471 if(success)
473 PRINT("cleared.");
475 else
477 PRINT("failed to clean clipboard!");
480 return success;
483 // ==========================================================================
484 // Process CLI arguments
485 // ==========================================================================
487 static UINT parse_arguments(const WCHAR *const command_line)
489 const WCHAR **argv;
490 int i, argc;
491 UINT mode = 0U;
493 argv = CommandLineToArgvW(command_line, &argc);
494 if(argv)
496 for(i = 1; i < argc; ++i)
498 const WCHAR *value = argv[i];
499 while((*value) && (*value <= 0x20))
501 ++value;
503 if(*value)
505 if(!lstrcmpiW(value, L"--close"))
507 mode = 1U;
509 else if(!lstrcmpiW(value, L"--restart"))
511 mode = 2U;
513 else if(!lstrcmpiW(value, L"--install"))
515 mode = 3U;
517 else if(!lstrcmpiW(value, L"--uninstall"))
519 mode = 4U;
521 #ifndef _DEBUG
522 else if(!lstrcmpiW(value, L"--debug"))
524 g_debug = TRUE;
526 #endif //_DEBUG
527 else if(!lstrcmpiW(value, L"--slunk"))
529 ShellExecuteW(NULL, NULL, L"https://youtu.be/n4bply6Ibqw", NULL, NULL, SW_SHOW);
531 else
533 mode = MAXUINT;
534 break; /*bad argument*/
538 LocalFree((HLOCAL)argv);
541 return mode;
544 // ==========================================================================
545 // Autorun support
546 // ==========================================================================
548 static BOOL update_autorun_entry(const BOOL remove)
550 static const WCHAR *const REG_VALUE_NAME = L"com.muldersoft.clear_clipboard";
551 static const WCHAR *const REG_VALUE_PATH = L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
553 BOOL success = FALSE;
555 if(!remove)
557 const WCHAR *const executable_path = get_executable_path();
558 if(executable_path)
560 WCHAR *const buffer = (WCHAR*) LocalAlloc(LPTR, (lstrlenW(executable_path) + 3U) * sizeof(WCHAR));
561 if(buffer)
563 PRINT("adding autorun entry to registry...");
564 lstrcpyW(buffer, L"\"");
565 lstrcatW(buffer, executable_path);
566 lstrcatW(buffer, L"\"");
567 if(reg_write_string(HKEY_CURRENT_USER, REG_VALUE_PATH, REG_VALUE_NAME, buffer))
569 PRINT("succeeded.");
570 success = TRUE;
572 else
574 PRINT("failed to add autorun entry to registry!");
576 LocalFree((HLOCAL)buffer);
578 else
580 PRINT("failed to allocate string buffer!");
582 LocalFree((HLOCAL)executable_path);
584 else
586 PRINT("failed to determine executable path!");
589 else
591 PRINT("removing autorun entry from registry...");
592 if(reg_delete_value(HKEY_CURRENT_USER, REG_VALUE_PATH, REG_VALUE_NAME))
594 PRINT("succeeded.");
595 success = TRUE;
597 else
599 PRINT("failed to remove autorun entry from registry!");
603 return success;
606 // ==========================================================================
607 // Shell notification icon
608 // ==========================================================================
610 static BOOL create_shell_notify_icon(const HWND hwnd, const BOOL remove)
612 NOTIFYICONDATAW shell_icon_data;
613 SecureZeroMemory(&shell_icon_data, sizeof(NOTIFYICONDATAW));
615 shell_icon_data.cbSize = sizeof(NOTIFYICONDATAW);
616 shell_icon_data.hWnd = hwnd;
617 shell_icon_data.uID = ID_NOTIFYICON;
619 if(!remove)
621 shell_icon_data.hIcon = g_app_icon;
622 shell_icon_data.uCallbackMessage = WM_NOTIFYICON;
623 shell_icon_data.uFlags = NIF_ICON | NIF_MESSAGE;
626 if(Shell_NotifyIconW(remove ? NIM_DELETE : NIM_ADD, &shell_icon_data))
628 if(!remove)
630 shell_icon_data.uVersion = NOTIFYICON_VERSION_4;
631 Shell_NotifyIconW(NIM_SETVERSION, &shell_icon_data);
634 else
636 PRINT("failed to create/remove shell notification icon!");
637 return FALSE;
640 return TRUE;
643 static BOOL update_shell_notify_icon(const HWND hwnd, const BOOL suspended)
645 NOTIFYICONDATAW shell_icon_data;
646 SecureZeroMemory(&shell_icon_data, sizeof(NOTIFYICONDATAW));
648 shell_icon_data.cbSize = sizeof(NOTIFYICONDATAW);
649 shell_icon_data.hWnd = hwnd;
650 shell_icon_data.uID = ID_NOTIFYICON;
651 lstrcpyW(shell_icon_data.szTip, L"ClearClipboard v" WTEXT(VERSION_STR));
652 if(suspended)
654 lstrcatW(shell_icon_data.szTip, L" - suspended");
656 shell_icon_data.uFlags = NIF_TIP | NIF_SHOWTIP;
658 if(!Shell_NotifyIconW(NIM_MODIFY, &shell_icon_data))
660 PRINT("failed to modify shell notification icon!");
661 return FALSE;
664 return TRUE;
667 // ==========================================================================
668 // About dialog
669 // ==========================================================================
671 static BOOL about_screen(const BOOL first_run)
673 const int resut = MessageBoxW(
674 NULL,
675 L"ClearClipboard v" WTEXT(VERSION_STR) L" [" WTEXT(__DATE__) L"]\n"
676 L"Copyright(\x24B8) 2019 LoRd_MuldeR <mulder2@gmx.de>\n\n"
677 L"This software is released under the MIT License.\n"
678 L"(https://opensource.org/licenses/MIT)\n\n"
679 L"For news and updates please check the website at:\n"
680 L"\x2022 http://muldersoft.com/\n"
681 L"\x2022 https://github.com/lordmulder/ClearClipboard\n\n"
682 L"DISCLAIMER: "
683 L"The software is provided \"as is\", without warranty of any kind, express or implied, including"
684 L"but not limited to the warranties of merchantability, fitness for a particular purpose and"
685 L"noninfringement. In no event shall the authors or copyright holders be liable for any claim,"
686 L"damages or other liability, whether in an action of contract, tort or otherwise, arising from,"
687 L"out of or in connection with the software or the use or other dealings in the software.",
688 L"About...",
689 MB_ICONINFORMATION | MB_TOPMOST | (first_run ? MB_OKCANCEL|MB_DEFBUTTON2 : MB_OK)
692 return (resut == IDOK);
695 static BOOL show_disclaimer(void)
697 static const DWORD REG_VALUE_DATA = (((DWORD)VERSION_MAJOR) << 16) | (((DWORD)VERSION_MINOR_HI) << 8) | ((DWORD)VERSION_MINOR_LO);
698 static const WCHAR *const REG_VALUE_NAME = L"DisclaimerAccepted";
699 static const WCHAR *const REG_VALUE_PATH = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{7816D5D9-5D9D-4B3A-B5A8-DD7A7F4C44A3}";
701 if(reg_read_value(HKEY_CURRENT_USER, REG_VALUE_PATH, REG_VALUE_NAME, 0U) != REG_VALUE_DATA)
703 if(about_screen(TRUE))
705 reg_write_value(HKEY_CURRENT_USER, REG_VALUE_PATH, REG_VALUE_NAME, REG_VALUE_DATA);
707 else
709 return FALSE; /*rejected*/
713 return TRUE;
716 // ==========================================================================
717 // Play sound effect
718 // ==========================================================================
720 static BOOL play_sound_effect(void)
722 BOOL success = FALSE;
724 const WCHAR *const sound_file = reg_read_string(HKEY_CURRENT_USER, L"AppEvents\\Schemes\\Apps\\Explorer\\EmptyRecycleBin\\.Current", L"");
725 if(sound_file)
727 if(sound_file[0] && (GetFileAttributesW(sound_file) != INVALID_FILE_ATTRIBUTES))
729 success = PlaySoundW(sound_file, NULL, SND_ASYNC);
731 LocalFree((HLOCAL)sound_file);
734 if(!success)
736 success = PlaySoundW(L"SystemAsterisk", NULL, SND_ALIAS | SND_ASYNC);
739 return success;
742 // ==========================================================================
743 // File path routines
744 // ==========================================================================
746 static WCHAR *get_configuration_path(void)
748 static const WCHAR *const DEFAULT_PATH = L"ClearClipboard.ini";
749 WCHAR *buffer = NULL;
750 WCHAR *const path = get_executable_path();
752 if(path)
754 const DWORD path_len = lstrlenW(path);
755 if(path_len > 1U)
757 DWORD pos, last_sep = 0U;
758 for(pos = 0U; pos < path_len; ++pos)
760 if((path[pos] == L'/') || (path[pos] == L'\\') || (path[pos] == L'.'))
762 last_sep = pos;
765 if(last_sep > 1U)
767 const DWORD copy_len = (path[last_sep] == '.') ? last_sep : path_len;
768 buffer = (WCHAR*) LocalAlloc(LPTR, (5U + copy_len) * sizeof(WCHAR));
769 if(buffer)
771 lstrcpynW(buffer, path, last_sep + 1U);
772 lstrcatW(buffer, L".ini");
776 LocalFree((HLOCAL)path);
779 if(!buffer)
781 buffer = (WCHAR*) LocalAlloc(LPTR, (1U + lstrlenW(DEFAULT_PATH)) * sizeof(WCHAR));
782 if(buffer)
784 lstrcpyW(buffer, DEFAULT_PATH);
788 return buffer;
791 static WCHAR *get_executable_path(void)
793 DWORD size = 256U;
795 WCHAR *buffer = (WCHAR*) LocalAlloc(LPTR, size * sizeof(WCHAR));
796 if(!buffer)
798 return NULL; /*malloc failed*/
801 for(;;)
803 const DWORD result = GetModuleFileNameW(NULL, buffer, size);
804 if((result > 0) && (result < size))
806 return buffer;
808 if((size < MAXWORD) && (result >= size))
810 LocalFree((HLOCAL)buffer);
811 size *= 2U;
812 if(!(buffer = (WCHAR*) LocalAlloc(LPTR, size * sizeof(WCHAR))))
814 return NULL; /*malloc failed*/
817 else
819 break; /*something else went wrong*/
823 LocalFree((HLOCAL)buffer);
824 return NULL;
827 // ==========================================================================
828 // Configuration routines
829 // ==========================================================================
831 static UINT get_config_value(const WCHAR *const path, const WCHAR *const name, const UINT default_value, const UINT min_value, const UINT max_value)
833 static const WCHAR *const SECTION_NAME = L"ClearClipboard";
834 const UINT value = GetPrivateProfileIntW(SECTION_NAME, name , default_value, path);
835 return max(min_value, min(max_value, value));
838 // ==========================================================================
839 // Registry routines
840 // ==========================================================================
842 static DWORD reg_read_value(const HKEY root, const WCHAR *const path, const WCHAR *const name, const DWORD default_value)
844 HKEY hkey = NULL;
845 DWORD result = 0U, type = REG_NONE, size = sizeof(DWORD);
847 if(RegOpenKeyExW(root, path, 0U, KEY_READ, &hkey) != ERROR_SUCCESS)
849 PRINT("failed to open registry key for reading!");
850 return default_value;
853 if(RegQueryValueExW(hkey, name, NULL, &type, (BYTE*)&result, &size) == ERROR_SUCCESS)
855 if((type == REG_DWORD) || (type == REG_DWORD_BIG_ENDIAN))
857 RegCloseKey(hkey);
858 return result;
862 RegCloseKey(hkey);
863 PRINT("failed to read registry value!");
864 return default_value;
867 static WCHAR *reg_read_string(const HKEY root, const WCHAR *const path, const WCHAR *const name)
869 HKEY hkey = NULL;
870 DWORD capacity;
872 if(RegOpenKeyExW(root, path, 0U, KEY_READ, &hkey) != ERROR_SUCCESS)
874 PRINT("failed to open registry key for reading!");
875 return NULL;
878 for(capacity = 4096U; capacity <= 65536U; capacity *= 2U)
880 WCHAR *const buffer = (WCHAR*) LocalAlloc(LPTR, capacity);
881 if(buffer)
883 DWORD type = REG_NONE, size = capacity;
884 const LSTATUS error = RegQueryValueExW(hkey, name, NULL, &type, (BYTE*)buffer, &size);
885 if((error == ERROR_SUCCESS) && ((type == REG_SZ) || (type == REG_EXPAND_SZ)))
887 RegCloseKey(hkey);
888 return buffer;
890 LocalFree((HLOCAL)buffer);
891 if(error != ERROR_MORE_DATA)
893 break; /*failure*/
896 else
898 RegCloseKey(hkey);
899 PRINT("failed to allocate buffer!");
900 return NULL;
904 RegCloseKey(hkey);
905 PRINT("failed to read registry value!");
906 return NULL;
909 static BOOL reg_write_value(const HKEY root, const WCHAR *const path, const WCHAR *const name, const DWORD value)
911 HKEY hkey = NULL;
913 if(RegCreateKeyExW(root, path, 0U, NULL, 0U, KEY_WRITE, NULL, &hkey, NULL) != ERROR_SUCCESS)
915 PRINT("failed to open registry key for writing!");
916 return FALSE;
919 if(RegSetValueExW(hkey, name, 0U, REG_DWORD, (BYTE*)&value, sizeof(DWORD)) != ERROR_SUCCESS)
921 RegCloseKey(hkey);
922 PRINT("failed to write registry value!");
923 return FALSE;
926 RegCloseKey(hkey);
927 return TRUE;
930 static BOOL reg_write_string(const HKEY root, const WCHAR *const path, const WCHAR *const name, const WCHAR *const text)
932 HKEY hkey = NULL;
934 if(RegCreateKeyExW(root, path, 0U, NULL, 0U, KEY_WRITE, NULL, &hkey, NULL) != ERROR_SUCCESS)
936 RegCloseKey(hkey);
937 PRINT("failed to open registry key for writing!");
938 return FALSE;
941 if(RegSetValueExW(hkey, name, 0U, REG_SZ, (BYTE*)&text, (lstrlenW(text) + 1U) * sizeof(WCHAR)) != ERROR_SUCCESS)
943 RegCloseKey(hkey);
944 PRINT("failed to write registry value!");
945 return FALSE;
948 RegCloseKey(hkey);
949 return TRUE;
952 static BOOL reg_delete_value(const HKEY root, const WCHAR *const path, const WCHAR *const name)
954 HKEY hkey = NULL;
955 LSTATUS error = 0U;
957 if((error = RegOpenKeyExW(root, path, 0U, KEY_WRITE, &hkey)) != ERROR_SUCCESS)
959 if(error != ERROR_FILE_NOT_FOUND)
961 PRINT("failed to open registry key for writing!");
962 return FALSE;
964 return TRUE;
967 if((error = RegDeleteValueW(hkey, name)) != ERROR_SUCCESS)
969 if(error != ERROR_FILE_NOT_FOUND)
971 RegCloseKey(hkey);
972 PRINT("failed to write registry value!");
973 return FALSE;
977 RegCloseKey(hkey);
978 return TRUE;