]> git.saurik.com Git - wxWidgets.git/blame - src/msw/debughlp.cpp
Correct making the newly inserted menu item owner drawn in some cases.
[wxWidgets.git] / src / msw / debughlp.cpp
CommitLineData
3d8b5d85 1/////////////////////////////////////////////////////////////////////////////
80fdcdb9 2// Name: src/msw/debughlp.cpp
3d8b5d85
VZ
3// Purpose: various Win32 debug helpers
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 2005-01-08 (extracted from crashrpt.cpp)
3d8b5d85
VZ
7// Copyright: (c) 2003-2005 Vadim Zeitlin <vadim@wxwindows.org>
8// Licence: wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// headers
17// ----------------------------------------------------------------------------
18
19#include "wx/wxprec.h"
20
21#ifdef __BORLANDC__
22 #pragma hdrstop
23#endif
24
25#include "wx/msw/debughlp.h"
26
64c288fa 27#if wxUSE_DBGHELP && wxUSE_DYNLIB_CLASS
3d8b5d85 28
97799a96
VZ
29// ----------------------------------------------------------------------------
30// constants
31// ----------------------------------------------------------------------------
32
33// to prevent recursion which could result from corrupted data we limit
34// ourselves to that many levels of embedded fields inside structs
35static const unsigned MAX_DUMP_DEPTH = 20;
36
3d8b5d85
VZ
37// ----------------------------------------------------------------------------
38// globals
39// ----------------------------------------------------------------------------
40
41// error message from Init()
42static wxString gs_errMsg;
43
44// ============================================================================
45// wxDbgHelpDLL implementation
46// ============================================================================
47
48// ----------------------------------------------------------------------------
49// static members
50// ----------------------------------------------------------------------------
51
603c73db
VZ
52#define DEFINE_SYM_FUNCTION(func, name) \
53 wxDbgHelpDLL::func ## _t wxDbgHelpDLL::func = 0
3d8b5d85
VZ
54
55wxDO_FOR_ALL_SYM_FUNCS(DEFINE_SYM_FUNCTION);
56
57#undef DEFINE_SYM_FUNCTION
58
59// ----------------------------------------------------------------------------
60// initialization methods
61// ----------------------------------------------------------------------------
62
63// load all function we need from the DLL
64
65static bool BindDbgHelpFunctions(const wxDynamicLibrary& dllDbgHelp)
66{
603c73db
VZ
67 #define LOAD_SYM_FUNCTION(func, name) \
68 wxDbgHelpDLL::func = (wxDbgHelpDLL::func ## _t) \
69 dllDbgHelp.GetSymbol(wxT(#name)); \
70 if ( !wxDbgHelpDLL::func ) \
3d8b5d85 71 { \
603c73db 72 gs_errMsg += wxT("Function ") wxT(#name) wxT("() not found.\n"); \
3d8b5d85
VZ
73 return false; \
74 }
75
76 wxDO_FOR_ALL_SYM_FUNCS(LOAD_SYM_FUNCTION);
77
78 #undef LOAD_SYM_FUNCTION
79
80 return true;
81}
82
83// called by Init() if we hadn't done this before
84static bool DoInit()
85{
9a83f860 86 wxDynamicLibrary dllDbgHelp(wxT("dbghelp.dll"), wxDL_VERBATIM);
3d8b5d85
VZ
87 if ( dllDbgHelp.IsLoaded() )
88 {
89 if ( BindDbgHelpFunctions(dllDbgHelp) )
90 {
91 // turn on default options
92 DWORD options = wxDbgHelpDLL::SymGetOptions();
93
94 options |= SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_DEBUG;
95
96 wxDbgHelpDLL::SymSetOptions(options);
97
98 dllDbgHelp.Detach();
99 return true;
100 }
101
9a83f860
VZ
102 gs_errMsg += wxT("\nPlease update your dbghelp.dll version, ")
103 wxT("at least version 5.1 is needed!\n")
104 wxT("(if you already have a new version, please ")
105 wxT("put it in the same directory where the program is.)\n");
3d8b5d85
VZ
106 }
107 else // failed to load dbghelp.dll
108 {
9a83f860
VZ
109 gs_errMsg += wxT("Please install dbghelp.dll available free of charge ")
110 wxT("from Microsoft to get more detailed crash information!");
3d8b5d85
VZ
111 }
112
9a83f860
VZ
113 gs_errMsg += wxT("\nLatest dbghelp.dll is available at ")
114 wxT("http://www.microsoft.com/whdc/ddk/debugging/\n");
3d8b5d85
VZ
115
116 return false;
117}
118
119/* static */
120bool wxDbgHelpDLL::Init()
121{
122 // this flag is -1 until Init() is called for the first time, then it's set
123 // to either false or true depending on whether we could load the functions
124 static int s_loaded = -1;
125
126 if ( s_loaded == -1 )
127 {
128 s_loaded = DoInit();
129 }
130
131 return s_loaded != 0;
132}
133
134// ----------------------------------------------------------------------------
135// error handling
136// ----------------------------------------------------------------------------
137
138/* static */
139const wxString& wxDbgHelpDLL::GetErrorMessage()
140{
141 return gs_errMsg;
142}
143
144/* static */
145void wxDbgHelpDLL::LogError(const wxChar *func)
146{
9a83f860 147 ::OutputDebugString(wxString::Format(wxT("dbghelp: %s() failed: %s\r\n"),
b2b6da49 148 func, wxSysErrorMsg(::GetLastError())).t_str());
3d8b5d85
VZ
149}
150
151// ----------------------------------------------------------------------------
152// data dumping
153// ----------------------------------------------------------------------------
154
155static inline
156bool
157DoGetTypeInfo(DWORD64 base, ULONG ti, IMAGEHLP_SYMBOL_TYPE_INFO type, void *rc)
158{
159 static HANDLE s_hProcess = ::GetCurrentProcess();
160
161 return wxDbgHelpDLL::SymGetTypeInfo
162 (
163 s_hProcess,
164 base,
165 ti,
166 type,
167 rc
168 ) != 0;
169}
170
171static inline
172bool
173DoGetTypeInfo(PSYMBOL_INFO pSym, IMAGEHLP_SYMBOL_TYPE_INFO type, void *rc)
174{
175 return DoGetTypeInfo(pSym->ModBase, pSym->TypeIndex, type, rc);
176}
177
178static inline
179wxDbgHelpDLL::BasicType GetBasicType(PSYMBOL_INFO pSym)
180{
181 wxDbgHelpDLL::BasicType bt;
182 return DoGetTypeInfo(pSym, TI_GET_BASETYPE, &bt)
183 ? bt
184 : wxDbgHelpDLL::BASICTYPE_NOTYPE;
185}
186
187/* static */
188wxString wxDbgHelpDLL::GetSymbolName(PSYMBOL_INFO pSym)
189{
190 wxString s;
191
192 WCHAR *pwszTypeName;
193 if ( SymGetTypeInfo
194 (
195 GetCurrentProcess(),
196 pSym->ModBase,
197 pSym->TypeIndex,
198 TI_GET_SYMNAME,
199 &pwszTypeName
200 ) )
201 {
202 s = wxConvCurrent->cWC2WX(pwszTypeName);
203
204 ::LocalFree(pwszTypeName);
205 }
206
207 return s;
208}
209
210/* static */ wxString
211wxDbgHelpDLL::DumpBaseType(BasicType bt, DWORD64 length, PVOID pAddress)
212{
213 if ( !pAddress )
214 {
9a83f860 215 return wxT("null");
3d8b5d85
VZ
216 }
217
218 if ( ::IsBadReadPtr(pAddress, length) != 0 )
219 {
9a83f860 220 return wxT("BAD");
3d8b5d85
VZ
221 }
222
223
224 wxString s;
225 s.reserve(256);
226
227 if ( length == 1 )
228 {
229 const BYTE b = *(PBYTE)pAddress;
230
231 if ( bt == BASICTYPE_BOOL )
9a83f860 232 s = b ? wxT("true") : wxT("false");
3d8b5d85 233 else
9a83f860 234 s.Printf(wxT("%#04x"), b);
3d8b5d85
VZ
235 }
236 else if ( length == 2 )
237 {
9a83f860 238 s.Printf(bt == BASICTYPE_UINT ? wxT("%#06x") : wxT("%d"),
3d8b5d85
VZ
239 *(PWORD)pAddress);
240 }
241 else if ( length == 4 )
242 {
243 bool handled = false;
244
245 if ( bt == BASICTYPE_FLOAT )
246 {
9a83f860 247 s.Printf(wxT("%f"), *(PFLOAT)pAddress);
3d8b5d85
VZ
248
249 handled = true;
250 }
251 else if ( bt == BASICTYPE_CHAR )
252 {
253 // don't take more than 32 characters of a string
254 static const size_t NUM_CHARS = 64;
255
256 const char *pc = *(PSTR *)pAddress;
257 if ( ::IsBadStringPtrA(pc, NUM_CHARS) == 0 )
258 {
9a83f860 259 s += wxT('"');
3d8b5d85
VZ
260 for ( size_t n = 0; n < NUM_CHARS && *pc; n++, pc++ )
261 {
262 s += *pc;
263 }
9a83f860 264 s += wxT('"');
3d8b5d85
VZ
265
266 handled = true;
267 }
268 }
269
270 if ( !handled )
271 {
272 // treat just as an opaque DWORD
9a83f860 273 s.Printf(wxT("%#x"), *(PDWORD)pAddress);
3d8b5d85
VZ
274 }
275 }
276 else if ( length == 8 )
277 {
278 if ( bt == BASICTYPE_FLOAT )
279 {
9a83f860 280 s.Printf(wxT("%lf"), *(double *)pAddress);
3d8b5d85
VZ
281 }
282 else // opaque 64 bit value
283 {
603c73db 284 s.Printf("%#" wxLongLongFmtSpec "x", *(wxLongLong_t *)pAddress);
3d8b5d85
VZ
285 }
286 }
287
288 return s;
289}
290
291wxString
292wxDbgHelpDLL::DumpField(PSYMBOL_INFO pSym, void *pVariable, unsigned level)
293{
294 wxString s;
295
296 // avoid infinite recursion
97799a96 297 if ( level > MAX_DUMP_DEPTH )
3d8b5d85
VZ
298 {
299 return s;
300 }
301
302 SymbolTag tag = SYMBOL_TAG_NULL;
303 if ( !DoGetTypeInfo(pSym, TI_GET_SYMTAG, &tag) )
304 {
305 return s;
306 }
307
308 switch ( tag )
309 {
310 case SYMBOL_TAG_UDT:
311 case SYMBOL_TAG_BASE_CLASS:
312 s = DumpUDT(pSym, pVariable, level);
313 break;
314
315 case SYMBOL_TAG_DATA:
120678ee 316 if ( !pVariable )
3d8b5d85 317 {
9a83f860 318 s = wxT("NULL");
3d8b5d85 319 }
120678ee
VZ
320 else // valid location
321 {
322 wxDbgHelpDLL::DataKind kind;
323 if ( !DoGetTypeInfo(pSym, TI_GET_DATAKIND, &kind) ||
324 kind != DATA_MEMBER )
325 {
326 // maybe it's a static member? we're not interested in them...
327 break;
328 }
3d8b5d85 329
120678ee
VZ
330 // get the offset of the child member, relative to its parent
331 DWORD ofs = 0;
332 if ( !DoGetTypeInfo(pSym, TI_GET_OFFSET, &ofs) )
333 break;
3d8b5d85 334
120678ee 335 pVariable = (void *)((DWORD_PTR)pVariable + ofs);
3d8b5d85
VZ
336
337
120678ee
VZ
338 // now pass to the type representing the type of this member
339 SYMBOL_INFO sym = *pSym;
340 if ( !DoGetTypeInfo(pSym, TI_GET_TYPEID, &sym.TypeIndex) )
341 break;
3d8b5d85 342
120678ee
VZ
343 ULONG64 size;
344 DoGetTypeInfo(&sym, TI_GET_LENGTH, &size);
3d8b5d85 345
120678ee
VZ
346 switch ( DereferenceSymbol(&sym, &pVariable) )
347 {
348 case SYMBOL_TAG_BASE_TYPE:
3d8b5d85 349 {
120678ee
VZ
350 BasicType bt = GetBasicType(&sym);
351 if ( bt )
352 {
353 s = DumpBaseType(bt, size, pVariable);
354 }
3d8b5d85 355 }
120678ee 356 break;
3d8b5d85 357
120678ee
VZ
358 case SYMBOL_TAG_UDT:
359 case SYMBOL_TAG_BASE_CLASS:
360 s = DumpUDT(&sym, pVariable, level);
361 break;
459c6f2a
VZ
362
363 default:
364 // Suppress gcc warnings about unhandled enum values.
365 break;
120678ee 366 }
3d8b5d85
VZ
367 }
368
369 if ( !s.empty() )
370 {
9a83f860 371 s = GetSymbolName(pSym) + wxT(" = ") + s;
3d8b5d85
VZ
372 }
373 break;
459c6f2a
VZ
374
375 default:
376 // Suppress gcc warnings about unhandled enum values, don't assert
377 // to avoid problems during fatal crash generation.
378 break;
3d8b5d85
VZ
379 }
380
381 if ( !s.empty() )
382 {
9a83f860 383 s = wxString(wxT('\t'), level + 1) + s + wxT('\n');
3d8b5d85
VZ
384 }
385
386 return s;
387}
388
389/* static */ wxString
390wxDbgHelpDLL::DumpUDT(PSYMBOL_INFO pSym, void *pVariable, unsigned level)
391{
392 wxString s;
737c443b
VZ
393
394 // we have to limit the depth of UDT dumping as otherwise we get in
395 // infinite loops trying to dump linked lists... 10 levels seems quite
396 // reasonable, full information is in minidump file anyhow
397 if ( level > 10 )
398 return s;
399
3d8b5d85
VZ
400 s.reserve(512);
401 s = GetSymbolName(pSym);
402
e8089d44 403#if !wxUSE_STD_STRING
3d8b5d85
VZ
404 // special handling for ubiquitous wxString: although the code below works
405 // for it as well, it shows the wxStringBase class and takes 4 lines
406 // instead of only one as this branch
9a83f860 407 if ( s == wxT("wxString") )
3d8b5d85
VZ
408 {
409 wxString *ps = (wxString *)pVariable;
b903802e 410
e1f2de5a
VZ
411 // we can't just dump wxString directly as it could be corrupted or
412 // invalid and it could also be locked for writing (i.e. if we're
413 // between GetWriteBuf() and UngetWriteBuf() calls) and assert when we
414 // try to access it contents using public methods, so instead use our
415 // knowledge of its internals
b0c4316e
VZ
416 const wxChar *p = NULL;
417 if ( !::IsBadReadPtr(ps, sizeof(wxString)) )
e1f2de5a 418 {
b0c4316e
VZ
419 p = ps->data();
420 wxStringData *data = (wxStringData *)p - 1;
421 if ( ::IsBadReadPtr(data, sizeof(wxStringData)) ||
422 ::IsBadReadPtr(p, sizeof(wxChar *)*data->nAllocLength) )
423 {
424 p = NULL; // don't touch this pointer with 10 feet pole
425 }
e1f2de5a
VZ
426 }
427
9a83f860 428 s << wxT("(\"") << (p ? p : wxT("???")) << wxT(")\"");
3d8b5d85
VZ
429 }
430 else // any other UDT
e8089d44 431#endif // !wxUSE_STD_STRING
3d8b5d85
VZ
432 {
433 // Determine how many children this type has.
434 DWORD dwChildrenCount = 0;
435 DoGetTypeInfo(pSym, TI_GET_CHILDRENCOUNT, &dwChildrenCount);
436
437 // Prepare to get an array of "TypeIds", representing each of the children.
438 TI_FINDCHILDREN_PARAMS *children = (TI_FINDCHILDREN_PARAMS *)
439 malloc(sizeof(TI_FINDCHILDREN_PARAMS) +
440 (dwChildrenCount - 1)*sizeof(ULONG));
441 if ( !children )
442 return s;
443
444 children->Count = dwChildrenCount;
445 children->Start = 0;
446
447 // Get the array of TypeIds, one for each child type
448 if ( !DoGetTypeInfo(pSym, TI_FINDCHILDREN, children) )
449 {
450 free(children);
451 return s;
452 }
453
9a83f860 454 s << wxT(" {\n");
3d8b5d85
VZ
455
456 // Iterate through all children
457 SYMBOL_INFO sym;
458 wxZeroMemory(sym);
459 sym.ModBase = pSym->ModBase;
460 for ( unsigned i = 0; i < dwChildrenCount; i++ )
461 {
462 sym.TypeIndex = children->ChildId[i];
463
464 // children here are in lexicographic sense, i.e. we get all our nested
465 // classes and not only our member fields, but we can't get the values
466 // for the members of the nested classes, of course!
467 DWORD nested;
468 if ( DoGetTypeInfo(&sym, TI_GET_NESTED, &nested) && nested )
469 continue;
470
471 // avoid infinite recursion: this does seem to happen sometimes with
472 // complex typedefs...
473 if ( sym.TypeIndex == pSym->TypeIndex )
474 continue;
475
476 s += DumpField(&sym, pVariable, level + 1);
477 }
478
479 free(children);
480
9a83f860 481 s << wxString(wxT('\t'), level + 1) << wxT('}');
3d8b5d85
VZ
482 }
483
484 return s;
485}
486
487/* static */
488wxDbgHelpDLL::SymbolTag
489wxDbgHelpDLL::DereferenceSymbol(PSYMBOL_INFO pSym, void **ppData)
490{
491 SymbolTag tag = SYMBOL_TAG_NULL;
492 for ( ;; )
493 {
494 if ( !DoGetTypeInfo(pSym, TI_GET_SYMTAG, &tag) )
495 break;
496
497 if ( tag != SYMBOL_TAG_POINTER_TYPE )
498 break;
499
500 ULONG tiNew;
501 if ( !DoGetTypeInfo(pSym, TI_GET_TYPEID, &tiNew) ||
502 tiNew == pSym->TypeIndex )
503 break;
504
505 pSym->TypeIndex = tiNew;
506
507 // remove one level of indirection except for the char strings: we want
508 // to dump "char *" and not a single "char" for them
509 if ( ppData && *ppData && GetBasicType(pSym) != BASICTYPE_CHAR )
120678ee
VZ
510 {
511 DWORD_PTR *pData = (DWORD_PTR *)*ppData;
512
513 if ( ::IsBadReadPtr(pData, sizeof(DWORD_PTR *)) )
514 {
515 break;
516 }
517
518 *ppData = (void *)*pData;
519 }
3d8b5d85
VZ
520 }
521
522 return tag;
523}
524
525/* static */ wxString
526wxDbgHelpDLL::DumpSymbol(PSYMBOL_INFO pSym, void *pVariable)
527{
528 wxString s;
529 SYMBOL_INFO symDeref = *pSym;
530 switch ( DereferenceSymbol(&symDeref, &pVariable) )
531 {
459c6f2a
VZ
532 default:
533 // Suppress gcc warnings about unhandled enum values, don't assert
534 // to avoid problems during fatal crash generation.
535 break;
536
3d8b5d85
VZ
537 case SYMBOL_TAG_UDT:
538 // show UDT recursively
539 s = DumpUDT(&symDeref, pVariable);
540 break;
541
542 case SYMBOL_TAG_BASE_TYPE:
543 // variable of simple type, show directly
544 BasicType bt = GetBasicType(&symDeref);
545 if ( bt )
546 {
547 s = DumpBaseType(bt, pSym->Size, pVariable);
548 }
549 break;
550 }
551
552 return s;
553}
554
555// ----------------------------------------------------------------------------
556// debugging helpers
557// ----------------------------------------------------------------------------
558
e84ba59e
VZ
559// this code is very useful when debugging debughlp.dll-related code but
560// probably not worth having compiled in normally, please do not remove it!
561#if 0 // ndef NDEBUG
3d8b5d85
VZ
562
563static wxString TagString(wxDbgHelpDLL::SymbolTag tag)
564{
565 static const wxChar *tags[] =
566 {
9a83f860
VZ
567 wxT("null"),
568 wxT("exe"),
569 wxT("compiland"),
570 wxT("compiland details"),
571 wxT("compiland env"),
572 wxT("function"),
573 wxT("block"),
574 wxT("data"),
575 wxT("annotation"),
576 wxT("label"),
577 wxT("public symbol"),
578 wxT("udt"),
579 wxT("enum"),
580 wxT("function type"),
581 wxT("pointer type"),
582 wxT("array type"),
583 wxT("base type"),
584 wxT("typedef"),
585 wxT("base class"),
586 wxT("friend"),
587 wxT("function arg type"),
588 wxT("func debug start"),
589 wxT("func debug end"),
590 wxT("using namespace"),
591 wxT("vtable shape"),
592 wxT("vtable"),
593 wxT("custom"),
594 wxT("thunk"),
595 wxT("custom type"),
596 wxT("managed type"),
597 wxT("dimension"),
3d8b5d85
VZ
598 };
599
600 wxCOMPILE_TIME_ASSERT( WXSIZEOF(tags) == wxDbgHelpDLL::SYMBOL_TAG_MAX,
601 SymbolTagStringMismatch );
602
603 wxString s;
604 if ( tag < WXSIZEOF(tags) )
605 s = tags[tag];
606 else
9a83f860 607 s.Printf(wxT("unrecognized tag (%d)"), tag);
3d8b5d85
VZ
608
609 return s;
610}
611
612static wxString KindString(wxDbgHelpDLL::DataKind kind)
613{
614 static const wxChar *kinds[] =
615 {
9a83f860
VZ
616 wxT("unknown"),
617 wxT("local"),
618 wxT("static local"),
619 wxT("param"),
620 wxT("object ptr"),
621 wxT("file static"),
622 wxT("global"),
623 wxT("member"),
624 wxT("static member"),
625 wxT("constant"),
3d8b5d85
VZ
626 };
627
628 wxCOMPILE_TIME_ASSERT( WXSIZEOF(kinds) == wxDbgHelpDLL::DATA_MAX,
629 DataKindStringMismatch );
630
631 wxString s;
632 if ( kind < WXSIZEOF(kinds) )
633 s = kinds[kind];
634 else
9a83f860 635 s.Printf(wxT("unrecognized kind (%d)"), kind);
3d8b5d85
VZ
636
637 return s;
638}
639
640static wxString UdtKindString(wxDbgHelpDLL::UdtKind kind)
641{
642 static const wxChar *kinds[] =
643 {
9a83f860
VZ
644 wxT("struct"),
645 wxT("class"),
646 wxT("union"),
3d8b5d85
VZ
647 };
648
649 wxCOMPILE_TIME_ASSERT( WXSIZEOF(kinds) == wxDbgHelpDLL::UDT_MAX,
650 UDTKindStringMismatch );
651
652 wxString s;
653 if ( kind < WXSIZEOF(kinds) )
654 s = kinds[kind];
655 else
9a83f860 656 s.Printf(wxT("unrecognized UDT (%d)"), kind);
3d8b5d85
VZ
657
658 return s;
659}
660
661static wxString TypeString(wxDbgHelpDLL::BasicType bt)
662{
663 static const wxChar *types[] =
664 {
9a83f860
VZ
665 wxT("no type"),
666 wxT("void"),
667 wxT("char"),
668 wxT("wchar"),
669 wxT(""),
670 wxT(""),
671 wxT("int"),
672 wxT("uint"),
673 wxT("float"),
674 wxT("bcd"),
675 wxT("bool"),
676 wxT(""),
677 wxT(""),
678 wxT("long"),
679 wxT("ulong"),
680 wxT(""),
681 wxT(""),
682 wxT(""),
683 wxT(""),
684 wxT(""),
685 wxT(""),
686 wxT(""),
687 wxT(""),
688 wxT(""),
689 wxT(""),
690 wxT("CURRENCY"),
691 wxT("DATE"),
692 wxT("VARIANT"),
693 wxT("complex"),
694 wxT("bit"),
695 wxT("BSTR"),
696 wxT("HRESULT"),
3d8b5d85
VZ
697 };
698
699 wxCOMPILE_TIME_ASSERT( WXSIZEOF(types) == wxDbgHelpDLL::BASICTYPE_MAX,
700 BasicTypeStringMismatch );
701
702 wxString s;
703 if ( bt < WXSIZEOF(types) )
704 s = types[bt];
705
706 if ( s.empty() )
9a83f860 707 s.Printf(wxT("unrecognized type (%d)"), bt);
3d8b5d85
VZ
708
709 return s;
710}
711
712// this function is meant to be called from under debugger to see the
713// proprieties of the given type id
714extern "C" void DumpTI(ULONG ti)
715{
716 SYMBOL_INFO sym = { sizeof(SYMBOL_INFO) };
717 sym.ModBase = 0x400000; // it's a constant under Win32
718 sym.TypeIndex = ti;
719
720 wxDbgHelpDLL::SymbolTag tag = wxDbgHelpDLL::SYMBOL_TAG_NULL;
721 DoGetTypeInfo(&sym, TI_GET_SYMTAG, &tag);
722 DoGetTypeInfo(&sym, TI_GET_TYPEID, &ti);
723
9a83f860 724 OutputDebugString(wxString::Format(wxT("Type 0x%x: "), sym.TypeIndex));
3d8b5d85
VZ
725 wxString name = wxDbgHelpDLL::GetSymbolName(&sym);
726 if ( !name.empty() )
727 {
9a83f860 728 OutputDebugString(wxString::Format(wxT("name=\"%s\", "), name.c_str()));
3d8b5d85
VZ
729 }
730
731 DWORD nested;
732 if ( !DoGetTypeInfo(&sym, TI_GET_NESTED, &nested) )
733 {
734 nested = FALSE;
735 }
736
9a83f860
VZ
737 OutputDebugString(wxString::Format(wxT("tag=%s%s"),
738 nested ? wxT("nested ") : wxEmptyString,
3d8b5d85
VZ
739 TagString(tag).c_str()));
740 if ( tag == wxDbgHelpDLL::SYMBOL_TAG_UDT )
741 {
742 wxDbgHelpDLL::UdtKind udtKind;
743 if ( DoGetTypeInfo(&sym, TI_GET_UDTKIND, &udtKind) )
744 {
9a83f860 745 OutputDebugString(wxT(" (") + UdtKindString(udtKind) + wxT(')'));
3d8b5d85
VZ
746 }
747 }
748
749 wxDbgHelpDLL::DataKind kind = wxDbgHelpDLL::DATA_UNKNOWN;
750 if ( DoGetTypeInfo(&sym, TI_GET_DATAKIND, &kind) )
751 {
752 OutputDebugString(wxString::Format(
9a83f860 753 wxT(", kind=%s"), KindString(kind).c_str()));
3d8b5d85
VZ
754 if ( kind == wxDbgHelpDLL::DATA_MEMBER )
755 {
756 DWORD ofs = 0;
757 if ( DoGetTypeInfo(&sym, TI_GET_OFFSET, &ofs) )
758 {
9a83f860 759 OutputDebugString(wxString::Format(wxT(" (ofs=0x%x)"), ofs));
3d8b5d85
VZ
760 }
761 }
762 }
763
764 wxDbgHelpDLL::BasicType bt = GetBasicType(&sym);
765 if ( bt )
766 {
9a83f860 767 OutputDebugString(wxString::Format(wxT(", type=%s"),
3d8b5d85
VZ
768 TypeString(bt).c_str()));
769 }
770
771 if ( ti != sym.TypeIndex )
772 {
9a83f860 773 OutputDebugString(wxString::Format(wxT(", next ti=0x%x"), ti));
3d8b5d85
VZ
774 }
775
9a83f860 776 OutputDebugString(wxT("\r\n"));
3d8b5d85
VZ
777}
778
779#endif // NDEBUG
780
781#endif // wxUSE_DBGHELP