1 /* 2 * hhctrl implementation 3 * 4 * Copyright 2004 Krzysztof Foltman 5 * Copyright 2007 Jacek Caban for CodeWeavers 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 #include "wine/debug.h" 23 24 #include <stdarg.h> 25 26 #define COBJMACROS 27 28 #include "windef.h" 29 #include "winbase.h" 30 #include "winuser.h" 31 #include "winnls.h" 32 #include "htmlhelp.h" 33 #include "ole2.h" 34 #include "rpcproxy.h" 35 36 #define INIT_GUID 37 #include "hhctrl.h" 38 39 WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp); 40 41 HINSTANCE hhctrl_hinstance; 42 BOOL hh_process = FALSE; 43 44 45 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID lpvReserved) 46 { 47 TRACE("(%p,%d,%p)\n", hInstance, fdwReason, lpvReserved); 48 49 switch (fdwReason) 50 { 51 case DLL_PROCESS_ATTACH: 52 hhctrl_hinstance = hInstance; 53 DisableThreadLibraryCalls(hInstance); 54 break; 55 } 56 return TRUE; 57 } 58 59 static const char *command_to_string(UINT command) 60 { 61 #define X(x) case x: return #x 62 switch (command) 63 { 64 X( HH_DISPLAY_TOPIC ); 65 X( HH_DISPLAY_TOC ); 66 X( HH_DISPLAY_INDEX ); 67 X( HH_DISPLAY_SEARCH ); 68 X( HH_SET_WIN_TYPE ); 69 X( HH_GET_WIN_TYPE ); 70 X( HH_GET_WIN_HANDLE ); 71 X( HH_ENUM_INFO_TYPE ); 72 X( HH_SET_INFO_TYPE ); 73 X( HH_SYNC ); 74 X( HH_RESERVED1 ); 75 X( HH_RESERVED2 ); 76 X( HH_RESERVED3 ); 77 X( HH_KEYWORD_LOOKUP ); 78 X( HH_DISPLAY_TEXT_POPUP ); 79 X( HH_HELP_CONTEXT ); 80 X( HH_TP_HELP_CONTEXTMENU ); 81 X( HH_TP_HELP_WM_HELP ); 82 X( HH_CLOSE_ALL ); 83 X( HH_ALINK_LOOKUP ); 84 X( HH_GET_LAST_ERROR ); 85 X( HH_ENUM_CATEGORY ); 86 X( HH_ENUM_CATEGORY_IT ); 87 X( HH_RESET_IT_FILTER ); 88 X( HH_SET_INCLUSIVE_FILTER ); 89 X( HH_SET_EXCLUSIVE_FILTER ); 90 X( HH_INITIALIZE ); 91 X( HH_UNINITIALIZE ); 92 X( HH_SAFE_DISPLAY_TOPIC ); 93 X( HH_PRETRANSLATEMESSAGE ); 94 X( HH_SET_GLOBAL_PROPERTY ); 95 default: return "???"; 96 } 97 #undef X 98 } 99 100 static BOOL resolve_filename(const WCHAR *env_filename, WCHAR *fullname, DWORD buflen, WCHAR **index, WCHAR **window) 101 { 102 static const WCHAR helpW[] = {'\\','h','e','l','p','\\',0}; 103 static const WCHAR delimW[] = {':',':',0}; 104 static const WCHAR delim2W[] = {'>',0}; 105 106 DWORD env_len; 107 WCHAR *filename, *extra; 108 109 env_filename = skip_schema(env_filename); 110 111 /* the format is "helpFile[::/index][>window]" */ 112 if (index) *index = NULL; 113 if (window) *window = NULL; 114 115 env_len = ExpandEnvironmentStringsW(env_filename, NULL, 0); 116 if (!env_len) 117 return 0; 118 119 filename = heap_alloc(env_len * sizeof(WCHAR)); 120 if (filename == NULL) 121 return 0; 122 123 ExpandEnvironmentStringsW(env_filename, filename, env_len); 124 125 extra = wcsstr(filename, delim2W); 126 if (extra) 127 { 128 *extra = 0; 129 if (window) 130 *window = strdupW(extra+1); 131 } 132 133 extra = wcsstr(filename, delimW); 134 if (extra) 135 { 136 *extra = 0; 137 if (index) 138 *index = strdupW(extra+2); 139 } 140 141 GetFullPathNameW(filename, buflen, fullname, NULL); 142 if (GetFileAttributesW(fullname) == INVALID_FILE_ATTRIBUTES) 143 { 144 GetWindowsDirectoryW(fullname, buflen); 145 lstrcatW(fullname, helpW); 146 lstrcatW(fullname, filename); 147 } 148 149 heap_free(filename); 150 151 return (GetFileAttributesW(fullname) != INVALID_FILE_ATTRIBUTES); 152 } 153 154 /****************************************************************** 155 * HtmlHelpW (HHCTRL.OCX.15) 156 */ 157 HWND WINAPI HtmlHelpW(HWND caller, LPCWSTR filename, UINT command, DWORD_PTR data) 158 { 159 WCHAR fullname[MAX_PATH]; 160 161 TRACE("(%p, %s, command=%s, data=%lx)\n", 162 caller, debugstr_w( filename ), 163 command_to_string( command ), data); 164 165 switch (command) 166 { 167 case HH_DISPLAY_TOPIC: 168 case HH_DISPLAY_TOC: 169 case HH_DISPLAY_INDEX: 170 case HH_DISPLAY_SEARCH:{ 171 BOOL res; 172 NMHDR nmhdr; 173 HHInfo *info = NULL; 174 WCHAR *window = NULL; 175 const WCHAR *index = NULL; 176 WCHAR *default_index = NULL; 177 int tab_index = TAB_CONTENTS; 178 179 if (!filename) 180 return NULL; 181 182 if (!resolve_filename(filename, fullname, MAX_PATH, &default_index, &window)) 183 { 184 WARN("can't find %s\n", debugstr_w(filename)); 185 return 0; 186 } 187 index = default_index; 188 189 if (window) 190 info = find_window(window); 191 192 info = CreateHelpViewer(info, fullname, caller); 193 if(!info) 194 { 195 heap_free(default_index); 196 heap_free(window); 197 return NULL; 198 } 199 200 if(!index) 201 index = info->WinType.pszFile; 202 if(!info->WinType.pszType) 203 info->WinType.pszType = info->stringsW.pszType = window; 204 else 205 heap_free(window); 206 207 /* called to load a specified topic */ 208 switch(command) 209 { 210 case HH_DISPLAY_TOPIC: 211 case HH_DISPLAY_TOC: 212 if (data) 213 { 214 static const WCHAR delimW[] = {':',':',0}; 215 const WCHAR *i = (const WCHAR *)data; 216 217 index = wcsstr(i, delimW); 218 if(index) 219 { 220 if(memcmp(info->pCHMInfo->szFile, i, index-i)) 221 FIXME("Opening a CHM file in the context of another is not supported.\n"); 222 index += lstrlenW(delimW); 223 } 224 else 225 index = i; 226 } 227 break; 228 } 229 230 res = NavigateToChm(info, info->pCHMInfo->szFile, index); 231 heap_free(default_index); 232 233 if(!res) 234 { 235 ReleaseHelpViewer(info); 236 return NULL; 237 } 238 239 switch(command) 240 { 241 case HH_DISPLAY_TOPIC: 242 case HH_DISPLAY_TOC: 243 tab_index = TAB_CONTENTS; 244 break; 245 case HH_DISPLAY_INDEX: 246 tab_index = TAB_INDEX; 247 if (data) 248 FIXME("Should select keyword '%s'.\n", debugstr_w((WCHAR *)data)); 249 break; 250 case HH_DISPLAY_SEARCH: 251 tab_index = TAB_SEARCH; 252 if (data) 253 FIXME("Should display search specified by HH_FTS_QUERY structure.\n"); 254 break; 255 } 256 /* open the requested tab */ 257 memset(&nmhdr, 0, sizeof(nmhdr)); 258 nmhdr.code = TCN_SELCHANGE; 259 SendMessageW(info->hwndTabCtrl, TCM_SETCURSEL, (WPARAM)info->tabs[tab_index].id, 0); 260 SendMessageW(info->WinType.hwndNavigation, WM_NOTIFY, 0, (LPARAM)&nmhdr); 261 262 return info->WinType.hwndHelp; 263 } 264 case HH_HELP_CONTEXT: { 265 WCHAR *window = NULL; 266 HHInfo *info = NULL; 267 LPWSTR url; 268 269 if (!filename) 270 return NULL; 271 272 if (!resolve_filename(filename, fullname, MAX_PATH, NULL, &window)) 273 { 274 WARN("can't find %s\n", debugstr_w(filename)); 275 return 0; 276 } 277 278 if (window) 279 info = find_window(window); 280 281 info = CreateHelpViewer(info, fullname, caller); 282 if(!info) 283 { 284 heap_free(window); 285 return NULL; 286 } 287 288 if(!info->WinType.pszType) 289 info->WinType.pszType = info->stringsW.pszType = window; 290 else 291 heap_free(window); 292 293 url = FindContextAlias(info->pCHMInfo, data); 294 if(!url) 295 { 296 if(!data) /* there may legitimately be no context alias for id 0 */ 297 return info->WinType.hwndHelp; 298 ReleaseHelpViewer(info); 299 return NULL; 300 } 301 302 NavigateToUrl(info, url); 303 heap_free(url); 304 return info->WinType.hwndHelp; 305 } 306 case HH_PRETRANSLATEMESSAGE: { 307 static BOOL warned = FALSE; 308 309 if (!warned) 310 { 311 FIXME("HH_PRETRANSLATEMESSAGE unimplemented\n"); 312 warned = TRUE; 313 } 314 return 0; 315 } 316 case HH_CLOSE_ALL: { 317 HHInfo *info, *next; 318 319 LIST_FOR_EACH_ENTRY_SAFE(info, next, &window_list, HHInfo, entry) 320 { 321 TRACE("Destroying window %s.\n", debugstr_w(info->WinType.pszType)); 322 ReleaseHelpViewer(info); 323 } 324 return 0; 325 } 326 case HH_SET_WIN_TYPE: { 327 HH_WINTYPEW *wintype = (HH_WINTYPEW *)data; 328 WCHAR *window = NULL; 329 HHInfo *info = NULL; 330 331 if (!filename && wintype->pszType) 332 window = strdupW(wintype->pszType); 333 else if (!filename || !resolve_filename(filename, fullname, MAX_PATH, NULL, &window) || !window) 334 { 335 WARN("can't find window name: %s\n", debugstr_w(filename)); 336 return 0; 337 } 338 info = find_window(window); 339 if (!info) 340 { 341 info = heap_alloc_zero(sizeof(HHInfo)); 342 info->WinType.pszType = info->stringsW.pszType = window; 343 list_add_tail(&window_list, &info->entry); 344 } 345 else 346 heap_free(window); 347 348 TRACE("Changing WINTYPE, fsValidMembers=0x%x\n", wintype->fsValidMembers); 349 350 MergeChmProperties(wintype, info, TRUE); 351 UpdateHelpWindow(info); 352 return 0; 353 } 354 case HH_GET_WIN_TYPE: { 355 HH_WINTYPEW *wintype = (HH_WINTYPEW *)data; 356 WCHAR *window = NULL; 357 HHInfo *info = NULL; 358 359 if (!filename || !resolve_filename(filename, fullname, MAX_PATH, NULL, &window) || !window) 360 { 361 WARN("can't find window name: %s\n", debugstr_w(filename)); 362 return 0; 363 } 364 info = find_window(window); 365 if (!info) 366 { 367 WARN("Could not find window named %s.\n", debugstr_w(window)); 368 heap_free(window); 369 return (HWND)~0; 370 } 371 372 TRACE("Retrieving WINTYPE for %s.\n", debugstr_w(window)); 373 *wintype = info->WinType; 374 heap_free(window); 375 return 0; 376 } 377 default: 378 FIXME("HH case %s not handled.\n", command_to_string( command )); 379 } 380 381 return 0; 382 } 383 384 static void wintypeAtoW(const HH_WINTYPEA *data, HH_WINTYPEW *wdata, struct wintype_stringsW *stringsW) 385 { 386 memcpy(wdata, data, sizeof(*data)); 387 /* convert all of the ANSI strings to Unicode */ 388 wdata->pszType = stringsW->pszType = strdupAtoW(data->pszType); 389 wdata->pszCaption = stringsW->pszCaption = strdupAtoW(data->pszCaption); 390 wdata->pszToc = stringsW->pszToc = strdupAtoW(data->pszToc); 391 wdata->pszIndex = stringsW->pszIndex = strdupAtoW(data->pszIndex); 392 wdata->pszFile = stringsW->pszFile = strdupAtoW(data->pszFile); 393 wdata->pszHome = stringsW->pszHome = strdupAtoW(data->pszHome); 394 wdata->pszJump1 = stringsW->pszJump1 = strdupAtoW(data->pszJump1); 395 wdata->pszJump2 = stringsW->pszJump2 = strdupAtoW(data->pszJump2); 396 wdata->pszUrlJump1 = stringsW->pszUrlJump1 = strdupAtoW(data->pszUrlJump1); 397 wdata->pszUrlJump2 = stringsW->pszUrlJump2 = strdupAtoW(data->pszUrlJump2); 398 wdata->pszCustomTabs = stringsW->pszCustomTabs = strdupAtoW(data->pszCustomTabs); 399 } 400 401 static void wintypeWtoA(const HH_WINTYPEW *wdata, HH_WINTYPEA *data, struct wintype_stringsA *stringsA) 402 { 403 memcpy(data, wdata, sizeof(*wdata)); 404 /* convert all of the Unicode strings to ANSI */ 405 data->pszType = stringsA->pszType = strdupWtoA(wdata->pszType); 406 data->pszCaption = stringsA->pszCaption = strdupWtoA(wdata->pszCaption); 407 data->pszToc = stringsA->pszToc = strdupWtoA(wdata->pszToc); 408 data->pszIndex = stringsA->pszFile = strdupWtoA(wdata->pszIndex); 409 data->pszFile = stringsA->pszFile = strdupWtoA(wdata->pszFile); 410 data->pszHome = stringsA->pszHome = strdupWtoA(wdata->pszHome); 411 data->pszJump1 = stringsA->pszJump1 = strdupWtoA(wdata->pszJump1); 412 data->pszJump2 = stringsA->pszJump2 = strdupWtoA(wdata->pszJump2); 413 data->pszUrlJump1 = stringsA->pszUrlJump1 = strdupWtoA(wdata->pszUrlJump1); 414 data->pszUrlJump2 = stringsA->pszUrlJump2 = strdupWtoA(wdata->pszUrlJump2); 415 data->pszCustomTabs = stringsA->pszCustomTabs = strdupWtoA(wdata->pszCustomTabs); 416 } 417 418 /****************************************************************** 419 * HtmlHelpA (HHCTRL.OCX.14) 420 */ 421 HWND WINAPI HtmlHelpA(HWND caller, LPCSTR filename, UINT command, DWORD_PTR data) 422 { 423 WCHAR *wfile = strdupAtoW( filename ); 424 HWND result = 0; 425 426 if (data) 427 { 428 switch(command) 429 { 430 case HH_ALINK_LOOKUP: 431 case HH_DISPLAY_SEARCH: 432 case HH_DISPLAY_TEXT_POPUP: 433 case HH_GET_LAST_ERROR: 434 case HH_KEYWORD_LOOKUP: 435 case HH_SYNC: 436 FIXME("structures not handled yet\n"); 437 break; 438 439 case HH_SET_WIN_TYPE: 440 { 441 struct wintype_stringsW stringsW; 442 HH_WINTYPEW wdata; 443 444 wintypeAtoW((HH_WINTYPEA *)data, &wdata, &stringsW); 445 result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)&wdata ); 446 wintype_stringsW_free(&stringsW); 447 goto done; 448 } 449 case HH_GET_WIN_TYPE: 450 { 451 HH_WINTYPEW wdata; 452 HHInfo *info; 453 454 result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)&wdata ); 455 if (!wdata.pszType) break; 456 info = find_window(wdata.pszType); 457 if (!info) break; 458 wintype_stringsA_free(&info->stringsA); 459 wintypeWtoA(&wdata, (HH_WINTYPEA *)data, &info->stringsA); 460 goto done; 461 } 462 463 case HH_DISPLAY_INDEX: 464 case HH_DISPLAY_TOPIC: 465 case HH_DISPLAY_TOC: 466 case HH_GET_WIN_HANDLE: 467 case HH_SAFE_DISPLAY_TOPIC: 468 { 469 WCHAR *wdata = strdupAtoW( (const char *)data ); 470 result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)wdata ); 471 heap_free(wdata); 472 goto done; 473 } 474 475 case HH_CLOSE_ALL: 476 case HH_HELP_CONTEXT: 477 case HH_INITIALIZE: 478 case HH_PRETRANSLATEMESSAGE: 479 case HH_TP_HELP_CONTEXTMENU: 480 case HH_TP_HELP_WM_HELP: 481 case HH_UNINITIALIZE: 482 /* either scalar or pointer to scalar - do nothing */ 483 break; 484 485 default: 486 FIXME("Unknown command: %s (%d)\n", command_to_string(command), command); 487 break; 488 } 489 } 490 491 result = HtmlHelpW( caller, wfile, command, data ); 492 done: 493 heap_free(wfile); 494 return result; 495 } 496 497 /****************************************************************** 498 * doWinMain (HHCTRL.OCX.13) 499 */ 500 int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine) 501 { 502 MSG msg; 503 int len, buflen, mapid = -1; 504 WCHAR *filename; 505 char *endq = NULL; 506 HWND hwnd; 507 508 hh_process = TRUE; 509 510 /* Parse command line option of the HTML Help command. 511 * 512 * Note: The only currently handled action is "mapid", 513 * which corresponds to opening a specific page. 514 */ 515 while(*szCmdLine == '-') 516 { 517 LPSTR space, ptr; 518 519 ptr = szCmdLine + 1; 520 space = strchr(ptr, ' '); 521 if(!strncmp(ptr, "mapid", space-ptr)) 522 { 523 char idtxt[10]; 524 525 ptr += strlen("mapid")+1; 526 space = strchr(ptr, ' '); 527 /* command line ends without number */ 528 if (!space) 529 return 0; 530 memcpy(idtxt, ptr, space-ptr); 531 idtxt[space-ptr] = '\0'; 532 mapid = atoi(idtxt); 533 szCmdLine = space+1; 534 } 535 else 536 { 537 FIXME("Unhandled HTML Help command line parameter! (%.*s)\n", (int)(space-szCmdLine), szCmdLine); 538 return 0; 539 } 540 } 541 542 /* FIXME: Check szCmdLine for bad arguments */ 543 if (*szCmdLine == '\"') 544 endq = strchr(++szCmdLine, '\"'); 545 546 if (endq) 547 len = endq - szCmdLine; 548 else 549 len = strlen(szCmdLine); 550 551 /* no filename given */ 552 if (!len) 553 return 0; 554 555 buflen = MultiByteToWideChar(CP_ACP, 0, szCmdLine, len, NULL, 0) + 1; 556 filename = heap_alloc(buflen * sizeof(WCHAR)); 557 MultiByteToWideChar(CP_ACP, 0, szCmdLine, len, filename, buflen); 558 filename[buflen-1] = 0; 559 560 /* Open a specific help topic */ 561 if(mapid != -1) 562 hwnd = HtmlHelpW(GetDesktopWindow(), filename, HH_HELP_CONTEXT, mapid); 563 else 564 hwnd = HtmlHelpW(GetDesktopWindow(), filename, HH_DISPLAY_TOPIC, 0); 565 566 heap_free(filename); 567 568 if (!hwnd) 569 { 570 ERR("Failed to open HTML Help file '%s'.\n", szCmdLine); 571 return 0; 572 } 573 574 while (GetMessageW(&msg, 0, 0, 0)) 575 { 576 TranslateMessage(&msg); 577 DispatchMessageW(&msg); 578 } 579 580 return 0; 581 } 582 583 /****************************************************************** 584 * DllGetClassObject (HHCTRL.OCX.@) 585 */ 586 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) 587 { 588 FIXME("(%s %s %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); 589 return CLASS_E_CLASSNOTAVAILABLE; 590 } 591 592 /*********************************************************************** 593 * DllRegisterServer (HHCTRL.OCX.@) 594 */ 595 HRESULT WINAPI DllRegisterServer(void) 596 { 597 return __wine_register_resources( hhctrl_hinstance ); 598 } 599 600 /*********************************************************************** 601 * DllUnregisterServer (HHCTRL.OCX.@) 602 */ 603 HRESULT WINAPI DllUnregisterServer(void) 604 { 605 return __wine_unregister_resources( hhctrl_hinstance ); 606 } 607