msi: Don't print the function name twice in a WARN.
[wine] / dlls / dbghelp / dbghelp.c
1 /*
2  * File dbghelp.c - generic routines (process) for dbghelp DLL
3  *
4  * Copyright (C) 2004, Eric Pouech
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22
23 #include "dbghelp_private.h"
24 #include "winerror.h"
25 #include "psapi.h"
26 #include "wine/debug.h"
27 #include "wdbgexts.h"
28 #include "winnls.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
31
32 /* TODO
33  *  - support for symbols' types is still partly missing
34  *      + C++ support
35  *      + we should store the underlying type for an enum in the symt_enum struct
36  *      + for enums, we store the names & values (associated to the enum type), 
37  *        but those values are not directly usable from a debugger (that's why, I
38  *        assume, that we have also to define constants for enum values, as 
39  *        Codeview does BTW.
40  *      + SymEnumTypes should only return *user* defined types (UDT, typedefs...) not
41  *        all the types stored/used in the modules (like char*)
42  *  - SymGetLine{Next|Prev} don't work as expected (they don't seem to work across
43  *    functions, and even across function blocks...). Basically, for *Next* to work
44  *    it requires an address after the prolog of the func (the base address of the 
45  *    func doesn't work)
46  *  - most options (dbghelp_options) are not used (loading lines...)
47  *  - in symbol lookup by name, we don't use RE everywhere we should. Moreover, when
48  *    we're supposed to use RE, it doesn't make use of our hash tables. Therefore,
49  *    we could use hash if name isn't a RE, and fall back to a full search when we
50  *    get a full RE
51  *  - msc:
52  *      + we should add parameters' types to the function's signature
53  *        while processing a function's parameters
54  *      + add support for function-less labels (as MSC seems to define them)
55  *      + C++ management
56  *  - stabs: 
57  *      + when, in a same module, the same definition is used in several compilation
58  *        units, we get several definitions of the same object (especially 
59  *        struct/union). we should find a way not to duplicate them
60  *      + in some cases (dlls/user/dialog16.c DIALOG_GetControl16), the same static
61  *        global variable is defined several times (at different scopes). We are
62  *        getting several of those while looking for a unique symbol. Part of the 
63  *        issue is that we don't give a scope to a static variable inside a function
64  *      + C++ management
65  */
66
67 unsigned   dbghelp_options = SYMOPT_UNDNAME;
68 HANDLE     hMsvcrt = NULL;
69
70 /***********************************************************************
71  *           DllMain (DEBUGHLP.@)
72  */
73 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
74 {
75     switch (fdwReason)
76     {
77     case DLL_PROCESS_ATTACH:    break;
78     case DLL_PROCESS_DETACH:
79         if (hMsvcrt) FreeLibrary(hMsvcrt);
80         break;
81     case DLL_THREAD_ATTACH:     break;
82     case DLL_THREAD_DETACH:     break;
83     default:                    break;
84     }
85     return TRUE;
86 }
87
88 static struct process* process_first /* = NULL */;
89
90 /******************************************************************
91  *              process_find_by_handle
92  *
93  */
94 struct process*    process_find_by_handle(HANDLE hProcess)
95 {
96     struct process* p;
97
98     for (p = process_first; p && p->handle != hProcess; p = p->next);
99     if (!p) SetLastError(ERROR_INVALID_HANDLE);
100     return p;
101 }
102
103 /******************************************************************
104  *             validate_addr64 (internal)
105  *
106  */
107 BOOL validate_addr64(DWORD64 addr)
108 {
109     if (sizeof(void*) == sizeof(int) && (addr >> 32))
110     {
111         FIXME("Unsupported address %s\n", wine_dbgstr_longlong(addr));
112         SetLastError(ERROR_INVALID_PARAMETER);
113         return FALSE;
114     }
115     return TRUE;
116 }
117
118 /******************************************************************
119  *              fetch_buffer
120  *
121  * Ensures process' internal buffer is large enough.
122  */
123 void* fetch_buffer(struct process* pcs, unsigned size)
124 {
125     if (size > pcs->buffer_size)
126     {
127         if (pcs->buffer)
128             pcs->buffer = HeapReAlloc(GetProcessHeap(), 0, pcs->buffer, size);
129         else
130             pcs->buffer = HeapAlloc(GetProcessHeap(), 0, size);
131         pcs->buffer_size = (pcs->buffer) ? size : 0;
132     }
133     return pcs->buffer;
134 }
135
136 const char* wine_dbgstr_addr(const ADDRESS64* addr)
137 {
138     if (!addr) return "(null)";
139     switch (addr->Mode)
140     {
141     case AddrModeFlat:
142         return wine_dbg_sprintf("flat<%s>", wine_dbgstr_longlong(addr->Offset));
143     case AddrMode1616:
144         return wine_dbg_sprintf("1616<%04x:%04x>", addr->Segment, (DWORD)addr->Offset);
145     case AddrMode1632:
146         return wine_dbg_sprintf("1632<%04x:%08x>", addr->Segment, (DWORD)addr->Offset);
147     case AddrModeReal:
148         return wine_dbg_sprintf("real<%04x:%04x>", addr->Segment, (DWORD)addr->Offset);
149     default:
150         return "unknown";
151     }
152 }
153
154 extern struct cpu       cpu_i386, cpu_x86_64;
155
156 static struct cpu*      dbghelp_cpus[] = {&cpu_i386, &cpu_x86_64, NULL};
157 struct cpu*             dbghelp_current_cpu =
158 #if defined(__i386__)
159     &cpu_i386
160 #elif defined(__x86_64__)
161     &cpu_x86_64
162 #else
163 #error define support for your CPU
164 #endif
165     ;
166
167 struct cpu* cpu_find(DWORD machine)
168 {
169     struct cpu** cpu;
170
171     for (cpu = dbghelp_cpus ; *cpu; cpu++)
172     {
173         if (cpu[0]->machine == machine) return cpu[0];
174     }
175     return NULL;
176 }
177
178 /******************************************************************
179  *              SymSetSearchPathW (DBGHELP.@)
180  *
181  */
182 BOOL WINAPI SymSetSearchPathW(HANDLE hProcess, PCWSTR searchPath)
183 {
184     struct process* pcs = process_find_by_handle(hProcess);
185
186     if (!pcs) return FALSE;
187     if (!searchPath) return FALSE;
188
189     HeapFree(GetProcessHeap(), 0, pcs->search_path);
190     pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0, 
191                                           (lstrlenW(searchPath) + 1) * sizeof(WCHAR)),
192                                 searchPath);
193     return TRUE;
194 }
195
196 /******************************************************************
197  *              SymSetSearchPath (DBGHELP.@)
198  *
199  */
200 BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PCSTR searchPath)
201 {
202     BOOL        ret = FALSE;
203     unsigned    len;
204     WCHAR*      sp;
205
206     len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0);
207     if ((sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
208     {
209         MultiByteToWideChar(CP_ACP, 0, searchPath, -1, sp, len);
210
211         ret = SymSetSearchPathW(hProcess, sp);
212         HeapFree(GetProcessHeap(), 0, sp);
213     }
214     return ret;
215 }
216
217 /***********************************************************************
218  *              SymGetSearchPathW (DBGHELP.@)
219  */
220 BOOL WINAPI SymGetSearchPathW(HANDLE hProcess, PWSTR szSearchPath,
221                               DWORD SearchPathLength)
222 {
223     struct process* pcs = process_find_by_handle(hProcess);
224     if (!pcs) return FALSE;
225
226     lstrcpynW(szSearchPath, pcs->search_path, SearchPathLength);
227     return TRUE;
228 }
229
230 /***********************************************************************
231  *              SymGetSearchPath (DBGHELP.@)
232  */
233 BOOL WINAPI SymGetSearchPath(HANDLE hProcess, PSTR szSearchPath,
234                              DWORD SearchPathLength)
235 {
236     WCHAR*      buffer = HeapAlloc(GetProcessHeap(), 0, SearchPathLength * sizeof(WCHAR));
237     BOOL        ret = FALSE;
238
239     if (buffer)
240     {
241         ret = SymGetSearchPathW(hProcess, buffer, SearchPathLength);
242         if (ret)
243             WideCharToMultiByte(CP_ACP, 0, buffer, SearchPathLength,
244                                 szSearchPath, SearchPathLength, NULL, NULL);
245         HeapFree(GetProcessHeap(), 0, buffer);
246     }
247     return ret;
248 }
249
250 /******************************************************************
251  *              invade_process
252  *
253  * SymInitialize helper: loads in dbghelp all known (and loaded modules)
254  * this assumes that hProcess is a handle on a valid process
255  */
256 static BOOL WINAPI process_invade_cb(PCWSTR name, ULONG64 base, ULONG size, PVOID user)
257 {
258     WCHAR       tmp[MAX_PATH];
259     HANDLE      hProcess = user;
260
261     if (!GetModuleFileNameExW(hProcess, (HMODULE)(DWORD_PTR)base,
262                               tmp, sizeof(tmp) / sizeof(WCHAR)))
263         lstrcpynW(tmp, name, sizeof(tmp) / sizeof(WCHAR));
264
265     SymLoadModuleExW(hProcess, 0, tmp, name, base, size, NULL, 0);
266     return TRUE;
267 }
268
269 /******************************************************************
270  *              check_live_target
271  *
272  */
273 static BOOL check_live_target(struct process* pcs)
274 {
275     if (!GetProcessId(pcs->handle)) return FALSE;
276     if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL, 0)) return FALSE;
277     if (!elf_read_wine_loader_dbg_info(pcs))
278         macho_read_wine_loader_dbg_info(pcs);
279     return TRUE;
280 }
281
282 /******************************************************************
283  *              SymInitializeW (DBGHELP.@)
284  *
285  * The initialisation of a dbghelp's context.
286  * Note that hProcess doesn't need to be a valid process handle (except
287  * when fInvadeProcess is TRUE).
288  * Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries 
289  * containing PE (and NE) module(s), here's how we handle it:
290  * - we load every module (ELF, NE, PE) passed in SymLoadModule
291  * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
292  *   synchronization: hProcess should be a valid process handle, and we hook
293  *   ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
294  *   our internal ELF modules representation (loading / unloading). This way,
295  *   we'll pair every loaded builtin PE module with its ELF counterpart (and
296  *   access its debug information).
297  * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the 
298  *   hProcess refers to a running process. We use some heuristics here, so YMMV.
299  *   If we detect a live target, then we get the same handling as if
300  *   fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
301  *   we won't be able to make the peering between a builtin PE module and its ELF
302  *   counterpart. Hence we won't be able to provide the requested debug
303  *   information. We'll however be able to load native PE modules (and their
304  *   debug information) without any trouble.
305  * Note also that this scheme can be intertwined with the deferred loading 
306  * mechanism (ie only load the debug information when we actually need it).
307  */
308 BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess)
309 {
310     struct process*     pcs;
311
312     TRACE("(%p %s %u)\n", hProcess, debugstr_w(UserSearchPath), fInvadeProcess);
313
314     if (process_find_by_handle(hProcess)){
315         WARN("the symbols for this process have already been initialized!\n");
316
317         /* MSDN says to only call this function once unless SymCleanup() has been called since the last call.
318            It also says to call SymRefreshModuleList() instead if you just want the module list refreshed.
319            Native still returns TRUE even if the process has already been initialized. */
320         return TRUE;
321     }
322
323     pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
324     if (!pcs) return FALSE;
325
326     pcs->handle = hProcess;
327
328     if (UserSearchPath)
329     {
330         pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,      
331                                               (lstrlenW(UserSearchPath) + 1) * sizeof(WCHAR)),
332                                     UserSearchPath);
333     }
334     else
335     {
336         unsigned        size;
337         unsigned        len;
338         static const WCHAR      sym_path[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0};
339         static const WCHAR      alt_sym_path[] = {'_','N','T','_','A','L','T','E','R','N','A','T','E','_','S','Y','M','B','O','L','_','P','A','T','H',0};
340
341         pcs->search_path = HeapAlloc(GetProcessHeap(), 0, (len = MAX_PATH) * sizeof(WCHAR));
342         while ((size = GetCurrentDirectoryW(len, pcs->search_path)) >= len)
343             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (len *= 2) * sizeof(WCHAR));
344         pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1) * sizeof(WCHAR));
345
346         len = GetEnvironmentVariableW(sym_path, NULL, 0);
347         if (len)
348         {
349             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
350             pcs->search_path[size] = ';';
351             GetEnvironmentVariableW(sym_path, pcs->search_path + size + 1, len);
352             size += 1 + len;
353         }
354         len = GetEnvironmentVariableW(alt_sym_path, NULL, 0);
355         if (len)
356         {
357             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
358             pcs->search_path[size] = ';';
359             GetEnvironmentVariableW(alt_sym_path, pcs->search_path + size + 1, len);
360         }
361     }
362
363     pcs->lmodules = NULL;
364     pcs->dbg_hdr_addr = 0;
365     pcs->next = process_first;
366     process_first = pcs;
367     
368     if (check_live_target(pcs))
369     {
370         if (fInvadeProcess)
371             EnumerateLoadedModulesW64(hProcess, process_invade_cb, hProcess);
372         elf_synchronize_module_list(pcs);
373         macho_synchronize_module_list(pcs);
374     }
375     else if (fInvadeProcess)
376     {
377         SymCleanup(hProcess);
378         SetLastError(ERROR_INVALID_PARAMETER);
379         return FALSE;
380     }
381
382     return TRUE;
383 }
384
385 /******************************************************************
386  *              SymInitialize (DBGHELP.@)
387  *
388  *
389  */
390 BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess)
391 {
392     WCHAR*              sp = NULL;
393     BOOL                ret;
394
395     if (UserSearchPath)
396     {
397         unsigned len;
398
399         len = MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, NULL, 0);
400         sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
401         MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, sp, len);
402     }
403
404     ret = SymInitializeW(hProcess, sp, fInvadeProcess);
405     HeapFree(GetProcessHeap(), 0, sp);
406     return ret;
407 }
408
409 /******************************************************************
410  *              SymCleanup (DBGHELP.@)
411  *
412  */
413 BOOL WINAPI SymCleanup(HANDLE hProcess)
414 {
415     struct process**    ppcs;
416     struct process*     next;
417
418     for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
419     {
420         if ((*ppcs)->handle == hProcess)
421         {
422             while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
423
424             HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
425             next = (*ppcs)->next;
426             HeapFree(GetProcessHeap(), 0, *ppcs);
427             *ppcs = next;
428             return TRUE;
429         }
430     }
431
432     ERR("this process has not had SymInitialize() called for it!\n");
433     return FALSE;
434 }
435
436 /******************************************************************
437  *              SymSetOptions (DBGHELP.@)
438  *
439  */
440 DWORD WINAPI SymSetOptions(DWORD opts)
441 {
442     struct process* pcs;
443
444     for (pcs = process_first; pcs; pcs = pcs->next)
445     {
446         pcs_callback(pcs, CBA_SET_OPTIONS, &opts);
447     }
448     return dbghelp_options = opts;
449 }
450
451 /******************************************************************
452  *              SymGetOptions (DBGHELP.@)
453  *
454  */
455 DWORD WINAPI SymGetOptions(void)
456 {
457     return dbghelp_options;
458 }
459
460 /******************************************************************
461  *              SymSetParentWindow (DBGHELP.@)
462  *
463  */
464 BOOL WINAPI SymSetParentWindow(HWND hwnd)
465 {
466     /* Save hwnd so it can be used as parent window */
467     FIXME("(%p): stub\n", hwnd);
468     return TRUE;
469 }
470
471 /******************************************************************
472  *              SymSetContext (DBGHELP.@)
473  *
474  */
475 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
476                           PIMAGEHLP_CONTEXT Context)
477 {
478     struct process* pcs = process_find_by_handle(hProcess);
479     if (!pcs) return FALSE;
480
481     if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset &&
482         pcs->ctx_frame.FrameOffset  == StackFrame->FrameOffset  &&
483         pcs->ctx_frame.StackOffset  == StackFrame->StackOffset)
484     {
485         TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
486               wine_dbgstr_longlong(pcs->ctx_frame.ReturnOffset),
487               wine_dbgstr_longlong(pcs->ctx_frame.FrameOffset),
488               wine_dbgstr_longlong(pcs->ctx_frame.StackOffset));
489         pcs->ctx_frame.InstructionOffset = StackFrame->InstructionOffset;
490         SetLastError(ERROR_ACCESS_DENIED); /* latest MSDN says ERROR_SUCCESS */
491         return FALSE;
492     }
493
494     pcs->ctx_frame = *StackFrame;
495     /* MSDN states that Context is not (no longer?) used */
496     return TRUE;
497 }
498
499 /******************************************************************
500  *              reg_cb64to32 (internal)
501  *
502  * Registered callback for converting information from 64 bit to 32 bit
503  */
504 static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user)
505 {
506     struct process*                     pcs = process_find_by_handle(hProcess);
507     void*                               data32;
508     IMAGEHLP_DEFERRED_SYMBOL_LOAD64*    idsl64;
509     IMAGEHLP_DEFERRED_SYMBOL_LOAD       idsl;
510
511     if (!pcs) return FALSE;
512     switch (action)
513     {
514     case CBA_DEBUG_INFO:
515     case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
516     case CBA_SET_OPTIONS:
517     case CBA_SYMBOLS_UNLOADED:
518         data32 = (void*)(DWORD_PTR)data;
519         break;
520     case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
521     case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
522     case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
523     case CBA_DEFERRED_SYMBOL_LOAD_START:
524         idsl64 = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD_PTR)data;
525         if (!validate_addr64(idsl64->BaseOfImage))
526             return FALSE;
527         idsl.SizeOfStruct = sizeof(idsl);
528         idsl.BaseOfImage = (DWORD)idsl64->BaseOfImage;
529         idsl.CheckSum = idsl64->CheckSum;
530         idsl.TimeDateStamp = idsl64->TimeDateStamp;
531         memcpy(idsl.FileName, idsl64->FileName, sizeof(idsl.FileName));
532         idsl.Reparse = idsl64->Reparse;
533         data32 = &idsl;
534         break;
535     case CBA_DUPLICATE_SYMBOL:
536     case CBA_EVENT:
537     case CBA_READ_MEMORY:
538     default:
539         FIXME("No mapping for action %u\n", action);
540         return FALSE;
541     }
542     return pcs->reg_cb32(hProcess, action, data32, (PVOID)(DWORD_PTR)user);
543 }
544
545 /******************************************************************
546  *              pcs_callback (internal)
547  */
548 BOOL pcs_callback(const struct process* pcs, ULONG action, void* data)
549 {
550     TRACE("%p %u %p\n", pcs, action, data);
551
552     if (!pcs->reg_cb) return FALSE;
553     if (!pcs->reg_is_unicode)
554     {
555         IMAGEHLP_DEFERRED_SYMBOL_LOAD64     idsl;
556         IMAGEHLP_DEFERRED_SYMBOL_LOADW64*   idslW;
557
558         switch (action)
559         {
560         case CBA_DEBUG_INFO:
561         case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
562         case CBA_SET_OPTIONS:
563         case CBA_SYMBOLS_UNLOADED:
564             break;
565         case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
566         case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
567         case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
568         case CBA_DEFERRED_SYMBOL_LOAD_START:
569             idslW = data;
570             idsl.SizeOfStruct = sizeof(idsl);
571             idsl.BaseOfImage = idslW->BaseOfImage;
572             idsl.CheckSum = idslW->CheckSum;
573             idsl.TimeDateStamp = idslW->TimeDateStamp;
574             WideCharToMultiByte(CP_ACP, 0, idslW->FileName, -1,
575                                 idsl.FileName, sizeof(idsl.FileName), NULL, NULL);
576             idsl.Reparse = idslW->Reparse;
577             data = &idsl;
578             break;
579         case CBA_DUPLICATE_SYMBOL:
580         case CBA_EVENT:
581         case CBA_READ_MEMORY:
582         default:
583             FIXME("No mapping for action %u\n", action);
584             return FALSE;
585         }
586     }
587     return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user);
588 }
589
590 /******************************************************************
591  *              sym_register_cb
592  *
593  * Helper for registering a callback.
594  */
595 static BOOL sym_register_cb(HANDLE hProcess,
596                             PSYMBOL_REGISTERED_CALLBACK64 cb,
597                             PSYMBOL_REGISTERED_CALLBACK cb32,
598                             DWORD64 user, BOOL unicode)
599 {
600     struct process* pcs = process_find_by_handle(hProcess);
601
602     if (!pcs) return FALSE;
603     pcs->reg_cb = cb;
604     pcs->reg_cb32 = cb32;
605     pcs->reg_is_unicode = unicode;
606     pcs->reg_user = user;
607
608     return TRUE;
609 }
610
611 /***********************************************************************
612  *              SymRegisterCallback (DBGHELP.@)
613  */
614 BOOL WINAPI SymRegisterCallback(HANDLE hProcess, 
615                                 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
616                                 PVOID UserContext)
617 {
618     TRACE("(%p, %p, %p)\n", 
619           hProcess, CallbackFunction, UserContext);
620     return sym_register_cb(hProcess, reg_cb64to32, CallbackFunction, (DWORD_PTR)UserContext, FALSE);
621 }
622
623 /***********************************************************************
624  *              SymRegisterCallback64 (DBGHELP.@)
625  */
626 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess, 
627                                   PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
628                                   ULONG64 UserContext)
629 {
630     TRACE("(%p, %p, %s)\n", 
631           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
632     return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, FALSE);
633 }
634
635 /***********************************************************************
636  *              SymRegisterCallbackW64 (DBGHELP.@)
637  */
638 BOOL WINAPI SymRegisterCallbackW64(HANDLE hProcess, 
639                                    PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
640                                    ULONG64 UserContext)
641 {
642     TRACE("(%p, %p, %s)\n", 
643           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
644     return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, TRUE);
645 }
646
647 /* This is imagehlp version not dbghelp !! */
648 static API_VERSION api_version = { 4, 0, 2, 0 };
649
650 /***********************************************************************
651  *           ImagehlpApiVersion (DBGHELP.@)
652  */
653 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
654 {
655     return &api_version;
656 }
657
658 /***********************************************************************
659  *           ImagehlpApiVersionEx (DBGHELP.@)
660  */
661 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
662 {
663     if (!AppVersion) return NULL;
664
665     AppVersion->MajorVersion = api_version.MajorVersion;
666     AppVersion->MinorVersion = api_version.MinorVersion;
667     AppVersion->Revision = api_version.Revision;
668     AppVersion->Reserved = api_version.Reserved;
669
670     return AppVersion;
671 }
672
673 /******************************************************************
674  *              ExtensionApiVersion (DBGHELP.@)
675  */
676 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
677 {
678     static EXT_API_VERSION      eav = {5, 5, 5, 0};
679     return &eav;
680 }
681
682 /******************************************************************
683  *              WinDbgExtensionDllInit (DBGHELP.@)
684  */
685 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
686                                    unsigned short major, unsigned short minor)
687 {
688 }