Replace functions which called once with their bodies
[pidgin-git.git] / pidgin / win32 / winpidgin.c
blobdb13f0e15e291286f1ba3cdcc73d27a72c032167
1 /*
2 * winpidgin.c
4 * Date: June, 2002
5 * Description: Entry point for win32 pidgin, and various win32 dependant
6 * routines.
8 * Pidgin is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
28 /* This file must remain without any immediate dependencies aka don't link
29 * directly to Pidgin, libpidgin, GLib, etc. WinPidgin adds a DLL directory
30 * at runtime if needed and dynamically loads libpidgin via LoadLibrary().
33 #include "config.h"
35 #include <windows.h>
36 #include <shellapi.h>
37 #include <fcntl.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <stdio.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
44 typedef int (__cdecl* LPFNPIDGINMAIN)(HINSTANCE, int, char**);
45 typedef BOOL (WINAPI* LPFNSETDLLDIRECTORY)(LPCWSTR);
46 typedef BOOL (WINAPI* LPFNATTACHCONSOLE)(DWORD);
47 typedef BOOL (WINAPI* LPFNSETPROCESSDEPPOLICY)(DWORD);
50 * PROTOTYPES
52 static LPFNPIDGINMAIN pidgin_main = NULL;
54 static const wchar_t *get_win32_error_message(DWORD err) {
55 static wchar_t err_msg[512];
57 FormatMessageW(
58 FORMAT_MESSAGE_FROM_SYSTEM,
59 NULL, err,
60 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
61 (LPWSTR) &err_msg, sizeof(err_msg) / sizeof(wchar_t), NULL);
63 return err_msg;
66 #define PIDGIN_WM_FOCUS_REQUEST (WM_APP + 13)
67 #define PIDGIN_WM_PROTOCOL_HANDLE (WM_APP + 14)
69 static BOOL winpidgin_set_running(BOOL fail_if_running) {
70 HANDLE h;
72 if ((h = CreateMutexW(NULL, FALSE, L"pidgin_is_running"))) {
73 DWORD err = GetLastError();
74 if (err == ERROR_ALREADY_EXISTS) {
75 if (fail_if_running) {
76 HWND msg_win;
78 printf("An instance of Pidgin is already running.\n");
80 if((msg_win = FindWindowExW(NULL, NULL, L"WinpidginMsgWinCls", NULL)))
81 if(SendMessage(msg_win, PIDGIN_WM_FOCUS_REQUEST, (WPARAM) NULL, (LPARAM) NULL))
82 return FALSE;
84 /* If we get here, the focus request wasn't successful */
86 MessageBoxW(NULL,
87 L"An instance of Pidgin is already running",
88 NULL, MB_OK | MB_TOPMOST);
90 return FALSE;
92 } else if (err != ERROR_SUCCESS)
93 printf("Error (%u) accessing \"pidgin_is_running\" mutex.\n", (UINT) err);
95 return TRUE;
98 #define PROTO_HANDLER_SWITCH L"--protocolhandler="
100 static void handle_protocol(wchar_t *cmd) {
101 char *remote_msg, *utf8msg;
102 wchar_t *tmp1, *tmp2;
103 int len, wlen;
104 SIZE_T len_written;
105 HWND msg_win;
106 DWORD pid;
107 HANDLE process;
109 /* The start of the message */
110 tmp1 = cmd + wcslen(PROTO_HANDLER_SWITCH);
112 /* The end of the message */
113 if ((tmp2 = wcschr(tmp1, L' ')))
114 wlen = (tmp2 - tmp1);
115 else
116 wlen = wcslen(tmp1);
118 if (wlen == 0) {
119 printf("No protocol message specified.\n");
120 return;
123 if (!(msg_win = FindWindowExW(NULL, NULL, L"WinpidginMsgWinCls", NULL))) {
124 printf("Unable to find an instance of Pidgin to handle protocol message.\n");
125 return;
128 len = WideCharToMultiByte(CP_UTF8, 0, tmp1,
129 wlen, NULL, 0, NULL, NULL);
130 if (len) {
131 utf8msg = malloc(len);
132 len = WideCharToMultiByte(CP_UTF8, 0, tmp1,
133 wlen, utf8msg, len, NULL, NULL);
136 if (len == 0) {
137 printf("No protocol message specified.\n");
138 return;
141 GetWindowThreadProcessId(msg_win, &pid);
142 if (!(process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, pid))) {
143 DWORD dw = GetLastError();
144 const wchar_t *err_msg = get_win32_error_message(dw);
145 wprintf(L"Unable to open Pidgin process. (%u) %ls\n",
146 (UINT)dw, err_msg);
147 return;
150 wprintf(L"Trying to handle protocol message:\n'%.*ls'\n", wlen, tmp1);
152 /* MEM_COMMIT initializes the memory to zero
153 * so we don't need to worry that our section of utf8msg isn't nul-terminated */
154 if ((remote_msg = (char*) VirtualAllocEx(process, NULL, len + 1, MEM_COMMIT, PAGE_READWRITE))) {
155 if (WriteProcessMemory(process, remote_msg, utf8msg, len, &len_written)) {
156 if (!SendMessageA(msg_win, PIDGIN_WM_PROTOCOL_HANDLE, len_written, (LPARAM) remote_msg))
157 printf("Unable to send protocol message to Pidgin instance.\n");
158 } else {
159 DWORD dw = GetLastError();
160 const wchar_t *err_msg = get_win32_error_message(dw);
161 wprintf(L"Unable to write to remote memory. (%u) %ls\n",
162 (UINT)dw, err_msg);
165 VirtualFreeEx(process, remote_msg, 0, MEM_RELEASE);
166 } else {
167 DWORD dw = GetLastError();
168 const wchar_t *err_msg = get_win32_error_message(dw);
169 wprintf(L"Unable to allocate remote memory. (%u) %ls\n",
170 (UINT)dw, err_msg);
173 CloseHandle(process);
174 free(utf8msg);
178 int _stdcall
179 WinMain (struct HINSTANCE__ *hInstance, struct HINSTANCE__ *hPrevInstance,
180 char *lpszCmdLine, int nCmdShow) {
181 wchar_t errbuf[512];
182 wchar_t pidgin_dir[MAX_PATH];
183 wchar_t *pidgin_dir_start = NULL;
184 wchar_t exe_name[MAX_PATH];
185 HMODULE hmod;
186 wchar_t *wtmp;
187 int pidgin_argc;
188 char **pidgin_argv; /* This is in utf-8 */
189 int i, j, k;
190 BOOL debug = FALSE, help = FALSE, version = FALSE, multiple = FALSE, success;
191 LPWSTR *szArglist;
192 LPWSTR cmdLine;
194 /* If debug or help or version flag used, create console for output */
195 for (i = 1; i < __argc; i++) {
196 if (strlen(__argv[i]) > 1 && __argv[i][0] == '-') {
197 /* check if we're looking at -- or - option */
198 if (__argv[i][1] == '-') {
199 if (strstr(__argv[i], "--debug") == __argv[i])
200 debug = TRUE;
201 else if (strstr(__argv[i], "--help") == __argv[i])
202 help = TRUE;
203 else if (strstr(__argv[i], "--version") == __argv[i])
204 version = TRUE;
205 else if (strstr(__argv[i], "--multiple") == __argv[i])
206 multiple = TRUE;
207 } else {
208 if (strchr(__argv[i], 'd'))
209 debug = TRUE;
210 if (strchr(__argv[i], 'h'))
211 help = TRUE;
212 if (strchr(__argv[i], 'v'))
213 version = TRUE;
214 if (strchr(__argv[i], 'm'))
215 multiple = TRUE;
220 /* Permanently enable DEP if the OS supports it */
221 if ((hmod = GetModuleHandleW(L"kernel32.dll"))) {
222 LPFNSETPROCESSDEPPOLICY MySetProcessDEPPolicy =
223 (LPFNSETPROCESSDEPPOLICY)
224 GetProcAddress(hmod, "SetProcessDEPPolicy");
225 if (MySetProcessDEPPolicy)
226 MySetProcessDEPPolicy(1); //PROCESS_DEP_ENABLE
229 if (debug || help || version) {
230 /* If stdout hasn't been redirected to a file, alloc a console
231 * (_istty() doesn't work for stuff using the GUI subsystem) */
232 if (_fileno(stdout) == -1 || _fileno(stdout) == -2) {
233 LPFNATTACHCONSOLE MyAttachConsole = NULL;
234 if (hmod)
235 MyAttachConsole =
236 (LPFNATTACHCONSOLE)
237 GetProcAddress(hmod, "AttachConsole");
238 if ((MyAttachConsole && MyAttachConsole(ATTACH_PARENT_PROCESS))
239 || AllocConsole()) {
240 freopen("CONOUT$", "w", stdout);
241 freopen("CONOUT$", "w", stderr);
246 cmdLine = GetCommandLineW();
248 /* If this is a protocol handler invocation, deal with it accordingly */
249 if ((wtmp = wcsstr(cmdLine, PROTO_HANDLER_SWITCH)) != NULL) {
250 handle_protocol(wtmp);
251 return 0;
254 /* Load exception handler if we have it */
255 if (GetModuleFileNameW(NULL, pidgin_dir, MAX_PATH) != 0) {
257 /* primitive dirname() */
258 pidgin_dir_start = wcsrchr(pidgin_dir, L'\\');
260 if (pidgin_dir_start) {
261 HMODULE hmod;
262 pidgin_dir_start[0] = L'\0';
264 /* tmp++ will now point to the executable file name */
265 wcscpy(exe_name, pidgin_dir_start + 1);
267 wcscat(pidgin_dir, L"\\exchndl.dll");
268 if ((hmod = LoadLibraryW(pidgin_dir))) {
269 typedef void (__cdecl* LPFNSETLOGFILE)(const LPCSTR);
270 LPFNSETLOGFILE MySetLogFile;
271 /* exchndl.dll is built without UNICODE */
272 char debug_dir[MAX_PATH];
273 printf("Loaded exchndl.dll\n");
274 /* Temporarily override exchndl.dll's logfile
275 * to something sane (Pidgin will override it
276 * again when it initializes) */
277 MySetLogFile = (LPFNSETLOGFILE) GetProcAddress(hmod, "SetLogFile");
278 if (MySetLogFile) {
279 if (GetTempPathA(sizeof(debug_dir), debug_dir) != 0) {
280 strcat(debug_dir, "pidgin.RPT");
281 printf(" Setting exchndl.dll LogFile to %s\n",
282 debug_dir);
283 MySetLogFile(debug_dir);
286 /* The function signature for SetDebugInfoDir is the same as SetLogFile,
287 * so we can reuse the variable */
288 MySetLogFile = (LPFNSETLOGFILE) GetProcAddress(hmod, "SetDebugInfoDir");
289 if (MySetLogFile) {
290 char *pidgin_dir_ansi = NULL;
291 /* Restore pidgin_dir to point to where the executable is */
292 pidgin_dir_start[0] = L'\0';
293 i = WideCharToMultiByte(CP_ACP, 0, pidgin_dir,
294 -1, NULL, 0, NULL, NULL);
295 if (i != 0) {
296 pidgin_dir_ansi = malloc(i);
297 i = WideCharToMultiByte(CP_ACP, 0, pidgin_dir,
298 -1, pidgin_dir_ansi, i, NULL, NULL);
299 if (i == 0) {
300 free(pidgin_dir_ansi);
301 pidgin_dir_ansi = NULL;
304 if (pidgin_dir_ansi != NULL) {
305 _snprintf(debug_dir, sizeof(debug_dir),
306 "%s\\pidgin-%s-dbgsym",
307 pidgin_dir_ansi, VERSION);
308 debug_dir[sizeof(debug_dir) - 1] = '\0';
309 printf(" Setting exchndl.dll DebugInfoDir to %s\n",
310 debug_dir);
311 MySetLogFile(debug_dir);
312 free(pidgin_dir_ansi);
318 /* Restore pidgin_dir to point to where the executable is */
319 pidgin_dir_start[0] = L'\0';
322 /* Find parent directory to see if it's bin/ */
323 pidgin_dir_start = wcsrchr(pidgin_dir, L'\\');
325 /* Add bin/ subdirectory to DLL path if not already in bin/ */
326 if (pidgin_dir_start == NULL ||
327 wcscmp(pidgin_dir_start + 1, L"bin") != 0) {
328 LPFNSETDLLDIRECTORY MySetDllDirectory = NULL;
330 hmod = GetModuleHandleW(L"kernel32.dll");
332 if (hmod != NULL) {
333 MySetDllDirectory = (LPFNSETDLLDIRECTORY) GetProcAddress(hmod, "SetDllDirectoryW");
334 if (MySetDllDirectory == NULL) {
335 DWORD dw = GetLastError();
336 const wchar_t *err_msg = get_win32_error_message(dw);
337 wprintf(L"Error loading SetDllDirectory(): (%u) %ls\n", dw, err_msg);
339 } else {
340 printf("Error getting kernel32.dll handle\n");
343 if (MySetDllDirectory) {
344 wcscat(pidgin_dir, L"\\bin");
345 if (MySetDllDirectory(pidgin_dir)) {
346 wprintf(L"Added DLL directory to search path: %ls\n",
347 pidgin_dir);
348 } else {
349 DWORD dw = GetLastError();
350 const wchar_t *err_msg = get_win32_error_message(dw);
351 wprintf(L"Error calling SetDllDirectory(): (%u) %ls\n", dw, err_msg);
355 } else {
356 DWORD dw = GetLastError();
357 const wchar_t *err_msg = get_win32_error_message(dw);
358 _snwprintf(errbuf, sizeof(errbuf) / sizeof(wchar_t),
359 L"Error getting module filename.\nError: (%u) %ls",
360 (UINT) dw, err_msg);
361 wprintf(L"%ls\n", errbuf);
362 MessageBoxW(NULL, errbuf, NULL, MB_OK | MB_TOPMOST);
363 pidgin_dir[0] = L'\0';
366 /* If help, version or multiple flag used, do not check Mutex */
367 if (!help && !version)
368 if (!winpidgin_set_running(getenv("PIDGIN_MULTI_INST") == NULL && !multiple))
369 return 0;
371 /* Now we are ready for Pidgin .. */
372 if ((hmod = LoadLibraryW(LIBPIDGIN_DLL_NAMEW)))
373 pidgin_main = (LPFNPIDGINMAIN) GetProcAddress(hmod, "pidgin_main");
375 if (!pidgin_main) {
376 DWORD dw = GetLastError();
377 const wchar_t *err_msg = get_win32_error_message(dw);
379 _snwprintf(errbuf, sizeof(errbuf) / sizeof(wchar_t),
380 L"Error loading %ls.\nError: (%u) %ls",
381 LIBPIDGIN_DLL_NAMEW, (UINT) dw, err_msg);
382 wprintf(L"%ls\n", errbuf);
383 MessageBoxW(NULL, errbuf, L"Error", MB_OK | MB_TOPMOST);
385 return 0;
388 /* Convert argv to utf-8*/
389 szArglist = CommandLineToArgvW(cmdLine, &j);
390 pidgin_argc = j;
391 pidgin_argv = malloc(pidgin_argc* sizeof(char*));
392 k = 0;
393 for (i = 0; i < j; i++) {
394 success = FALSE;
395 /* Remove the --portable-mode arg from the args passed to pidgin so it doesn't choke */
396 if (wcsstr(szArglist[i], L"--portable-mode") == NULL) {
397 int len = WideCharToMultiByte(CP_UTF8, 0, szArglist[i],
398 -1, NULL, 0, NULL, NULL);
399 if (len != 0) {
400 char *arg = malloc(len);
401 len = WideCharToMultiByte(CP_UTF8, 0, szArglist[i],
402 -1, arg, len, NULL, NULL);
403 if (len != 0) {
404 pidgin_argv[k++] = arg;
405 success = TRUE;
408 if (!success)
409 wprintf(L"Error converting argument '%ls' to UTF-8\n",
410 szArglist[i]);
412 if (!success)
413 pidgin_argc--;
415 LocalFree(szArglist);
418 return pidgin_main(hInstance, pidgin_argc, pidgin_argv);