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