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