shdoclc: Remove a space before an ellipsis in the Italian translation.
[wine/hramrach.git] / dlls / dbghelp / dbghelp.c
blobfe7d892ba1b1f0f0f68d8baac753ef5882e333e6
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;
156 static struct cpu* dbghelp_cpus[] = {&cpu_i386, &cpu_x86_64, &cpu_ppc, &cpu_sparc, 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 #else
167 #error define support for your CPU
168 #endif
171 struct cpu* cpu_find(DWORD machine)
173 struct cpu** cpu;
175 for (cpu = dbghelp_cpus ; *cpu; cpu++)
177 if (cpu[0]->machine == machine) return cpu[0];
179 return NULL;
182 /******************************************************************
183 * SymSetSearchPathW (DBGHELP.@)
186 BOOL WINAPI SymSetSearchPathW(HANDLE hProcess, PCWSTR searchPath)
188 struct process* pcs = process_find_by_handle(hProcess);
190 if (!pcs) return FALSE;
191 if (!searchPath) return FALSE;
193 HeapFree(GetProcessHeap(), 0, pcs->search_path);
194 pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
195 (lstrlenW(searchPath) + 1) * sizeof(WCHAR)),
196 searchPath);
197 return TRUE;
200 /******************************************************************
201 * SymSetSearchPath (DBGHELP.@)
204 BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PCSTR searchPath)
206 BOOL ret = FALSE;
207 unsigned len;
208 WCHAR* sp;
210 len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0);
211 if ((sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
213 MultiByteToWideChar(CP_ACP, 0, searchPath, -1, sp, len);
215 ret = SymSetSearchPathW(hProcess, sp);
216 HeapFree(GetProcessHeap(), 0, sp);
218 return ret;
221 /***********************************************************************
222 * SymGetSearchPathW (DBGHELP.@)
224 BOOL WINAPI SymGetSearchPathW(HANDLE hProcess, PWSTR szSearchPath,
225 DWORD SearchPathLength)
227 struct process* pcs = process_find_by_handle(hProcess);
228 if (!pcs) return FALSE;
230 lstrcpynW(szSearchPath, pcs->search_path, SearchPathLength);
231 return TRUE;
234 /***********************************************************************
235 * SymGetSearchPath (DBGHELP.@)
237 BOOL WINAPI SymGetSearchPath(HANDLE hProcess, PSTR szSearchPath,
238 DWORD SearchPathLength)
240 WCHAR* buffer = HeapAlloc(GetProcessHeap(), 0, SearchPathLength * sizeof(WCHAR));
241 BOOL ret = FALSE;
243 if (buffer)
245 ret = SymGetSearchPathW(hProcess, buffer, SearchPathLength);
246 if (ret)
247 WideCharToMultiByte(CP_ACP, 0, buffer, SearchPathLength,
248 szSearchPath, SearchPathLength, NULL, NULL);
249 HeapFree(GetProcessHeap(), 0, buffer);
251 return ret;
254 /******************************************************************
255 * invade_process
257 * SymInitialize helper: loads in dbghelp all known (and loaded modules)
258 * this assumes that hProcess is a handle on a valid process
260 static BOOL WINAPI process_invade_cb(PCWSTR name, ULONG64 base, ULONG size, PVOID user)
262 WCHAR tmp[MAX_PATH];
263 HANDLE hProcess = user;
265 if (!GetModuleFileNameExW(hProcess, (HMODULE)(DWORD_PTR)base,
266 tmp, sizeof(tmp) / sizeof(WCHAR)))
267 lstrcpynW(tmp, name, sizeof(tmp) / sizeof(WCHAR));
269 SymLoadModuleExW(hProcess, 0, tmp, name, base, size, NULL, 0);
270 return TRUE;
273 /******************************************************************
274 * check_live_target
277 static BOOL check_live_target(struct process* pcs)
279 if (!GetProcessId(pcs->handle)) return FALSE;
280 if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL, 0)) return FALSE;
281 if (!elf_read_wine_loader_dbg_info(pcs))
282 macho_read_wine_loader_dbg_info(pcs);
283 return TRUE;
286 /******************************************************************
287 * SymInitializeW (DBGHELP.@)
289 * The initialisation of a dbghelp's context.
290 * Note that hProcess doesn't need to be a valid process handle (except
291 * when fInvadeProcess is TRUE).
292 * Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries
293 * containing PE (and NE) module(s), here's how we handle it:
294 * - we load every module (ELF, NE, PE) passed in SymLoadModule
295 * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
296 * synchronization: hProcess should be a valid process handle, and we hook
297 * ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
298 * our internal ELF modules representation (loading / unloading). This way,
299 * we'll pair every loaded builtin PE module with its ELF counterpart (and
300 * access its debug information).
301 * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the
302 * hProcess refers to a running process. We use some heuristics here, so YMMV.
303 * If we detect a live target, then we get the same handling as if
304 * fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
305 * we won't be able to make the peering between a builtin PE module and its ELF
306 * counterpart. Hence we won't be able to provide the requested debug
307 * information. We'll however be able to load native PE modules (and their
308 * debug information) without any trouble.
309 * Note also that this scheme can be intertwined with the deferred loading
310 * mechanism (ie only load the debug information when we actually need it).
312 BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess)
314 struct process* pcs;
316 TRACE("(%p %s %u)\n", hProcess, debugstr_w(UserSearchPath), fInvadeProcess);
318 if (process_find_by_handle(hProcess)){
319 WARN("the symbols for this process have already been initialized!\n");
321 /* MSDN says to only call this function once unless SymCleanup() has been called since the last call.
322 It also says to call SymRefreshModuleList() instead if you just want the module list refreshed.
323 Native still returns TRUE even if the process has already been initialized. */
324 return TRUE;
327 pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
328 if (!pcs) return FALSE;
330 pcs->handle = hProcess;
332 if (UserSearchPath)
334 pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
335 (lstrlenW(UserSearchPath) + 1) * sizeof(WCHAR)),
336 UserSearchPath);
338 else
340 unsigned size;
341 unsigned len;
342 static const WCHAR sym_path[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0};
343 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};
345 pcs->search_path = HeapAlloc(GetProcessHeap(), 0, (len = MAX_PATH) * sizeof(WCHAR));
346 while ((size = GetCurrentDirectoryW(len, pcs->search_path)) >= len)
347 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (len *= 2) * sizeof(WCHAR));
348 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1) * sizeof(WCHAR));
350 len = GetEnvironmentVariableW(sym_path, NULL, 0);
351 if (len)
353 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
354 pcs->search_path[size] = ';';
355 GetEnvironmentVariableW(sym_path, pcs->search_path + size + 1, len);
356 size += 1 + len;
358 len = GetEnvironmentVariableW(alt_sym_path, NULL, 0);
359 if (len)
361 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
362 pcs->search_path[size] = ';';
363 GetEnvironmentVariableW(alt_sym_path, pcs->search_path + size + 1, len);
367 pcs->lmodules = NULL;
368 pcs->dbg_hdr_addr = 0;
369 pcs->next = process_first;
370 process_first = pcs;
372 if (check_live_target(pcs))
374 if (fInvadeProcess)
375 EnumerateLoadedModulesW64(hProcess, process_invade_cb, hProcess);
376 elf_synchronize_module_list(pcs);
377 macho_synchronize_module_list(pcs);
379 else if (fInvadeProcess)
381 SymCleanup(hProcess);
382 SetLastError(ERROR_INVALID_PARAMETER);
383 return FALSE;
386 return TRUE;
389 /******************************************************************
390 * SymInitialize (DBGHELP.@)
394 BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess)
396 WCHAR* sp = NULL;
397 BOOL ret;
399 if (UserSearchPath)
401 unsigned len;
403 len = MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, NULL, 0);
404 sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
405 MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, sp, len);
408 ret = SymInitializeW(hProcess, sp, fInvadeProcess);
409 HeapFree(GetProcessHeap(), 0, sp);
410 return ret;
413 /******************************************************************
414 * SymCleanup (DBGHELP.@)
417 BOOL WINAPI SymCleanup(HANDLE hProcess)
419 struct process** ppcs;
420 struct process* next;
422 for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
424 if ((*ppcs)->handle == hProcess)
426 while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
428 HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
429 next = (*ppcs)->next;
430 HeapFree(GetProcessHeap(), 0, *ppcs);
431 *ppcs = next;
432 return TRUE;
436 ERR("this process has not had SymInitialize() called for it!\n");
437 return FALSE;
440 /******************************************************************
441 * SymSetOptions (DBGHELP.@)
444 DWORD WINAPI SymSetOptions(DWORD opts)
446 struct process* pcs;
448 for (pcs = process_first; pcs; pcs = pcs->next)
450 pcs_callback(pcs, CBA_SET_OPTIONS, &opts);
452 return dbghelp_options = opts;
455 /******************************************************************
456 * SymGetOptions (DBGHELP.@)
459 DWORD WINAPI SymGetOptions(void)
461 return dbghelp_options;
464 /******************************************************************
465 * SymSetParentWindow (DBGHELP.@)
468 BOOL WINAPI SymSetParentWindow(HWND hwnd)
470 /* Save hwnd so it can be used as parent window */
471 FIXME("(%p): stub\n", hwnd);
472 return TRUE;
475 /******************************************************************
476 * SymSetContext (DBGHELP.@)
479 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
480 PIMAGEHLP_CONTEXT Context)
482 struct process* pcs = process_find_by_handle(hProcess);
483 if (!pcs) return FALSE;
485 if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset &&
486 pcs->ctx_frame.FrameOffset == StackFrame->FrameOffset &&
487 pcs->ctx_frame.StackOffset == StackFrame->StackOffset)
489 TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
490 wine_dbgstr_longlong(pcs->ctx_frame.ReturnOffset),
491 wine_dbgstr_longlong(pcs->ctx_frame.FrameOffset),
492 wine_dbgstr_longlong(pcs->ctx_frame.StackOffset));
493 pcs->ctx_frame.InstructionOffset = StackFrame->InstructionOffset;
494 SetLastError(ERROR_ACCESS_DENIED); /* latest MSDN says ERROR_SUCCESS */
495 return FALSE;
498 pcs->ctx_frame = *StackFrame;
499 /* MSDN states that Context is not (no longer?) used */
500 return TRUE;
503 /******************************************************************
504 * reg_cb64to32 (internal)
506 * Registered callback for converting information from 64 bit to 32 bit
508 static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user)
510 struct process* pcs = process_find_by_handle(hProcess);
511 void* data32;
512 IMAGEHLP_DEFERRED_SYMBOL_LOAD64* idsl64;
513 IMAGEHLP_DEFERRED_SYMBOL_LOAD idsl;
515 if (!pcs) return FALSE;
516 switch (action)
518 case CBA_DEBUG_INFO:
519 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
520 case CBA_SET_OPTIONS:
521 case CBA_SYMBOLS_UNLOADED:
522 data32 = (void*)(DWORD_PTR)data;
523 break;
524 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
525 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
526 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
527 case CBA_DEFERRED_SYMBOL_LOAD_START:
528 idsl64 = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD_PTR)data;
529 if (!validate_addr64(idsl64->BaseOfImage))
530 return FALSE;
531 idsl.SizeOfStruct = sizeof(idsl);
532 idsl.BaseOfImage = (DWORD)idsl64->BaseOfImage;
533 idsl.CheckSum = idsl64->CheckSum;
534 idsl.TimeDateStamp = idsl64->TimeDateStamp;
535 memcpy(idsl.FileName, idsl64->FileName, sizeof(idsl.FileName));
536 idsl.Reparse = idsl64->Reparse;
537 data32 = &idsl;
538 break;
539 case CBA_DUPLICATE_SYMBOL:
540 case CBA_EVENT:
541 case CBA_READ_MEMORY:
542 default:
543 FIXME("No mapping for action %u\n", action);
544 return FALSE;
546 return pcs->reg_cb32(hProcess, action, data32, (PVOID)(DWORD_PTR)user);
549 /******************************************************************
550 * pcs_callback (internal)
552 BOOL pcs_callback(const struct process* pcs, ULONG action, void* data)
554 TRACE("%p %u %p\n", pcs, action, data);
556 if (!pcs->reg_cb) return FALSE;
557 if (!pcs->reg_is_unicode)
559 IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
560 IMAGEHLP_DEFERRED_SYMBOL_LOADW64* idslW;
562 switch (action)
564 case CBA_DEBUG_INFO:
565 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
566 case CBA_SET_OPTIONS:
567 case CBA_SYMBOLS_UNLOADED:
568 break;
569 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
570 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
571 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
572 case CBA_DEFERRED_SYMBOL_LOAD_START:
573 idslW = data;
574 idsl.SizeOfStruct = sizeof(idsl);
575 idsl.BaseOfImage = idslW->BaseOfImage;
576 idsl.CheckSum = idslW->CheckSum;
577 idsl.TimeDateStamp = idslW->TimeDateStamp;
578 WideCharToMultiByte(CP_ACP, 0, idslW->FileName, -1,
579 idsl.FileName, sizeof(idsl.FileName), NULL, NULL);
580 idsl.Reparse = idslW->Reparse;
581 data = &idsl;
582 break;
583 case CBA_DUPLICATE_SYMBOL:
584 case CBA_EVENT:
585 case CBA_READ_MEMORY:
586 default:
587 FIXME("No mapping for action %u\n", action);
588 return FALSE;
591 return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user);
594 /******************************************************************
595 * sym_register_cb
597 * Helper for registering a callback.
599 static BOOL sym_register_cb(HANDLE hProcess,
600 PSYMBOL_REGISTERED_CALLBACK64 cb,
601 PSYMBOL_REGISTERED_CALLBACK cb32,
602 DWORD64 user, BOOL unicode)
604 struct process* pcs = process_find_by_handle(hProcess);
606 if (!pcs) return FALSE;
607 pcs->reg_cb = cb;
608 pcs->reg_cb32 = cb32;
609 pcs->reg_is_unicode = unicode;
610 pcs->reg_user = user;
612 return TRUE;
615 /***********************************************************************
616 * SymRegisterCallback (DBGHELP.@)
618 BOOL WINAPI SymRegisterCallback(HANDLE hProcess,
619 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
620 PVOID UserContext)
622 TRACE("(%p, %p, %p)\n",
623 hProcess, CallbackFunction, UserContext);
624 return sym_register_cb(hProcess, reg_cb64to32, CallbackFunction, (DWORD_PTR)UserContext, FALSE);
627 /***********************************************************************
628 * SymRegisterCallback64 (DBGHELP.@)
630 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess,
631 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
632 ULONG64 UserContext)
634 TRACE("(%p, %p, %s)\n",
635 hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
636 return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, FALSE);
639 /***********************************************************************
640 * SymRegisterCallbackW64 (DBGHELP.@)
642 BOOL WINAPI SymRegisterCallbackW64(HANDLE hProcess,
643 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
644 ULONG64 UserContext)
646 TRACE("(%p, %p, %s)\n",
647 hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
648 return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, TRUE);
651 /* This is imagehlp version not dbghelp !! */
652 static API_VERSION api_version = { 4, 0, 2, 0 };
654 /***********************************************************************
655 * ImagehlpApiVersion (DBGHELP.@)
657 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
659 return &api_version;
662 /***********************************************************************
663 * ImagehlpApiVersionEx (DBGHELP.@)
665 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
667 if (!AppVersion) return NULL;
669 AppVersion->MajorVersion = api_version.MajorVersion;
670 AppVersion->MinorVersion = api_version.MinorVersion;
671 AppVersion->Revision = api_version.Revision;
672 AppVersion->Reserved = api_version.Reserved;
674 return AppVersion;
677 /******************************************************************
678 * ExtensionApiVersion (DBGHELP.@)
680 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
682 static EXT_API_VERSION eav = {5, 5, 5, 0};
683 return &eav;
686 /******************************************************************
687 * WinDbgExtensionDllInit (DBGHELP.@)
689 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
690 unsigned short major, unsigned short minor)