kernel32: GetLongPathName should fail when called with a wildcard.
[wine.git] / dlls / dbghelp / dbghelp.c
bloba085cd140ef75a09fe1e4e83e087e5b94136a75f
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;
69 static struct process* process_first /* = NULL */;
71 /******************************************************************
72 * process_find_by_handle
75 struct process* process_find_by_handle(HANDLE hProcess)
77 struct process* p;
79 for (p = process_first; p && p->handle != hProcess; p = p->next);
80 if (!p) SetLastError(ERROR_INVALID_HANDLE);
81 return p;
84 /******************************************************************
85 * validate_addr64 (internal)
88 BOOL validate_addr64(DWORD64 addr)
90 if (sizeof(void*) == sizeof(int) && (addr >> 32))
92 FIXME("Unsupported address %s\n", wine_dbgstr_longlong(addr));
93 SetLastError(ERROR_INVALID_PARAMETER);
94 return FALSE;
96 return TRUE;
99 /******************************************************************
100 * fetch_buffer
102 * Ensures process' internal buffer is large enough.
104 void* fetch_buffer(struct process* pcs, unsigned size)
106 if (size > pcs->buffer_size)
108 if (pcs->buffer)
109 pcs->buffer = HeapReAlloc(GetProcessHeap(), 0, pcs->buffer, size);
110 else
111 pcs->buffer = HeapAlloc(GetProcessHeap(), 0, size);
112 pcs->buffer_size = (pcs->buffer) ? size : 0;
114 return pcs->buffer;
117 const char* wine_dbgstr_addr(const ADDRESS64* addr)
119 if (!addr) return "(null)";
120 switch (addr->Mode)
122 case AddrModeFlat:
123 return wine_dbg_sprintf("flat<%s>", wine_dbgstr_longlong(addr->Offset));
124 case AddrMode1616:
125 return wine_dbg_sprintf("1616<%04x:%04x>", addr->Segment, (DWORD)addr->Offset);
126 case AddrMode1632:
127 return wine_dbg_sprintf("1632<%04x:%08x>", addr->Segment, (DWORD)addr->Offset);
128 case AddrModeReal:
129 return wine_dbg_sprintf("real<%04x:%04x>", addr->Segment, (DWORD)addr->Offset);
130 default:
131 return "unknown";
135 extern struct cpu cpu_i386, cpu_x86_64, cpu_ppc, cpu_arm, cpu_arm64;
137 static struct cpu* dbghelp_cpus[] = {&cpu_i386, &cpu_x86_64, &cpu_ppc, &cpu_arm, &cpu_arm64, NULL};
138 struct cpu* dbghelp_current_cpu =
139 #if defined(__i386__)
140 &cpu_i386
141 #elif defined(__x86_64__)
142 &cpu_x86_64
143 #elif defined(__powerpc__)
144 &cpu_ppc
145 #elif defined(__arm__)
146 &cpu_arm
147 #elif defined(__aarch64__)
148 &cpu_arm64
149 #else
150 #error define support for your CPU
151 #endif
154 struct cpu* cpu_find(DWORD machine)
156 struct cpu** cpu;
158 for (cpu = dbghelp_cpus ; *cpu; cpu++)
160 if (cpu[0]->machine == machine) return cpu[0];
162 return NULL;
165 /******************************************************************
166 * SymSetSearchPathW (DBGHELP.@)
169 BOOL WINAPI SymSetSearchPathW(HANDLE hProcess, PCWSTR searchPath)
171 struct process* pcs = process_find_by_handle(hProcess);
173 if (!pcs) return FALSE;
174 if (!searchPath) return FALSE;
176 HeapFree(GetProcessHeap(), 0, pcs->search_path);
177 pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
178 (lstrlenW(searchPath) + 1) * sizeof(WCHAR)),
179 searchPath);
180 return TRUE;
183 /******************************************************************
184 * SymSetSearchPath (DBGHELP.@)
187 BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PCSTR searchPath)
189 BOOL ret = FALSE;
190 unsigned len;
191 WCHAR* sp;
193 len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0);
194 if ((sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
196 MultiByteToWideChar(CP_ACP, 0, searchPath, -1, sp, len);
198 ret = SymSetSearchPathW(hProcess, sp);
199 HeapFree(GetProcessHeap(), 0, sp);
201 return ret;
204 /***********************************************************************
205 * SymGetSearchPathW (DBGHELP.@)
207 BOOL WINAPI SymGetSearchPathW(HANDLE hProcess, PWSTR szSearchPath,
208 DWORD SearchPathLength)
210 struct process* pcs = process_find_by_handle(hProcess);
211 if (!pcs) return FALSE;
213 lstrcpynW(szSearchPath, pcs->search_path, SearchPathLength);
214 return TRUE;
217 /***********************************************************************
218 * SymGetSearchPath (DBGHELP.@)
220 BOOL WINAPI SymGetSearchPath(HANDLE hProcess, PSTR szSearchPath,
221 DWORD SearchPathLength)
223 WCHAR* buffer = HeapAlloc(GetProcessHeap(), 0, SearchPathLength * sizeof(WCHAR));
224 BOOL ret = FALSE;
226 if (buffer)
228 ret = SymGetSearchPathW(hProcess, buffer, SearchPathLength);
229 if (ret)
230 WideCharToMultiByte(CP_ACP, 0, buffer, SearchPathLength,
231 szSearchPath, SearchPathLength, NULL, NULL);
232 HeapFree(GetProcessHeap(), 0, buffer);
234 return ret;
237 /******************************************************************
238 * invade_process
240 * SymInitialize helper: loads in dbghelp all known (and loaded modules)
241 * this assumes that hProcess is a handle on a valid process
243 static BOOL WINAPI process_invade_cb(PCWSTR name, ULONG64 base, ULONG size, PVOID user)
245 WCHAR tmp[MAX_PATH];
246 HANDLE hProcess = user;
248 if (!GetModuleFileNameExW(hProcess, (HMODULE)(DWORD_PTR)base, tmp, ARRAY_SIZE(tmp)))
249 lstrcpynW(tmp, name, ARRAY_SIZE(tmp));
251 SymLoadModuleExW(hProcess, 0, tmp, name, base, size, NULL, 0);
252 return TRUE;
255 /******************************************************************
256 * check_live_target
259 static BOOL check_live_target(struct process* pcs)
261 if (!GetProcessId(pcs->handle)) return FALSE;
262 if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL, 0)) return FALSE;
263 if (!elf_read_wine_loader_dbg_info(pcs))
264 macho_read_wine_loader_dbg_info(pcs);
265 return TRUE;
268 /******************************************************************
269 * SymInitializeW (DBGHELP.@)
271 * The initialisation of a dbghelp's context.
272 * Note that hProcess doesn't need to be a valid process handle (except
273 * when fInvadeProcess is TRUE).
274 * Since we also allow loading ELF (pure) libraries and Wine ELF libraries
275 * containing PE (and NE) module(s), here's how we handle it:
276 * - we load every module (ELF, NE, PE) passed in SymLoadModule
277 * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
278 * synchronization: hProcess should be a valid process handle, and we hook
279 * ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
280 * our internal ELF modules representation (loading / unloading). This way,
281 * we'll pair every loaded builtin PE module with its ELF counterpart (and
282 * access its debug information).
283 * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the
284 * hProcess refers to a running process. We use some heuristics here, so YMMV.
285 * If we detect a live target, then we get the same handling as if
286 * fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
287 * we won't be able to make the peering between a builtin PE module and its ELF
288 * counterpart. Hence we won't be able to provide the requested debug
289 * information. We'll however be able to load native PE modules (and their
290 * debug information) without any trouble.
291 * Note also that this scheme can be intertwined with the deferred loading
292 * mechanism (ie only load the debug information when we actually need it).
294 BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess)
296 struct process* pcs;
297 BOOL wow64, child_wow64;
299 TRACE("(%p %s %u)\n", hProcess, debugstr_w(UserSearchPath), fInvadeProcess);
301 if (process_find_by_handle(hProcess))
303 WARN("the symbols for this process have already been initialized!\n");
305 /* MSDN says to only call this function once unless SymCleanup() has been called since the last call.
306 It also says to call SymRefreshModuleList() instead if you just want the module list refreshed.
307 Native still returns TRUE even if the process has already been initialized. */
308 return TRUE;
311 IsWow64Process(GetCurrentProcess(), &wow64);
313 if (!IsWow64Process(hProcess, &child_wow64))
314 return FALSE;
316 pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
317 if (!pcs) return FALSE;
319 pcs->handle = hProcess;
320 pcs->is_64bit = (sizeof(void *) == 8 || wow64) && !child_wow64;
322 if (UserSearchPath)
324 pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
325 (lstrlenW(UserSearchPath) + 1) * sizeof(WCHAR)),
326 UserSearchPath);
328 else
330 unsigned size;
331 unsigned len;
332 static const WCHAR sym_path[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0};
333 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};
335 pcs->search_path = HeapAlloc(GetProcessHeap(), 0, (len = MAX_PATH) * sizeof(WCHAR));
336 while ((size = GetCurrentDirectoryW(len, pcs->search_path)) >= len)
337 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (len *= 2) * sizeof(WCHAR));
338 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1) * sizeof(WCHAR));
340 len = GetEnvironmentVariableW(sym_path, NULL, 0);
341 if (len)
343 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
344 pcs->search_path[size] = ';';
345 GetEnvironmentVariableW(sym_path, pcs->search_path + size + 1, len);
346 size += 1 + len;
348 len = GetEnvironmentVariableW(alt_sym_path, NULL, 0);
349 if (len)
351 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
352 pcs->search_path[size] = ';';
353 GetEnvironmentVariableW(alt_sym_path, pcs->search_path + size + 1, len);
357 pcs->lmodules = NULL;
358 pcs->dbg_hdr_addr = 0;
359 pcs->next = process_first;
360 process_first = pcs;
362 if (check_live_target(pcs))
364 if (fInvadeProcess)
365 EnumerateLoadedModulesW64(hProcess, process_invade_cb, hProcess);
366 elf_synchronize_module_list(pcs);
367 macho_synchronize_module_list(pcs);
369 else if (fInvadeProcess)
371 SymCleanup(hProcess);
372 SetLastError(ERROR_INVALID_PARAMETER);
373 return FALSE;
376 return TRUE;
379 /******************************************************************
380 * SymInitialize (DBGHELP.@)
384 BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess)
386 WCHAR* sp = NULL;
387 BOOL ret;
389 if (UserSearchPath)
391 unsigned len;
393 len = MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, NULL, 0);
394 sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
395 MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, sp, len);
398 ret = SymInitializeW(hProcess, sp, fInvadeProcess);
399 HeapFree(GetProcessHeap(), 0, sp);
400 return ret;
403 /******************************************************************
404 * SymCleanup (DBGHELP.@)
407 BOOL WINAPI SymCleanup(HANDLE hProcess)
409 struct process** ppcs;
410 struct process* next;
412 for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
414 if ((*ppcs)->handle == hProcess)
416 while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
418 HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
419 next = (*ppcs)->next;
420 HeapFree(GetProcessHeap(), 0, *ppcs);
421 *ppcs = next;
422 return TRUE;
426 ERR("this process has not had SymInitialize() called for it!\n");
427 return FALSE;
430 /******************************************************************
431 * SymSetOptions (DBGHELP.@)
434 DWORD WINAPI SymSetOptions(DWORD opts)
436 struct process* pcs;
438 for (pcs = process_first; pcs; pcs = pcs->next)
440 pcs_callback(pcs, CBA_SET_OPTIONS, &opts);
442 return dbghelp_options = opts;
445 /******************************************************************
446 * SymGetOptions (DBGHELP.@)
449 DWORD WINAPI SymGetOptions(void)
451 return dbghelp_options;
454 /******************************************************************
455 * SymSetParentWindow (DBGHELP.@)
458 BOOL WINAPI SymSetParentWindow(HWND hwnd)
460 /* Save hwnd so it can be used as parent window */
461 FIXME("(%p): stub\n", hwnd);
462 return TRUE;
465 /******************************************************************
466 * SymSetContext (DBGHELP.@)
469 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
470 PIMAGEHLP_CONTEXT Context)
472 struct process* pcs = process_find_by_handle(hProcess);
473 if (!pcs) return FALSE;
475 if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset &&
476 pcs->ctx_frame.FrameOffset == StackFrame->FrameOffset &&
477 pcs->ctx_frame.StackOffset == StackFrame->StackOffset)
479 TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
480 wine_dbgstr_longlong(pcs->ctx_frame.ReturnOffset),
481 wine_dbgstr_longlong(pcs->ctx_frame.FrameOffset),
482 wine_dbgstr_longlong(pcs->ctx_frame.StackOffset));
483 pcs->ctx_frame.InstructionOffset = StackFrame->InstructionOffset;
484 SetLastError(ERROR_ACCESS_DENIED); /* latest MSDN says ERROR_SUCCESS */
485 return FALSE;
488 pcs->ctx_frame = *StackFrame;
489 /* MSDN states that Context is not (no longer?) used */
490 return TRUE;
493 /******************************************************************
494 * reg_cb64to32 (internal)
496 * Registered callback for converting information from 64 bit to 32 bit
498 static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user)
500 struct process* pcs = process_find_by_handle(hProcess);
501 void* data32;
502 IMAGEHLP_DEFERRED_SYMBOL_LOAD64* idsl64;
503 IMAGEHLP_DEFERRED_SYMBOL_LOAD idsl;
505 if (!pcs) return FALSE;
506 switch (action)
508 case CBA_DEBUG_INFO:
509 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
510 case CBA_SET_OPTIONS:
511 case CBA_SYMBOLS_UNLOADED:
512 data32 = (void*)(DWORD_PTR)data;
513 break;
514 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
515 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
516 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
517 case CBA_DEFERRED_SYMBOL_LOAD_START:
518 idsl64 = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD_PTR)data;
519 if (!validate_addr64(idsl64->BaseOfImage))
520 return FALSE;
521 idsl.SizeOfStruct = sizeof(idsl);
522 idsl.BaseOfImage = (DWORD)idsl64->BaseOfImage;
523 idsl.CheckSum = idsl64->CheckSum;
524 idsl.TimeDateStamp = idsl64->TimeDateStamp;
525 memcpy(idsl.FileName, idsl64->FileName, sizeof(idsl.FileName));
526 idsl.Reparse = idsl64->Reparse;
527 data32 = &idsl;
528 break;
529 case CBA_DUPLICATE_SYMBOL:
530 case CBA_EVENT:
531 case CBA_READ_MEMORY:
532 default:
533 FIXME("No mapping for action %u\n", action);
534 return FALSE;
536 return pcs->reg_cb32(hProcess, action, data32, (PVOID)(DWORD_PTR)user);
539 /******************************************************************
540 * pcs_callback (internal)
542 BOOL pcs_callback(const struct process* pcs, ULONG action, void* data)
544 IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
546 TRACE("%p %u %p\n", pcs, action, data);
548 if (!pcs->reg_cb) return FALSE;
549 if (!pcs->reg_is_unicode)
551 IMAGEHLP_DEFERRED_SYMBOL_LOADW64* idslW;
553 switch (action)
555 case CBA_DEBUG_INFO:
556 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
557 case CBA_SET_OPTIONS:
558 case CBA_SYMBOLS_UNLOADED:
559 break;
560 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
561 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
562 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
563 case CBA_DEFERRED_SYMBOL_LOAD_START:
564 idslW = data;
565 idsl.SizeOfStruct = sizeof(idsl);
566 idsl.BaseOfImage = idslW->BaseOfImage;
567 idsl.CheckSum = idslW->CheckSum;
568 idsl.TimeDateStamp = idslW->TimeDateStamp;
569 WideCharToMultiByte(CP_ACP, 0, idslW->FileName, -1,
570 idsl.FileName, sizeof(idsl.FileName), NULL, NULL);
571 idsl.Reparse = idslW->Reparse;
572 data = &idsl;
573 break;
574 case CBA_DUPLICATE_SYMBOL:
575 case CBA_EVENT:
576 case CBA_READ_MEMORY:
577 default:
578 FIXME("No mapping for action %u\n", action);
579 return FALSE;
582 return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user);
585 /******************************************************************
586 * sym_register_cb
588 * Helper for registering a callback.
590 static BOOL sym_register_cb(HANDLE hProcess,
591 PSYMBOL_REGISTERED_CALLBACK64 cb,
592 PSYMBOL_REGISTERED_CALLBACK cb32,
593 DWORD64 user, BOOL unicode)
595 struct process* pcs = process_find_by_handle(hProcess);
597 if (!pcs) return FALSE;
598 pcs->reg_cb = cb;
599 pcs->reg_cb32 = cb32;
600 pcs->reg_is_unicode = unicode;
601 pcs->reg_user = user;
603 return TRUE;
606 /***********************************************************************
607 * SymRegisterCallback (DBGHELP.@)
609 BOOL WINAPI SymRegisterCallback(HANDLE hProcess,
610 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
611 PVOID UserContext)
613 TRACE("(%p, %p, %p)\n",
614 hProcess, CallbackFunction, UserContext);
615 return sym_register_cb(hProcess, reg_cb64to32, CallbackFunction, (DWORD_PTR)UserContext, FALSE);
618 /***********************************************************************
619 * SymRegisterCallback64 (DBGHELP.@)
621 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess,
622 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
623 ULONG64 UserContext)
625 TRACE("(%p, %p, %s)\n",
626 hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
627 return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, FALSE);
630 /***********************************************************************
631 * SymRegisterCallbackW64 (DBGHELP.@)
633 BOOL WINAPI SymRegisterCallbackW64(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, TRUE);
642 /* This is imagehlp version not dbghelp !! */
643 static API_VERSION api_version = { 4, 0, 2, 0 };
645 /***********************************************************************
646 * ImagehlpApiVersion (DBGHELP.@)
648 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
650 return &api_version;
653 /***********************************************************************
654 * ImagehlpApiVersionEx (DBGHELP.@)
656 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
658 if (!AppVersion) return NULL;
660 AppVersion->MajorVersion = api_version.MajorVersion;
661 AppVersion->MinorVersion = api_version.MinorVersion;
662 AppVersion->Revision = api_version.Revision;
663 AppVersion->Reserved = api_version.Reserved;
665 return AppVersion;
668 /******************************************************************
669 * ExtensionApiVersion (DBGHELP.@)
671 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
673 static EXT_API_VERSION eav = {5, 5, 5, 0};
674 return &eav;
677 /******************************************************************
678 * WinDbgExtensionDllInit (DBGHELP.@)
680 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
681 unsigned short major, unsigned short minor)