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 *filename, WCHAR *fullname, DWORD buflen, WCHAR **index, WCHAR **window) 101 { 102 const WCHAR *extra; 103 WCHAR chm_file[MAX_PATH]; 104 105 static const WCHAR helpW[] = {'\\','h','e','l','p','\\',0}; 106 static const WCHAR delimW[] = {':',':',0}; 107 static const WCHAR delim2W[] = {'>',0}; 108 109 filename = skip_schema(filename); 110 111 /* the format is "helpFile[::/index][>window]" */ 112 if (index) *index = NULL; 113 if (window) *window = NULL; 114 115 extra = strstrW(filename, delim2W); 116 if (extra) 117 { 118 memcpy(chm_file, filename, (extra-filename)*sizeof(WCHAR)); 119 chm_file[extra-filename] = 0; 120 filename = chm_file; 121 if (window) 122 *window = strdupW(extra+1); 123 } 124 125 extra = strstrW(filename, delimW); 126 if (extra) 127 { 128 if (filename != chm_file) 129 memcpy(chm_file, filename, (extra-filename)*sizeof(WCHAR)); 130 chm_file[extra-filename] = 0; 131 filename = chm_file; 132 if (index) 133 *index = strdupW(extra+2); 134 } 135 136 GetFullPathNameW(filename, buflen, fullname, NULL); 137 if (GetFileAttributesW(fullname) == INVALID_FILE_ATTRIBUTES) 138 { 139 GetWindowsDirectoryW(fullname, buflen); 140 strcatW(fullname, helpW); 141 strcatW(fullname, filename); 142 } 143 return (GetFileAttributesW(fullname) != INVALID_FILE_ATTRIBUTES); 144 } 145 146 /****************************************************************** 147 * HtmlHelpW (HHCTRL.OCX.15) 148 */ 149 HWND WINAPI HtmlHelpW(HWND caller, LPCWSTR filename, UINT command, DWORD_PTR data) 150 { 151 WCHAR fullname[MAX_PATH]; 152 153 TRACE("(%p, %s, command=%s, data=%lx)\n", 154 caller, debugstr_w( filename ), 155 command_to_string( command ), data); 156 157 switch (command) 158 { 159 case HH_DISPLAY_TOPIC: 160 case HH_DISPLAY_TOC: 161 case HH_DISPLAY_INDEX: 162 case HH_DISPLAY_SEARCH:{ 163 BOOL res; 164 NMHDR nmhdr; 165 HHInfo *info = NULL; 166 WCHAR *window = NULL; 167 const WCHAR *index = NULL; 168 WCHAR *default_index = NULL; 169 int tab_index = TAB_CONTENTS; 170 171 if (!filename) 172 return NULL; 173 174 if (!resolve_filename(filename, fullname, MAX_PATH, &default_index, &window)) 175 { 176 WARN("can't find %s\n", debugstr_w(filename)); 177 return 0; 178 } 179 index = default_index; 180 181 if (window) 182 info = find_window(window); 183 184 info = CreateHelpViewer(info, fullname, caller); 185 if(!info) 186 { 187 heap_free(default_index); 188 heap_free(window); 189 return NULL; 190 } 191 192 if(!index) 193 index = info->WinType.pszFile; 194 if(!info->WinType.pszType) 195 info->WinType.pszType = info->stringsW.pszType = window; 196 else 197 heap_free(window); 198 199 /* called to load a specified topic */ 200 switch(command) 201 { 202 case HH_DISPLAY_TOPIC: 203 case HH_DISPLAY_TOC: 204 if (data) 205 { 206 static const WCHAR delimW[] = {':',':',0}; 207 const WCHAR *i = (const WCHAR *)data; 208 209 index = strstrW(i, delimW); 210 if(index) 211 { 212 if(memcmp(info->pCHMInfo->szFile, i, index-i)) 213 FIXME("Opening a CHM file in the context of another is not supported.\n"); 214 index += strlenW(delimW); 215 } 216 else 217 index = i; 218 } 219 break; 220 } 221 222 res = NavigateToChm(info, info->pCHMInfo->szFile, index); 223 heap_free(default_index); 224 225 if(!res) 226 { 227 ReleaseHelpViewer(info); 228 return NULL; 229 } 230 231 switch(command) 232 { 233 case HH_DISPLAY_TOPIC: 234 case HH_DISPLAY_TOC: 235 tab_index = TAB_CONTENTS; 236 break; 237 case HH_DISPLAY_INDEX: 238 tab_index = TAB_INDEX; 239 if (data) 240 FIXME("Should select keyword '%s'.\n", debugstr_w((WCHAR *)data)); 241 break; 242 case HH_DISPLAY_SEARCH: 243 tab_index = TAB_SEARCH; 244 if (data) 245 FIXME("Should display search specified by HH_FTS_QUERY structure.\n"); 246 break; 247 } 248 /* open the requested tab */ 249 memset(&nmhdr, 0, sizeof(nmhdr)); 250 nmhdr.code = TCN_SELCHANGE; 251 SendMessageW(info->hwndTabCtrl, TCM_SETCURSEL, (WPARAM)info->tabs[tab_index].id, 0); 252 SendMessageW(info->WinType.hwndNavigation, WM_NOTIFY, 0, (LPARAM)&nmhdr); 253 254 return info->WinType.hwndHelp; 255 } 256 case HH_HELP_CONTEXT: { 257 WCHAR *window = NULL; 258 HHInfo *info = NULL; 259 LPWSTR url; 260 261 if (!filename) 262 return NULL; 263 264 if (!resolve_filename(filename, fullname, MAX_PATH, NULL, &window)) 265 { 266 WARN("can't find %s\n", debugstr_w(filename)); 267 return 0; 268 } 269 270 if (window) 271 info = find_window(window); 272 273 info = CreateHelpViewer(info, fullname, caller); 274 if(!info) 275 { 276 heap_free(window); 277 return NULL; 278 } 279 280 if(!info->WinType.pszType) 281 info->WinType.pszType = info->stringsW.pszType = window; 282 else 283 heap_free(window); 284 285 url = FindContextAlias(info->pCHMInfo, data); 286 if(!url) 287 { 288 if(!data) /* there may legitimately be no context alias for id 0 */ 289 return info->WinType.hwndHelp; 290 ReleaseHelpViewer(info); 291 return NULL; 292 } 293 294 NavigateToUrl(info, url); 295 heap_free(url); 296 return info->WinType.hwndHelp; 297 } 298 case HH_PRETRANSLATEMESSAGE: { 299 static BOOL warned = FALSE; 300 301 if (!warned) 302 { 303 FIXME("HH_PRETRANSLATEMESSAGE unimplemented\n"); 304 warned = TRUE; 305 } 306 return 0; 307 } 308 case HH_CLOSE_ALL: { 309 HHInfo *info, *next; 310 311 LIST_FOR_EACH_ENTRY_SAFE(info, next, &window_list, HHInfo, entry) 312 { 313 TRACE("Destroying window %s.\n", debugstr_w(info->WinType.pszType)); 314 ReleaseHelpViewer(info); 315 } 316 return 0; 317 } 318 case HH_SET_WIN_TYPE: { 319 HH_WINTYPEW *wintype = (HH_WINTYPEW *)data; 320 WCHAR *window = NULL; 321 HHInfo *info = NULL; 322 323 if (!filename && wintype->pszType) 324 window = strdupW(wintype->pszType); 325 else if (!filename || !resolve_filename(filename, fullname, MAX_PATH, NULL, &window) || !window) 326 { 327 WARN("can't find window name: %s\n", debugstr_w(filename)); 328 return 0; 329 } 330 info = find_window(window); 331 if (!info) 332 { 333 info = heap_alloc_zero(sizeof(HHInfo)); 334 info->WinType.pszType = info->stringsW.pszType = window; 335 list_add_tail(&window_list, &info->entry); 336 } 337 else 338 heap_free(window); 339 340 TRACE("Changing WINTYPE, fsValidMembers=0x%x\n", wintype->fsValidMembers); 341 342 MergeChmProperties(wintype, info, TRUE); 343 UpdateHelpWindow(info); 344 return 0; 345 } 346 case HH_GET_WIN_TYPE: { 347 HH_WINTYPEW *wintype = (HH_WINTYPEW *)data; 348 WCHAR *window = NULL; 349 HHInfo *info = NULL; 350 351 if (!filename || !resolve_filename(filename, fullname, MAX_PATH, NULL, &window) || !window) 352 { 353 WARN("can't find window name: %s\n", debugstr_w(filename)); 354 return 0; 355 } 356 info = find_window(window); 357 if (!info) 358 { 359 WARN("Could not find window named %s.\n", debugstr_w(window)); 360 heap_free(window); 361 return (HWND)~0; 362 } 363 364 TRACE("Retrieving WINTYPE for %s.\n", debugstr_w(window)); 365 *wintype = info->WinType; 366 heap_free(window); 367 return 0; 368 } 369 default: 370 FIXME("HH case %s not handled.\n", command_to_string( command )); 371 } 372 373 return 0; 374 } 375 376 static void wintypeAtoW(const HH_WINTYPEA *data, HH_WINTYPEW *wdata, struct wintype_stringsW *stringsW) 377 { 378 memcpy(wdata, data, sizeof(*data)); 379 /* convert all of the ANSI strings to Unicode */ 380 wdata->pszType = stringsW->pszType = strdupAtoW(data->pszType); 381 wdata->pszCaption = stringsW->pszCaption = strdupAtoW(data->pszCaption); 382 wdata->pszToc = stringsW->pszToc = strdupAtoW(data->pszToc); 383 wdata->pszIndex = stringsW->pszIndex = strdupAtoW(data->pszIndex); 384 wdata->pszFile = stringsW->pszFile = strdupAtoW(data->pszFile); 385 wdata->pszHome = stringsW->pszHome = strdupAtoW(data->pszHome); 386 wdata->pszJump1 = stringsW->pszJump1 = strdupAtoW(data->pszJump1); 387 wdata->pszJump2 = stringsW->pszJump2 = strdupAtoW(data->pszJump2); 388 wdata->pszUrlJump1 = stringsW->pszUrlJump1 = strdupAtoW(data->pszUrlJump1); 389 wdata->pszUrlJump2 = stringsW->pszUrlJump2 = strdupAtoW(data->pszUrlJump2); 390 wdata->pszCustomTabs = stringsW->pszCustomTabs = strdupAtoW(data->pszCustomTabs); 391 } 392 393 static void wintypeWtoA(const HH_WINTYPEW *wdata, HH_WINTYPEA *data, struct wintype_stringsA *stringsA) 394 { 395 memcpy(data, wdata, sizeof(*wdata)); 396 /* convert all of the Unicode strings to ANSI */ 397 data->pszType = stringsA->pszType = strdupWtoA(wdata->pszType); 398 data->pszCaption = stringsA->pszCaption = strdupWtoA(wdata->pszCaption); 399 data->pszToc = stringsA->pszToc = strdupWtoA(wdata->pszToc); 400 data->pszIndex = stringsA->pszFile = strdupWtoA(wdata->pszIndex); 401 data->pszFile = stringsA->pszFile = strdupWtoA(wdata->pszFile); 402 data->pszHome = stringsA->pszHome = strdupWtoA(wdata->pszHome); 403 data->pszJump1 = stringsA->pszJump1 = strdupWtoA(wdata->pszJump1); 404 data->pszJump2 = stringsA->pszJump2 = strdupWtoA(wdata->pszJump2); 405 data->pszUrlJump1 = stringsA->pszUrlJump1 = strdupWtoA(wdata->pszUrlJump1); 406 data->pszUrlJump2 = stringsA->pszUrlJump2 = strdupWtoA(wdata->pszUrlJump2); 407 data->pszCustomTabs = stringsA->pszCustomTabs = strdupWtoA(wdata->pszCustomTabs); 408 } 409 410 /****************************************************************** 411 * HtmlHelpA (HHCTRL.OCX.14) 412 */ 413 HWND WINAPI HtmlHelpA(HWND caller, LPCSTR filename, UINT command, DWORD_PTR data) 414 { 415 WCHAR *wfile = strdupAtoW( filename ); 416 HWND result = 0; 417 418 if (data) 419 { 420 switch(command) 421 { 422 case HH_ALINK_LOOKUP: 423 case HH_DISPLAY_SEARCH: 424 case HH_DISPLAY_TEXT_POPUP: 425 case HH_GET_LAST_ERROR: 426 case HH_KEYWORD_LOOKUP: 427 case HH_SYNC: 428 FIXME("structures not handled yet\n"); 429 break; 430 431 case HH_SET_WIN_TYPE: 432 { 433 struct wintype_stringsW stringsW; 434 HH_WINTYPEW wdata; 435 436 wintypeAtoW((HH_WINTYPEA *)data, &wdata, &stringsW); 437 result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)&wdata ); 438 wintype_stringsW_free(&stringsW); 439 goto done; 440 } 441 case HH_GET_WIN_TYPE: 442 { 443 HH_WINTYPEW wdata; 444 HHInfo *info; 445 446 result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)&wdata ); 447 if (!wdata.pszType) break; 448 info = find_window(wdata.pszType); 449 if (!info) break; 450 wintype_stringsA_free(&info->stringsA); 451 wintypeWtoA(&wdata, (HH_WINTYPEA *)data, &info->stringsA); 452 goto done; 453 } 454 455 case HH_DISPLAY_INDEX: 456 case HH_DISPLAY_TOPIC: 457 case HH_DISPLAY_TOC: 458 case HH_GET_WIN_HANDLE: 459 case HH_SAFE_DISPLAY_TOPIC: 460 { 461 WCHAR *wdata = strdupAtoW( (const char *)data ); 462 result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)wdata ); 463 heap_free(wdata); 464 goto done; 465 } 466 467 case HH_CLOSE_ALL: 468 case HH_HELP_CONTEXT: 469 case HH_INITIALIZE: 470 case HH_PRETRANSLATEMESSAGE: 471 case HH_TP_HELP_CONTEXTMENU: 472 case HH_TP_HELP_WM_HELP: 473 case HH_UNINITIALIZE: 474 /* either scalar or pointer to scalar - do nothing */ 475 break; 476 477 default: 478 FIXME("Unknown command: %s (%d)\n", command_to_string(command), command); 479 break; 480 } 481 } 482 483 result = HtmlHelpW( caller, wfile, command, data ); 484 done: 485 heap_free(wfile); 486 return result; 487 } 488 489 /****************************************************************** 490 * doWinMain (HHCTRL.OCX.13) 491 */ 492 int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine) 493 { 494 MSG msg; 495 int len, buflen, mapid = -1; 496 WCHAR *filename; 497 char *endq = NULL; 498 HWND hwnd; 499 500 hh_process = TRUE; 501 502 /* Parse command line option of the HTML Help command. 503 * 504 * Note: The only currently handled action is "mapid", 505 * which corresponds to opening a specific page. 506 */ 507 while(*szCmdLine == '-') 508 { 509 LPSTR space, ptr; 510 511 ptr = szCmdLine + 1; 512 space = strchr(ptr, ' '); 513 if(!strncmp(ptr, "mapid", space-ptr)) 514 { 515 char idtxt[10]; 516 517 ptr += strlen("mapid")+1; 518 space = strchr(ptr, ' '); 519 /* command line ends without number */ 520 if (!space) 521 return 0; 522 memcpy(idtxt, ptr, space-ptr); 523 idtxt[space-ptr] = '\0'; 524 mapid = atoi(idtxt); 525 szCmdLine = space+1; 526 } 527 else 528 { 529 FIXME("Unhandled HTML Help command line parameter! (%.*s)\n", (int)(space-szCmdLine), szCmdLine); 530 return 0; 531 } 532 } 533 534 /* FIXME: Check szCmdLine for bad arguments */ 535 if (*szCmdLine == '\"') 536 endq = strchr(++szCmdLine, '\"'); 537 538 if (endq) 539 len = endq - szCmdLine; 540 else 541 len = strlen(szCmdLine); 542 543 /* no filename given */ 544 if (!len) 545 return 0; 546 547 buflen = MultiByteToWideChar(CP_ACP, 0, szCmdLine, len, NULL, 0) + 1; 548 filename = heap_alloc(buflen * sizeof(WCHAR)); 549 MultiByteToWideChar(CP_ACP, 0, szCmdLine, len, filename, buflen); 550 filename[buflen-1] = 0; 551 552 /* Open a specific help topic */ 553 if(mapid != -1) 554 hwnd = HtmlHelpW(GetDesktopWindow(), filename, HH_HELP_CONTEXT, mapid); 555 else 556 hwnd = HtmlHelpW(GetDesktopWindow(), filename, HH_DISPLAY_TOPIC, 0); 557 558 heap_free(filename); 559 560 if (!hwnd) 561 { 562 ERR("Failed to open HTML Help file '%s'.\n", szCmdLine); 563 return 0; 564 } 565 566 while (GetMessageW(&msg, 0, 0, 0)) 567 { 568 TranslateMessage(&msg); 569 DispatchMessageW(&msg); 570 } 571 572 return 0; 573 } 574 575 /****************************************************************** 576 * DllGetClassObject (HHCTRL.OCX.@) 577 */ 578 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) 579 { 580 FIXME("(%s %s %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); 581 return CLASS_E_CLASSNOTAVAILABLE; 582 } 583 584 /*********************************************************************** 585 * DllRegisterServer (HHCTRL.OCX.@) 586 */ 587 HRESULT WINAPI DllRegisterServer(void) 588 { 589 return __wine_register_resources( hhctrl_hinstance ); 590 } 591 592 /*********************************************************************** 593 * DllUnregisterServer (HHCTRL.OCX.@) 594 */ 595 HRESULT WINAPI DllUnregisterServer(void) 596 { 597 return __wine_unregister_resources( hhctrl_hinstance ); 598 } 599