ole32: Return an error in CoMarshalInterface if pStream is NULL.
[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, LPWSTR 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, LPSTR szSearchPath, 
192                              DWORD SearchPathLength)
193 {
194     WCHAR*      buffer = HeapAlloc(GetProcessHeap(), 0, SearchPathLength);
195     BOOL        ret = FALSE;
196
197     if (buffer)
198     {
199         ret = SymGetSearchPathW(hProcess, buffer,
200                                 SearchPathLength * sizeof(WCHAR));
201         if (ret)
202             WideCharToMultiByte(CP_ACP, 0, buffer, SearchPathLength,
203                                 szSearchPath, SearchPathLength, NULL, NULL);
204         HeapFree(GetProcessHeap(), 0, buffer);
205     }
206     return ret;
207 }
208
209 /******************************************************************
210  *              invade_process
211  *
212  * SymInitialize helper: loads in dbghelp all known (and loaded modules)
213  * this assumes that hProcess is a handle on a valid process
214  */
215 static BOOL WINAPI process_invade_cb(char* name, DWORD base, DWORD size, void* user)
216 {
217     char        tmp[MAX_PATH];
218     HANDLE      hProcess = (HANDLE)user;
219
220     if (!GetModuleFileNameExA(hProcess, (HMODULE)base, 
221                               tmp, sizeof(tmp)))
222         lstrcpynA(tmp, name, sizeof(tmp));
223
224     SymLoadModule(hProcess, 0, tmp, name, base, size);
225     return TRUE;
226 }
227
228 /******************************************************************
229  *              check_live_target
230  *
231  */
232 static BOOL check_live_target(struct process* pcs)
233 {
234     if (!GetProcessId(pcs->handle)) return FALSE;
235     if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL, 0)) return FALSE;
236     elf_read_wine_loader_dbg_info(pcs);
237     return TRUE;
238 }
239
240 /******************************************************************
241  *              SymInitializeW (DBGHELP.@)
242  *
243  * The initialisation of a dbghelp's context.
244  * Note that hProcess doesn't need to be a valid process handle (except
245  * when fInvadeProcess is TRUE).
246  * Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries 
247  * containing PE (and NE) module(s), here's how we handle it:
248  * - we load every module (ELF, NE, PE) passed in SymLoadModule
249  * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
250  *   synchronization: hProcess should be a valid process handle, and we hook
251  *   ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
252  *   our internal ELF modules representation (loading / unloading). This way,
253  *   we'll pair every loaded builtin PE module with its ELF counterpart (and
254  *   access its debug information).
255  * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the 
256  *   hProcess refers to a running process. We use some heuristics here, so YMMV.
257  *   If we detect a live target, then we get the same handling as if
258  *   fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
259  *   we won't be able to make the peering between a builtin PE module and its ELF
260  *   counterpart. Hence we won't be able to provide the requested debug
261  *   information. We'll however be able to load native PE modules (and their
262  *   debug information) without any trouble.
263  * Note also that this scheme can be intertwined with the deferred loading 
264  * mechanism (ie only load the debug information when we actually need it).
265  */
266 BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess)
267 {
268     struct process*     pcs;
269
270     TRACE("(%p %s %u)\n", hProcess, debugstr_w(UserSearchPath), fInvadeProcess);
271
272     if (process_find_by_handle(hProcess))
273         FIXME("what to do ??\n");
274
275     pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
276     if (!pcs) return FALSE;
277
278     pcs->handle = hProcess;
279
280     if (UserSearchPath)
281     {
282         pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,      
283                                               (lstrlenW(UserSearchPath) + 1) * sizeof(WCHAR)),
284                                     UserSearchPath);
285     }
286     else
287     {
288         unsigned        size;
289         unsigned        len;
290         static const WCHAR      sym_path[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0};
291         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};
292
293         pcs->search_path = HeapAlloc(GetProcessHeap(), 0, (len = MAX_PATH) * sizeof(WCHAR));
294         while ((size = GetCurrentDirectoryW(len, pcs->search_path)) >= len)
295             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (len *= 2) * sizeof(WCHAR));
296         pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1) * sizeof(WCHAR));
297
298         len = GetEnvironmentVariableW(sym_path, NULL, 0);
299         if (len)
300         {
301             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
302             pcs->search_path[size] = ';';
303             GetEnvironmentVariableW(sym_path, pcs->search_path + size + 1, len);
304             size += 1 + len;
305         }
306         len = GetEnvironmentVariableW(alt_sym_path, NULL, 0);
307         if (len)
308         {
309             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
310             pcs->search_path[size] = ';';
311             GetEnvironmentVariableW(alt_sym_path, pcs->search_path + size + 1, len);
312             size += 1 + len;
313         }
314     }
315
316     pcs->lmodules = NULL;
317     pcs->dbg_hdr_addr = 0;
318     pcs->next = process_first;
319     process_first = pcs;
320     
321     if (check_live_target(pcs))
322     {
323         if (fInvadeProcess)
324             EnumerateLoadedModules(hProcess, process_invade_cb, (void*)hProcess);
325         elf_synchronize_module_list(pcs);
326     }
327     else if (fInvadeProcess)
328     {
329         SymCleanup(hProcess);
330         SetLastError(ERROR_INVALID_PARAMETER);
331         return FALSE;
332     }
333
334     return TRUE;
335 }
336
337 /******************************************************************
338  *              SymInitialize (DBGHELP.@)
339  *
340  *
341  */
342 BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess)
343 {
344     WCHAR*              sp = NULL;
345     BOOL                ret;
346
347     if (UserSearchPath)
348     {
349         unsigned len;
350
351         len = MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, NULL, 0);
352         sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
353         MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, sp, len);
354     }
355
356     ret = SymInitializeW(hProcess, sp, fInvadeProcess);
357     HeapFree(GetProcessHeap(), 0, sp);
358     return ret;
359 }
360
361 /******************************************************************
362  *              SymCleanup (DBGHELP.@)
363  *
364  */
365 BOOL WINAPI SymCleanup(HANDLE hProcess)
366 {
367     struct process**    ppcs;
368     struct process*     next;
369
370     for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
371     {
372         if ((*ppcs)->handle == hProcess)
373         {
374             while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
375
376             HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
377             next = (*ppcs)->next;
378             HeapFree(GetProcessHeap(), 0, *ppcs);
379             *ppcs = next;
380             return TRUE;
381         }
382     }
383     return FALSE;
384 }
385
386 /******************************************************************
387  *              SymSetOptions (DBGHELP.@)
388  *
389  */
390 DWORD WINAPI SymSetOptions(DWORD opts)
391 {
392     struct process* pcs;
393
394     for (pcs = process_first; pcs; pcs = pcs->next)
395     {
396         pcs_callback(pcs, CBA_SET_OPTIONS, &opts);
397     }
398     return dbghelp_options = opts;
399 }
400
401 /******************************************************************
402  *              SymGetOptions (DBGHELP.@)
403  *
404  */
405 DWORD WINAPI SymGetOptions(void)
406 {
407     return dbghelp_options;
408 }
409
410 /******************************************************************
411  *              SymSetParentWindow (DBGHELP.@)
412  *
413  */
414 BOOL WINAPI SymSetParentWindow(HWND hwnd)
415 {
416     /* Save hwnd so it can be used as parent window */
417     FIXME("(%p): stub\n", hwnd);
418     return TRUE;
419 }
420
421 /******************************************************************
422  *              SymSetContext (DBGHELP.@)
423  *
424  */
425 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
426                           PIMAGEHLP_CONTEXT Context)
427 {
428     struct process* pcs = process_find_by_handle(hProcess);
429     if (!pcs) return FALSE;
430
431     if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset &&
432         pcs->ctx_frame.FrameOffset  == StackFrame->FrameOffset  &&
433         pcs->ctx_frame.StackOffset  == StackFrame->StackOffset)
434     {
435         TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
436               wine_dbgstr_longlong(pcs->ctx_frame.ReturnOffset),
437               wine_dbgstr_longlong(pcs->ctx_frame.FrameOffset),
438               wine_dbgstr_longlong(pcs->ctx_frame.StackOffset));
439         pcs->ctx_frame.InstructionOffset = StackFrame->InstructionOffset;
440         SetLastError(ERROR_ACCESS_DENIED); /* latest MSDN says ERROR_SUCCESS */
441         return FALSE;
442     }
443
444     pcs->ctx_frame = *StackFrame;
445     /* MSDN states that Context is not (no longer?) used */
446     return TRUE;
447 }
448
449 /******************************************************************
450  *              reg_cb64to32 (internal)
451  *
452  * Registered callback for converting information from 64 bit to 32 bit
453  */
454 static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user)
455 {
456     PSYMBOL_REGISTERED_CALLBACK         cb32 = (PSYMBOL_REGISTERED_CALLBACK)(DWORD)(user >> 32);
457     DWORD                               user32 = (DWORD)user;
458     void*                               data32;
459     IMAGEHLP_DEFERRED_SYMBOL_LOAD64*    idsl64;
460     IMAGEHLP_DEFERRED_SYMBOL_LOAD       idsl;
461
462     switch (action)
463     {
464     case CBA_DEBUG_INFO:
465     case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
466     case CBA_SET_OPTIONS:
467     case CBA_SYMBOLS_UNLOADED:
468         data32 = (void*)(DWORD)data;
469         break;
470     case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
471     case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
472     case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
473     case CBA_DEFERRED_SYMBOL_LOAD_START:
474         idsl64 = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD)data;
475         if (!validate_addr64(idsl64->BaseOfImage))
476             return FALSE;
477         idsl.SizeOfStruct = sizeof(idsl);
478         idsl.BaseOfImage = (DWORD)idsl64->BaseOfImage;
479         idsl.CheckSum = idsl64->CheckSum;
480         idsl.TimeDateStamp = idsl64->TimeDateStamp;
481         memcpy(idsl.FileName, idsl64->FileName, sizeof(idsl.FileName));
482         idsl.Reparse = idsl64->Reparse;
483         data32 = &idsl;
484         break;
485     case CBA_DUPLICATE_SYMBOL:
486     case CBA_EVENT:
487     case CBA_READ_MEMORY:
488     default:
489         FIXME("No mapping for action %u\n", action);
490         return FALSE;
491     }
492     return cb32(hProcess, action, (PVOID)data32, (PVOID)user32);
493 }
494
495 /******************************************************************
496  *              pcs_callback (internal)
497  */
498 BOOL pcs_callback(const struct process* pcs, ULONG action, void* data)
499 {
500     TRACE("%p %u %p\n", pcs, action, data);
501
502     if (!pcs->reg_cb) return FALSE;
503     if (pcs->reg_is_unicode)
504     {
505         IMAGEHLP_DEFERRED_SYMBOL_LOAD64*    idsl;
506         IMAGEHLP_DEFERRED_SYMBOL_LOADW64    idslW;
507
508         switch (action)
509         {
510         case CBA_DEBUG_INFO:
511         case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
512         case CBA_SET_OPTIONS:
513         case CBA_SYMBOLS_UNLOADED:
514             break;
515         case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
516         case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
517         case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
518         case CBA_DEFERRED_SYMBOL_LOAD_START:
519             idsl = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD)data;
520             idslW.SizeOfStruct = sizeof(idslW);
521             idslW.BaseOfImage = idsl->BaseOfImage;
522             idslW.CheckSum = idsl->CheckSum;
523             idslW.TimeDateStamp = idsl->TimeDateStamp;
524             MultiByteToWideChar(CP_ACP, 0, idsl->FileName, -1, 
525                                 idslW.FileName, sizeof(idslW.FileName) / sizeof(WCHAR));
526             idslW.Reparse = idsl->Reparse;
527             data = &idslW;
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;
535         }
536     }
537     return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user);
538 }
539
540 /******************************************************************
541  *              sym_register_cb
542  *
543  * Helper for registering a callback.
544  */
545 static BOOL sym_register_cb(HANDLE hProcess, 
546                             PSYMBOL_REGISTERED_CALLBACK64 cb,
547                             DWORD64 user, BOOL unicode)
548 {
549     struct process* pcs = process_find_by_handle(hProcess);
550
551     if (!pcs) return FALSE;
552     pcs->reg_cb = cb;
553     pcs->reg_is_unicode = unicode;
554     pcs->reg_user = user;
555
556     return TRUE;
557 }
558
559 /***********************************************************************
560  *              SymRegisterCallback (DBGHELP.@)
561  */
562 BOOL WINAPI SymRegisterCallback(HANDLE hProcess, 
563                                 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
564                                 PVOID UserContext)
565 {
566     DWORD64 tmp = ((ULONGLONG)(DWORD)CallbackFunction << 32) | (DWORD)UserContext;
567     TRACE("(%p, %p, %p)\n", 
568           hProcess, CallbackFunction, UserContext);
569     return sym_register_cb(hProcess, reg_cb64to32, tmp, FALSE);
570 }
571
572 /***********************************************************************
573  *              SymRegisterCallback64 (DBGHELP.@)
574  */
575 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess, 
576                                   PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
577                                   ULONG64 UserContext)
578 {
579     TRACE("(%p, %p, %s)\n", 
580           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
581     return sym_register_cb(hProcess, CallbackFunction, UserContext, FALSE);
582 }
583
584 /***********************************************************************
585  *              SymRegisterCallbackW64 (DBGHELP.@)
586  */
587 BOOL WINAPI SymRegisterCallbackW64(HANDLE hProcess, 
588                                    PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
589                                    ULONG64 UserContext)
590 {
591     TRACE("(%p, %p, %s)\n", 
592           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
593     return sym_register_cb(hProcess, CallbackFunction, UserContext, TRUE);
594 }
595
596 /* This is imagehlp version not dbghelp !! */
597 static API_VERSION api_version = { 4, 0, 2, 0 };
598
599 /***********************************************************************
600  *           ImagehlpApiVersion (DBGHELP.@)
601  */
602 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
603 {
604     return &api_version;
605 }
606
607 /***********************************************************************
608  *           ImagehlpApiVersionEx (DBGHELP.@)
609  */
610 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
611 {
612     if (!AppVersion) return NULL;
613
614     AppVersion->MajorVersion = api_version.MajorVersion;
615     AppVersion->MinorVersion = api_version.MinorVersion;
616     AppVersion->Revision = api_version.Revision;
617     AppVersion->Reserved = api_version.Reserved;
618
619     return AppVersion;
620 }
621
622 /******************************************************************
623  *              ExtensionApiVersion (DBGHELP.@)
624  */
625 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
626 {
627     static EXT_API_VERSION      eav = {5, 5, 5, 0};
628     return &eav;
629 }
630
631 /******************************************************************
632  *              WinDbgExtensionDllInit (DBGHELP.@)
633  */
634 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
635                                    unsigned short major, unsigned short minor)
636 {
637 }