5 * Description: Entry point for win32 pidgin, and various win32 dependant
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().
41 #include <sys/types.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
);
52 static LPFNPIDGINMAIN pidgin_main
= NULL
;
54 static const wchar_t *get_win32_error_message(DWORD err
) {
55 static wchar_t err_msg
[512];
58 FORMAT_MESSAGE_FROM_SYSTEM
,
60 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
61 (LPWSTR
) &err_msg
, sizeof(err_msg
) / sizeof(wchar_t), NULL
);
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
) {
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
) {
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
))
84 /* If we get here, the focus request wasn't successful */
87 L
"An instance of Pidgin is already running",
88 NULL
, MB_OK
| MB_TOPMOST
);
92 } else if (err
!= ERROR_SUCCESS
)
93 printf("Error (%u) accessing \"pidgin_is_running\" mutex.\n", (UINT
) err
);
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
;
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
);
119 printf("No protocol message specified.\n");
123 if (!(msg_win
= FindWindowExW(NULL
, NULL
, L
"WinpidginMsgWinCls", NULL
))) {
124 printf("Unable to find an instance of Pidgin to handle protocol message.\n");
128 len
= WideCharToMultiByte(CP_UTF8
, 0, tmp1
,
129 wlen
, NULL
, 0, NULL
, NULL
);
131 utf8msg
= malloc(len
);
132 len
= WideCharToMultiByte(CP_UTF8
, 0, tmp1
,
133 wlen
, utf8msg
, len
, NULL
, NULL
);
137 printf("No protocol message specified.\n");
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",
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");
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",
165 VirtualFreeEx(process
, remote_msg
, 0, MEM_RELEASE
);
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",
173 CloseHandle(process
);
179 WinMain (struct HINSTANCE__
*hInstance
, struct HINSTANCE__
*hPrevInstance
,
180 char *lpszCmdLine
, int nCmdShow
) {
182 wchar_t pidgin_dir
[MAX_PATH
];
183 wchar_t *pidgin_dir_start
= NULL
;
184 wchar_t exe_name
[MAX_PATH
];
188 char **pidgin_argv
; /* This is in utf-8 */
190 BOOL debug
= FALSE
, help
= FALSE
, version
= FALSE
, multiple
= FALSE
, success
;
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
])
201 else if (strstr(__argv
[i
], "--help") == __argv
[i
])
203 else if (strstr(__argv
[i
], "--version") == __argv
[i
])
205 else if (strstr(__argv
[i
], "--multiple") == __argv
[i
])
208 if (strchr(__argv
[i
], 'd'))
210 if (strchr(__argv
[i
], 'h'))
212 if (strchr(__argv
[i
], 'v'))
214 if (strchr(__argv
[i
], 'm'))
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
;
237 GetProcAddress(hmod
, "AttachConsole");
238 if ((MyAttachConsole
&& MyAttachConsole(ATTACH_PARENT_PROCESS
))
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
);
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
) {
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");
279 if (GetTempPathA(sizeof(debug_dir
), debug_dir
) != 0) {
280 strcat(debug_dir
, "pidgin.RPT");
281 printf(" Setting exchndl.dll LogFile to %s\n",
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");
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
);
296 pidgin_dir_ansi
= malloc(i
);
297 i
= WideCharToMultiByte(CP_ACP
, 0, pidgin_dir
,
298 -1, pidgin_dir_ansi
, i
, NULL
, NULL
);
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",
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");
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
);
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",
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
);
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",
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
))
371 /* Now we are ready for Pidgin .. */
372 if ((hmod
= LoadLibraryW(LIBPIDGIN_DLL_NAMEW
)))
373 pidgin_main
= (LPFNPIDGINMAIN
) GetProcAddress(hmod
, "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
);
388 /* Convert argv to utf-8*/
389 szArglist
= CommandLineToArgvW(cmdLine
, &j
);
391 pidgin_argv
= malloc(pidgin_argc
* sizeof(char*));
393 for (i
= 0; i
< j
; i
++) {
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
);
400 char *arg
= malloc(len
);
401 len
= WideCharToMultiByte(CP_UTF8
, 0, szArglist
[i
],
402 -1, arg
, len
, NULL
, NULL
);
404 pidgin_argv
[k
++] = arg
;
409 wprintf(L
"Error converting argument '%ls' to UTF-8\n",
415 LocalFree(szArglist
);
418 return pidgin_main(hInstance
, pidgin_argc
, pidgin_argv
);