- Create another branch for networking fixes
[reactos.git] / dll / win32 / 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 }