gdiplus: Implemented GdipFillPolygon2/GdipFillPolygon2I.
[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             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             EnumerateLoadedModules(hProcess, process_invade_cb, hProcess);
330         elf_synchronize_module_list(pcs);
331     }
332     else if (fInvadeProcess)
333     {
334         SymCleanup(hProcess);
335         SetLastError(ERROR_INVALID_PARAMETER);
336         return FALSE;
337     }
338
339     return TRUE;
340 }
341
342 /******************************************************************
343  *              SymInitialize (DBGHELP.@)
344  *
345  *
346  */
347 BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess)
348 {
349     WCHAR*              sp = NULL;
350     BOOL                ret;
351
352     if (UserSearchPath)
353     {
354         unsigned len;
355
356         len = MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, NULL, 0);
357         sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
358         MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, sp, len);
359     }
360
361     ret = SymInitializeW(hProcess, sp, fInvadeProcess);
362     HeapFree(GetProcessHeap(), 0, sp);
363     return ret;
364 }
365
366 /******************************************************************
367  *              SymCleanup (DBGHELP.@)
368  *
369  */
370 BOOL WINAPI SymCleanup(HANDLE hProcess)
371 {
372     struct process**    ppcs;
373     struct process*     next;
374
375     for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
376     {
377         if ((*ppcs)->handle == hProcess)
378         {
379             while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
380
381             HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
382             next = (*ppcs)->next;
383             HeapFree(GetProcessHeap(), 0, *ppcs);
384             *ppcs = next;
385             return TRUE;
386         }
387     }
388
389     ERR("this process has not had SymInitialize() called for it!\n");
390     return FALSE;
391 }
392
393 /******************************************************************
394  *              SymSetOptions (DBGHELP.@)
395  *
396  */
397 DWORD WINAPI SymSetOptions(DWORD opts)
398 {
399     struct process* pcs;
400
401     for (pcs = process_first; pcs; pcs = pcs->next)
402     {
403         pcs_callback(pcs, CBA_SET_OPTIONS, &opts);
404     }
405     return dbghelp_options = opts;
406 }
407
408 /******************************************************************
409  *              SymGetOptions (DBGHELP.@)
410  *
411  */
412 DWORD WINAPI SymGetOptions(void)
413 {
414     return dbghelp_options;
415 }
416
417 /******************************************************************
418  *              SymSetParentWindow (DBGHELP.@)
419  *
420  */
421 BOOL WINAPI SymSetParentWindow(HWND hwnd)
422 {
423     /* Save hwnd so it can be used as parent window */
424     FIXME("(%p): stub\n", hwnd);
425     return TRUE;
426 }
427
428 /******************************************************************
429  *              SymSetContext (DBGHELP.@)
430  *
431  */
432 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
433                           PIMAGEHLP_CONTEXT Context)
434 {
435     struct process* pcs = process_find_by_handle(hProcess);
436     if (!pcs) return FALSE;
437
438     if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset &&
439         pcs->ctx_frame.FrameOffset  == StackFrame->FrameOffset  &&
440         pcs->ctx_frame.StackOffset  == StackFrame->StackOffset)
441     {
442         TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
443               wine_dbgstr_longlong(pcs->ctx_frame.ReturnOffset),
444               wine_dbgstr_longlong(pcs->ctx_frame.FrameOffset),
445               wine_dbgstr_longlong(pcs->ctx_frame.StackOffset));
446         pcs->ctx_frame.InstructionOffset = StackFrame->InstructionOffset;
447         SetLastError(ERROR_ACCESS_DENIED); /* latest MSDN says ERROR_SUCCESS */
448         return FALSE;
449     }
450
451     pcs->ctx_frame = *StackFrame;
452     /* MSDN states that Context is not (no longer?) used */
453     return TRUE;
454 }
455
456 /******************************************************************
457  *              reg_cb64to32 (internal)
458  *
459  * Registered callback for converting information from 64 bit to 32 bit
460  */
461 static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user)
462 {
463     PSYMBOL_REGISTERED_CALLBACK         cb32 = (PSYMBOL_REGISTERED_CALLBACK)(DWORD)(user >> 32);
464     DWORD                               user32 = (DWORD)user;
465     void*                               data32;
466     IMAGEHLP_DEFERRED_SYMBOL_LOAD64*    idsl64;
467     IMAGEHLP_DEFERRED_SYMBOL_LOAD       idsl;
468
469     switch (action)
470     {
471     case CBA_DEBUG_INFO:
472     case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
473     case CBA_SET_OPTIONS:
474     case CBA_SYMBOLS_UNLOADED:
475         data32 = (void*)(DWORD)data;
476         break;
477     case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
478     case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
479     case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
480     case CBA_DEFERRED_SYMBOL_LOAD_START:
481         idsl64 = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD)data;
482         if (!validate_addr64(idsl64->BaseOfImage))
483             return FALSE;
484         idsl.SizeOfStruct = sizeof(idsl);
485         idsl.BaseOfImage = (DWORD)idsl64->BaseOfImage;
486         idsl.CheckSum = idsl64->CheckSum;
487         idsl.TimeDateStamp = idsl64->TimeDateStamp;
488         memcpy(idsl.FileName, idsl64->FileName, sizeof(idsl.FileName));
489         idsl.Reparse = idsl64->Reparse;
490         data32 = &idsl;
491         break;
492     case CBA_DUPLICATE_SYMBOL:
493     case CBA_EVENT:
494     case CBA_READ_MEMORY:
495     default:
496         FIXME("No mapping for action %u\n", action);
497         return FALSE;
498     }
499     return cb32(hProcess, action, data32, (PVOID)user32);
500 }
501
502 /******************************************************************
503  *              pcs_callback (internal)
504  */
505 BOOL pcs_callback(const struct process* pcs, ULONG action, void* data)
506 {
507     TRACE("%p %u %p\n", pcs, action, data);
508
509     if (!pcs->reg_cb) return FALSE;
510     if (!pcs->reg_is_unicode)
511     {
512         IMAGEHLP_DEFERRED_SYMBOL_LOAD64     idsl;
513         IMAGEHLP_DEFERRED_SYMBOL_LOADW64*   idslW;
514
515         switch (action)
516         {
517         case CBA_DEBUG_INFO:
518         case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
519         case CBA_SET_OPTIONS:
520         case CBA_SYMBOLS_UNLOADED:
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             idslW = (IMAGEHLP_DEFERRED_SYMBOL_LOADW64*)(DWORD)data;
527             idsl.SizeOfStruct = sizeof(idsl);
528             idsl.BaseOfImage = idslW->BaseOfImage;
529             idsl.CheckSum = idslW->CheckSum;
530             idsl.TimeDateStamp = idslW->TimeDateStamp;
531             WideCharToMultiByte(CP_ACP, 0, idslW->FileName, -1,
532                                 idsl.FileName, sizeof(idsl.FileName), NULL, NULL);
533             idsl.Reparse = idslW->Reparse;
534             data = &idsl;
535             break;
536         case CBA_DUPLICATE_SYMBOL:
537         case CBA_EVENT:
538         case CBA_READ_MEMORY:
539         default:
540             FIXME("No mapping for action %u\n", action);
541             return FALSE;
542         }
543     }
544     return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user);
545 }
546
547 /******************************************************************
548  *              sym_register_cb
549  *
550  * Helper for registering a callback.
551  */
552 static BOOL sym_register_cb(HANDLE hProcess, 
553                             PSYMBOL_REGISTERED_CALLBACK64 cb,
554                             DWORD64 user, BOOL unicode)
555 {
556     struct process* pcs = process_find_by_handle(hProcess);
557
558     if (!pcs) return FALSE;
559     pcs->reg_cb = cb;
560     pcs->reg_is_unicode = unicode;
561     pcs->reg_user = user;
562
563     return TRUE;
564 }
565
566 /***********************************************************************
567  *              SymRegisterCallback (DBGHELP.@)
568  */
569 BOOL WINAPI SymRegisterCallback(HANDLE hProcess, 
570                                 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
571                                 PVOID UserContext)
572 {
573     DWORD64 tmp = ((ULONGLONG)(DWORD)CallbackFunction << 32) | (DWORD)UserContext;
574     TRACE("(%p, %p, %p)\n", 
575           hProcess, CallbackFunction, UserContext);
576     return sym_register_cb(hProcess, reg_cb64to32, tmp, FALSE);
577 }
578
579 /***********************************************************************
580  *              SymRegisterCallback64 (DBGHELP.@)
581  */
582 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess, 
583                                   PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
584                                   ULONG64 UserContext)
585 {
586     TRACE("(%p, %p, %s)\n", 
587           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
588     return sym_register_cb(hProcess, CallbackFunction, UserContext, FALSE);
589 }
590
591 /***********************************************************************
592  *              SymRegisterCallbackW64 (DBGHELP.@)
593  */
594 BOOL WINAPI SymRegisterCallbackW64(HANDLE hProcess, 
595                                    PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
596                                    ULONG64 UserContext)
597 {
598     TRACE("(%p, %p, %s)\n", 
599           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
600     return sym_register_cb(hProcess, CallbackFunction, UserContext, TRUE);
601 }
602
603 /* This is imagehlp version not dbghelp !! */
604 static API_VERSION api_version = { 4, 0, 2, 0 };
605
606 /***********************************************************************
607  *           ImagehlpApiVersion (DBGHELP.@)
608  */
609 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
610 {
611     return &api_version;
612 }
613
614 /***********************************************************************
615  *           ImagehlpApiVersionEx (DBGHELP.@)
616  */
617 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
618 {
619     if (!AppVersion) return NULL;
620
621     AppVersion->MajorVersion = api_version.MajorVersion;
622     AppVersion->MinorVersion = api_version.MinorVersion;
623     AppVersion->Revision = api_version.Revision;
624     AppVersion->Reserved = api_version.Reserved;
625
626     return AppVersion;
627 }
628
629 /******************************************************************
630  *              ExtensionApiVersion (DBGHELP.@)
631  */
632 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
633 {
634     static EXT_API_VERSION      eav = {5, 5, 5, 0};
635     return &eav;
636 }
637
638 /******************************************************************
639  *              WinDbgExtensionDllInit (DBGHELP.@)
640  */
641 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
642                                    unsigned short major, unsigned short minor)
643 {
644 }