user32/tests: Restructure the DDE end-to-end tests to make it easier to add new cases.
[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, cpu_ppc;
155
156 static struct cpu*      dbghelp_cpus[] = {&cpu_i386, &cpu_x86_64, &cpu_ppc, NULL};
157 struct cpu*             dbghelp_current_cpu =
158 #if defined(__i386__)
159     &cpu_i386
160 #elif defined(__x86_64__)
161     &cpu_x86_64
162 #elif defined(__powerpc__)
163     &cpu_ppc
164 #else
165 #error define support for your CPU
166 #endif
167     ;
168
169 struct cpu* cpu_find(DWORD machine)
170 {
171     struct cpu** cpu;
172
173     for (cpu = dbghelp_cpus ; *cpu; cpu++)
174     {
175         if (cpu[0]->machine == machine) return cpu[0];
176     }
177     return NULL;
178 }
179
180 /******************************************************************
181  *              SymSetSearchPathW (DBGHELP.@)
182  *
183  */
184 BOOL WINAPI SymSetSearchPathW(HANDLE hProcess, PCWSTR searchPath)
185 {
186     struct process* pcs = process_find_by_handle(hProcess);
187
188     if (!pcs) return FALSE;
189     if (!searchPath) return FALSE;
190
191     HeapFree(GetProcessHeap(), 0, pcs->search_path);
192     pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0, 
193                                           (lstrlenW(searchPath) + 1) * sizeof(WCHAR)),
194                                 searchPath);
195     return TRUE;
196 }
197
198 /******************************************************************
199  *              SymSetSearchPath (DBGHELP.@)
200  *
201  */
202 BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PCSTR searchPath)
203 {
204     BOOL        ret = FALSE;
205     unsigned    len;
206     WCHAR*      sp;
207
208     len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0);
209     if ((sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
210     {
211         MultiByteToWideChar(CP_ACP, 0, searchPath, -1, sp, len);
212
213         ret = SymSetSearchPathW(hProcess, sp);
214         HeapFree(GetProcessHeap(), 0, sp);
215     }
216     return ret;
217 }
218
219 /***********************************************************************
220  *              SymGetSearchPathW (DBGHELP.@)
221  */
222 BOOL WINAPI SymGetSearchPathW(HANDLE hProcess, PWSTR szSearchPath,
223                               DWORD SearchPathLength)
224 {
225     struct process* pcs = process_find_by_handle(hProcess);
226     if (!pcs) return FALSE;
227
228     lstrcpynW(szSearchPath, pcs->search_path, SearchPathLength);
229     return TRUE;
230 }
231
232 /***********************************************************************
233  *              SymGetSearchPath (DBGHELP.@)
234  */
235 BOOL WINAPI SymGetSearchPath(HANDLE hProcess, PSTR szSearchPath,
236                              DWORD SearchPathLength)
237 {
238     WCHAR*      buffer = HeapAlloc(GetProcessHeap(), 0, SearchPathLength * sizeof(WCHAR));
239     BOOL        ret = FALSE;
240
241     if (buffer)
242     {
243         ret = SymGetSearchPathW(hProcess, buffer, SearchPathLength);
244         if (ret)
245             WideCharToMultiByte(CP_ACP, 0, buffer, SearchPathLength,
246                                 szSearchPath, SearchPathLength, NULL, NULL);
247         HeapFree(GetProcessHeap(), 0, buffer);
248     }
249     return ret;
250 }
251
252 /******************************************************************
253  *              invade_process
254  *
255  * SymInitialize helper: loads in dbghelp all known (and loaded modules)
256  * this assumes that hProcess is a handle on a valid process
257  */
258 static BOOL WINAPI process_invade_cb(PCWSTR name, ULONG64 base, ULONG size, PVOID user)
259 {
260     WCHAR       tmp[MAX_PATH];
261     HANDLE      hProcess = user;
262
263     if (!GetModuleFileNameExW(hProcess, (HMODULE)(DWORD_PTR)base,
264                               tmp, sizeof(tmp) / sizeof(WCHAR)))
265         lstrcpynW(tmp, name, sizeof(tmp) / sizeof(WCHAR));
266
267     SymLoadModuleExW(hProcess, 0, tmp, name, base, size, NULL, 0);
268     return TRUE;
269 }
270
271 /******************************************************************
272  *              check_live_target
273  *
274  */
275 static BOOL check_live_target(struct process* pcs)
276 {
277     if (!GetProcessId(pcs->handle)) return FALSE;
278     if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL, 0)) return FALSE;
279     if (!elf_read_wine_loader_dbg_info(pcs))
280         macho_read_wine_loader_dbg_info(pcs);
281     return TRUE;
282 }
283
284 /******************************************************************
285  *              SymInitializeW (DBGHELP.@)
286  *
287  * The initialisation of a dbghelp's context.
288  * Note that hProcess doesn't need to be a valid process handle (except
289  * when fInvadeProcess is TRUE).
290  * Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries 
291  * containing PE (and NE) module(s), here's how we handle it:
292  * - we load every module (ELF, NE, PE) passed in SymLoadModule
293  * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
294  *   synchronization: hProcess should be a valid process handle, and we hook
295  *   ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
296  *   our internal ELF modules representation (loading / unloading). This way,
297  *   we'll pair every loaded builtin PE module with its ELF counterpart (and
298  *   access its debug information).
299  * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the 
300  *   hProcess refers to a running process. We use some heuristics here, so YMMV.
301  *   If we detect a live target, then we get the same handling as if
302  *   fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
303  *   we won't be able to make the peering between a builtin PE module and its ELF
304  *   counterpart. Hence we won't be able to provide the requested debug
305  *   information. We'll however be able to load native PE modules (and their
306  *   debug information) without any trouble.
307  * Note also that this scheme can be intertwined with the deferred loading 
308  * mechanism (ie only load the debug information when we actually need it).
309  */
310 BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess)
311 {
312     struct process*     pcs;
313
314     TRACE("(%p %s %u)\n", hProcess, debugstr_w(UserSearchPath), fInvadeProcess);
315
316     if (process_find_by_handle(hProcess)){
317         WARN("the symbols for this process have already been initialized!\n");
318
319         /* MSDN says to only call this function once unless SymCleanup() has been called since the last call.
320            It also says to call SymRefreshModuleList() instead if you just want the module list refreshed.
321            Native still returns TRUE even if the process has already been initialized. */
322         return TRUE;
323     }
324
325     pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
326     if (!pcs) return FALSE;
327
328     pcs->handle = hProcess;
329
330     if (UserSearchPath)
331     {
332         pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,      
333                                               (lstrlenW(UserSearchPath) + 1) * sizeof(WCHAR)),
334                                     UserSearchPath);
335     }
336     else
337     {
338         unsigned        size;
339         unsigned        len;
340         static const WCHAR      sym_path[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0};
341         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};
342
343         pcs->search_path = HeapAlloc(GetProcessHeap(), 0, (len = MAX_PATH) * sizeof(WCHAR));
344         while ((size = GetCurrentDirectoryW(len, pcs->search_path)) >= len)
345             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (len *= 2) * sizeof(WCHAR));
346         pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1) * sizeof(WCHAR));
347
348         len = GetEnvironmentVariableW(sym_path, NULL, 0);
349         if (len)
350         {
351             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
352             pcs->search_path[size] = ';';
353             GetEnvironmentVariableW(sym_path, pcs->search_path + size + 1, len);
354             size += 1 + len;
355         }
356         len = GetEnvironmentVariableW(alt_sym_path, NULL, 0);
357         if (len)
358         {
359             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
360             pcs->search_path[size] = ';';
361             GetEnvironmentVariableW(alt_sym_path, pcs->search_path + size + 1, len);
362         }
363     }
364
365     pcs->lmodules = NULL;
366     pcs->dbg_hdr_addr = 0;
367     pcs->next = process_first;
368     process_first = pcs;
369     
370     if (check_live_target(pcs))
371     {
372         if (fInvadeProcess)
373             EnumerateLoadedModulesW64(hProcess, process_invade_cb, hProcess);
374         elf_synchronize_module_list(pcs);
375         macho_synchronize_module_list(pcs);
376     }
377     else if (fInvadeProcess)
378     {
379         SymCleanup(hProcess);
380         SetLastError(ERROR_INVALID_PARAMETER);
381         return FALSE;
382     }
383
384     return TRUE;
385 }
386
387 /******************************************************************
388  *              SymInitialize (DBGHELP.@)
389  *
390  *
391  */
392 BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess)
393 {
394     WCHAR*              sp = NULL;
395     BOOL                ret;
396
397     if (UserSearchPath)
398     {
399         unsigned len;
400
401         len = MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, NULL, 0);
402         sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
403         MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, sp, len);
404     }
405
406     ret = SymInitializeW(hProcess, sp, fInvadeProcess);
407     HeapFree(GetProcessHeap(), 0, sp);
408     return ret;
409 }
410
411 /******************************************************************
412  *              SymCleanup (DBGHELP.@)
413  *
414  */
415 BOOL WINAPI SymCleanup(HANDLE hProcess)
416 {
417     struct process**    ppcs;
418     struct process*     next;
419
420     for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
421     {
422         if ((*ppcs)->handle == hProcess)
423         {
424             while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
425
426             HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
427             next = (*ppcs)->next;
428             HeapFree(GetProcessHeap(), 0, *ppcs);
429             *ppcs = next;
430             return TRUE;
431         }
432     }
433
434     ERR("this process has not had SymInitialize() called for it!\n");
435     return FALSE;
436 }
437
438 /******************************************************************
439  *              SymSetOptions (DBGHELP.@)
440  *
441  */
442 DWORD WINAPI SymSetOptions(DWORD opts)
443 {
444     struct process* pcs;
445
446     for (pcs = process_first; pcs; pcs = pcs->next)
447     {
448         pcs_callback(pcs, CBA_SET_OPTIONS, &opts);
449     }
450     return dbghelp_options = opts;
451 }
452
453 /******************************************************************
454  *              SymGetOptions (DBGHELP.@)
455  *
456  */
457 DWORD WINAPI SymGetOptions(void)
458 {
459     return dbghelp_options;
460 }
461
462 /******************************************************************
463  *              SymSetParentWindow (DBGHELP.@)
464  *
465  */
466 BOOL WINAPI SymSetParentWindow(HWND hwnd)
467 {
468     /* Save hwnd so it can be used as parent window */
469     FIXME("(%p): stub\n", hwnd);
470     return TRUE;
471 }
472
473 /******************************************************************
474  *              SymSetContext (DBGHELP.@)
475  *
476  */
477 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
478                           PIMAGEHLP_CONTEXT Context)
479 {
480     struct process* pcs = process_find_by_handle(hProcess);
481     if (!pcs) return FALSE;
482
483     if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset &&
484         pcs->ctx_frame.FrameOffset  == StackFrame->FrameOffset  &&
485         pcs->ctx_frame.StackOffset  == StackFrame->StackOffset)
486     {
487         TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
488               wine_dbgstr_longlong(pcs->ctx_frame.ReturnOffset),
489               wine_dbgstr_longlong(pcs->ctx_frame.FrameOffset),
490               wine_dbgstr_longlong(pcs->ctx_frame.StackOffset));
491         pcs->ctx_frame.InstructionOffset = StackFrame->InstructionOffset;
492         SetLastError(ERROR_ACCESS_DENIED); /* latest MSDN says ERROR_SUCCESS */
493         return FALSE;
494     }
495
496     pcs->ctx_frame = *StackFrame;
497     /* MSDN states that Context is not (no longer?) used */
498     return TRUE;
499 }
500
501 /******************************************************************
502  *              reg_cb64to32 (internal)
503  *
504  * Registered callback for converting information from 64 bit to 32 bit
505  */
506 static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user)
507 {
508     struct process*                     pcs = process_find_by_handle(hProcess);
509     void*                               data32;
510     IMAGEHLP_DEFERRED_SYMBOL_LOAD64*    idsl64;
511     IMAGEHLP_DEFERRED_SYMBOL_LOAD       idsl;
512
513     if (!pcs) return FALSE;
514     switch (action)
515     {
516     case CBA_DEBUG_INFO:
517     case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
518     case CBA_SET_OPTIONS:
519     case CBA_SYMBOLS_UNLOADED:
520         data32 = (void*)(DWORD_PTR)data;
521         break;
522     case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
523     case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
524     case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
525     case CBA_DEFERRED_SYMBOL_LOAD_START:
526         idsl64 = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD_PTR)data;
527         if (!validate_addr64(idsl64->BaseOfImage))
528             return FALSE;
529         idsl.SizeOfStruct = sizeof(idsl);
530         idsl.BaseOfImage = (DWORD)idsl64->BaseOfImage;
531         idsl.CheckSum = idsl64->CheckSum;
532         idsl.TimeDateStamp = idsl64->TimeDateStamp;
533         memcpy(idsl.FileName, idsl64->FileName, sizeof(idsl.FileName));
534         idsl.Reparse = idsl64->Reparse;
535         data32 = &idsl;
536         break;
537     case CBA_DUPLICATE_SYMBOL:
538     case CBA_EVENT:
539     case CBA_READ_MEMORY:
540     default:
541         FIXME("No mapping for action %u\n", action);
542         return FALSE;
543     }
544     return pcs->reg_cb32(hProcess, action, data32, (PVOID)(DWORD_PTR)user);
545 }
546
547 /******************************************************************
548  *              pcs_callback (internal)
549  */
550 BOOL pcs_callback(const struct process* pcs, ULONG action, void* data)
551 {
552     TRACE("%p %u %p\n", pcs, action, data);
553
554     if (!pcs->reg_cb) return FALSE;
555     if (!pcs->reg_is_unicode)
556     {
557         IMAGEHLP_DEFERRED_SYMBOL_LOAD64     idsl;
558         IMAGEHLP_DEFERRED_SYMBOL_LOADW64*   idslW;
559
560         switch (action)
561         {
562         case CBA_DEBUG_INFO:
563         case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
564         case CBA_SET_OPTIONS:
565         case CBA_SYMBOLS_UNLOADED:
566             break;
567         case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
568         case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
569         case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
570         case CBA_DEFERRED_SYMBOL_LOAD_START:
571             idslW = data;
572             idsl.SizeOfStruct = sizeof(idsl);
573             idsl.BaseOfImage = idslW->BaseOfImage;
574             idsl.CheckSum = idslW->CheckSum;
575             idsl.TimeDateStamp = idslW->TimeDateStamp;
576             WideCharToMultiByte(CP_ACP, 0, idslW->FileName, -1,
577                                 idsl.FileName, sizeof(idsl.FileName), NULL, NULL);
578             idsl.Reparse = idslW->Reparse;
579             data = &idsl;
580             break;
581         case CBA_DUPLICATE_SYMBOL:
582         case CBA_EVENT:
583         case CBA_READ_MEMORY:
584         default:
585             FIXME("No mapping for action %u\n", action);
586             return FALSE;
587         }
588     }
589     return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user);
590 }
591
592 /******************************************************************
593  *              sym_register_cb
594  *
595  * Helper for registering a callback.
596  */
597 static BOOL sym_register_cb(HANDLE hProcess,
598                             PSYMBOL_REGISTERED_CALLBACK64 cb,
599                             PSYMBOL_REGISTERED_CALLBACK cb32,
600                             DWORD64 user, BOOL unicode)
601 {
602     struct process* pcs = process_find_by_handle(hProcess);
603
604     if (!pcs) return FALSE;
605     pcs->reg_cb = cb;
606     pcs->reg_cb32 = cb32;
607     pcs->reg_is_unicode = unicode;
608     pcs->reg_user = user;
609
610     return TRUE;
611 }
612
613 /***********************************************************************
614  *              SymRegisterCallback (DBGHELP.@)
615  */
616 BOOL WINAPI SymRegisterCallback(HANDLE hProcess, 
617                                 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
618                                 PVOID UserContext)
619 {
620     TRACE("(%p, %p, %p)\n", 
621           hProcess, CallbackFunction, UserContext);
622     return sym_register_cb(hProcess, reg_cb64to32, CallbackFunction, (DWORD_PTR)UserContext, FALSE);
623 }
624
625 /***********************************************************************
626  *              SymRegisterCallback64 (DBGHELP.@)
627  */
628 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess, 
629                                   PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
630                                   ULONG64 UserContext)
631 {
632     TRACE("(%p, %p, %s)\n", 
633           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
634     return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, FALSE);
635 }
636
637 /***********************************************************************
638  *              SymRegisterCallbackW64 (DBGHELP.@)
639  */
640 BOOL WINAPI SymRegisterCallbackW64(HANDLE hProcess, 
641                                    PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
642                                    ULONG64 UserContext)
643 {
644     TRACE("(%p, %p, %s)\n", 
645           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
646     return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, TRUE);
647 }
648
649 /* This is imagehlp version not dbghelp !! */
650 static API_VERSION api_version = { 4, 0, 2, 0 };
651
652 /***********************************************************************
653  *           ImagehlpApiVersion (DBGHELP.@)
654  */
655 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
656 {
657     return &api_version;
658 }
659
660 /***********************************************************************
661  *           ImagehlpApiVersionEx (DBGHELP.@)
662  */
663 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
664 {
665     if (!AppVersion) return NULL;
666
667     AppVersion->MajorVersion = api_version.MajorVersion;
668     AppVersion->MinorVersion = api_version.MinorVersion;
669     AppVersion->Revision = api_version.Revision;
670     AppVersion->Reserved = api_version.Reserved;
671
672     return AppVersion;
673 }
674
675 /******************************************************************
676  *              ExtensionApiVersion (DBGHELP.@)
677  */
678 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
679 {
680     static EXT_API_VERSION      eav = {5, 5, 5, 0};
681     return &eav;
682 }
683
684 /******************************************************************
685  *              WinDbgExtensionDllInit (DBGHELP.@)
686  */
687 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
688                                    unsigned short major, unsigned short minor)
689 {
690 }