xref: /reactos/dll/win32/hhctrl.ocx/hhctrl.c (revision f986527d)
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