regedit: An English (United States) spelling fix.
[wine/multimedia.git] / dlls / dbghelp / dbghelp.c
blob358902bfbaa91da669cff88ab1b390799981e0ad
1 /*
2 * File dbghelp.c - generic routines (process) for dbghelp DLL
4 * Copyright (C) 2004, Eric Pouech
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.
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.
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
21 #include "config.h"
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"
30 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
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
67 unsigned dbghelp_options = SYMOPT_UNDNAME;
68 HANDLE hMsvcrt = NULL;
70 /***********************************************************************
71 * DllMain (DEBUGHLP.@)
73 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
75 switch (fdwReason)
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;
85 return TRUE;
88 static struct process* process_first /* = NULL */;
90 /******************************************************************
91 * process_find_by_handle
94 struct process* process_find_by_handle(HANDLE hProcess)
96 struct process* p;
98 for (p = process_first; p && p->handle != hProcess; p = p->next);
99 if (!p) SetLastError(ERROR_INVALID_HANDLE);
100 return p;
103 /******************************************************************
104 * validate_addr64 (internal)
107 BOOL validate_addr64(DWORD64 addr)
109 if (sizeof(void*) == sizeof(int) && (addr >> 32))
111 FIXME("Unsupported address %s\n", wine_dbgstr_longlong(addr));
112 SetLastError(ERROR_INVALID_PARAMETER);
113 return FALSE;
115 return TRUE;
118 /******************************************************************
119 * fetch_buffer
121 * Ensures process' internal buffer is large enough.
123 void* fetch_buffer(struct process* pcs, unsigned size)
125 if (size > pcs->buffer_size)
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;
133 return pcs->buffer;
136 const char* wine_dbgstr_addr(const ADDRESS64* addr)
138 if (!addr) return "(null)";
139 switch (addr->Mode)
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";
154 extern struct cpu cpu_i386, cpu_x86_64, cpu_ppc, cpu_sparc, cpu_arm;
156 static struct cpu* dbghelp_cpus[] = {&cpu_i386, &cpu_x86_64, &cpu_ppc, &cpu_sparc, &cpu_arm, 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 #elif defined(__sparc__)
165 &cpu_sparc
166 #elif defined(__arm__)
167 &cpu_arm
168 #else
169 #error define support for your CPU
170 #endif
173 struct cpu* cpu_find(DWORD machine)
175 struct cpu** cpu;
177 for (cpu = dbghelp_cpus ; *cpu; cpu++)
179 if (cpu[0]->machine == machine) return cpu[0];
181 return NULL;
184 /******************************************************************
185 * SymSetSearchPathW (DBGHELP.@)
188 BOOL WINAPI SymSetSearchPathW(HANDLE hProcess, PCWSTR searchPath)
190 struct process* pcs = process_find_by_handle(hProcess);
192 if (!pcs) return FALSE;
193 if (!searchPath) return FALSE;
195 HeapFree(GetProcessHeap(), 0, pcs->search_path);
196 pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
197 (lstrlenW(searchPath) + 1) * sizeof(WCHAR)),
198 searchPath);
199 return TRUE;
202 /******************************************************************
203 * SymSetSearchPath (DBGHELP.@)
206 BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PCSTR searchPath)
208 BOOL ret = FALSE;
209 unsigned len;
210 WCHAR* sp;
212 len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0);
213 if ((sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
215 MultiByteToWideChar(CP_ACP, 0, searchPath, -1, sp, len);
217 ret = SymSetSearchPathW(hProcess, sp);
218 HeapFree(GetProcessHeap(), 0, sp);
220 return ret;
223 /***********************************************************************
224 * SymGetSearchPathW (DBGHELP.@)
226 BOOL WINAPI SymGetSearchPathW(HANDLE hProcess, PWSTR szSearchPath,
227 DWORD SearchPathLength)
229 struct process* pcs = process_find_by_handle(hProcess);
230 if (!pcs) return FALSE;
232 lstrcpynW(szSearchPath, pcs->search_path, SearchPathLength);
233 return TRUE;
236 /***********************************************************************
237 * SymGetSearchPath (DBGHELP.@)
239 BOOL WINAPI SymGetSearchPath(HANDLE hProcess, PSTR szSearchPath,
240 DWORD SearchPathLength)
242 WCHAR* buffer = HeapAlloc(GetProcessHeap(), 0, SearchPathLength * sizeof(WCHAR));
243 BOOL ret = FALSE;
245 if (buffer)
247 ret = SymGetSearchPathW(hProcess, buffer, SearchPathLength);
248 if (ret)
249 WideCharToMultiByte(CP_ACP, 0, buffer, SearchPathLength,
250 szSearchPath, SearchPathLength, NULL, NULL);
251 HeapFree(GetProcessHeap(), 0, buffer);
253 return ret;
256 /******************************************************************
257 * invade_process
259 * SymInitialize helper: loads in dbghelp all known (and loaded modules)
260 * this assumes that hProcess is a handle on a valid process
262 static BOOL WINAPI process_invade_cb(PCWSTR name, ULONG64 base, ULONG size, PVOID user)
264 WCHAR tmp[MAX_PATH];
265 HANDLE hProcess = user;
267 if (!GetModuleFileNameExW(hProcess, (HMODULE)(DWORD_PTR)base,
268 tmp, sizeof(tmp) / sizeof(WCHAR)))
269 lstrcpynW(tmp, name, sizeof(tmp) / sizeof(WCHAR));
271 SymLoadModuleExW(hProcess, 0, tmp, name, base, size, NULL, 0);
272 return TRUE;
275 /******************************************************************
276 * check_live_target
279 static BOOL check_live_target(struct process* pcs)
281 if (!GetProcessId(pcs->handle)) return FALSE;
282 if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL, 0)) return FALSE;
283 if (!elf_read_wine_loader_dbg_info(pcs))
284 macho_read_wine_loader_dbg_info(pcs);
285 return TRUE;
288 /******************************************************************
289 * SymInitializeW (DBGHELP.@)
291 * The initialisation of a dbghelp's context.
292 * Note that hProcess doesn't need to be a valid process handle (except
293 * when fInvadeProcess is TRUE).
294 * Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries
295 * containing PE (and NE) module(s), here's how we handle it:
296 * - we load every module (ELF, NE, PE) passed in SymLoadModule
297 * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
298 * synchronization: hProcess should be a valid process handle, and we hook
299 * ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
300 * our internal ELF modules representation (loading / unloading). This way,
301 * we'll pair every loaded builtin PE module with its ELF counterpart (and
302 * access its debug information).
303 * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the
304 * hProcess refers to a running process. We use some heuristics here, so YMMV.
305 * If we detect a live target, then we get the same handling as if
306 * fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
307 * we won't be able to make the peering between a builtin PE module and its ELF
308 * counterpart. Hence we won't be able to provide the requested debug
309 * information. We'll however be able to load native PE modules (and their
310 * debug information) without any trouble.
311 * Note also that this scheme can be intertwined with the deferred loading
312 * mechanism (ie only load the debug information when we actually need it).
314 BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess)
316 struct process* pcs;
318 TRACE("(%p %s %u)\n", hProcess, debugstr_w(UserSearchPath), fInvadeProcess);
320 if (process_find_by_handle(hProcess)){
321 WARN("the symbols for this process have already been initialized!\n");
323 /* MSDN says to only call this function once unless SymCleanup() has been called since the last call.
324 It also says to call SymRefreshModuleList() instead if you just want the module list refreshed.
325 Native still returns TRUE even if the process has already been initialized. */
326 return TRUE;
329 pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
330 if (!pcs) return FALSE;
332 pcs->handle = hProcess;
334 if (UserSearchPath)
336 pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
337 (lstrlenW(UserSearchPath) + 1) * sizeof(WCHAR)),
338 UserSearchPath);
340 else
342 unsigned size;
343 unsigned len;
344 static const WCHAR sym_path[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0};
345 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};
347 pcs->search_path = HeapAlloc(GetProcessHeap(), 0, (len = MAX_PATH) * sizeof(WCHAR));
348 while ((size = GetCurrentDirectoryW(len, pcs->search_path)) >= len)
349 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (len *= 2) * sizeof(WCHAR));
350 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1) * sizeof(WCHAR));
352 len = GetEnvironmentVariableW(sym_path, NULL, 0);
353 if (len)
355 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
356 pcs->search_path[size] = ';';
357 GetEnvironmentVariableW(sym_path, pcs->search_path + size + 1, len);
358 size += 1 + len;
360 len = GetEnvironmentVariableW(alt_sym_path, NULL, 0);
361 if (len)
363 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
364 pcs->search_path[size] = ';';
365 GetEnvironmentVariableW(alt_sym_path, pcs->search_path + size + 1, len);
369 pcs->lmodules = NULL;
370 pcs->dbg_hdr_addr = 0;
371 pcs->next = process_first;
372 process_first = pcs;
374 if (check_live_target(pcs))
376 if (fInvadeProcess)
377 EnumerateLoadedModulesW64(hProcess, process_invade_cb, hProcess);
378 elf_synchronize_module_list(pcs);
379 macho_synchronize_module_list(pcs);
381 else if (fInvadeProcess)
383 SymCleanup(hProcess);
384 SetLastError(ERROR_INVALID_PARAMETER);
385 return FALSE;
388 return TRUE;
391 /******************************************************************
392 * SymInitialize (DBGHELP.@)
396 BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess)
398 WCHAR* sp = NULL;
399 BOOL ret;
401 if (UserSearchPath)
403 unsigned len;
405 len = MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, NULL, 0);
406 sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
407 MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, sp, len);
410 ret = SymInitializeW(hProcess, sp, fInvadeProcess);
411 HeapFree(GetProcessHeap(), 0, sp);
412 return ret;
415 /******************************************************************
416 * SymCleanup (DBGHELP.@)
419 BOOL WINAPI SymCleanup(HANDLE hProcess)
421 struct process** ppcs;
422 struct process* next;
424 for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
426 if ((*ppcs)->handle == hProcess)
428 while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
430 HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
431 next = (*ppcs)->next;
432 HeapFree(GetProcessHeap(), 0, *ppcs);
433 *ppcs = next;
434 return TRUE;
438 ERR("this process has not had SymInitialize() called for it!\n");
439 return FALSE;
442 /******************************************************************
443 * SymSetOptions (DBGHELP.@)
446 DWORD WINAPI SymSetOptions(DWORD opts)
448 struct process* pcs;
450 for (pcs = process_first; pcs; pcs = pcs->next)
452 pcs_callback(pcs, CBA_SET_OPTIONS, &opts);
454 return dbghelp_options = opts;
457 /******************************************************************
458 * SymGetOptions (DBGHELP.@)
461 DWORD WINAPI SymGetOptions(void)
463 return dbghelp_options;
466 /******************************************************************
467 * SymSetParentWindow (DBGHELP.@)
470 BOOL WINAPI SymSetParentWindow(HWND hwnd)
472 /* Save hwnd so it can be used as parent window */
473 FIXME("(%p): stub\n", hwnd);
474 return TRUE;
477 /******************************************************************
478 * SymSetContext (DBGHELP.@)
481 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
482 PIMAGEHLP_CONTEXT Context)
484 struct process* pcs = process_find_by_handle(hProcess);
485 if (!pcs) return FALSE;
487 if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset &&
488 pcs->ctx_frame.FrameOffset == StackFrame->FrameOffset &&
489 pcs->ctx_frame.StackOffset == StackFrame->StackOffset)
491 TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
492 wine_dbgstr_longlong(pcs->ctx_frame.ReturnOffset),
493 wine_dbgstr_longlong(pcs->ctx_frame.FrameOffset),
494 wine_dbgstr_longlong(pcs->ctx_frame.StackOffset));
495 pcs->ctx_frame.InstructionOffset = StackFrame->InstructionOffset;
496 SetLastError(ERROR_ACCESS_DENIED); /* latest MSDN says ERROR_SUCCESS */
497 return FALSE;
500 pcs->ctx_frame = *StackFrame;
501 /* MSDN states that Context is not (no longer?) used */
502 return TRUE;
505 /******************************************************************
506 * reg_cb64to32 (internal)
508 * Registered callback for converting information from 64 bit to 32 bit
510 static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user)
512 struct process* pcs = process_find_by_handle(hProcess);
513 void* data32;
514 IMAGEHLP_DEFERRED_SYMBOL_LOAD64* idsl64;
515 IMAGEHLP_DEFERRED_SYMBOL_LOAD idsl;
517 if (!pcs) return FALSE;
518 switch (action)
520 case CBA_DEBUG_INFO:
521 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
522 case CBA_SET_OPTIONS:
523 case CBA_SYMBOLS_UNLOADED:
524 data32 = (void*)(DWORD_PTR)data;
525 break;
526 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
527 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
528 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
529 case CBA_DEFERRED_SYMBOL_LOAD_START:
530 idsl64 = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD_PTR)data;
531 if (!validate_addr64(idsl64->BaseOfImage))
532 return FALSE;
533 idsl.SizeOfStruct = sizeof(idsl);
534 idsl.BaseOfImage = (DWORD)idsl64->BaseOfImage;
535 idsl.CheckSum = idsl64->CheckSum;
536 idsl.TimeDateStamp = idsl64->TimeDateStamp;
537 memcpy(idsl.FileName, idsl64->FileName, sizeof(idsl.FileName));
538 idsl.Reparse = idsl64->Reparse;
539 data32 = &idsl;
540 break;
541 case CBA_DUPLICATE_SYMBOL:
542 case CBA_EVENT:
543 case CBA_READ_MEMORY:
544 default:
545 FIXME("No mapping for action %u\n", action);
546 return FALSE;
548 return pcs->reg_cb32(hProcess, action, data32, (PVOID)(DWORD_PTR)user);
551 /******************************************************************
552 * pcs_callback (internal)
554 BOOL pcs_callback(const struct process* pcs, ULONG action, void* data)
556 IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
558 TRACE("%p %u %p\n", pcs, action, data);
560 if (!pcs->reg_cb) return FALSE;
561 if (!pcs->reg_is_unicode)
563 IMAGEHLP_DEFERRED_SYMBOL_LOADW64* idslW;
565 switch (action)
567 case CBA_DEBUG_INFO:
568 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
569 case CBA_SET_OPTIONS:
570 case CBA_SYMBOLS_UNLOADED:
571 break;
572 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
573 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
574 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
575 case CBA_DEFERRED_SYMBOL_LOAD_START:
576 idslW = data;
577 idsl.SizeOfStruct = sizeof(idsl);
578 idsl.BaseOfImage = idslW->BaseOfImage;
579 idsl.CheckSum = idslW->CheckSum;
580 idsl.TimeDateStamp = idslW->TimeDateStamp;
581 WideCharToMultiByte(CP_ACP, 0, idslW->FileName, -1,
582 idsl.FileName, sizeof(idsl.FileName), NULL, NULL);
583 idsl.Reparse = idslW->Reparse;
584 data = &idsl;
585 break;
586 case CBA_DUPLICATE_SYMBOL:
587 case CBA_EVENT:
588 case CBA_READ_MEMORY:
589 default:
590 FIXME("No mapping for action %u\n", action);
591 return FALSE;
594 return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user);
597 /******************************************************************
598 * sym_register_cb
600 * Helper for registering a callback.
602 static BOOL sym_register_cb(HANDLE hProcess,
603 PSYMBOL_REGISTERED_CALLBACK64 cb,
604 PSYMBOL_REGISTERED_CALLBACK cb32,
605 DWORD64 user, BOOL unicode)
607 struct process* pcs = process_find_by_handle(hProcess);
609 if (!pcs) return FALSE;
610 pcs->reg_cb = cb;
611 pcs->reg_cb32 = cb32;
612 pcs->reg_is_unicode = unicode;
613 pcs->reg_user = user;
615 return TRUE;
618 /***********************************************************************
619 * SymRegisterCallback (DBGHELP.@)
621 BOOL WINAPI SymRegisterCallback(HANDLE hProcess,
622 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
623 PVOID UserContext)
625 TRACE("(%p, %p, %p)\n",
626 hProcess, CallbackFunction, UserContext);
627 return sym_register_cb(hProcess, reg_cb64to32, CallbackFunction, (DWORD_PTR)UserContext, FALSE);
630 /***********************************************************************
631 * SymRegisterCallback64 (DBGHELP.@)
633 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess,
634 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
635 ULONG64 UserContext)
637 TRACE("(%p, %p, %s)\n",
638 hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
639 return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, FALSE);
642 /***********************************************************************
643 * SymRegisterCallbackW64 (DBGHELP.@)
645 BOOL WINAPI SymRegisterCallbackW64(HANDLE hProcess,
646 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
647 ULONG64 UserContext)
649 TRACE("(%p, %p, %s)\n",
650 hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
651 return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, TRUE);
654 /* This is imagehlp version not dbghelp !! */
655 static API_VERSION api_version = { 4, 0, 2, 0 };
657 /***********************************************************************
658 * ImagehlpApiVersion (DBGHELP.@)
660 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
662 return &api_version;
665 /***********************************************************************
666 * ImagehlpApiVersionEx (DBGHELP.@)
668 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
670 if (!AppVersion) return NULL;
672 AppVersion->MajorVersion = api_version.MajorVersion;
673 AppVersion->MinorVersion = api_version.MinorVersion;
674 AppVersion->Revision = api_version.Revision;
675 AppVersion->Reserved = api_version.Reserved;
677 return AppVersion;
680 /******************************************************************
681 * ExtensionApiVersion (DBGHELP.@)
683 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
685 static EXT_API_VERSION eav = {5, 5, 5, 0};
686 return &eav;
689 /******************************************************************
690 * WinDbgExtensionDllInit (DBGHELP.@)
692 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
693 unsigned short major, unsigned short minor)