ntdbgui.c 152 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772
  1. /*++
  2. Copyright (c) 2012 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. ntdbgui.c
  9. Abstract:
  10. This module implements the graphical UI for the debugger running on
  11. Windows.
  12. Author:
  13. Evan Green 14-Jul-2012
  14. Environment:
  15. Debugger (Windows)
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. //
  21. // Define WIN32 versions to enable group view for the list view control.
  22. //
  23. #define _WIN32_WINNT 0x0600
  24. #define _WIN32_IE 0x0600
  25. #include <windows.h>
  26. #include <windowsx.h>
  27. #include <commctrl.h>
  28. #include <commdlg.h>
  29. #include <richedit.h>
  30. #include <shlwapi.h>
  31. #include <fcntl.h>
  32. #include <limits.h>
  33. #include <assert.h>
  34. #include <stdio.h>
  35. #include <minoca/debug/spproto.h>
  36. #include <minoca/debug/dbgext.h>
  37. #include "dbgrprof.h"
  38. #include "console.h"
  39. #include "resource.h"
  40. #include "missing.h"
  41. //
  42. // ---------------------------------------------------------------- Definitions
  43. //
  44. #define MAX_KEYWORD 30
  45. #define MAX_FILENAME MAX_PATH
  46. #define BACKGROUND_COLOR RGB(39, 40, 34)
  47. #define BREAKPOINT_COLOR RGB(140, 0, 0)
  48. #define EXECUTING_COLOR RGB(9, 2, 134)
  49. #define PLAIN_TEXT_COLOR "\\red248\\green248\\blue242"
  50. #define CONSTANT_COLOR "\\red174\\green129\\blue255"
  51. #define KEYWORD_COLOR "\\red249\\green38\\blue114"
  52. #define COMMENT_COLOR "\\red117\\green113\\blue94"
  53. #define BRACE_COLOR "\\red240\\green240\\blue240"
  54. #define QUOTE_COLOR "\\red230\\green219\\blue90"
  55. #define DISABLED_COLOR "\\red70\\green70\\blue70"
  56. #define RTF_HEADER "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deftab720{\\fonttbl{" \
  57. "\\f0\\fmodern\\fcharset1 Courier New;}}{\\colortbl ;" \
  58. PLAIN_TEXT_COLOR ";" CONSTANT_COLOR ";" KEYWORD_COLOR ";" \
  59. COMMENT_COLOR ";" BRACE_COLOR ";" QUOTE_COLOR ";" \
  60. DISABLED_COLOR ";}" \
  61. "\\deflang1033\\pard\\plain\\f0\\fs18 \\cf1"
  62. #define RTF_FOOTER "}"
  63. #define RTF_NEWLINE "\\highlight0\\par"
  64. #define RTF_NEWLINE_SIZE 15
  65. #define RTF_PLAIN_TEXT "\\cf1 "
  66. #define RTF_CONSTANT "\\cf2 "
  67. #define RTF_KEYWORD "\\cf3 "
  68. #define RTF_COMMENT "\\cf4 "
  69. #define RTF_BRACE "\\cf5 "
  70. #define RTF_QUOTE "\\cf6 "
  71. #define RTF_DISABLED "\\cf7 "
  72. #define RTF_COLOR_SIZE 5
  73. #define RTF_TEST "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deftab720{\\fonttbl{\\f0" \
  74. "\\fmodern\\fcharset1 Courier New;}}\\deflang1033\\pard" \
  75. "\\plain\\f0\\fs22 howdy }"
  76. //
  77. // Define values associated with the profiler display timer.
  78. //
  79. #define PROFILER_TIMER_ID 0x1
  80. #define PROFILER_TIMER_PERIOD 1000
  81. //
  82. // Define the name for the root of the call stack tree.
  83. //
  84. #define CALL_STACK_TREE_ROOT_STRING "Root"
  85. //
  86. // Define the number of columns in the memory statistics list view.
  87. //
  88. #define MEMORY_STATISTICS_COLUMN_COUNT 7
  89. //
  90. // Add some extra padding to make mouse click regions bigger.
  91. //
  92. #define UI_MOUSE_PLAY 8
  93. //
  94. // Define the current debugger UI preferences version number.
  95. //
  96. #define DEBUGGER_UI_PREFERENCES_VERSION 2
  97. //
  98. // ------------------------------------------------------ Data Type Definitions
  99. //
  100. //
  101. // Define the function type for the common controls initialization routine.
  102. //
  103. typedef
  104. BOOL
  105. (WINAPI *PINITCOMMONCONTROLSEX) (
  106. LPINITCOMMONCONTROLSEX lpInitCtrls
  107. );
  108. typedef
  109. INT
  110. (*PNTDBGCOMPAREROUTINE) (
  111. ULONGLONG ValueOne,
  112. ULONGLONG ValueTwo
  113. );
  114. typedef
  115. ULONGLONG
  116. (*PGETCOLUMNVALUE) (
  117. PVOID Structure,
  118. ULONG Offset
  119. );
  120. typedef struct _STREAM_IN_DATA {
  121. PCHAR Buffer;
  122. ULONG BufferSize;
  123. ULONG CurrentPosition;
  124. } STREAM_IN_DATA, *PSTREAM_IN_DATA;
  125. /*++
  126. Structure Description:
  127. This structure defines strings, string formats, and various routines
  128. associated with a column in the memory statistics list view.
  129. Members:
  130. Header - Stores the string to use as the column header.
  131. Format - Stores the FormatMessage style format message for the column's
  132. data.
  133. DeltaFormat - Stores the FormatMessage style format message to use for the
  134. column's data when in delta mode.
  135. CompareRoutine - Stores a pointer to a routine that can be used to compare
  136. two elements in this column.
  137. DeltaCompareRoutine - Stores a pointer to a routine that can be used to
  138. compare two elements in this column when in delta mode.
  139. Offset - Stores the offset within the PROFILER_MEMORY_POOL_TAG_STATISTIC
  140. structure that holds the column's data.
  141. --*/
  142. typedef struct _MEMORY_COLUMN {
  143. LPTSTR Header;
  144. LPTSTR Format;
  145. LPTSTR DeltaFormat;
  146. PNTDBGCOMPAREROUTINE CompareRoutine;
  147. PNTDBGCOMPAREROUTINE DeltaCompareRoutine;
  148. PGETCOLUMNVALUE GetColumnValueRoutine;
  149. ULONG Offset;
  150. } MEMORY_COLUMN, *PMEMORY_COLUMN;
  151. /*++
  152. Structure Description:
  153. This structure defines strings, string formats, and various routines
  154. associated with a column in the memory statistics list view.
  155. Members:
  156. Version - Stores the version of the structure. Set to
  157. DEBUGGER_UI_PREFERENCES_VERSION.
  158. WindowX - Stores the X position of the debugger window in pixels.
  159. WindowY - Stores the Y position of the debugger window in pixels.
  160. WindowWidth - Stores the width of the debugger window in pixels.
  161. WindowHeight - Stores the height of the debugger window in pixels.
  162. MainPaneXPosition - Stores the X position of the divider between the two
  163. main panes.
  164. MainPainXPositionWidth - Stores the width of the main pane at the time the
  165. X position was stored. The X position is used to create a percentage of
  166. the current width and is only relevant if the old width is saved.
  167. ProfilerPaneYPosition - Stores the Y position of the diveder between the
  168. profiler and source code.
  169. ProfilerPaneYPositionHeight - Store the height of the left pain at the time
  170. the Y position for the profiler was stored. The Y position is used to
  171. create a percentage of the current height and is only relevant if the
  172. old height is stored.
  173. --*/
  174. typedef struct _DEBUGGER_UI_PREFERENCES {
  175. ULONG Version;
  176. DWORD WindowX;
  177. DWORD WindowY;
  178. DWORD WindowWidth;
  179. DWORD WindowHeight;
  180. DWORD MainPaneXPosition;
  181. DWORD MainPaneXPositionWidth;
  182. DWORD ProfilerPaneYPosition;
  183. DWORD ProfilerPaneYPositionHeight;
  184. } DEBUGGER_UI_PREFERENCES, *PDEBUGGER_UI_PREFERENCES;
  185. //
  186. // ----------------------------------------------- Internal Function Prototypes
  187. //
  188. DWORD
  189. WINAPI
  190. ConsoleStandardOutThread (
  191. LPVOID WindowHandle
  192. );
  193. DWORD
  194. WINAPI
  195. UiThreadMain (
  196. LPVOID Parameter
  197. );
  198. INT_PTR
  199. CALLBACK
  200. MainDialogProc (
  201. HWND DialogHandle,
  202. UINT Message,
  203. WPARAM WParam,
  204. LPARAM LParam
  205. );
  206. LRESULT
  207. CALLBACK
  208. TreeViewWindowProcedure (
  209. HWND Window,
  210. UINT Message,
  211. WPARAM WParam,
  212. LPARAM LParam
  213. );
  214. DWORD
  215. CALLBACK
  216. RichEditLoadCallback (
  217. DWORD_PTR Context,
  218. LPBYTE Buffer,
  219. LONG Bytes,
  220. PLONG BytesComplete
  221. );
  222. BOOL
  223. LoadFileIntoRichEdit (
  224. HWND RichEdit,
  225. LPCTSTR Filename,
  226. PUCHAR FileBuffer,
  227. ULONGLONG FileSize
  228. );
  229. BOOL
  230. HighlightSyntax (
  231. PUCHAR TextBuffer,
  232. ULONG TextBufferSize,
  233. PCHAR *BufferOut,
  234. PULONG FileSizeOut
  235. );
  236. BOOL
  237. IsKeyword (
  238. PSTR String
  239. );
  240. BOOL
  241. IsKeywordSeparator (
  242. UCHAR Character
  243. );
  244. VOID
  245. HighlightLine (
  246. HWND RichEdit,
  247. LONG LineNumber,
  248. COLORREF Color,
  249. BOOL ScrollToLine
  250. );
  251. VOID
  252. HandleResize (
  253. HWND Dialog
  254. );
  255. VOID
  256. HandleCommandMessage (
  257. HWND Dialog,
  258. WPARAM WParam
  259. );
  260. VOID
  261. HandleCommonControlMessage (
  262. HWND Dialog,
  263. LPARAM LParam
  264. );
  265. VOID
  266. HandleCommandEnter (
  267. HWND CommandEdit
  268. );
  269. VOID
  270. WriteByteToInput (
  271. BYTE Byte
  272. );
  273. VOID
  274. InitializeProfilerControls (
  275. VOID
  276. );
  277. VOID
  278. UpdateProfilerWindowType (
  279. HWND Dialog,
  280. PROFILER_DATA_TYPE DataType
  281. );
  282. VOID
  283. HandleProfilerTreeViewCommand (
  284. HWND Dialog,
  285. LPARAM LParam
  286. );
  287. VOID
  288. HandleProfilerListViewCommand (
  289. HWND Dialog,
  290. LPARAM LParam
  291. );
  292. PSTACK_DATA_ENTRY
  293. FindStackDataEntryByHandle (
  294. PSTACK_DATA_ENTRY Root,
  295. HTREEITEM Handle
  296. );
  297. VOID
  298. SetProfilerTimer (
  299. PROFILER_DATA_TYPE DataType
  300. );
  301. VOID
  302. KillProfilerTimer (
  303. PROFILER_DATA_TYPE DataType
  304. );
  305. VOID
  306. PauseProfilerTimer (
  307. VOID
  308. );
  309. VOID
  310. ResumeProfilerTimer (
  311. VOID
  312. );
  313. VOID
  314. CALLBACK
  315. ProfilerTimerCallback (
  316. HWND DialogHandle,
  317. UINT Message,
  318. UINT_PTR EventId,
  319. DWORD Time
  320. );
  321. BOOL
  322. UpdateProfilerDisplay (
  323. PROFILER_DATA_TYPE DataType,
  324. PROFILER_DISPLAY_REQUEST DisplayRequest,
  325. ULONG Threshold
  326. );
  327. VOID
  328. UpdateCallStackTree (
  329. HTREEITEM Parent,
  330. PSTACK_DATA_ENTRY Root,
  331. ULONG TotalCount
  332. );
  333. INT
  334. CALLBACK
  335. StackProfilerTreeCompare (
  336. LPARAM LParamOne,
  337. LPARAM LParamTwo,
  338. LPARAM LParamSort
  339. );
  340. VOID
  341. UpdateMemoryStatisticsListView (
  342. PLIST_ENTRY PoolListHead
  343. );
  344. BOOL
  345. CreateMemoryPoolListViewGroup (
  346. PPROFILER_MEMORY_POOL MemoryPool,
  347. PINT GroupId
  348. );
  349. BOOL
  350. DoesMemoryPoolListViewGroupExist (
  351. PPROFILER_MEMORY_POOL MemoryPool,
  352. PINT GroupId
  353. );
  354. BOOL
  355. UpdateMemoryPoolListViewGroup (
  356. PPROFILER_MEMORY_POOL MemoryPool,
  357. INT GroupId
  358. );
  359. INT
  360. GetMemoryPoolGroupId (
  361. PPROFILER_MEMORY_POOL MemoryPool
  362. );
  363. BOOL
  364. CreateMemoryPoolTagListViewItem (
  365. ULONG Tag,
  366. INT GroupId,
  367. PINT ItemIndex
  368. );
  369. VOID
  370. DeleteMemoryPoolTagListViewItem (
  371. INT ListViewIndex
  372. );
  373. BOOL
  374. DoesMemoryPoolTagListViewItemExist (
  375. PPROFILER_MEMORY_POOL_TAG_STATISTIC Statistic,
  376. INT GroupId,
  377. PINT ListViewIndex
  378. );
  379. BOOL
  380. UpdateMemoryPoolTagListViewItem (
  381. INT ItemIndex,
  382. INT GroupId,
  383. PPROFILER_MEMORY_POOL_TAG_STATISTIC Statistic
  384. );
  385. INT
  386. CALLBACK
  387. MemoryProfilerListViewCompare (
  388. LPARAM LParamOne,
  389. LPARAM LParamTwo,
  390. LPARAM LParamSort
  391. );
  392. BOOL
  393. TreeViewIsTreeItemVisible (
  394. HWND TreeViewWindow,
  395. HTREEITEM TreeItem
  396. );
  397. BOOL
  398. TreeViewGetItemRect (
  399. HWND Window,
  400. HTREEITEM Item,
  401. LPRECT Rect,
  402. BOOL ItemRect
  403. );
  404. LPTSTR
  405. GetFormattedMessageA (
  406. LPTSTR Message,
  407. ...
  408. );
  409. LPWSTR
  410. GetFormattedMessageW (
  411. LPWSTR Message,
  412. ...
  413. );
  414. VOID
  415. ListViewSetItemText (
  416. HWND Window,
  417. int Item,
  418. int SubItem,
  419. LPTSTR Text
  420. );
  421. INT
  422. ComparePoolTag (
  423. ULONGLONG ValueOne,
  424. ULONGLONG ValueTwo
  425. );
  426. INT
  427. CompareUlong (
  428. ULONGLONG ValueOne,
  429. ULONGLONG ValueTwo
  430. );
  431. INT
  432. CompareLong (
  433. ULONGLONG ValueOne,
  434. ULONGLONG ValueTwo
  435. );
  436. INT
  437. CompareLonglong (
  438. ULONGLONG ValueOne,
  439. ULONGLONG ValueTwo
  440. );
  441. INT
  442. CompareUlonglong (
  443. ULONGLONG ValueOne,
  444. ULONGLONG ValueTwo
  445. );
  446. ULONGLONG
  447. GetUlonglongValue (
  448. PVOID Structure,
  449. ULONG Offset
  450. );
  451. ULONGLONG
  452. GetUlongValue (
  453. PVOID Structure,
  454. ULONG Offset
  455. );
  456. VOID
  457. UiGetWindowPreferences (
  458. HWND Dialog
  459. );
  460. VOID
  461. UiLoadPreferences (
  462. HWND Dialog
  463. );
  464. VOID
  465. UiSavePreferences (
  466. HWND Dialog
  467. );
  468. BOOL
  469. UiReadPreferences (
  470. PDEBUGGER_UI_PREFERENCES Preferences
  471. );
  472. BOOL
  473. UiWritePreferences (
  474. PDEBUGGER_UI_PREFERENCES Preferences
  475. );
  476. HANDLE
  477. UiOpenPreferences (
  478. VOID
  479. );
  480. //
  481. // -------------------------------------------------------------------- Globals
  482. //
  483. HANDLE StdInPipeRead;
  484. HANDLE StdInPipeWrite;
  485. HANDLE StdOutPipe;
  486. BOOL ConsoleInitialized = FALSE;
  487. HWND DialogWindow;
  488. extern HANDLE StdInPipeWrite;
  489. //
  490. // Remember whether or not commands are currently enabled.
  491. //
  492. BOOL CommandsEnabled;
  493. //
  494. // Stores an enumerated type indicating which data type is currently showing in
  495. // the profiler window. The max type means the window is hidden.
  496. //
  497. PROFILER_DATA_TYPE ProfilerWindowType = ProfilerDataTypeMax;
  498. //
  499. // Stores a pointer to the root of the profiler stack tree.
  500. //
  501. PSTACK_DATA_ENTRY StackTreeRoot = NULL;
  502. //
  503. // Stores a handle to a lock that protects access to the stack tree.
  504. //
  505. HANDLE StackTreeLock;
  506. //
  507. // Stores an array describing which profiling types are using the timer.
  508. //
  509. BOOL ProfilerTimerTypes[ProfilerDataTypeMax];
  510. //
  511. // Stores a pointer to the original Tree View Window message procedure call.
  512. //
  513. WNDPROC OriginalTreeViewWindowProcedure;
  514. //
  515. // Stores a handle to the currently selected Tree View item.
  516. //
  517. HTREEITEM TreeViewSelection = NULL;
  518. //
  519. // Stores a boolean indicating whether the currently selected Tree View item is
  520. // visible.
  521. //
  522. BOOL TreeViewSelectionVisible = FALSE;
  523. //
  524. // Stores an array of memory statistics list view column names.
  525. //
  526. MEMORY_COLUMN MemoryStatisticsColumns[MEMORY_STATISTICS_COLUMN_COUNT] = {
  527. {"Tag",
  528. "%1!c!%2!c!%3!c!%4!c!",
  529. "%1!c!%2!c!%3!c!%4!c!",
  530. ComparePoolTag,
  531. ComparePoolTag,
  532. GetUlongValue,
  533. FIELD_OFFSET(PROFILER_MEMORY_POOL_TAG_STATISTIC, Tag)},
  534. {"Largest Alloc",
  535. "%1!#I32x!",
  536. "%1!#I32x!",
  537. CompareUlong,
  538. CompareUlong,
  539. GetUlongValue,
  540. FIELD_OFFSET(PROFILER_MEMORY_POOL_TAG_STATISTIC, LargestAllocation)},
  541. {"Active Bytes",
  542. "%1!#I64x!",
  543. "%1!I64d!",
  544. CompareUlonglong,
  545. CompareLonglong,
  546. GetUlonglongValue,
  547. FIELD_OFFSET(PROFILER_MEMORY_POOL_TAG_STATISTIC, ActiveSize)},
  548. {"Max Active Bytes",
  549. "%1!#I64x!",
  550. "%1!#I64x!",
  551. CompareUlonglong,
  552. CompareUlonglong,
  553. GetUlonglongValue,
  554. FIELD_OFFSET(PROFILER_MEMORY_POOL_TAG_STATISTIC, LargestActiveSize)},
  555. {"Active Count",
  556. "%1!I32u!",
  557. "%1!I32d!",
  558. CompareUlong,
  559. CompareLong,
  560. GetUlongValue,
  561. FIELD_OFFSET(PROFILER_MEMORY_POOL_TAG_STATISTIC, ActiveAllocationCount)},
  562. {"Max Count",
  563. "%1!I32u!",
  564. "%1!I32u!",
  565. CompareUlong,
  566. CompareUlong,
  567. GetUlongValue,
  568. FIELD_OFFSET(PROFILER_MEMORY_POOL_TAG_STATISTIC,
  569. LargestActiveAllocationCount)},
  570. {"Lifetime Alloc",
  571. "%1!#I64x!",
  572. "%1!#I64x!",
  573. CompareUlonglong,
  574. CompareUlonglong,
  575. GetUlonglongValue,
  576. FIELD_OFFSET(PROFILER_MEMORY_POOL_TAG_STATISTIC, LifetimeAllocationSize)}
  577. };
  578. //
  579. // Stores an array of headers for each of the profiler memory types.
  580. //
  581. LPWSTR MemoryStatisticsPoolHeaders[ProfilerMemoryTypeMax] = {
  582. L"Non-Paged Pool",
  583. L"Paged Pool"
  584. };
  585. //
  586. // Stores a handle to a lock that protects access to the memory lists.
  587. //
  588. HANDLE MemoryListLock;
  589. //
  590. // Stores a list of memory pools.
  591. //
  592. PLIST_ENTRY MemoryPoolListHead = NULL;
  593. //
  594. // Stores a list of base line memory statistics used to display deltas.
  595. //
  596. PLIST_ENTRY MemoryBaseListHead = NULL;
  597. //
  598. // Stores a point to the memory pool deltas between the current list and the
  599. // base line list.
  600. //
  601. PLIST_ENTRY MemoryDeltaListHead = NULL;
  602. //
  603. // Stores a boolean indicating whether or not delta memory display mode is
  604. // enabled.
  605. //
  606. BOOL MemoryDeltaModeEnabled = FALSE;
  607. //
  608. // Stores memory list view sorting variables.
  609. //
  610. INT CurrentSortColumn = INT_MAX;
  611. BOOL SortAscending = TRUE;
  612. //
  613. // Store whether or not various panes are currently being resized.
  614. //
  615. BOOL WindowSizesInitialized = FALSE;
  616. BOOL ResizingMainPanes = FALSE;
  617. LONG MainPaneXPosition;
  618. LONG MainPaneXPositionWidth;
  619. BOOL ResizingProfilerPane = FALSE;
  620. LONG ProfilerPaneYPosition;
  621. LONG ProfilerPaneYPositionHeight;
  622. LONG ProfilerPaneCurrentYPosition;
  623. //
  624. // Stores the window rect last captured before a minimize or maximize. This is
  625. // used to save the UI preferences.
  626. //
  627. RECT CurrentWindowRect;
  628. //
  629. // ------------------------------------------------------------------ Functions
  630. //
  631. int
  632. APIENTRY
  633. WinMain (
  634. HINSTANCE Instance,
  635. HINSTANCE PreviousInstance,
  636. LPSTR CommandLine,
  637. int CmdShow
  638. )
  639. /*++
  640. Routine Description:
  641. This routine is the main entry point for a Win32 application. It simply
  642. calls the plaform-independent main function.
  643. Arguments:
  644. Instance - Supplies a handle to the current instance of the application.
  645. PreviousInstance - Supplies a handle to the previous instance of the
  646. application.
  647. CommandLine - Supplies a pointer to a null-terminated string specifying the
  648. command line for the application, excluding the program name.
  649. CmdShow - Supplies how the window is to be shown.
  650. Return Value:
  651. Returns TRUE on success, FALSE on failure.
  652. --*/
  653. {
  654. HANDLE PipeRead;
  655. HANDLE PipeWrite;
  656. BOOL Result;
  657. INT ReturnValue;
  658. HANDLE UiThread;
  659. //
  660. // Create a pipe for the standard output.
  661. //
  662. Result = CreatePipe(&PipeRead, &PipeWrite, NULL, 0);
  663. if (Result == FALSE) {
  664. DbgOut("Error: Could not create stdout pipe.\n");
  665. return 1;
  666. }
  667. //
  668. // Set standard output to point to the pipe.
  669. //
  670. Result = SetStdHandle(STD_OUTPUT_HANDLE, PipeWrite);
  671. if (Result == FALSE) {
  672. DbgOut("Error: Could not redirect stdout.\n");
  673. return 2;
  674. }
  675. StdOutPipe = PipeRead;
  676. //
  677. // Create a pipe for standard input.
  678. //
  679. Result = CreatePipe(&StdInPipeRead, &StdInPipeWrite, NULL, 0);
  680. if (Result == FALSE) {
  681. DbgOut("Error: Could not create stdin pipe.\n");
  682. return 3;
  683. }
  684. //
  685. // Redirect Standard output to the pipe.
  686. //
  687. stdout->_file = _open_osfhandle((LONG)PipeWrite, 0);
  688. setvbuf(stdout, NULL, _IONBF, 0);
  689. //
  690. // Kick off the UI thread.
  691. //
  692. UiThread = CreateThread(NULL, 0, UiThreadMain, NULL, 0, NULL);
  693. if (UiThread == NULL) {
  694. DbgOut("Unable to create the UI thread!\n");
  695. return 4;
  696. }
  697. ReturnValue = DbgrMain(__argc, __argv);
  698. return ReturnValue;
  699. }
  700. BOOL
  701. DbgrOsInitializeConsole (
  702. PBOOL EchoCommands
  703. )
  704. /*++
  705. Routine Description:
  706. This routine performs any initialization steps necessary before the console
  707. can be used.
  708. Arguments:
  709. EchoCommands - Supplies a pointer where a boolean will be returned
  710. indicating if the debugger should echo commands received (TRUE) or if
  711. the console has already echoed the command (FALSE).
  712. Return Value:
  713. Returns TRUE on success, FALSE on failure.
  714. --*/
  715. {
  716. ULONG Index;
  717. ULONG Retries;
  718. *EchoCommands = TRUE;
  719. //
  720. // Wait for the UI to initialize.
  721. //
  722. Retries = 10;
  723. while (Retries != 0) {
  724. if (ConsoleInitialized != FALSE) {
  725. break;
  726. }
  727. Sleep(100);
  728. Retries -= 1;
  729. }
  730. //
  731. // If the UI timed out, fail.
  732. //
  733. if (Retries == 0) {
  734. return FALSE;
  735. }
  736. //
  737. // Disable commands from being sent.
  738. //
  739. SetFocus(GetDlgItem(DialogWindow, IDE_COMMAND));
  740. UiEnableCommands(FALSE);
  741. //
  742. // Create a lock to protect access to the stack data tree.
  743. //
  744. StackTreeLock = CreateDebuggerLock();
  745. if (StackTreeLock == NULL) {
  746. return FALSE;
  747. }
  748. //
  749. // Create a lock to protect access to the memory pool lists.
  750. //
  751. MemoryListLock = CreateDebuggerLock();
  752. if (MemoryListLock == NULL) {
  753. return FALSE;
  754. }
  755. //
  756. // Initialize the profiler timer references.
  757. //
  758. for (Index = 0; Index < ProfilerDataTypeMax; Index += 1) {
  759. ProfilerTimerTypes[Index] = FALSE;
  760. }
  761. return TRUE;
  762. }
  763. VOID
  764. DbgrOsDestroyConsole (
  765. VOID
  766. )
  767. /*++
  768. Routine Description:
  769. This routine cleans up anything related to console functionality as a
  770. debugger is exiting.
  771. Arguments:
  772. None.
  773. Return Value:
  774. None.
  775. --*/
  776. {
  777. if (StackTreeLock != NULL) {
  778. AcquireDebuggerLock(StackTreeLock);
  779. DbgrDestroyProfilerStackData(StackTreeRoot);
  780. ReleaseDebuggerLock(StackTreeLock);
  781. DestroyDebuggerLock(StackTreeLock);
  782. }
  783. if (MemoryListLock != NULL) {
  784. AcquireDebuggerLock(MemoryListLock);
  785. if (MemoryPoolListHead != MemoryBaseListHead) {
  786. DbgrDestroyProfilerMemoryData(MemoryBaseListHead);
  787. }
  788. DbgrDestroyProfilerMemoryData(MemoryPoolListHead);
  789. DbgrDestroyProfilerMemoryData(MemoryDeltaListHead);
  790. ReleaseDebuggerLock(MemoryListLock);
  791. DestroyDebuggerLock(MemoryListLock);
  792. }
  793. return;
  794. }
  795. VOID
  796. DbgrOsPrepareToReadInput (
  797. VOID
  798. )
  799. /*++
  800. Routine Description:
  801. This routine is called before the debugger begins to read a line of input
  802. from the user.
  803. Arguments:
  804. None.
  805. Return Value:
  806. None.
  807. --*/
  808. {
  809. return;
  810. }
  811. BOOL
  812. DbgrOsGetCharacter (
  813. PUCHAR Key,
  814. PUCHAR ControlKey
  815. )
  816. /*++
  817. Routine Description:
  818. This routine gets one character from the standard input console.
  819. Arguments:
  820. Key - Supplies a pointer that receives the printable character. If this
  821. parameter is NULL, printing characters will be discarded from the input
  822. buffer.
  823. ControlKey - Supplies a pointer that receives the non-printable character.
  824. If this parameter is NULL, non-printing characters will be discarded
  825. from the input buffer.
  826. Return Value:
  827. Returns TRUE on success, FALSE on failure.
  828. --*/
  829. {
  830. ULONG BytesRead;
  831. UCHAR Character;
  832. UCHAR ControlKeyValue;
  833. BOOL Result;
  834. ControlKeyValue = 0;
  835. while (TRUE) {
  836. Result = ReadFile(StdInPipeRead, &Character, 1, &BytesRead, NULL);
  837. if (Result == FALSE) {
  838. goto GetCharacterEnd;
  839. }
  840. if (BytesRead != 1) {
  841. continue;
  842. }
  843. //
  844. // If it's the magic escape character, look to see if it's a literal
  845. // escape or just a poke character since there's remote input.
  846. //
  847. if (Character == 0xFF) {
  848. Result = ReadFile(StdInPipeRead, &Character, 1, &BytesRead, NULL);
  849. if (Result == FALSE) {
  850. goto GetCharacterEnd;
  851. }
  852. if (BytesRead != 1) {
  853. DbgOut("Failed to read a second byte.\n");
  854. continue;
  855. }
  856. if (Character != 0xFF) {
  857. Character = 0;
  858. ControlKeyValue = KEY_REMOTE;
  859. }
  860. }
  861. break;
  862. }
  863. //
  864. // Handle non-printing characters.
  865. //
  866. if (Character == '\n') {
  867. Character = 0;
  868. ControlKeyValue = KEY_RETURN;
  869. }
  870. if ((Character == KEY_UP) || (Character == KEY_DOWN) ||
  871. (Character == KEY_ESCAPE)) {
  872. ControlKeyValue = Character;
  873. Character = 0;
  874. }
  875. Result = TRUE;
  876. GetCharacterEnd:
  877. if (Key != NULL) {
  878. *Key = Character;
  879. }
  880. if (ControlKey != NULL) {
  881. *ControlKey = ControlKeyValue;
  882. }
  883. return Result;
  884. }
  885. VOID
  886. DbgrOsRemoteInputAdded (
  887. VOID
  888. )
  889. /*++
  890. Routine Description:
  891. This routine is called after a remote command is received and placed on the
  892. standard input remote command list. It wakes up a thread blocked on local
  893. user input in an OS-dependent fashion.
  894. Arguments:
  895. None.
  896. Return Value:
  897. None.
  898. --*/
  899. {
  900. DWORD BytesWritten;
  901. unsigned char Message[2];
  902. //
  903. // Write the escaped "remote" sequence into the input pipe funnel.
  904. //
  905. Message[0] = 0xFF;
  906. Message[1] = 0x00;
  907. WriteFile(StdInPipeWrite,
  908. Message,
  909. sizeof(Message),
  910. &BytesWritten,
  911. NULL);
  912. return;
  913. }
  914. VOID
  915. DbgrOsPostInputCallback (
  916. VOID
  917. )
  918. /*++
  919. Routine Description:
  920. This routine is called after a line of input is read from the user, giving
  921. the OS specific code a chance to restore anything it did in the prepare
  922. to read input function.
  923. Arguments:
  924. None.
  925. Return Value:
  926. None.
  927. --*/
  928. {
  929. return;
  930. }
  931. BOOL
  932. UiLoadSourceFile (
  933. PSTR Path,
  934. PVOID Contents,
  935. ULONGLONG Size
  936. )
  937. /*++
  938. Routine Description:
  939. This routine loads the contents of a file into the source window.
  940. Arguments:
  941. Path - Supplies the path of the file being loaded. If this is NULL, then
  942. the source window should be cleared.
  943. Contents - Supplies the source file data. This can be NULL.
  944. Size - Supplies the size of the source file data in bytes.
  945. Return Value:
  946. Returns TRUE if there was no error, or FALSE if there was an error.
  947. --*/
  948. {
  949. BOOL Result;
  950. HWND RichEdit;
  951. HWND SourceFileEdit;
  952. INT TextLength;
  953. if (DialogWindow == NULL) {
  954. return FALSE;
  955. }
  956. Result = TRUE;
  957. RichEdit = GetDlgItem(DialogWindow, IDE_SOURCE_RICHEDIT);
  958. SourceFileEdit = GetDlgItem(DialogWindow, IDE_SOURCE_FILE);
  959. //
  960. // If NULL was passed in for the file name, just pass that along. This has
  961. // the effect of clearing the source window.
  962. //
  963. if (Path == NULL) {
  964. Result = LoadFileIntoRichEdit(RichEdit, NULL, NULL, 0);
  965. Edit_SetText(SourceFileEdit, "");
  966. goto LoadSourceFileEnd;
  967. }
  968. //
  969. // If the file is not already loaded, then load it.
  970. //
  971. Result = LoadFileIntoRichEdit(RichEdit, Path, Contents, Size);
  972. Edit_SetText(SourceFileEdit, Path);
  973. LoadSourceFileEnd:
  974. //
  975. // Point the cursor at the end of the text.
  976. //
  977. TextLength = Edit_GetTextLength(SourceFileEdit);
  978. Edit_SetSel(SourceFileEdit, TextLength, TextLength);
  979. return Result;
  980. }
  981. BOOL
  982. UiHighlightExecutingLine (
  983. LONG LineNumber,
  984. BOOL Enable
  985. )
  986. /*++
  987. Routine Description:
  988. This routine highlights the currently executing line and scrolls the source
  989. window to it, or restores a previously executing source line to the
  990. normal background color.
  991. Arguments:
  992. LineNumber - Supplies the 1-based line number to highlight (ie the first
  993. line in the source file is line 1).
  994. Enable - Supplies a flag indicating whether to highlight this line (TRUE)
  995. or restore the line to its original color (FALSE).
  996. Return Value:
  997. Returns TRUE if there was no error, or FALSE if there was an error.
  998. --*/
  999. {
  1000. HWND RichEdit;
  1001. if (DialogWindow == NULL) {
  1002. return FALSE;
  1003. }
  1004. RichEdit = GetDlgItem(DialogWindow, IDE_SOURCE_RICHEDIT);
  1005. if (Enable != FALSE) {
  1006. HighlightLine(RichEdit, LineNumber, EXECUTING_COLOR, TRUE);
  1007. } else {
  1008. HighlightLine(RichEdit, LineNumber, BACKGROUND_COLOR, FALSE);
  1009. }
  1010. return TRUE;
  1011. }
  1012. VOID
  1013. UiEnableCommands (
  1014. BOOL Enable
  1015. )
  1016. /*++
  1017. Routine Description:
  1018. This routine enables or disables the command edit control from being
  1019. enabled. If disabled, the edit control will be made read only.
  1020. Arguments:
  1021. Enable - Supplies a flag indicating whether or not to enable (TRUE) or
  1022. disable (FALSE) the command box.
  1023. Return Value:
  1024. None.
  1025. --*/
  1026. {
  1027. HWND CommandEdit;
  1028. CommandEdit = GetDlgItem(DialogWindow, IDE_COMMAND);
  1029. CommandsEnabled = Enable;
  1030. if (Enable != FALSE) {
  1031. SendMessage(CommandEdit, EM_SETREADONLY, (WPARAM)FALSE, 0);
  1032. } else {
  1033. SendMessage(CommandEdit, EM_SETREADONLY, (WPARAM)TRUE, 0);
  1034. }
  1035. return;
  1036. }
  1037. VOID
  1038. UiSetCommandText (
  1039. PSTR Text
  1040. )
  1041. /*++
  1042. Routine Description:
  1043. This routine sets the text inside the command edit box.
  1044. Arguments:
  1045. Text - Supplies a pointer to a null terminated string to set the command
  1046. text to.
  1047. Return Value:
  1048. None.
  1049. --*/
  1050. {
  1051. HWND CommandEdit;
  1052. INT TextLength;
  1053. CommandEdit = GetDlgItem(DialogWindow, IDE_COMMAND);
  1054. Edit_SetText(CommandEdit, Text);
  1055. //
  1056. // Point the cursor at the end of the text.
  1057. //
  1058. TextLength = Edit_GetTextLength(CommandEdit);
  1059. Edit_SetSel(CommandEdit, TextLength, TextLength);
  1060. return;
  1061. }
  1062. DWORD
  1063. CALLBACK
  1064. RichEditLoadCallback (
  1065. DWORD_PTR Context,
  1066. LPBYTE Buffer,
  1067. LONG Bytes,
  1068. PLONG BytesComplete
  1069. )
  1070. /*++
  1071. Routine Description:
  1072. This routine is the callback function Windows uses to get input into the
  1073. rich edit control. When a EM_STREAMIN message is processed, the OS calls
  1074. this function repeatedly to get little chunks of data at a time to put into
  1075. the rich edit control.
  1076. Arguments:
  1077. Context - Supplies a context pointer supplied by the user when the
  1078. EM_STREAMIN message was passed.
  1079. Buffer - Supplies a pointer to the OS created buffer to stream data into.
  1080. Bytes - Supplies the number of bytes the OS wants this routine to put into
  1081. the Buffer.
  1082. BytesComplete - Supplies a pointer where the number of bytes actually
  1083. written into the buffer by this function is returned.
  1084. Return Value:
  1085. Returns 0 on success, and nonzero on failure.
  1086. --*/
  1087. {
  1088. ULONG BytesToTransfer;
  1089. PSTREAM_IN_DATA StreamData;
  1090. StreamData = (PSTREAM_IN_DATA)Context;
  1091. //
  1092. // If the caller didn't pass anything, just bail out now.
  1093. //
  1094. if (StreamData == NULL) {
  1095. return -1;
  1096. }
  1097. if (StreamData->CurrentPosition + Bytes > StreamData->BufferSize) {
  1098. BytesToTransfer = StreamData->BufferSize - StreamData->CurrentPosition;
  1099. } else {
  1100. BytesToTransfer = Bytes;
  1101. }
  1102. *BytesComplete = BytesToTransfer;
  1103. //
  1104. // If no bytes can be transferred, error out.
  1105. //
  1106. if (BytesToTransfer == 0) {
  1107. return -1;
  1108. }
  1109. //
  1110. // Some bytes can be copied, so do it and return success.
  1111. //
  1112. memcpy(Buffer,
  1113. StreamData->Buffer + StreamData->CurrentPosition,
  1114. BytesToTransfer);
  1115. StreamData->CurrentPosition += BytesToTransfer;
  1116. return 0;
  1117. }
  1118. VOID
  1119. UiSetPromptText (
  1120. PSTR Text
  1121. )
  1122. /*++
  1123. Routine Description:
  1124. This routine sets the text inside the prompt edit box.
  1125. Arguments:
  1126. Text - Supplies a pointer to a null terminated string to set the prompt
  1127. text to.
  1128. Return Value:
  1129. None.
  1130. --*/
  1131. {
  1132. HWND CommandEdit;
  1133. CommandEdit = GetDlgItem(DialogWindow, IDE_PROMPT);
  1134. Edit_SetText(CommandEdit, Text);
  1135. return;
  1136. }
  1137. VOID
  1138. UiDisplayProfilerData (
  1139. PROFILER_DATA_TYPE DataType,
  1140. PROFILER_DISPLAY_REQUEST DisplayRequest,
  1141. ULONG Threshold
  1142. )
  1143. /*++
  1144. Routine Description:
  1145. This routine displays the profiler data collected by the core debugging
  1146. infrastructure.
  1147. Arguments:
  1148. DataType - Supplies the type of profiler data that is to be displayed.
  1149. DisplayRequest - Supplies a value requesting a display action, which can
  1150. either be to display data once, continually, or to stop continually
  1151. displaying data.
  1152. Threshold - Supplies the minimum percentage a stack entry hit must be in
  1153. order to be displayed.
  1154. Return Value:
  1155. None.
  1156. --*/
  1157. {
  1158. BOOL DataDisplayed;
  1159. HWND Profiler;
  1160. //
  1161. // Pause the profiler timer before taking any action. If the timer goes
  1162. // off it will try to acquire one or more of the profiler locks, which
  1163. // could deadlock with this routine trying to output to the main dialog
  1164. // window.
  1165. //
  1166. PauseProfilerTimer();
  1167. switch (DisplayRequest) {
  1168. //
  1169. // If the debugger requested a one-time display of the profiler data, try
  1170. // to display the data.
  1171. //
  1172. case ProfilerDisplayOneTime:
  1173. case ProfilerDisplayOneTimeThreshold:
  1174. DataDisplayed = UpdateProfilerDisplay(DataType,
  1175. DisplayRequest,
  1176. Threshold);
  1177. if (DataDisplayed == FALSE) {
  1178. DbgOut("There was no new profiler data to display.\n");
  1179. goto DisplayProfilerDataEnd;
  1180. }
  1181. //
  1182. // If no threshold was supplied, it will get displayed in the GUI
  1183. // window; make sure it is visible.
  1184. //
  1185. if (DisplayRequest == ProfilerDisplayOneTime) {
  1186. UpdateProfilerWindowType(DialogWindow, DataType);
  1187. }
  1188. break;
  1189. //
  1190. // If a continuous display was requested, then set the timer for the given
  1191. // type. Additionally, immediately display the data to give the user a good
  1192. // response time since the timer doesn't fire until after the first period.
  1193. //
  1194. case ProfilerDisplayStart:
  1195. UpdateProfilerDisplay(DataType, DisplayRequest, Threshold);
  1196. SetProfilerTimer(DataType);
  1197. break;
  1198. //
  1199. // If a stop was requested, kill the timer for the provided type, hiding
  1200. // the profiler window for that type.
  1201. //
  1202. case ProfilerDisplayStop:
  1203. KillProfilerTimer(DataType);
  1204. break;
  1205. //
  1206. // Handle clear requests.
  1207. //
  1208. case ProfilerDisplayClear:
  1209. //
  1210. // The clear should only be requested for the stack profiler.
  1211. //
  1212. assert(DataType == ProfilerDataTypeStack);
  1213. //
  1214. // Erase the tree control and erase the previously collected stack data.
  1215. //
  1216. if (StackTreeRoot != NULL) {
  1217. AcquireDebuggerLock(StackTreeLock);
  1218. Profiler = GetDlgItem(DialogWindow, IDC_STACK_PROFILER);
  1219. TreeView_DeleteItem(Profiler, (HTREEITEM)StackTreeRoot->UiHandle);
  1220. DbgrDestroyProfilerStackData(StackTreeRoot);
  1221. StackTreeRoot = NULL;
  1222. ReleaseDebuggerLock(StackTreeLock);
  1223. }
  1224. break;
  1225. case ProfilerDisplayStartDelta:
  1226. //
  1227. // The delta request should only be for the memory profiler.
  1228. //
  1229. assert(DataType == ProfilerDataTypeMemory);
  1230. //
  1231. // The delta start request always moves the most recent full statistics
  1232. // to become the base statistics, destroying the old base statistics.
  1233. // It also destroys the delta statistics, which can be released after
  1234. // it wipes the list view from the screen..
  1235. //
  1236. AcquireDebuggerLock(MemoryListLock);
  1237. Profiler = GetDlgItem(DialogWindow, IDC_MEMORY_PROFILER);
  1238. ListView_DeleteAllItems(Profiler);
  1239. DbgrDestroyProfilerMemoryData(MemoryDeltaListHead);
  1240. MemoryDeltaListHead = NULL;
  1241. DbgrDestroyProfilerMemoryData(MemoryBaseListHead);
  1242. //
  1243. // If there are no current statistics, collect them.
  1244. //
  1245. if (MemoryPoolListHead != NULL) {
  1246. MemoryBaseListHead = MemoryPoolListHead;
  1247. MemoryPoolListHead = NULL;
  1248. } else {
  1249. MemoryBaseListHead = NULL;
  1250. DbgrGetProfilerMemoryData(&MemoryBaseListHead);
  1251. }
  1252. MemoryDeltaModeEnabled = TRUE;
  1253. ReleaseDebuggerLock(MemoryListLock);
  1254. //
  1255. // Display the most recent data and make sure the timer is enabled.
  1256. //
  1257. UpdateProfilerDisplay(DataType, DisplayRequest, Threshold);
  1258. SetProfilerTimer(DataType);
  1259. break;
  1260. case ProfilerDisplayStopDelta:
  1261. //
  1262. // The delta request should only be for the memory profiler.
  1263. //
  1264. assert(DataType == ProfilerDataTypeMemory);
  1265. AcquireDebuggerLock(MemoryListLock);
  1266. //
  1267. // Do nothing if delta mode is not enabled.
  1268. //
  1269. if (MemoryDeltaModeEnabled == FALSE) {
  1270. ReleaseDebuggerLock(MemoryListLock);
  1271. break;
  1272. }
  1273. //
  1274. // The delta stop request always destroys all the memory lists and sets
  1275. // their pointers to NULL after clearing the display of all list items.
  1276. // Note that delta mode stop does not disable the timer, it takes a
  1277. // full stop command to stop the memory profiler.
  1278. //
  1279. Profiler = GetDlgItem(DialogWindow, IDC_MEMORY_PROFILER);
  1280. ListView_DeleteAllItems(Profiler);
  1281. DbgrDestroyProfilerMemoryData(MemoryDeltaListHead);
  1282. MemoryDeltaListHead = NULL;
  1283. DbgrDestroyProfilerMemoryData(MemoryBaseListHead);
  1284. MemoryBaseListHead = NULL;
  1285. DbgrDestroyProfilerMemoryData(MemoryPoolListHead);
  1286. MemoryPoolListHead = NULL;
  1287. MemoryDeltaModeEnabled = FALSE;
  1288. ReleaseDebuggerLock(MemoryListLock);
  1289. break;
  1290. default:
  1291. DbgOut("Error: Invalid profiler display request %d.\n", DisplayRequest);
  1292. break;
  1293. }
  1294. DisplayProfilerDataEnd:
  1295. //
  1296. // Restart the profiler timer.
  1297. //
  1298. ResumeProfilerTimer();
  1299. return;
  1300. }
  1301. //
  1302. // --------------------------------------------------------- Internal Functions
  1303. //
  1304. DWORD
  1305. WINAPI
  1306. ConsoleStandardOutThread (
  1307. LPVOID WindowHandle
  1308. )
  1309. /*++
  1310. Routine Description:
  1311. This routine is the worker thread that simply receives data from the
  1312. standard out pipe and puts it in the command window.
  1313. Arguments:
  1314. WindowHandle - Supplies a pointer to the command window edit box.
  1315. Return Value:
  1316. 0 Always.
  1317. --*/
  1318. {
  1319. CHAR Buffer[1024];
  1320. ULONG BytesRead;
  1321. BOOL Result;
  1322. ULONG TextLength;
  1323. HWND Window;
  1324. Window = (HWND)WindowHandle;
  1325. ConsoleInitialized = TRUE;
  1326. while (TRUE) {
  1327. //
  1328. // Read data out of the stdout pipe.
  1329. //
  1330. Result = ReadFile(StdOutPipe,
  1331. Buffer,
  1332. sizeof(Buffer) - 1,
  1333. &BytesRead,
  1334. NULL);
  1335. if (Result == FALSE) {
  1336. break;
  1337. }
  1338. Buffer[BytesRead] = '\0';
  1339. //
  1340. // Send the character to the command window.
  1341. //
  1342. TextLength = GetWindowTextLength(Window);
  1343. SendMessage(Window, EM_SETSEL, (WPARAM)TextLength, (LPARAM)TextLength);
  1344. SendMessage(Window, EM_REPLACESEL, (WPARAM)FALSE, (LPARAM)Buffer);
  1345. }
  1346. return 0;
  1347. }
  1348. DWORD
  1349. WINAPI
  1350. UiThreadMain (
  1351. LPVOID Parameter
  1352. )
  1353. /*++
  1354. Routine Description:
  1355. This routine is the startup routine for the UI thread.
  1356. Arguments:
  1357. Parameter - Supplies an unused parameter.
  1358. Return Value:
  1359. Returns 0 on success, or nonzero if there was an error.
  1360. --*/
  1361. {
  1362. HACCEL Accelerators;
  1363. HANDLE CommonControl;
  1364. HANDLE CurrentInstance;
  1365. PINITCOMMONCONTROLSEX InitCommonControlsEx;
  1366. INITCOMMONCONTROLSEX InitControls;
  1367. HICON LargeIcon;
  1368. MSG Message;
  1369. BOOL Result;
  1370. HANDLE RichEditDll;
  1371. HICON SmallIcon;
  1372. HWND StackProfiler;
  1373. //
  1374. // Initialize globals.
  1375. //
  1376. DialogWindow = NULL;
  1377. //
  1378. // Load the rich edit DLL.
  1379. //
  1380. RichEditDll = LoadLibrary(TEXT("Riched20.dll"));
  1381. if (RichEditDll == NULL) {
  1382. DbgOut("Error: Unable to load riched20.dll! The source window will "
  1383. "be unavailable.\n");
  1384. return 0;
  1385. }
  1386. //
  1387. // Load the common control DLL. This is used to create tree views.
  1388. //
  1389. CommonControl = LoadLibrary(TEXT("comctl32.dll"));
  1390. if (CommonControl == NULL) {
  1391. DbgOut("Error: Unable to load comctl32.dll! The source and profiler "
  1392. "window will be unavailable.\n");
  1393. return 0;
  1394. }
  1395. InitCommonControlsEx = (PINITCOMMONCONTROLSEX)GetProcAddress(
  1396. CommonControl,
  1397. TEXT("InitCommonControlsEx"));
  1398. if (InitCommonControlsEx == NULL) {
  1399. DbgOut("Error: Could not get the procedure address for "
  1400. "InitCommonControlsEx.\n");
  1401. return 0;
  1402. }
  1403. //
  1404. // Initialize the common controls.
  1405. //
  1406. // N.B. Rumor has it that adding ICC_LISTVIEW_CLASSES to the initialization
  1407. // flags prevents group views from working. It is omitted as a result.
  1408. //
  1409. InitControls.dwSize = sizeof(INITCOMMONCONTROLSEX);
  1410. InitControls.dwICC = ICC_TREEVIEW_CLASSES;
  1411. Result = InitCommonControlsEx(&InitControls);
  1412. if (Result == FALSE) {
  1413. DbgOut("InitCommonControlsEx failed\n");
  1414. }
  1415. //
  1416. // Create the main source window. The DialogBox function will not return
  1417. // until the dialog box has been closed, at which point the thread can
  1418. // clean up and exit.
  1419. //
  1420. CurrentInstance = GetModuleHandle(NULL);
  1421. Accelerators = LoadAccelerators(CurrentInstance,
  1422. MAKEINTRESOURCE(IDD_ACCELERATORS));
  1423. if (Accelerators == NULL) {
  1424. DbgOut("Error: Could not load accelerators.\n");
  1425. return 0;
  1426. }
  1427. DialogWindow = CreateDialog(CurrentInstance,
  1428. MAKEINTRESOURCE(IDD_MAIN_WINDOW),
  1429. NULL,
  1430. MainDialogProc);
  1431. //
  1432. // TODO: Add support for break-at-cursor and goto-cursor
  1433. //
  1434. ShowWindow(GetDlgItem(DialogWindow, IDC_BREAK_CURSOR), SW_HIDE);
  1435. ShowWindow(GetDlgItem(DialogWindow, IDC_GOTO_CURSOR), SW_HIDE);
  1436. ShowWindow(DialogWindow, SW_SHOW);
  1437. //
  1438. // Load the application icons.
  1439. //
  1440. LargeIcon = LoadImage(CurrentInstance,
  1441. MAKEINTRESOURCE(IDI_DEBUG_ICON),
  1442. IMAGE_ICON,
  1443. 32,
  1444. 32,
  1445. LR_DEFAULTSIZE);
  1446. if (LargeIcon != NULL) {
  1447. SendMessage(DialogWindow, WM_SETICON, ICON_BIG, (LPARAM)LargeIcon);
  1448. }
  1449. SmallIcon = LoadImage(CurrentInstance,
  1450. MAKEINTRESOURCE(IDI_DEBUG_ICON),
  1451. IMAGE_ICON,
  1452. 16,
  1453. 16,
  1454. LR_DEFAULTSIZE);
  1455. if (SmallIcon != NULL) {
  1456. SendMessage(DialogWindow, WM_SETICON, ICON_SMALL, (LPARAM)SmallIcon);
  1457. }
  1458. //
  1459. // Set focus on the input command box.
  1460. //
  1461. SetFocus(GetDlgItem(DialogWindow, IDE_COMMAND));
  1462. //
  1463. // Override the stack profiler's window message procedure call.
  1464. //
  1465. StackProfiler = GetDlgItem(DialogWindow, IDC_STACK_PROFILER);
  1466. OriginalTreeViewWindowProcedure = (WNDPROC)SetWindowLong(
  1467. StackProfiler,
  1468. GWL_WNDPROC,
  1469. (LONG)TreeViewWindowProcedure);
  1470. //
  1471. // Initialize the memory profiler list view control.
  1472. //
  1473. InitializeProfilerControls();
  1474. //
  1475. // Pump messages into the dialog processing function.
  1476. //
  1477. while (GetMessage(&Message, NULL, 0, 0) != FALSE) {
  1478. if ((TranslateAccelerator(DialogWindow, Accelerators, &Message) == 0) &&
  1479. (IsDialogMessage(DialogWindow, &Message) == FALSE)) {
  1480. TranslateMessage(&Message);
  1481. DispatchMessage(&Message);
  1482. }
  1483. }
  1484. DialogWindow = NULL;
  1485. FreeLibrary(CommonControl);
  1486. FreeLibrary(RichEditDll);
  1487. if (LargeIcon != NULL) {
  1488. CloseHandle(LargeIcon);
  1489. }
  1490. if (SmallIcon != NULL) {
  1491. CloseHandle(SmallIcon);
  1492. }
  1493. CloseHandle(StdInPipeWrite);
  1494. exit(0);
  1495. return 0;
  1496. }
  1497. INT_PTR
  1498. CALLBACK
  1499. MainDialogProc (
  1500. HWND DialogHandle,
  1501. UINT Message,
  1502. WPARAM WParam,
  1503. LPARAM LParam
  1504. )
  1505. /*++
  1506. Routine Description:
  1507. This routine is the main message pump for the source window. It receives
  1508. messages pertaining to the window and handles interesting ones.
  1509. Arguments:
  1510. DialogHandle - Supplies the handle for the overall dialog window.
  1511. Message - Supplies the message being sent to the window.
  1512. WParam - Supplies the "width" parameter, basically the first parameter of
  1513. the message.
  1514. LParam - Supplies the "length" parameter, basically the second parameter of
  1515. the message.
  1516. Return Value:
  1517. Returns TRUE if the message was handled, or FALSE if the message was not
  1518. handled and the default handler should be invoked.
  1519. --*/
  1520. {
  1521. RECT DialogRect;
  1522. LONG NewDivider;
  1523. CHARFORMAT2 NewFormat;
  1524. HANDLE OutputThread;
  1525. POINT Point;
  1526. BOOL Result;
  1527. HWND SourceEdit;
  1528. RECT SourceRect;
  1529. HWND StdOutEdit;
  1530. RECT StdOutRect;
  1531. Result = FALSE;
  1532. switch (Message) {
  1533. //
  1534. // The WM_INITDIALOG message handles the initial dialog creation.
  1535. //
  1536. case WM_INITDIALOG:
  1537. StdOutEdit = GetDlgItem(DialogHandle, IDE_STDOUT_RICHEDIT);
  1538. SourceEdit = GetDlgItem(DialogHandle, IDE_SOURCE_RICHEDIT);
  1539. //
  1540. // Perform a sanity check to make sure the richedit control is there.
  1541. //
  1542. if ((StdOutEdit != NULL) && (SourceEdit != NULL)) {
  1543. //
  1544. // Set the text color, size, and font of the rich edit controls.
  1545. // The Y height is the font's point size times twenty.
  1546. //
  1547. memset(&NewFormat, 0, sizeof(CHARFORMAT2));
  1548. NewFormat.cbSize = sizeof(CHARFORMAT2);
  1549. NewFormat.dwMask = CFM_FACE | CFM_SIZE;
  1550. NewFormat.yHeight = 10 * 20;
  1551. strcpy(NewFormat.szFaceName, "Courier");
  1552. SendMessage(SourceEdit,
  1553. EM_SETCHARFORMAT,
  1554. (WPARAM)SCF_ALL,
  1555. (LPARAM)&NewFormat);
  1556. SendMessage(StdOutEdit,
  1557. EM_SETCHARFORMAT,
  1558. (WPARAM)SCF_ALL,
  1559. (LPARAM)&NewFormat);
  1560. //
  1561. // Set the background color of the source area.
  1562. //
  1563. SendMessage(SourceEdit,
  1564. EM_SETBKGNDCOLOR,
  1565. (WPARAM)FALSE,
  1566. (LPARAM)BACKGROUND_COLOR);
  1567. //
  1568. // Kick off the stdout thread.
  1569. //
  1570. OutputThread = CreateThread(NULL,
  1571. 0,
  1572. ConsoleStandardOutThread,
  1573. StdOutEdit,
  1574. 0,
  1575. NULL);
  1576. if (OutputThread == NULL) {
  1577. DbgOut("Unable to create the output thread!\n");
  1578. }
  1579. }
  1580. //
  1581. // Position the elements in the window.
  1582. //
  1583. HandleResize(DialogHandle);
  1584. UiLoadPreferences(DialogHandle);
  1585. Result = TRUE;
  1586. break;
  1587. //
  1588. // The WM_LBUTTONDOWN message is sent when the user clicks in the main
  1589. // window.
  1590. //
  1591. case WM_LBUTTONDOWN:
  1592. StdOutEdit = GetDlgItem(DialogHandle, IDE_STDOUT_RICHEDIT);
  1593. SourceEdit = GetDlgItem(DialogHandle, IDE_SOURCE_RICHEDIT);
  1594. GetWindowRect(StdOutEdit, &StdOutRect);
  1595. GetWindowRect(SourceEdit, &SourceRect);
  1596. MapWindowPoints(NULL,
  1597. DialogHandle,
  1598. (LPPOINT)&StdOutRect,
  1599. sizeof(RECT) / sizeof(POINT));
  1600. MapWindowPoints(NULL,
  1601. DialogHandle,
  1602. (LPPOINT)&SourceRect,
  1603. sizeof(RECT) / sizeof(POINT));
  1604. Point.x = LOWORD(LParam);
  1605. Point.y = HIWORD(LParam);
  1606. //
  1607. // Check to see if the click happened between the two edit windows.
  1608. //
  1609. if ((Point.x >= (SourceRect.right - UI_MOUSE_PLAY)) &&
  1610. (Point.x <= (StdOutRect.left + UI_MOUSE_PLAY))) {
  1611. //
  1612. // Capture mouse events.
  1613. //
  1614. SetCapture(DialogHandle);
  1615. SetCursor(LoadCursor(NULL, IDC_SIZEWE));
  1616. ResizingMainPanes = TRUE;
  1617. //
  1618. // Check to see if the click happened between the profiler window and
  1619. // the source window.
  1620. //
  1621. } else if ((Point.y >= (SourceRect.bottom - UI_MOUSE_PLAY)) &&
  1622. (Point.y <=
  1623. (ProfilerPaneCurrentYPosition + UI_MOUSE_PLAY))) {
  1624. SetCapture(DialogHandle);
  1625. SetCursor(LoadCursor(NULL, IDC_SIZENS));
  1626. ResizingProfilerPane = TRUE;
  1627. }
  1628. break;
  1629. //
  1630. // The WM_LBUTTONUP message is sent when the user releases the mouse in the
  1631. // main window (or all mouse events are being captured).
  1632. //
  1633. case WM_LBUTTONUP:
  1634. if ((ResizingMainPanes != FALSE) || (ResizingProfilerPane != FALSE)) {
  1635. ReleaseCapture();
  1636. SetCursor(LoadCursor(NULL, IDC_ARROW));
  1637. ResizingProfilerPane = FALSE;
  1638. ResizingMainPanes = FALSE;
  1639. }
  1640. break;
  1641. //
  1642. // The WM_MOUSEMOVE message is sent when the mouse moves within the window.
  1643. //
  1644. case WM_MOUSEMOVE:
  1645. //
  1646. // Don't do anything if the left button isn't also held down.
  1647. //
  1648. if (WParam != MK_LBUTTON) {
  1649. break;
  1650. }
  1651. GetWindowRect(DialogHandle, &DialogRect);
  1652. MapWindowPoints(NULL,
  1653. DialogHandle,
  1654. (LPPOINT)&DialogRect,
  1655. sizeof(RECT) / sizeof(POINT));
  1656. StdOutEdit = GetDlgItem(DialogHandle, IDE_STDOUT_RICHEDIT);
  1657. SourceEdit = GetDlgItem(DialogHandle, IDE_SOURCE_RICHEDIT);
  1658. GetWindowRect(StdOutEdit, &StdOutRect);
  1659. MapWindowPoints(NULL,
  1660. DialogHandle,
  1661. (LPPOINT)&StdOutRect,
  1662. sizeof(RECT) / sizeof(POINT));
  1663. GetWindowRect(SourceEdit, &SourceRect);
  1664. MapWindowPoints(NULL,
  1665. DialogHandle,
  1666. (LPPOINT)&SourceRect,
  1667. sizeof(RECT) / sizeof(POINT));
  1668. Point.x = LOWORD(LParam);
  1669. Point.y = HIWORD(LParam);
  1670. //
  1671. // Resize the main panes if in the middle of that.
  1672. //
  1673. if (ResizingMainPanes != FALSE) {
  1674. NewDivider = (SHORT)Point.x;
  1675. MainPaneXPosition = NewDivider;
  1676. MainPaneXPositionWidth = DialogRect.right;
  1677. HandleResize(DialogHandle);
  1678. } else if (ResizingProfilerPane != FALSE) {
  1679. NewDivider = (SHORT)Point.y;
  1680. ProfilerPaneYPosition = NewDivider;
  1681. ProfilerPaneYPositionHeight = DialogRect.bottom;
  1682. HandleResize(DialogHandle);
  1683. }
  1684. break;
  1685. //
  1686. // The WM_COMMAND message indicates that a button or keyboard accelerator
  1687. // has been pressed.
  1688. //
  1689. case WM_COMMAND:
  1690. HandleCommandMessage(DialogHandle, WParam);
  1691. Result = TRUE;
  1692. break;
  1693. //
  1694. // The WM_NOTIFY message indicates that a common control event has
  1695. // occurred.
  1696. //
  1697. case WM_NOTIFY:
  1698. HandleCommonControlMessage(DialogHandle, LParam);
  1699. Result = TRUE;
  1700. break;
  1701. //
  1702. // The WM_SIZE message indicates that the window was resized.
  1703. //
  1704. case WM_SIZE:
  1705. HandleResize(DialogHandle);
  1706. Result = TRUE;
  1707. break;
  1708. //
  1709. // The WM_EXITSIZEMOVE message indicates that the window is done being
  1710. // moved (dragged or resized).
  1711. //
  1712. case WM_EXITSIZEMOVE:
  1713. UiGetWindowPreferences(DialogHandle);
  1714. Result = TRUE;
  1715. break;
  1716. //
  1717. // The program is exiting.
  1718. //
  1719. case WM_DESTROY:
  1720. UiSavePreferences(DialogHandle);
  1721. PostQuitMessage(0);
  1722. Result = TRUE;
  1723. break;
  1724. }
  1725. return Result;
  1726. }
  1727. LRESULT
  1728. CALLBACK
  1729. TreeViewWindowProcedure (
  1730. HWND Window,
  1731. UINT Message,
  1732. WPARAM WParam,
  1733. LPARAM LParam
  1734. )
  1735. /*++
  1736. Routine Description:
  1737. This routine handles window messages that are passed to the Tree View
  1738. control.
  1739. Arguments:
  1740. Window - Supplies the handle for the Tree View window.
  1741. Message - Supplies the message being sent to the window.
  1742. WParam - Supplies the "width" parameter, basically the first parameter of
  1743. the message.
  1744. LParam - Supplies the "length" parameter, basically the second parameter of
  1745. the message.
  1746. Return Value:
  1747. Returns TRUE if the message was handled, or FALSE if the message was not
  1748. handled and the default handler should be invoked.
  1749. --*/
  1750. {
  1751. LRESULT Result;
  1752. switch (Message) {
  1753. //
  1754. // On key up or down, if the currently "selected" item is not visible, then
  1755. // reselected it. This will snap it back into view.
  1756. //
  1757. case WM_KEYUP:
  1758. case WM_KEYDOWN:
  1759. if ((TreeViewSelection != NULL) &&
  1760. (TreeViewIsTreeItemVisible(Window, TreeViewSelection) == FALSE)) {
  1761. TreeView_SelectItem(Window, TreeViewSelection);
  1762. TreeViewSelectionVisible = TRUE;
  1763. }
  1764. break;
  1765. //
  1766. // If the window is scrolled and the selected item goes out of view, then
  1767. // deselect it. This makes updates smoother. If the scroll pulls the
  1768. // selected item into view, then select it again.
  1769. //
  1770. case WM_VSCROLL:
  1771. case WM_MOUSEWHEEL:
  1772. if ((TreeViewSelection != NULL) &&
  1773. (TreeViewIsTreeItemVisible(Window, TreeViewSelection) != FALSE)) {
  1774. if (TreeViewSelectionVisible == FALSE) {
  1775. TreeView_SelectItem(Window, TreeViewSelection);
  1776. TreeViewSelectionVisible = TRUE;
  1777. }
  1778. } else {
  1779. if (TreeViewSelectionVisible != FALSE) {
  1780. TreeView_SelectItem(Window, NULL);
  1781. TreeViewSelectionVisible = FALSE;
  1782. }
  1783. }
  1784. break;
  1785. default:
  1786. break;
  1787. }
  1788. //
  1789. // Always forwad the call on to the original window procedure call.
  1790. //
  1791. Result = CallWindowProc(OriginalTreeViewWindowProcedure,
  1792. Window,
  1793. Message,
  1794. WParam,
  1795. LParam);
  1796. return Result;
  1797. }
  1798. BOOL
  1799. LoadFileIntoRichEdit (
  1800. HWND RichEdit,
  1801. LPCTSTR Filename,
  1802. PUCHAR FileBuffer,
  1803. ULONGLONG FileSize
  1804. )
  1805. /*++
  1806. Routine Description:
  1807. This routine loads the contents of a file into the rich edit box.
  1808. Arguments:
  1809. RichEdit - Supplies the handle to the rich edit box.
  1810. Filename - Supplies a NULL terminated string containing the name of the file
  1811. to load.
  1812. FileBuffer - Supplies a pointer to the file contents.
  1813. FileSize - Supplies the size of the file contents in bytes.
  1814. Return Value:
  1815. Returns TRUE if there was no error, or FALSE if there was an error.
  1816. --*/
  1817. {
  1818. EDITSTREAM EditStream;
  1819. LRESULT Result;
  1820. STREAM_IN_DATA StreamData;
  1821. BOOL Success;
  1822. memset(&StreamData, 0, sizeof(STREAM_IN_DATA));
  1823. //
  1824. // Highlight C-Style syntax and convert the text file into a rich text
  1825. // formatted buffer.
  1826. //
  1827. Success = HighlightSyntax(FileBuffer,
  1828. FileSize,
  1829. &(StreamData.Buffer),
  1830. &(StreamData.BufferSize));
  1831. if (Success == FALSE) {
  1832. goto LoadFileIntoRichEditEnd;
  1833. }
  1834. //
  1835. // Set the maximum amount of rich text allowed in the control to twice the
  1836. // buffer size. Without this message, the default is 32 kilobytes.
  1837. //
  1838. SendMessage(RichEdit,
  1839. EM_EXLIMITTEXT,
  1840. (WPARAM)0,
  1841. (LPARAM)(StreamData.BufferSize * 2));
  1842. //
  1843. // Set up the EM_STREAMIN command by filling out the edit stream
  1844. // context and callback function.
  1845. //
  1846. memset(&EditStream, 0, sizeof(EDITSTREAM));
  1847. EditStream.pfnCallback = RichEditLoadCallback;
  1848. EditStream.dwCookie = (DWORD_PTR)&StreamData;
  1849. Result = SendMessage(RichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&EditStream);
  1850. if ((Result != 0) && (EditStream.dwError == 0)) {
  1851. Success = TRUE;
  1852. }
  1853. LoadFileIntoRichEditEnd:
  1854. //
  1855. // If a failure occurred, clear the source window.
  1856. //
  1857. if (Success == FALSE) {
  1858. EditStream.pfnCallback = RichEditLoadCallback;
  1859. EditStream.dwCookie = (DWORD_PTR)NULL;
  1860. SendMessage(RichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&EditStream);
  1861. }
  1862. if (StreamData.Buffer != NULL) {
  1863. free(StreamData.Buffer);
  1864. }
  1865. return Success;
  1866. }
  1867. BOOL
  1868. HighlightSyntax (
  1869. PUCHAR TextBuffer,
  1870. ULONG TextBufferSize,
  1871. PCHAR *BufferOut,
  1872. PULONG FileSizeOut
  1873. )
  1874. /*++
  1875. Routine Description:
  1876. This routine takes in a text file and adds rich text formatting to perform
  1877. C style syntax highlighting. The caller must remember to free memory
  1878. allocated here.
  1879. Arguments:
  1880. TextBuffer - Supplies a pointer to text contents to highlight.
  1881. TextBufferSize - Supplies the size of the text, in bytes.
  1882. BufferOut - Supplies a pointer that receives a pointer to the rich text
  1883. buffer. The caller must remember to free this buffer.
  1884. FileSizeOut - Supplies a pointer that receives the size of the highlighted
  1885. file. Note that the buffer may or may not be bigger than this value.
  1886. Return Value:
  1887. Returns TRUE if there was no error, or FALSE if there was an error.
  1888. --*/
  1889. {
  1890. ULONG BytesOut;
  1891. ULONG BytesReadIn;
  1892. ULONG BytesToCopy;
  1893. ULONG ColorChangeLength;
  1894. PCHAR Destination;
  1895. PCHAR FileBuffer;
  1896. ULONG FileBufferSize;
  1897. UCHAR FileByte;
  1898. BOOL InDisabledCode;
  1899. BOOL InDoubleQuotes;
  1900. BOOL InMultiLineComment;
  1901. BOOL InSingleLineComment;
  1902. BOOL InSingleQuotes;
  1903. CHAR Keyword[MAX_KEYWORD + 1];
  1904. ULONG KeywordIndex;
  1905. ULONG KeywordLength;
  1906. PSTR KeywordStart;
  1907. PSTR PoundIfStart;
  1908. UCHAR PreviousCharacter;
  1909. BOOL PreviousKeywordPoundIf;
  1910. BOOL ResetColor;
  1911. PCHAR Source;
  1912. BOOL Success;
  1913. BOOL WasBackslash;
  1914. BytesOut = 0;
  1915. BytesReadIn = 0;
  1916. InDisabledCode = FALSE;
  1917. InDoubleQuotes = FALSE;
  1918. InSingleLineComment = FALSE;
  1919. InSingleQuotes = FALSE;
  1920. InMultiLineComment = FALSE;
  1921. KeywordIndex = 0;
  1922. KeywordStart = NULL;
  1923. PoundIfStart = NULL;
  1924. PreviousKeywordPoundIf = FALSE;
  1925. Success = TRUE;
  1926. WasBackslash = FALSE;
  1927. *BufferOut = NULL;
  1928. *FileSizeOut = 0;
  1929. //
  1930. // Allocate a buffer big enough to hold the original text file plus all the
  1931. // formatting. Guess a size that should be big enough.
  1932. //
  1933. FileBufferSize = TextBufferSize * 5;
  1934. if (FileBufferSize < strlen(RTF_HEADER) + strlen(RTF_FOOTER) + 8192) {
  1935. FileBufferSize += strlen(RTF_HEADER) + strlen(RTF_FOOTER) + 8192;
  1936. }
  1937. FileBuffer = malloc(FileBufferSize);
  1938. if (FileBuffer == NULL) {
  1939. Success = FALSE;
  1940. goto HighlightSyntaxEnd;
  1941. }
  1942. *BufferOut = FileBuffer;
  1943. memset(Keyword, 0, MAX_KEYWORD + 1);
  1944. //
  1945. // Copy the RTF header.
  1946. //
  1947. memcpy(FileBuffer, RTF_HEADER, strlen(RTF_HEADER));
  1948. FileBuffer += strlen(RTF_HEADER);
  1949. BytesOut += strlen(RTF_HEADER);
  1950. ResetColor = FALSE;
  1951. PreviousCharacter = '\0';
  1952. while (TRUE) {
  1953. //
  1954. // If the entire input file has been read, end the loop.
  1955. //
  1956. if (BytesReadIn == TextBufferSize) {
  1957. break;
  1958. }
  1959. //
  1960. // Get a byte from the input buffer.
  1961. //
  1962. FileByte = TextBuffer[BytesReadIn];
  1963. BytesReadIn += 1;
  1964. //
  1965. // If this is a single quote, it's not preceded by a backslash, and
  1966. // it's not in any other sort of comment or quote, toggle the single
  1967. // line quote.
  1968. //
  1969. if ((FileByte == '\'') &&
  1970. (WasBackslash == FALSE) &&
  1971. (InSingleLineComment == FALSE) &&
  1972. (InMultiLineComment == FALSE) &&
  1973. (InDoubleQuotes == FALSE) &&
  1974. (InDisabledCode == FALSE)) {
  1975. if (InSingleQuotes == FALSE) {
  1976. InSingleQuotes = TRUE;
  1977. memcpy(FileBuffer, RTF_QUOTE, RTF_COLOR_SIZE);
  1978. FileBuffer += RTF_COLOR_SIZE;
  1979. BytesOut += RTF_COLOR_SIZE;
  1980. } else {
  1981. InSingleQuotes = FALSE;
  1982. ResetColor = TRUE;
  1983. }
  1984. }
  1985. //
  1986. // If this is a double quote, it's not preceded by a backslash, and it's
  1987. // not in any other sort of comment or quote, toggle the double line
  1988. // quote.
  1989. //
  1990. if ((FileByte == '\"') &&
  1991. (WasBackslash == FALSE) &&
  1992. (InSingleLineComment == FALSE) &&
  1993. (InMultiLineComment == FALSE) &&
  1994. (InSingleQuotes == FALSE) &&
  1995. (InDisabledCode == FALSE)) {
  1996. if (InDoubleQuotes == FALSE) {
  1997. InDoubleQuotes = TRUE;
  1998. memcpy(FileBuffer, RTF_QUOTE, RTF_COLOR_SIZE);
  1999. FileBuffer += RTF_COLOR_SIZE;
  2000. BytesOut += RTF_COLOR_SIZE;
  2001. } else {
  2002. InDoubleQuotes = FALSE;
  2003. ResetColor = TRUE;
  2004. }
  2005. }
  2006. //
  2007. // If this is a newline, end a single line comment now.
  2008. //
  2009. if ((InSingleLineComment != FALSE) &&
  2010. (InMultiLineComment == FALSE) &&
  2011. (InSingleQuotes == FALSE) &&
  2012. (InDoubleQuotes == FALSE) &&
  2013. (InDisabledCode == FALSE) &&
  2014. ((FileByte == '\n') || (FileByte == '\r'))) {
  2015. InSingleLineComment = FALSE;
  2016. ResetColor = TRUE;
  2017. }
  2018. //
  2019. // If this character is a / and so was the last one, this begins a
  2020. // single line comment. Back up a character to apply the formatting, but
  2021. // remember that the / got formatted as a divide sign, so there's a
  2022. // plain text directive after the slash already which needs to be
  2023. // backed out.
  2024. //
  2025. if ((InSingleLineComment == FALSE) &&
  2026. (InMultiLineComment == FALSE) &&
  2027. (InSingleQuotes == FALSE) &&
  2028. (InDoubleQuotes == FALSE) &&
  2029. (InDisabledCode == FALSE) &&
  2030. (FileByte == '/') &&
  2031. (PreviousCharacter == '/')) {
  2032. FileBuffer -= RTF_COLOR_SIZE + 1;
  2033. memcpy(FileBuffer, RTF_COMMENT, RTF_COLOR_SIZE);
  2034. FileBuffer += RTF_COLOR_SIZE;
  2035. BytesOut += RTF_COLOR_SIZE;
  2036. *FileBuffer = '/';
  2037. FileBuffer += 1;
  2038. BytesOut -= RTF_COLOR_SIZE;
  2039. InSingleLineComment = TRUE;
  2040. }
  2041. //
  2042. // If another comment or quote is not in progress, check for the
  2043. // beginning of a multiline comment. Back up to format the / as well,
  2044. // but watch out for that plain text directive.
  2045. //
  2046. if ((FileByte == '*') &&
  2047. (PreviousCharacter == '/') &&
  2048. (InSingleLineComment == FALSE) &&
  2049. (InSingleQuotes == FALSE) &&
  2050. (InDoubleQuotes == FALSE) &&
  2051. (InDisabledCode == FALSE)) {
  2052. FileBuffer -= RTF_COLOR_SIZE + 1;
  2053. memcpy(FileBuffer, RTF_COMMENT, RTF_COLOR_SIZE);
  2054. FileBuffer += RTF_COLOR_SIZE;
  2055. BytesOut += RTF_COLOR_SIZE;
  2056. *FileBuffer = '/';
  2057. FileBuffer += 1;
  2058. BytesOut -= RTF_COLOR_SIZE;
  2059. InMultiLineComment = TRUE;
  2060. }
  2061. //
  2062. // Don't do syntax highlighting while inside a comment or quote.
  2063. // *Do* go into this loop for disabled code to know when to get out of
  2064. // disabled code.
  2065. //
  2066. if ((InSingleLineComment == FALSE) &&
  2067. (InMultiLineComment == FALSE) &&
  2068. (InSingleQuotes == FALSE) &&
  2069. (InDoubleQuotes == FALSE)) {
  2070. //
  2071. // If this character marks the end of a keyword, evaluate the
  2072. // keyword.
  2073. //
  2074. if (IsKeywordSeparator(FileByte) != FALSE) {
  2075. //
  2076. // End the keyword and compare against known keywords (or a
  2077. // number).
  2078. //
  2079. Keyword[KeywordIndex] = '\0';
  2080. //
  2081. // If the current code is marked as disabled code, an #endif
  2082. // ends that.
  2083. //
  2084. if ((InDisabledCode != FALSE) &&
  2085. ((strcmp(Keyword, "#endif") == 0) ||
  2086. (strcmp(Keyword, "#else") == 0))) {
  2087. InDisabledCode = FALSE;
  2088. ResetColor = TRUE;
  2089. }
  2090. //
  2091. // If the keyword is a zero and the previous keyword was #if,
  2092. // then its a "#if 0" disabling the code.
  2093. //
  2094. if ((PreviousKeywordPoundIf != FALSE) &&
  2095. (strcmp(Keyword, "0") == 0)) {
  2096. //
  2097. // Copy the "#if 0" forward to make room for the color
  2098. // change. Standard memory copy routines are not appropriate
  2099. // here because the regions may be overlapping (which is
  2100. // also the reason for copying backwards).
  2101. //
  2102. ColorChangeLength = RTF_COLOR_SIZE;
  2103. KeywordLength = (ULONG)FileBuffer - (ULONG)PoundIfStart;
  2104. Source = PoundIfStart + KeywordLength - 1;
  2105. Destination = FileBuffer + ColorChangeLength - 1;
  2106. BytesToCopy = KeywordLength;
  2107. while (BytesToCopy != 0) {
  2108. *Destination = *Source;
  2109. Destination -= 1;
  2110. Source -= 1;
  2111. BytesToCopy -= 1;
  2112. }
  2113. InDisabledCode = TRUE;
  2114. //
  2115. // Copy the disabled code color into the text stream. Use
  2116. // memcpy to avoid copying a null terminator over the data.
  2117. //
  2118. memcpy(PoundIfStart, RTF_DISABLED, ColorChangeLength);
  2119. FileBuffer += ColorChangeLength;
  2120. BytesOut += ColorChangeLength;
  2121. }
  2122. //
  2123. // If it's #if, set that flag in preparation for a possible
  2124. // 0 coming next.
  2125. //
  2126. if (strcmp(Keyword, "#if") == 0) {
  2127. PreviousKeywordPoundIf = TRUE;
  2128. PoundIfStart = KeywordStart;
  2129. } else {
  2130. PreviousKeywordPoundIf = FALSE;
  2131. PoundIfStart = NULL;
  2132. }
  2133. //
  2134. // Highlight the keyword if it's a number or C reserved keyword.
  2135. // Don't highlight if in disabled code.
  2136. //
  2137. if ((InDisabledCode == FALSE) &&
  2138. (((Keyword[0] >= '0') && (Keyword[0] <= '9')) ||
  2139. (IsKeyword(Keyword) != FALSE))) {
  2140. //
  2141. // Copy the part of the buffer after the start of the
  2142. // keyword forward to make room for the color change text.
  2143. // Don't use standard routines because the regions may
  2144. // overlap. It's okay to copy overlapping regions manually
  2145. // because it's known that the destination comes after the
  2146. // source, so as long as copying is done right-to-left, the
  2147. // operation is safe.
  2148. //
  2149. ColorChangeLength = RTF_COLOR_SIZE;
  2150. KeywordLength = strlen(Keyword);
  2151. Destination = FileBuffer + ColorChangeLength - 1;
  2152. Source = KeywordStart + KeywordLength - 1;
  2153. BytesToCopy = KeywordLength;
  2154. while (BytesToCopy != 0) {
  2155. *Destination = *Source;
  2156. Destination -= 1;
  2157. Source -= 1;
  2158. BytesToCopy -= 1;
  2159. }
  2160. //
  2161. // Use memcpy instead of strcpy because the null terminator
  2162. // that was that strcpy would write on the end would clobber
  2163. // the data just shifted over.
  2164. //
  2165. memcpy(KeywordStart, RTF_KEYWORD, ColorChangeLength);
  2166. FileBuffer += ColorChangeLength;
  2167. BytesOut += ColorChangeLength;
  2168. ResetColor = TRUE;
  2169. }
  2170. //
  2171. // This was a keyword and it was just dealt with. Reset the
  2172. // keyword contents and pointer.
  2173. //
  2174. KeywordIndex = 0;
  2175. KeywordStart = NULL;
  2176. //
  2177. // This character is not a keyword separator. Save the new byte into
  2178. // the current keyword, but only if there's room.
  2179. //
  2180. } else if (KeywordIndex < MAX_KEYWORD) {
  2181. //
  2182. // If this is the first character in the token, save the
  2183. // position in the file buffer.
  2184. //
  2185. if (KeywordIndex == 0) {
  2186. KeywordStart = FileBuffer;
  2187. }
  2188. Keyword[KeywordIndex] = FileByte;
  2189. KeywordIndex += 1;
  2190. }
  2191. //
  2192. // Handle single character highlights. Don't highlight disabled
  2193. // code.
  2194. //
  2195. if (InDisabledCode == FALSE) {
  2196. switch (FileByte) {
  2197. //
  2198. // Operators +, -, *, /, >, <, =, ., !, ^, &, |, :, ;, ~, %
  2199. // should be highlighted with the constant color.
  2200. //
  2201. case '+':
  2202. case '-':
  2203. case '*':
  2204. case '/':
  2205. case '?':
  2206. case '>':
  2207. case '<':
  2208. case '=':
  2209. case '.':
  2210. case '!':
  2211. case '^':
  2212. case '&':
  2213. case '|':
  2214. case ':':
  2215. case ';':
  2216. case '~':
  2217. case '%':
  2218. memcpy(FileBuffer, RTF_CONSTANT, RTF_COLOR_SIZE);
  2219. FileBuffer += RTF_COLOR_SIZE;
  2220. BytesOut += RTF_COLOR_SIZE;
  2221. ResetColor = TRUE;
  2222. break;
  2223. //
  2224. // Braces {}, [], and () should be highlighted with the brace
  2225. // color.
  2226. //
  2227. case '(':
  2228. case ')':
  2229. case '[':
  2230. case ']':
  2231. case '{':
  2232. case '}':
  2233. memcpy(FileBuffer, RTF_BRACE, RTF_COLOR_SIZE);
  2234. FileBuffer += RTF_COLOR_SIZE;
  2235. BytesOut += RTF_COLOR_SIZE;
  2236. ResetColor = TRUE;
  2237. break;
  2238. default:
  2239. break;
  2240. }
  2241. }
  2242. }
  2243. //
  2244. // New lines must be replaced by /par. The actual new line characters
  2245. // seem to be ignored, so leave them in. Also reset the comment coloring
  2246. // if inside a multi-line comment.
  2247. //
  2248. if (FileByte == '\n') {
  2249. memcpy(FileBuffer, RTF_NEWLINE, RTF_NEWLINE_SIZE);
  2250. FileBuffer += RTF_NEWLINE_SIZE;
  2251. BytesOut += RTF_NEWLINE_SIZE;
  2252. }
  2253. //
  2254. // The characters }, {, and \ have to be preceded by a \.
  2255. //
  2256. if ((FileByte == '{') || (FileByte == '}') || (FileByte == '\\')) {
  2257. *FileBuffer = '\\';
  2258. FileBuffer += 1;
  2259. BytesOut += 1;
  2260. }
  2261. //
  2262. // Copy the character from the file into the buffer.
  2263. //
  2264. if (FileByte != '\r') {
  2265. *FileBuffer = FileByte;
  2266. FileBuffer += 1;
  2267. BytesOut += 1;
  2268. }
  2269. //
  2270. // If this is a */, end a multiline comment now. This couldn't be
  2271. // handled earlier because the / shouldn't be highlighted like a divide.
  2272. //
  2273. if ((InMultiLineComment != FALSE) &&
  2274. (FileByte == '/') &&
  2275. (PreviousCharacter == '*') &&
  2276. (InSingleQuotes == FALSE) &&
  2277. (InDoubleQuotes == FALSE) &&
  2278. (InSingleLineComment == FALSE) &&
  2279. (InDisabledCode == FALSE)) {
  2280. InMultiLineComment = FALSE;
  2281. ResetColor = TRUE;
  2282. }
  2283. PreviousCharacter = FileByte;
  2284. //
  2285. // Reset the color if something was highlighted but is finished now.
  2286. //
  2287. if (ResetColor != FALSE) {
  2288. ResetColor = FALSE;
  2289. memcpy(FileBuffer, RTF_PLAIN_TEXT, RTF_COLOR_SIZE);
  2290. FileBuffer += RTF_COLOR_SIZE;
  2291. BytesOut += RTF_COLOR_SIZE;
  2292. }
  2293. //
  2294. // Remember if the previous character was a backslash.
  2295. //
  2296. if (FileByte == '\\') {
  2297. if (WasBackslash == FALSE) {
  2298. WasBackslash = TRUE;
  2299. } else {
  2300. WasBackslash = FALSE;
  2301. }
  2302. } else {
  2303. WasBackslash = FALSE;
  2304. }
  2305. }
  2306. //
  2307. // Copy the footer, including the NULL terminator.
  2308. //
  2309. memcpy(FileBuffer, RTF_FOOTER, strlen(RTF_FOOTER) + 1);
  2310. FileBuffer += strlen(RTF_FOOTER) + 1;
  2311. BytesOut += strlen(RTF_FOOTER) + 1;
  2312. //
  2313. // Set the output size, and return.
  2314. //
  2315. *FileSizeOut = BytesOut;
  2316. Success = TRUE;
  2317. if (strlen(*BufferOut) + 1 != BytesOut) {
  2318. DbgOut("ERROR: Not all bytes were accounted for. The rich text buffer "
  2319. "is %d bytes, but only %d bytes were reported!\n",
  2320. strlen(*BufferOut) + 1,
  2321. BytesOut);
  2322. }
  2323. if (BytesOut >= FileBufferSize) {
  2324. DbgOut("ERROR: The rich text buffer was %d bytes, but %d were used. "
  2325. "The buffer was overrun!\n",
  2326. FileBufferSize,
  2327. BytesOut);
  2328. assert(BytesOut < FileBufferSize);
  2329. }
  2330. #if 0
  2331. DbgOut("File size: %d, File buffer size: %d, output file size: %d\n",
  2332. TextBufferSize,
  2333. FileBufferSize,
  2334. BytesOut);
  2335. #endif
  2336. HighlightSyntaxEnd:
  2337. if (Success == FALSE) {
  2338. if (*BufferOut != NULL) {
  2339. free(*BufferOut);
  2340. }
  2341. }
  2342. return Success;
  2343. }
  2344. BOOL
  2345. IsKeyword (
  2346. PSTR String
  2347. )
  2348. /*++
  2349. Routine Description:
  2350. This routine determines whether or not the given character is a C reserved
  2351. keyword.
  2352. Arguments:
  2353. String - Supplies the string containing the suspected keyword.
  2354. Return Value:
  2355. Returns TRUE if the keyword is a C reserved keyword. Returns FALSE if it is
  2356. not a C reserved keyword.
  2357. --*/
  2358. {
  2359. if ((strcmp("auto", String) == 0) ||
  2360. (strcmp("do", String) == 0) ||
  2361. (strcmp("for", String) == 0) ||
  2362. (strcmp("return", String) == 0) ||
  2363. (strcmp("typedef", String) == 0) ||
  2364. (strcmp("break", String) == 0) ||
  2365. (strcmp("double", String) == 0) ||
  2366. (strcmp("goto", String) == 0) ||
  2367. (strcmp("short", String) == 0) ||
  2368. (strcmp("union", String) == 0) ||
  2369. (strcmp("case", String) == 0) ||
  2370. (strcmp("else", String) == 0) ||
  2371. (strcmp("if", String) == 0) ||
  2372. (strcmp("sizeof", String) == 0) ||
  2373. (strcmp("unsigned", String) == 0) ||
  2374. (strcmp("char", String) == 0) ||
  2375. (strcmp("enum", String) == 0) ||
  2376. (strcmp("int", String) == 0) ||
  2377. (strcmp("static", String) == 0) ||
  2378. (strcmp("void", String) == 0) ||
  2379. (strcmp("continue", String) == 0) ||
  2380. (strcmp("extern", String) == 0) ||
  2381. (strcmp("long", String) == 0) ||
  2382. (strcmp("struct", String) == 0) ||
  2383. (strcmp("while", String) == 0) ||
  2384. (strcmp("default", String) == 0) ||
  2385. (strcmp("float", String) == 0) ||
  2386. (strcmp("register", String) == 0) ||
  2387. (strcmp("switch", String) == 0) ||
  2388. (strcmp("const", String) == 0) ||
  2389. (strcmp("signed", String) == 0) ||
  2390. (strcmp("volatile", String) == 0)) {
  2391. return TRUE;
  2392. }
  2393. return FALSE;
  2394. }
  2395. BOOL
  2396. IsKeywordSeparator (
  2397. UCHAR Character
  2398. )
  2399. /*++
  2400. Routine Description:
  2401. This routine determines whether or not the given character splits two
  2402. keywords.
  2403. Arguments:
  2404. Character - Supplies the character to evaluate.
  2405. Return Value:
  2406. Returns TRUE if the character could not exist in a keyword, and marks the
  2407. transition between two keywords. Returns FALSE if the character could be
  2408. part of a normal token/keyword.
  2409. --*/
  2410. {
  2411. switch (Character) {
  2412. case ' ':
  2413. case '\r':
  2414. case '\n':
  2415. case '\\':
  2416. case ',':
  2417. case '+':
  2418. case '-':
  2419. case '*':
  2420. case '?':
  2421. case '/':
  2422. case '>':
  2423. case '<':
  2424. case '=':
  2425. case '.':
  2426. case '!':
  2427. case '^':
  2428. case '&':
  2429. case '|':
  2430. case ':':
  2431. case ';':
  2432. case '~':
  2433. case '%':
  2434. case '(':
  2435. case ')':
  2436. case '[':
  2437. case ']':
  2438. case '{':
  2439. case '}':
  2440. return TRUE;
  2441. default:
  2442. break;
  2443. }
  2444. return FALSE;
  2445. }
  2446. VOID
  2447. HighlightLine (
  2448. HWND RichEdit,
  2449. LONG LineNumber,
  2450. COLORREF Color,
  2451. BOOL ScrollToLine
  2452. )
  2453. /*++
  2454. Routine Description:
  2455. This routine highlights or unhighlights a line in the currently loaded
  2456. source file.
  2457. Arguments:
  2458. RichEdit - Supplies a handle to the rich edit control.
  2459. LineNumber - Supplies the line number to change the background of. The first
  2460. line in the file is line 1 (ie line numbers are 1 based).
  2461. Color - Supplies the color to paint the background.
  2462. ScrollToLine - Supplies a flag indicating whether or not the window should
  2463. scroll to that line selection.
  2464. Return Value:
  2465. None.
  2466. --*/
  2467. {
  2468. CHARFORMAT2 Format;
  2469. LONG LineBegin;
  2470. LONG LineEnd;
  2471. ULONG OldSelectionBegin;
  2472. ULONG OldSelectionEnd;
  2473. //
  2474. // Get the character index of the line to highlight. Subtract 1 because the
  2475. // Rich Edit line numbers are zero based. Failure here indicates that the
  2476. // line number is greater than the number of lines in the currently loaded
  2477. // file.
  2478. //
  2479. LineBegin = SendMessage(RichEdit,
  2480. EM_LINEINDEX,
  2481. (WPARAM)(LineNumber - 1),
  2482. (LPARAM)0);
  2483. if (LineBegin == -1) {
  2484. return;
  2485. }
  2486. //
  2487. // Get the character index of the first character of the next line, to find
  2488. // out where highlighting should end. Failure here is okay because -1
  2489. // indicates the end of the file, which is correct for highlighting the
  2490. // last line of the file.
  2491. //
  2492. LineEnd = SendMessage(RichEdit,
  2493. EM_LINEINDEX,
  2494. (WPARAM)LineNumber,
  2495. (LPARAM)0);
  2496. //
  2497. // Get the current selection so it can be restored later.
  2498. //
  2499. if (ScrollToLine == FALSE) {
  2500. SendMessage(RichEdit,
  2501. EM_GETSEL,
  2502. (WPARAM)(&OldSelectionBegin),
  2503. (LPARAM)(&OldSelectionEnd));
  2504. }
  2505. //
  2506. // Set the selection to the line about to be highlighted.
  2507. //
  2508. SendMessage(RichEdit,
  2509. EM_SETSEL,
  2510. (WPARAM)LineBegin,
  2511. (LPARAM)LineEnd);
  2512. //
  2513. // Fill out a format structure indicating that the only valid field in the
  2514. // structure is the background color, which is about to be changed.
  2515. //
  2516. memset(&Format, 0, sizeof(CHARFORMAT2));
  2517. Format.cbSize = sizeof(CHARFORMAT2);
  2518. Format.dwMask = CFM_BACKCOLOR;
  2519. Format.crBackColor = Color;
  2520. //
  2521. // Send the message that actually sets the character formatting. Only format
  2522. // the current selection.
  2523. //
  2524. SendMessage(RichEdit,
  2525. EM_SETCHARFORMAT,
  2526. (WPARAM)(SCF_SELECTION),
  2527. (LPARAM)&Format);
  2528. //
  2529. // Restore the current selection if not scrolling to the line.
  2530. //
  2531. if (ScrollToLine == FALSE) {
  2532. SendMessage(RichEdit,
  2533. EM_SETSEL,
  2534. (WPARAM)OldSelectionBegin,
  2535. (LPARAM)OldSelectionEnd);
  2536. //
  2537. // Set the selection to the beginning of the line (but not highlighting the
  2538. // line anymore), and scroll to the caret.
  2539. //
  2540. } else {
  2541. SendMessage(RichEdit, EM_SETSEL, (WPARAM)LineBegin, (LPARAM)LineBegin);
  2542. SendMessage(RichEdit, EM_SCROLLCARET, 0, 0);
  2543. }
  2544. return;
  2545. }
  2546. VOID
  2547. HandleResize (
  2548. HWND Dialog
  2549. )
  2550. /*++
  2551. Routine Description:
  2552. This routine handles scaling the UI elements when the dialog window is
  2553. resized.
  2554. Arguments:
  2555. Dialog - Supplies the handle to the main dialog window.
  2556. Return Value:
  2557. None.
  2558. --*/
  2559. {
  2560. LONG AdjustedMainPaneXPosition;
  2561. LONG AdjustedProfilerPaneYPosition;
  2562. HWND BreakAtCursorButton;
  2563. HWND CommandEdit;
  2564. RECT Control;
  2565. ULONG DialogHeight;
  2566. RECT DialogSize;
  2567. ULONG DialogWidth;
  2568. HWND GotoCursorButton;
  2569. HWND MemoryToggle;
  2570. HWND MemoryView;
  2571. HWND OutputEdit;
  2572. LONG PaneXPosition;
  2573. HWND ProfilerView;
  2574. LONG ProfilerYPosition;
  2575. HWND PromptEdit;
  2576. BOOL Result;
  2577. HWND SourceEdit;
  2578. HWND SourceFileEdit;
  2579. HWND StackToggle;
  2580. HWND StackView;
  2581. //
  2582. // Get handles to all UI elements that need to be adjusted.
  2583. //
  2584. BreakAtCursorButton = GetDlgItem(Dialog, IDC_BREAK_CURSOR);
  2585. CommandEdit = GetDlgItem(Dialog, IDE_COMMAND);
  2586. GotoCursorButton = GetDlgItem(Dialog, IDC_GOTO_CURSOR);
  2587. MemoryToggle = GetDlgItem(Dialog, IDC_MEMORY_PROFILER_TOGGLE);
  2588. MemoryView = GetDlgItem(Dialog, IDC_MEMORY_PROFILER);
  2589. OutputEdit = GetDlgItem(Dialog, IDE_STDOUT_RICHEDIT);
  2590. PromptEdit = GetDlgItem(Dialog, IDE_PROMPT);
  2591. SourceEdit = GetDlgItem(Dialog, IDE_SOURCE_RICHEDIT);
  2592. SourceFileEdit = GetDlgItem(Dialog, IDE_SOURCE_FILE);
  2593. StackToggle = GetDlgItem(Dialog, IDC_STACK_PROFILER_TOGGLE);
  2594. StackView = GetDlgItem(Dialog, IDC_STACK_PROFILER);
  2595. //
  2596. // Get the size of the dialog window (in dialog units) and conversion
  2597. // factors.
  2598. //
  2599. Result = GetWindowRect(Dialog, &DialogSize);
  2600. if (Result == FALSE) {
  2601. DbgOut("Error: Unable to get dialog size.\n");
  2602. }
  2603. DialogWidth = DialogSize.right - DialogSize.left - 15;
  2604. DialogHeight = DialogSize.bottom - DialogSize.top - 37;
  2605. //
  2606. // Initialize the window sizes to a default value if not done.
  2607. //
  2608. if (WindowSizesInitialized == FALSE) {
  2609. MainPaneXPosition = (DialogWidth / 2) - (UI_BORDER / 2);
  2610. MainPaneXPositionWidth = DialogWidth;
  2611. ProfilerPaneYPosition = (DialogHeight / 2) + (UI_BORDER / 2);
  2612. ProfilerPaneYPositionHeight = DialogHeight;
  2613. WindowSizesInitialized = TRUE;
  2614. }
  2615. //
  2616. // Scale the pane positions.
  2617. //
  2618. AdjustedMainPaneXPosition = MainPaneXPosition;
  2619. if (AdjustedMainPaneXPosition < UI_BORDER - 1) {
  2620. AdjustedMainPaneXPosition = UI_BORDER - 1;
  2621. }
  2622. if (AdjustedMainPaneXPosition > DialogWidth - UI_BORDER) {
  2623. AdjustedMainPaneXPosition = DialogWidth - UI_BORDER;
  2624. }
  2625. AdjustedProfilerPaneYPosition = ProfilerPaneYPosition;
  2626. if (AdjustedProfilerPaneYPosition < UI_BUTTON_HEIGHT + (2 * UI_BORDER)) {
  2627. AdjustedProfilerPaneYPosition = UI_BUTTON_HEIGHT + (2 * UI_BORDER);
  2628. }
  2629. if (AdjustedProfilerPaneYPosition > DialogHeight - UI_BORDER) {
  2630. AdjustedProfilerPaneYPosition = DialogHeight - UI_BORDER;
  2631. }
  2632. PaneXPosition = (AdjustedMainPaneXPosition * DialogWidth) /
  2633. MainPaneXPositionWidth;
  2634. ProfilerYPosition = (AdjustedProfilerPaneYPosition * DialogHeight) /
  2635. ProfilerPaneYPositionHeight;
  2636. ProfilerPaneCurrentYPosition = ProfilerYPosition;
  2637. //
  2638. // Resize the source and output edit controls to split the screen.
  2639. //
  2640. Control.left = UI_BORDER;
  2641. Control.top = UI_BUTTON_HEIGHT + (2 * UI_BORDER);
  2642. Control.right = PaneXPosition;
  2643. if (ProfilerWindowType != ProfilerDataTypeMax) {
  2644. Control.bottom = ProfilerYPosition - UI_BORDER;
  2645. } else {
  2646. Control.bottom = DialogHeight - UI_BORDER;
  2647. }
  2648. MoveWindow(SourceEdit,
  2649. Control.left,
  2650. Control.top,
  2651. Control.right - Control.left,
  2652. Control.bottom - Control.top,
  2653. FALSE);
  2654. Control.left = PaneXPosition + UI_BORDER;
  2655. Control.top = UI_BUTTON_HEIGHT + (2 * UI_BORDER);
  2656. Control.right = DialogWidth - UI_BORDER;
  2657. Control.bottom = DialogHeight - (2 * UI_BORDER) - UI_BUTTON_HEIGHT;
  2658. MoveWindow(OutputEdit,
  2659. Control.left,
  2660. Control.top,
  2661. Control.right - Control.left,
  2662. Control.bottom - Control.top,
  2663. FALSE);
  2664. //
  2665. // Show or hide the correct profiler view depending on window state.
  2666. //
  2667. if (ProfilerWindowType != ProfilerDataTypeMax) {
  2668. if (ProfilerWindowType == ProfilerDataTypeStack) {
  2669. ProfilerView = StackView;
  2670. ShowWindow(MemoryView, SW_HIDE);
  2671. } else {
  2672. assert(ProfilerWindowType == ProfilerDataTypeMemory);
  2673. ProfilerView = MemoryView;
  2674. ShowWindow(StackView, SW_HIDE);
  2675. }
  2676. Control.left = UI_BORDER;
  2677. Control.top = ProfilerYPosition;
  2678. Control.right = PaneXPosition;
  2679. Control.bottom = DialogHeight - UI_BORDER;
  2680. MoveWindow(ProfilerView,
  2681. Control.left,
  2682. Control.top,
  2683. Control.right - Control.left,
  2684. Control.bottom - Control.top,
  2685. FALSE);
  2686. ShowWindow(ProfilerView, SW_SHOW);
  2687. } else {
  2688. ShowWindow(StackView, SW_HIDE);
  2689. ShowWindow(MemoryView, SW_HIDE);
  2690. }
  2691. //
  2692. // Move the prompt and command controls.
  2693. //
  2694. Control.left = PaneXPosition + UI_BORDER;
  2695. Control.top = DialogHeight - UI_BUTTON_HEIGHT - UI_BORDER;
  2696. Control.right = Control.left + UI_PROMPT_WIDTH;
  2697. Control.bottom = Control.top + UI_BUTTON_HEIGHT;
  2698. MoveWindow(PromptEdit,
  2699. Control.left,
  2700. Control.top,
  2701. Control.right - Control.left,
  2702. Control.bottom - Control.top,
  2703. FALSE);
  2704. Control.left = PaneXPosition + (UI_BORDER * 2) + UI_PROMPT_WIDTH;
  2705. Control.top = DialogHeight - UI_BUTTON_HEIGHT - UI_BORDER;
  2706. Control.right = DialogWidth - UI_BORDER;
  2707. Control.bottom = DialogHeight - UI_BORDER;
  2708. MoveWindow(CommandEdit,
  2709. Control.left,
  2710. Control.top,
  2711. Control.right - Control.left,
  2712. Control.bottom - Control.top,
  2713. FALSE);
  2714. //
  2715. // Move the source file edit and right buttons.
  2716. //
  2717. Control.left = UI_BORDER;
  2718. Control.top = UI_BORDER;
  2719. Control.right = PaneXPosition;
  2720. Control.bottom = UI_BORDER + UI_BUTTON_HEIGHT;
  2721. MoveWindow(SourceFileEdit,
  2722. Control.left,
  2723. Control.top,
  2724. Control.right - Control.left,
  2725. Control.bottom - Control.top,
  2726. FALSE);
  2727. Control.left = PaneXPosition + (3 * UI_LARGE_BUTTON_WIDTH) +
  2728. (UI_BORDER * 4);
  2729. Control.top = UI_BORDER;
  2730. Control.right = Control.left + UI_LARGE_BUTTON_WIDTH;
  2731. Control.bottom = Control.top + UI_BUTTON_HEIGHT;
  2732. MoveWindow(GotoCursorButton,
  2733. Control.left,
  2734. Control.top,
  2735. Control.right - Control.left,
  2736. Control.bottom - Control.top,
  2737. FALSE);
  2738. Control.left = PaneXPosition + (2 * UI_LARGE_BUTTON_WIDTH) +
  2739. (UI_BORDER * 3);
  2740. Control.top = UI_BORDER;
  2741. Control.right = Control.left + UI_LARGE_BUTTON_WIDTH;
  2742. Control.bottom = Control.top + UI_BUTTON_HEIGHT;
  2743. MoveWindow(BreakAtCursorButton,
  2744. Control.left,
  2745. Control.top,
  2746. Control.right - Control.left,
  2747. Control.bottom - Control.top,
  2748. FALSE);
  2749. //
  2750. // Move the profiler elements.
  2751. //
  2752. Control.left = PaneXPosition + UI_LARGE_BUTTON_WIDTH + (UI_BORDER * 2);
  2753. Control.top = UI_BORDER;
  2754. Control.right = Control.left + UI_LARGE_BUTTON_WIDTH;
  2755. Control.bottom = Control.top + UI_BUTTON_HEIGHT;
  2756. MoveWindow(MemoryToggle,
  2757. Control.left,
  2758. Control.top,
  2759. Control.right - Control.left,
  2760. Control.bottom - Control.top,
  2761. FALSE);
  2762. Control.left = PaneXPosition + UI_BORDER;
  2763. Control.top = UI_BORDER;
  2764. Control.right = Control.left + UI_LARGE_BUTTON_WIDTH;
  2765. Control.bottom = Control.top + UI_BUTTON_HEIGHT;
  2766. MoveWindow(StackToggle,
  2767. Control.left,
  2768. Control.top,
  2769. Control.right - Control.left,
  2770. Control.bottom - Control.top,
  2771. FALSE);
  2772. //
  2773. // Repaint the entire window.
  2774. //
  2775. RedrawWindow(Dialog, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  2776. return;
  2777. }
  2778. VOID
  2779. HandleCommandMessage (
  2780. HWND Dialog,
  2781. WPARAM WParam
  2782. )
  2783. /*++
  2784. Routine Description:
  2785. This routine handles WM_COMMAND messages coming into the dialog box.
  2786. Arguments:
  2787. Dialog - Supplies the handle to the main dialog window.
  2788. WParam - Supplies the W Parameter passed in with the message.
  2789. Return Value:
  2790. None.
  2791. --*/
  2792. {
  2793. HWND CommandEdit;
  2794. HWND Focus;
  2795. CommandEdit = GetDlgItem(Dialog, IDE_COMMAND);
  2796. switch (WParam & 0xFFFF) {
  2797. //
  2798. // Destroy the window if it was closed.
  2799. //
  2800. case IDCANCEL:
  2801. DestroyWindow(Dialog);
  2802. break;
  2803. //
  2804. // Control-B was pressed.
  2805. //
  2806. case IDA_CONTROL_B:
  2807. DbgrRequestBreakIn();
  2808. SetFocus(GetDlgItem(DialogWindow, IDE_COMMAND));
  2809. break;
  2810. //
  2811. // Control-K was pressed.
  2812. //
  2813. case IDA_CONTROL_K:
  2814. MessageBox(NULL, "Control K!", "Yippee!", MB_OK);
  2815. break;
  2816. //
  2817. // Up was pressed.
  2818. //
  2819. case IDA_UP:
  2820. Focus = GetFocus();
  2821. if (Focus == CommandEdit) {
  2822. WriteByteToInput(KEY_UP);
  2823. } else {
  2824. SendMessage(Focus, WM_KEYDOWN, VK_UP, 0);
  2825. }
  2826. break;
  2827. //
  2828. // Down was pressed.
  2829. //
  2830. case IDA_DOWN:
  2831. Focus = GetFocus();
  2832. if (Focus == CommandEdit) {
  2833. WriteByteToInput(KEY_DOWN);
  2834. } else {
  2835. SendMessage(Focus, WM_KEYDOWN, VK_DOWN, 0);
  2836. }
  2837. break;
  2838. //
  2839. // Escape was pressed.
  2840. //
  2841. case IDA_ESCAPE:
  2842. WriteByteToInput(KEY_ESCAPE);
  2843. break;
  2844. //
  2845. // The toggle stack profiler view button was pressed.
  2846. //
  2847. case IDC_STACK_PROFILER_TOGGLE:
  2848. if (ProfilerWindowType != ProfilerDataTypeStack) {
  2849. UpdateProfilerWindowType(Dialog, ProfilerDataTypeStack);
  2850. } else {
  2851. UpdateProfilerWindowType(Dialog, ProfilerDataTypeMax);
  2852. }
  2853. break;
  2854. //
  2855. // The toggle memory profiler view button was pressed.
  2856. //
  2857. case IDC_MEMORY_PROFILER_TOGGLE:
  2858. if (ProfilerWindowType != ProfilerDataTypeMemory) {
  2859. UpdateProfilerWindowType(Dialog, ProfilerDataTypeMemory);
  2860. } else {
  2861. UpdateProfilerWindowType(Dialog, ProfilerDataTypeMax);
  2862. }
  2863. break;
  2864. //
  2865. // The OK button means the enter key was pressed on an edit box.
  2866. //
  2867. case IDOK:
  2868. Focus = GetFocus();
  2869. if (Focus == CommandEdit) {
  2870. HandleCommandEnter(CommandEdit);
  2871. }
  2872. break;
  2873. }
  2874. return;
  2875. }
  2876. VOID
  2877. HandleCommonControlMessage (
  2878. HWND Dialog,
  2879. LPARAM LParam
  2880. )
  2881. /*++
  2882. Routine Description:
  2883. This routine handles WM_NOTIFY messages coming into the dialog box.
  2884. Arguments:
  2885. Dialog - Supplies the handle to the main dialog window.
  2886. LParam - Supplies the L Parameter passed in with the message.
  2887. Return Value:
  2888. None.
  2889. --*/
  2890. {
  2891. LPNMHDR MessageHeader;
  2892. MessageHeader = (LPNMHDR)LParam;
  2893. switch (MessageHeader->idFrom) {
  2894. case IDC_STACK_PROFILER:
  2895. HandleProfilerTreeViewCommand(Dialog, LParam);
  2896. break;
  2897. case IDC_MEMORY_PROFILER:
  2898. HandleProfilerListViewCommand(Dialog, LParam);
  2899. break;
  2900. default:
  2901. break;
  2902. }
  2903. return;
  2904. }
  2905. VOID
  2906. HandleCommandEnter (
  2907. HWND CommandEdit
  2908. )
  2909. /*++
  2910. Routine Description:
  2911. This routine handles a command entered into the command edit box.
  2912. Arguments:
  2913. CommandEdit - Supplies a handle to the command edit box containing the
  2914. command.
  2915. Return Value:
  2916. None.
  2917. --*/
  2918. {
  2919. PCHAR Buffer;
  2920. ULONG BytesWritten;
  2921. PCHAR CurrentBuffer;
  2922. BOOL Result;
  2923. INT TextLength;
  2924. Buffer = NULL;
  2925. //
  2926. // Do nothing if commands are not enabled.
  2927. //
  2928. if (CommandsEnabled == FALSE) {
  2929. return;
  2930. }
  2931. //
  2932. // Get the length of the text in the command control, allocate space,
  2933. // and read in the control.
  2934. //
  2935. TextLength = Edit_GetTextLength(CommandEdit);
  2936. if (TextLength < 0) {
  2937. goto HandleCommandEnterEnd;
  2938. }
  2939. Buffer = malloc(TextLength + 1);
  2940. if (Buffer == NULL) {
  2941. goto HandleCommandEnterEnd;
  2942. }
  2943. Edit_GetText(CommandEdit, Buffer, TextLength + 1);
  2944. //
  2945. // Write the data into the pipe.
  2946. //
  2947. CurrentBuffer = Buffer;
  2948. while (TextLength != 0) {
  2949. Result = WriteFile(StdInPipeWrite,
  2950. CurrentBuffer,
  2951. TextLength,
  2952. &BytesWritten,
  2953. NULL);
  2954. if (Result == FALSE) {
  2955. goto HandleCommandEnterEnd;
  2956. }
  2957. CurrentBuffer += BytesWritten;
  2958. TextLength -= BytesWritten;
  2959. }
  2960. //
  2961. // Write the final newline into the pipe.
  2962. //
  2963. Buffer[0] = '\n';
  2964. Result = WriteFile(StdInPipeWrite,
  2965. Buffer,
  2966. 1,
  2967. &BytesWritten,
  2968. NULL);
  2969. if ((Result == FALSE) || (BytesWritten != 1)) {
  2970. DbgOut("Error: final newline could not be sent.\n");
  2971. goto HandleCommandEnterEnd;
  2972. }
  2973. HandleCommandEnterEnd:
  2974. Edit_SetText(CommandEdit, "");
  2975. if (Buffer != NULL) {
  2976. free(Buffer);
  2977. }
  2978. return;
  2979. }
  2980. VOID
  2981. WriteByteToInput (
  2982. BYTE Byte
  2983. )
  2984. /*++
  2985. Routine Description:
  2986. This routine puts a byte of data into the standard input buffer.
  2987. Arguments:
  2988. Byte - Supplies the byte to insert into stdin.
  2989. Return Value:
  2990. None.
  2991. --*/
  2992. {
  2993. ULONG BytesWritten;
  2994. BOOL Result;
  2995. Result = WriteFile(StdInPipeWrite,
  2996. &Byte,
  2997. 1,
  2998. &BytesWritten,
  2999. NULL);
  3000. if ((Result == FALSE) || (BytesWritten != 1)) {
  3001. DbgOut("Error: could not send byte to stdin.\n");
  3002. }
  3003. return;
  3004. }
  3005. VOID
  3006. InitializeProfilerControls (
  3007. VOID
  3008. )
  3009. /*++
  3010. Routine Description:
  3011. This routine initializes the controls used by the profiler.
  3012. Arguments:
  3013. None.
  3014. Return Value:
  3015. None.
  3016. --*/
  3017. {
  3018. LPTSTR ColumnHeader;
  3019. ULONG Index;
  3020. LVCOLUMN ListViewColumn;
  3021. HWND MemoryProfiler;
  3022. MemoryProfiler = GetDlgItem(DialogWindow, IDC_MEMORY_PROFILER);
  3023. //
  3024. // Set full row select.
  3025. //
  3026. ListView_SetExtendedListViewStyle(MemoryProfiler, LVS_EX_FULLROWSELECT);
  3027. //
  3028. // Add the columns.
  3029. //
  3030. RtlZeroMemory(&ListViewColumn, sizeof(LVCOLUMN));
  3031. ListViewColumn.mask = LVCF_TEXT | LVCF_FMT;
  3032. ListViewColumn.fmt = LVCFMT_RIGHT;
  3033. for (Index = 0; Index < MEMORY_STATISTICS_COLUMN_COUNT; Index += 1) {
  3034. ColumnHeader = MemoryStatisticsColumns[Index].Header;
  3035. ListViewColumn.pszText = ColumnHeader;
  3036. ListViewColumn.cchTextMax = strlen(ColumnHeader) + 1;
  3037. ListView_InsertColumn(MemoryProfiler, Index, &ListViewColumn);
  3038. ListView_SetColumnWidth(MemoryProfiler,
  3039. Index,
  3040. LVSCW_AUTOSIZE_USEHEADER);
  3041. }
  3042. //
  3043. // With all the columns width's now appropriately sized, reset the width of
  3044. // the first column. Since it was the only column present when inserted, it
  3045. // greedily consumed the whole control width before others were added.
  3046. //
  3047. ListView_SetColumnWidth(MemoryProfiler, 0, LVSCW_AUTOSIZE_USEHEADER);
  3048. //
  3049. // Enable group mode.
  3050. //
  3051. ListView_EnableGroupView(MemoryProfiler, TRUE);
  3052. return;
  3053. }
  3054. VOID
  3055. UpdateProfilerWindowType (
  3056. HWND Dialog,
  3057. PROFILER_DATA_TYPE DataType
  3058. )
  3059. /*++
  3060. Routine Description:
  3061. This routine updates the profiler window to show the data of the supplied
  3062. type.
  3063. Arguments:
  3064. Dialog - Supplies the handle to the main dialog window.
  3065. DataType - Supplies the type of profiler data whose profiler window should
  3066. be shown.
  3067. Return Value:
  3068. None.
  3069. --*/
  3070. {
  3071. ProfilerWindowType = DataType;
  3072. HandleResize(Dialog);
  3073. return;
  3074. }
  3075. VOID
  3076. HandleProfilerTreeViewCommand (
  3077. HWND Dialog,
  3078. LPARAM LParam
  3079. )
  3080. /*++
  3081. Routine Description:
  3082. This routine handles tree view commands.
  3083. Arguments:
  3084. Dialog - Supplies a handle to the main dialog window.
  3085. LParam - Supplies the L Parameter passed in with the message.
  3086. Return Value:
  3087. None.
  3088. --*/
  3089. {
  3090. UINT Code;
  3091. PSTACK_DATA_ENTRY StackData;
  3092. LPNMTREEVIEW TreeView;
  3093. Code = ((LPNMHDR)LParam)->code;
  3094. switch (Code) {
  3095. //
  3096. // A tree item was selected.
  3097. //
  3098. case TVN_SELCHANGED:
  3099. TreeView = (LPNMTREEVIEW)LParam;
  3100. if (TreeView->itemNew.hItem == NULL) {
  3101. break;
  3102. }
  3103. AcquireDebuggerLock(StackTreeLock);
  3104. StackData = FindStackDataEntryByHandle(StackTreeRoot,
  3105. TreeView->itemNew.hItem);
  3106. ReleaseDebuggerLock(StackTreeLock);
  3107. //
  3108. // Save the selection.
  3109. //
  3110. TreeViewSelection = TreeView->itemNew.hItem;
  3111. TreeViewSelectionVisible = TRUE;
  3112. DbgrProfilerStackEntrySelected(StackData);
  3113. break;
  3114. default:
  3115. break;
  3116. }
  3117. return;
  3118. }
  3119. PSTACK_DATA_ENTRY
  3120. FindStackDataEntryByHandle (
  3121. PSTACK_DATA_ENTRY Root,
  3122. HTREEITEM Handle
  3123. )
  3124. /*++
  3125. Routine Description:
  3126. This routine searches the provided call stack tree to find the entry
  3127. belonging to the given tree item handle.
  3128. Arguments:
  3129. Root - Supplies a pointer to the root entry of the call stack tree.
  3130. Handle - Supplies the handle to be matched.
  3131. Return Value:
  3132. Returns a pointer to the call stack entry belonging to the given handle on
  3133. success, or NULL on failure.
  3134. --*/
  3135. {
  3136. PLIST_ENTRY CurrentEntry;
  3137. PSTACK_DATA_ENTRY StackData;
  3138. //
  3139. // If the handle matches, return immediately.
  3140. //
  3141. if (Root->UiHandle == Handle) {
  3142. return Root;
  3143. }
  3144. //
  3145. // Recursively search all the children of the tree. Exit if any tree finds
  3146. // a result.
  3147. //
  3148. CurrentEntry = Root->Children.Flink;
  3149. while (CurrentEntry != &(Root->Children)) {
  3150. StackData = CONTAINING_RECORD(CurrentEntry,
  3151. STACK_DATA_ENTRY,
  3152. SiblingEntry);
  3153. StackData = FindStackDataEntryByHandle(StackData, Handle);
  3154. if (StackData != NULL) {
  3155. return StackData;
  3156. }
  3157. CurrentEntry = CurrentEntry->Flink;
  3158. }
  3159. return NULL;
  3160. }
  3161. VOID
  3162. HandleProfilerListViewCommand (
  3163. HWND Dialog,
  3164. LPARAM LParam
  3165. )
  3166. /*++
  3167. Routine Description:
  3168. This routine handles list view commands.
  3169. Arguments:
  3170. Dialog - Supplies a handle to the main dialog window.
  3171. LParam - Supplies the L Parameter passed in with the message.
  3172. Return Value:
  3173. None.
  3174. --*/
  3175. {
  3176. UINT Code;
  3177. LPNMLISTVIEW ListView;
  3178. HWND MemoryProfiler;
  3179. Code = ((LPNMHDR)LParam)->code;
  3180. switch (Code) {
  3181. //
  3182. // A list view column was clicked.
  3183. //
  3184. case LVN_COLUMNCLICK:
  3185. //
  3186. // Prevent the list from updating during the sort operation as that can
  3187. // result in incorrectly sorted columns.
  3188. //
  3189. AcquireDebuggerLock(MemoryListLock);
  3190. ListView = (LPNMLISTVIEW)LParam;
  3191. if (ListView->iSubItem == CurrentSortColumn) {
  3192. if (SortAscending == FALSE) {
  3193. SortAscending = TRUE;
  3194. } else {
  3195. SortAscending = FALSE;
  3196. }
  3197. } else {
  3198. CurrentSortColumn = ListView->iSubItem;
  3199. SortAscending = TRUE;
  3200. }
  3201. MemoryProfiler = GetDlgItem(Dialog, IDC_MEMORY_PROFILER);
  3202. ListView_SortItems(MemoryProfiler, MemoryProfilerListViewCompare, 0);
  3203. ReleaseDebuggerLock(MemoryListLock);
  3204. break;
  3205. default:
  3206. break;
  3207. }
  3208. return;
  3209. }
  3210. VOID
  3211. SetProfilerTimer (
  3212. PROFILER_DATA_TYPE DataType
  3213. )
  3214. /*++
  3215. Routine Description:
  3216. This routine sets the profiler timer for the given profiler type and
  3217. prepares the profiler window to display the data.
  3218. Arguments:
  3219. DataType - Supplies the profiler data type for which the timer should be
  3220. set.
  3221. Return Value:
  3222. None.
  3223. --*/
  3224. {
  3225. UINT_PTR Result;
  3226. //
  3227. // Set this profiler type's window to come to the front.
  3228. //
  3229. UpdateProfilerWindowType(DialogWindow, DataType);
  3230. //
  3231. // Make this data type update when the timer expires.
  3232. //
  3233. ProfilerTimerTypes[DataType] = TRUE;
  3234. //
  3235. // Set the timer. It's OK if the timer is already set.
  3236. //
  3237. Result = SetTimer(DialogWindow,
  3238. PROFILER_TIMER_ID,
  3239. PROFILER_TIMER_PERIOD,
  3240. ProfilerTimerCallback);
  3241. if (Result == 0) {
  3242. DbgOut("Error: failed to set the profiler update timer.\n");
  3243. }
  3244. return;
  3245. }
  3246. VOID
  3247. KillProfilerTimer (
  3248. PROFILER_DATA_TYPE DataType
  3249. )
  3250. /*++
  3251. Routine Description:
  3252. This routine kills the profiler timer for the given profiler type and
  3253. hides the data from the profiler window.
  3254. Arguments:
  3255. DataType - Supplies the profiler data type for which the timer should be
  3256. set.
  3257. Return Value:
  3258. None.
  3259. --*/
  3260. {
  3261. ULONG Index;
  3262. BOOL TimerInUse;
  3263. //
  3264. // Disable this type for the timer callback.
  3265. //
  3266. ProfilerTimerTypes[DataType] = FALSE;
  3267. //
  3268. // Since this data type is no longer using the timer, determine if another
  3269. // type is using the timer.
  3270. //
  3271. TimerInUse = FALSE;
  3272. for (Index = 0; Index < ProfilerDataTypeMax; Index += 1) {
  3273. if (ProfilerTimerTypes[Index] != 0) {
  3274. TimerInUse = TRUE;
  3275. break;
  3276. }
  3277. }
  3278. //
  3279. // If the timer is still in use, toggle the profiler window to show the
  3280. // still running profiler data.
  3281. //
  3282. if (TimerInUse != FALSE) {
  3283. UpdateProfilerWindowType(DialogWindow, Index);
  3284. //
  3285. // If the timer is not in use, kill it.
  3286. //
  3287. } else {
  3288. KillTimer(DialogWindow, PROFILER_TIMER_ID);
  3289. UpdateProfilerWindowType(DialogWindow, ProfilerDataTypeMax);
  3290. }
  3291. return;
  3292. }
  3293. VOID
  3294. PauseProfilerTimer (
  3295. VOID
  3296. )
  3297. /*++
  3298. Routine Description:
  3299. This routine pauses the profile timer.
  3300. Arguments:
  3301. None.
  3302. Return Value:
  3303. None.
  3304. --*/
  3305. {
  3306. RECT DialogRect;
  3307. ULONG Index;
  3308. MSG Message;
  3309. BOOL Result;
  3310. BOOL TimerInUse;
  3311. //
  3312. // Determine if there is any work to be done.
  3313. //
  3314. TimerInUse = FALSE;
  3315. for (Index = 0; Index < ProfilerDataTypeMax; Index += 1) {
  3316. if (ProfilerTimerTypes[Index] != 0) {
  3317. TimerInUse = TRUE;
  3318. break;
  3319. }
  3320. }
  3321. //
  3322. // If the timer is enabled, then kill it and flush it.
  3323. //
  3324. if (TimerInUse != FALSE) {
  3325. KillTimer(DialogWindow, PROFILER_TIMER_ID);
  3326. RtlZeroMemory(&Message, sizeof(MSG));
  3327. while (Message.message != WM_QUIT) {
  3328. Result = PeekMessage(&Message,
  3329. DialogWindow,
  3330. WM_TIMER,
  3331. WM_TIMER,
  3332. PM_REMOVE);
  3333. if (Result == FALSE) {
  3334. break;
  3335. }
  3336. }
  3337. //
  3338. // Flush out any timer message that was in the middle of running by
  3339. // calling a routine that generates a window message.
  3340. //
  3341. GetWindowRect(DialogWindow, &DialogRect);
  3342. }
  3343. return;
  3344. }
  3345. VOID
  3346. ResumeProfilerTimer (
  3347. VOID
  3348. )
  3349. /*++
  3350. Routine Description:
  3351. This routine resumes the profiler timer.
  3352. Arguments:
  3353. None.
  3354. Return Value:
  3355. None.
  3356. --*/
  3357. {
  3358. ULONG Index;
  3359. UINT_PTR Result;
  3360. BOOL TimerInUse;
  3361. //
  3362. // Determine if any of the profile timers are in use.
  3363. //
  3364. TimerInUse = FALSE;
  3365. for (Index = 0; Index < ProfilerDataTypeMax; Index += 1) {
  3366. if (ProfilerTimerTypes[Index] != 0) {
  3367. TimerInUse = TRUE;
  3368. break;
  3369. }
  3370. }
  3371. //
  3372. // Set the timer. It's OK if the timer is already set.
  3373. //
  3374. if (TimerInUse != FALSE) {
  3375. Result = SetTimer(DialogWindow,
  3376. PROFILER_TIMER_ID,
  3377. PROFILER_TIMER_PERIOD,
  3378. ProfilerTimerCallback);
  3379. if (Result == 0) {
  3380. DbgOut("Error: failed to set the profiler update timer.\n");
  3381. }
  3382. }
  3383. return;
  3384. }
  3385. VOID
  3386. CALLBACK
  3387. ProfilerTimerCallback (
  3388. HWND DialogHandle,
  3389. UINT Message,
  3390. UINT_PTR EventId,
  3391. DWORD Time
  3392. )
  3393. /*++
  3394. Routine Description:
  3395. This routine handles profiler timer callbacks.
  3396. Arguments:
  3397. DialogHandle - Supplies a handle to the window dialog associated with the
  3398. timer.
  3399. Message - Supplies the message associated with this callback. It should be
  3400. the timer message.
  3401. EventId - Supplies the ID of the callback event. It should be the profiler
  3402. timer event ID.
  3403. Time - Supplies the number of milliseconds that have elapsed since the
  3404. system was started.
  3405. Return Value:
  3406. None.
  3407. --*/
  3408. {
  3409. ULONG Index;
  3410. assert(Message == WM_TIMER);
  3411. assert(EventId == PROFILER_TIMER_ID);
  3412. //
  3413. // Update the display for every profiler type that is registered with the
  3414. // timer.
  3415. //
  3416. for (Index = 0; Index < ProfilerDataTypeMax; Index += 1) {
  3417. if (ProfilerTimerTypes[Index] != FALSE) {
  3418. UpdateProfilerDisplay(Index, ProfilerDisplayOneTime, 0);
  3419. }
  3420. }
  3421. return;
  3422. }
  3423. BOOL
  3424. UpdateProfilerDisplay (
  3425. PROFILER_DATA_TYPE DataType,
  3426. PROFILER_DISPLAY_REQUEST DisplayRequest,
  3427. ULONG Threshold
  3428. )
  3429. /*++
  3430. Routine Description:
  3431. This routine updates the profiler display. It collects the updated data
  3432. from the common debugger code and then displays it.
  3433. Arguments:
  3434. DataType - Supplies the type of profiler data that is to be displayed.
  3435. DisplayRequest - Supplies a value requesting a display action, which can
  3436. either be to display data once, continually, or to stop continually
  3437. displaying data.
  3438. Threshold - Supplies the minimum percentage a stack entry hit must be in
  3439. order to be displayed.
  3440. Return Value:
  3441. Returns TRUE if new data was display, or FALSE otherwise.
  3442. --*/
  3443. {
  3444. PLIST_ENTRY PoolListHead;
  3445. BOOL Result;
  3446. PLIST_ENTRY ResultPoolListHead;
  3447. BOOL StackTreeLockHeld;
  3448. StackTreeLockHeld = FALSE;
  3449. switch (DataType) {
  3450. case ProfilerDataTypeStack:
  3451. //
  3452. // Acquire the stack tree lock to protect accesses between the profiler
  3453. // timer, run on the UI thread, and console requests from the main
  3454. // debugger thread.
  3455. //
  3456. AcquireDebuggerLock(StackTreeLock);
  3457. StackTreeLockHeld = TRUE;
  3458. //
  3459. // Attempt to get the most up-to-date profiler data.
  3460. //
  3461. Result = DbgrGetProfilerStackData(&StackTreeRoot);
  3462. if (Result == FALSE) {
  3463. goto UpdateProfilerDisplayEnd;
  3464. }
  3465. //
  3466. // If a threshold was specified, then print the stack contents to the
  3467. // display console.
  3468. //
  3469. if (DisplayRequest == ProfilerDisplayOneTimeThreshold) {
  3470. DbgrPrintProfilerStackData(StackTreeRoot, Threshold);
  3471. //
  3472. // Otherwise update the GUI stack tree display.
  3473. //
  3474. } else {
  3475. UpdateCallStackTree(NULL, StackTreeRoot, StackTreeRoot->Count);
  3476. }
  3477. ReleaseDebuggerLock(StackTreeLock);
  3478. StackTreeLockHeld = FALSE;
  3479. break;
  3480. case ProfilerDataTypeMemory:
  3481. Result = DbgrGetProfilerMemoryData(&PoolListHead);
  3482. if ((Result == FALSE) &&
  3483. (DisplayRequest != ProfilerDisplayOneTimeThreshold)) {
  3484. goto UpdateProfilerDisplayEnd;
  3485. }
  3486. //
  3487. // If a threshold was specified, then print the memory contents to the
  3488. // display console, using the saved data if nothing new was returned.
  3489. //
  3490. // N.B. This cannot delete any of the global lists because the UI is
  3491. // still using them for sorting.
  3492. //
  3493. AcquireDebuggerLock(MemoryListLock);
  3494. if (DisplayRequest == ProfilerDisplayOneTimeThreshold) {
  3495. if (Result == FALSE) {
  3496. PoolListHead = MemoryPoolListHead;
  3497. }
  3498. ResultPoolListHead = DbgrSubtractMemoryStatistics(
  3499. PoolListHead,
  3500. MemoryBaseListHead);
  3501. DbgrPrintProfilerMemoryData(ResultPoolListHead,
  3502. MemoryDeltaModeEnabled,
  3503. Threshold);
  3504. if (ResultPoolListHead != PoolListHead) {
  3505. DbgrDestroyProfilerMemoryData(ResultPoolListHead);
  3506. }
  3507. if (PoolListHead != MemoryPoolListHead) {
  3508. DbgrDestroyProfilerMemoryData(PoolListHead);
  3509. }
  3510. //
  3511. // Otherwise update the GUI memory list view.
  3512. //
  3513. } else {
  3514. UpdateMemoryStatisticsListView(PoolListHead);
  3515. }
  3516. ReleaseDebuggerLock(MemoryListLock);
  3517. break;
  3518. default:
  3519. DbgOut("Error: invalid profiler data type %d.\n", DataType);
  3520. break;
  3521. }
  3522. Result = TRUE;
  3523. UpdateProfilerDisplayEnd:
  3524. if (StackTreeLockHeld != FALSE) {
  3525. ReleaseDebuggerLock(StackTreeLock);
  3526. }
  3527. return Result;
  3528. }
  3529. VOID
  3530. UpdateCallStackTree (
  3531. HTREEITEM Parent,
  3532. PSTACK_DATA_ENTRY Root,
  3533. ULONG TotalCount
  3534. )
  3535. /*++
  3536. Routine Description:
  3537. This routine updates the tree view for the provided call stack tree entry.
  3538. It will either create a new element for this entry or update the count and
  3539. text associated with the entry. It then operates on the entry's children.
  3540. Once it completes the update of the children, it sorts the children based
  3541. on their count and address.
  3542. Arguments:
  3543. Parent - Supplies a handle to the stack entry's parent tree view item.
  3544. Root - Supplies a pointer to the root stack entry of this tree.
  3545. TotalCount - Supplies the total number of stack traces observed.
  3546. Return Value:
  3547. None.
  3548. --*/
  3549. {
  3550. PLIST_ENTRY CurrentEntry;
  3551. PSTR FunctionString;
  3552. LPTSTR ItemString;
  3553. ULONG Percent;
  3554. HWND Profiler;
  3555. BOOL Result;
  3556. LPTSTR ScratchString;
  3557. TVSORTCB Sort;
  3558. PSTACK_DATA_ENTRY StackData;
  3559. HTREEITEM TreeItem;
  3560. TV_INSERTSTRUCT TreeView;
  3561. TVITEM UpdateItem;
  3562. //
  3563. // Return if the total count is zero. There is nothing to do.
  3564. //
  3565. if (TotalCount == 0) {
  3566. return;
  3567. }
  3568. //
  3569. // Calculate the percentage of stack traces in which this entry has been
  3570. // observed.
  3571. //
  3572. Percent = (Root->Count * 100) / TotalCount;
  3573. //
  3574. // Get the symbol string associated with this stack entry. If there is no
  3575. // parent, then it is the root.
  3576. //
  3577. if (Parent == NULL) {
  3578. FunctionString = CALL_STACK_TREE_ROOT_STRING;
  3579. } else {
  3580. FunctionString = Root->AddressSymbol;
  3581. }
  3582. //
  3583. // Get the string for this tree view item.
  3584. //
  3585. ItemString = GetFormattedMessageA("%1: %2!lu!%%, %3!lu!",
  3586. FunctionString,
  3587. Percent,
  3588. Root->Count);
  3589. if (ItemString == NULL) {
  3590. DbgOut("Formatted message failed with status 0x%x\n", GetLastError());
  3591. goto UpdateCallStackTreeEnd;
  3592. }
  3593. //
  3594. // If the treeview item has never been created for this entry, then create
  3595. // a treeview item, supplying display text and a pointer to the stack entry.
  3596. //
  3597. Profiler = GetDlgItem(DialogWindow, IDC_STACK_PROFILER);
  3598. if (Root->UiHandle == NULL) {
  3599. TreeView.hParent = Parent;
  3600. TreeView.item.mask = TVIF_TEXT | TVIF_PARAM;
  3601. TreeView.item.pszText = ItemString;
  3602. TreeView.item.cchTextMax = strlen(ItemString) + 1;
  3603. TreeView.item.lParam = (LONG_PTR)Root;
  3604. TreeItem = TreeView_InsertItem(Profiler, &TreeView);
  3605. if (TreeItem == NULL) {
  3606. DbgOut("Failed to insert item: %s\n", ItemString);
  3607. goto UpdateCallStackTreeEnd;
  3608. }
  3609. //
  3610. // Save the tree item handle for future updates.
  3611. //
  3612. Root->UiHandle = TreeItem;
  3613. //
  3614. // If a treeview item exists, then update its text if necessary. The stack
  3615. // entry should be the same.
  3616. //
  3617. } else {
  3618. ScratchString = LocalAlloc(0, strlen(ItemString) + 1);
  3619. if (ScratchString == NULL) {
  3620. DbgOut("Failed to update item text: %s\n", ItemString);
  3621. goto UpdateCallStackTreeEnd;
  3622. }
  3623. UpdateItem.mask = TVIF_TEXT;
  3624. UpdateItem.pszText = ScratchString;
  3625. UpdateItem.cchTextMax = strlen(ItemString) + 1;
  3626. UpdateItem.hItem = Root->UiHandle;
  3627. Result = TreeView_GetItem(Profiler, &UpdateItem);
  3628. //
  3629. // If the current text could not be retrieved or it does not match the
  3630. // update text, then update the item.
  3631. //
  3632. if ((Result == FALSE) || (strcmp(ScratchString, ItemString) != 0)) {
  3633. LocalFree(ScratchString);
  3634. UpdateItem.mask = TVIF_TEXT;
  3635. UpdateItem.pszText = ItemString;
  3636. UpdateItem.cchTextMax = strlen(ItemString) + 1;
  3637. UpdateItem.hItem = Root->UiHandle;
  3638. Result = TreeView_SetItem(Profiler, &UpdateItem);
  3639. if (Result == FALSE) {
  3640. DbgOut("Failed to update item text %s\n", ItemString);
  3641. goto UpdateCallStackTreeEnd;
  3642. }
  3643. } else {
  3644. LocalFree(ScratchString);
  3645. }
  3646. TreeItem = Root->UiHandle;
  3647. }
  3648. //
  3649. // Release the formatted message string. The insert and set calls above
  3650. // cause the tree view to copy the string.
  3651. //
  3652. LocalFree(ItemString);
  3653. ItemString = NULL;
  3654. //
  3655. // Update the child tree entries.
  3656. //
  3657. CurrentEntry = Root->Children.Flink;
  3658. while (CurrentEntry != &(Root->Children)) {
  3659. StackData = CONTAINING_RECORD(CurrentEntry,
  3660. STACK_DATA_ENTRY,
  3661. SiblingEntry);
  3662. UpdateCallStackTree(TreeItem, StackData, TotalCount);
  3663. CurrentEntry = CurrentEntry->Flink;
  3664. }
  3665. //
  3666. // Since the children have been updated, sort them by hit count.
  3667. //
  3668. Sort.hParent = TreeItem;
  3669. Sort.lpfnCompare = StackProfilerTreeCompare;
  3670. Sort.lParam = 0;
  3671. TreeView_SortChildrenCB(Profiler, &Sort, FALSE);
  3672. UpdateCallStackTreeEnd:
  3673. if (ItemString != NULL) {
  3674. LocalFree(ItemString);
  3675. }
  3676. return;
  3677. }
  3678. INT
  3679. CALLBACK
  3680. StackProfilerTreeCompare (
  3681. LPARAM LParamOne,
  3682. LPARAM LParamTwo,
  3683. LPARAM LParamSort
  3684. )
  3685. /*++
  3686. Routine Description:
  3687. This routine compares two profiler stack entries and determines the order
  3688. in which they should be listed in the tree. This is used to sort a tree
  3689. item's children.
  3690. Arguments:
  3691. LParamOne - Supplies a pointer to the stack data entry for the first tree
  3692. item.
  3693. LParamTwo - Supplies a pointer to the stack data entry for the second tree
  3694. item.
  3695. LParamSort - Supplies an unused parameter that is supplied by the parent
  3696. whose children are being sorted.
  3697. Return Value:
  3698. Returns a negative value if the first stack entry should precede the second.
  3699. Returns a positive value if the second stack entry should preced the first.
  3700. --*/
  3701. {
  3702. PSTACK_DATA_ENTRY DataOne;
  3703. PSTACK_DATA_ENTRY DataTwo;
  3704. DataOne = (PSTACK_DATA_ENTRY)LParamOne;
  3705. DataTwo = (PSTACK_DATA_ENTRY)LParamTwo;
  3706. //
  3707. // If the first entry's count is greater, return a negative number to
  3708. // indicate that it should come first.
  3709. //
  3710. if (DataOne->Count > DataTwo->Count) {
  3711. return -1;
  3712. //
  3713. // If the first entry's count is less, return a positive number to indicate
  3714. // that it should come second.
  3715. //
  3716. } else if (DataOne->Count < DataTwo->Count) {
  3717. return 1;
  3718. //
  3719. // If the counts are equal, then compare the entries' addresses. The lower
  3720. // address comes first.
  3721. //
  3722. } else {
  3723. if (DataOne->Address < DataTwo->Address) {
  3724. return -1;
  3725. }
  3726. }
  3727. return 1;
  3728. }
  3729. VOID
  3730. UpdateMemoryStatisticsListView (
  3731. PLIST_ENTRY PoolListHead
  3732. )
  3733. /*++
  3734. Routine Description:
  3735. This routine updates the memory statistics list view control with the
  3736. newest data returned by the profiling target.
  3737. Arguments:
  3738. PoolListHead - Supplies a pointer to the head of the list of new memory
  3739. pool data.
  3740. Return Value:
  3741. None.
  3742. --*/
  3743. {
  3744. PLIST_ENTRY CurrentEntry;
  3745. PLIST_ENTRY CurrentListHead;
  3746. INT GroupId;
  3747. ULONG Index;
  3748. INT ListViewIndex;
  3749. PPROFILER_MEMORY_POOL MemoryPool;
  3750. PMEMORY_POOL_ENTRY MemoryPoolEntry;
  3751. BOOL ReenableDeltaMode;
  3752. BOOL Result;
  3753. PPROFILER_MEMORY_POOL_TAG_STATISTIC Statistic;
  3754. ULONG TagCount;
  3755. //
  3756. // Subtract the baseline memory statsitics from the current statistics.
  3757. //
  3758. CurrentListHead = DbgrSubtractMemoryStatistics(PoolListHead,
  3759. MemoryBaseListHead);
  3760. //
  3761. // If the subtraction didn't go succeed, temporarily disable delta mode.
  3762. //
  3763. if ((CurrentListHead == PoolListHead) &&
  3764. (MemoryDeltaModeEnabled != FALSE)) {
  3765. MemoryDeltaModeEnabled = TRUE;
  3766. ReenableDeltaMode = TRUE;
  3767. } else {
  3768. ReenableDeltaMode = FALSE;
  3769. }
  3770. //
  3771. // Display the memory statistics for each memory pool.
  3772. //
  3773. CurrentEntry = CurrentListHead->Flink;
  3774. while (CurrentEntry != CurrentListHead) {
  3775. MemoryPoolEntry = CONTAINING_RECORD(CurrentEntry,
  3776. MEMORY_POOL_ENTRY,
  3777. ListEntry);
  3778. CurrentEntry = CurrentEntry->Flink;
  3779. //
  3780. // Make sure the group exists for this memory pool. If a group does not
  3781. // exist, create one.
  3782. //
  3783. MemoryPool = &(MemoryPoolEntry->MemoryPool);
  3784. Result = DoesMemoryPoolListViewGroupExist(MemoryPool, &GroupId);
  3785. if (Result == FALSE) {
  3786. Result = CreateMemoryPoolListViewGroup(MemoryPool, &GroupId);
  3787. if (Result == FALSE) {
  3788. continue;
  3789. }
  3790. }
  3791. //
  3792. // Update the list view group based on the current memory pool data.
  3793. //
  3794. Result = UpdateMemoryPoolListViewGroup(MemoryPool, GroupId);
  3795. if (Result == FALSE) {
  3796. continue;
  3797. }
  3798. //
  3799. // Create and update list view items for each tag in this memory pool.
  3800. //
  3801. TagCount = MemoryPoolEntry->MemoryPool.TagCount;
  3802. for (Index = 0; Index < TagCount; Index += 1) {
  3803. Statistic = &(MemoryPoolEntry->TagStatistics[Index]);
  3804. //
  3805. // If the subtraction above resulted in a tag with no deltas, then
  3806. // do not display it.
  3807. //
  3808. if ((Statistic->ActiveSize == 0) &&
  3809. (Statistic->ActiveAllocationCount == 0) &&
  3810. (Statistic->LifetimeAllocationSize == 0) &&
  3811. (Statistic->LargestAllocation == 0) &&
  3812. (Statistic->LargestActiveAllocationCount == 0) &&
  3813. (Statistic->LargestActiveSize == 0)) {
  3814. //
  3815. // If, however, the tag already exists, remove it!
  3816. //
  3817. Result = DoesMemoryPoolTagListViewItemExist(Statistic,
  3818. GroupId,
  3819. &ListViewIndex);
  3820. if (Result != FALSE) {
  3821. DeleteMemoryPoolTagListViewItem(ListViewIndex);
  3822. }
  3823. continue;
  3824. }
  3825. //
  3826. // If there is not already a list view item for these tag
  3827. // statistics, then create one.
  3828. //
  3829. Result = DoesMemoryPoolTagListViewItemExist(Statistic,
  3830. GroupId,
  3831. &ListViewIndex);
  3832. if (Result == FALSE) {
  3833. Result = CreateMemoryPoolTagListViewItem(Statistic->Tag,
  3834. GroupId,
  3835. &ListViewIndex);
  3836. if (Result == FALSE) {
  3837. continue;
  3838. }
  3839. }
  3840. //
  3841. // Update the list view item for the current tag statistics.
  3842. //
  3843. Result = UpdateMemoryPoolTagListViewItem(ListViewIndex,
  3844. GroupId,
  3845. Statistic);
  3846. if (Result == FALSE) {
  3847. continue;
  3848. }
  3849. }
  3850. }
  3851. //
  3852. // Re-enable delta mode if necessary.
  3853. //
  3854. if (ReenableDeltaMode != FALSE) {
  3855. MemoryDeltaModeEnabled = TRUE;
  3856. }
  3857. //
  3858. // If delta mode is enabled, but no baseline has been established, use the
  3859. // most recent data.
  3860. //
  3861. if ((MemoryDeltaModeEnabled != FALSE) && (MemoryBaseListHead == NULL)) {
  3862. MemoryBaseListHead = PoolListHead;
  3863. }
  3864. //
  3865. // Destroy the saved memory list unless it is acting as the base line list.
  3866. //
  3867. if (MemoryPoolListHead != MemoryBaseListHead) {
  3868. DbgrDestroyProfilerMemoryData(MemoryPoolListHead);
  3869. }
  3870. //
  3871. // Always save the newest pool list.
  3872. //
  3873. MemoryPoolListHead = PoolListHead;
  3874. //
  3875. // If the base list was subtracted from the pool list, then delete the old
  3876. // delta list, saving the current list as the new delta list.
  3877. //
  3878. if (CurrentListHead != PoolListHead) {
  3879. DbgrDestroyProfilerMemoryData(MemoryDeltaListHead);
  3880. MemoryDeltaListHead = CurrentListHead;
  3881. }
  3882. return;
  3883. }
  3884. BOOL
  3885. CreateMemoryPoolListViewGroup (
  3886. PPROFILER_MEMORY_POOL MemoryPool,
  3887. PINT GroupId
  3888. )
  3889. /*++
  3890. Routine Description:
  3891. This routine creates a new list view group for the given memory pool.
  3892. Arguments:
  3893. MemoryPool - Supplies a pointer to the memory pool for which the group will
  3894. be created.
  3895. GroupId - Supplies a pointer that receives the ID of the new group.
  3896. Return Value:
  3897. Returns TRUE on success, or FALSE on failure.
  3898. --*/
  3899. {
  3900. LVGROUP Group;
  3901. INT GroupIndex;
  3902. LPWSTR HeaderString;
  3903. HWND MemoryProfiler;
  3904. BOOL Result;
  3905. *GroupId = GetMemoryPoolGroupId(MemoryPool);
  3906. //
  3907. // Get the header string for this memory pool.
  3908. //
  3909. HeaderString = MemoryStatisticsPoolHeaders[MemoryPool->ProfilerMemoryType];
  3910. //
  3911. // Initialize the list view group, providing a group ID, state, and a
  3912. // header. The header is based on the pool type.
  3913. //
  3914. RtlZeroMemory(&Group, sizeof(LVGROUP));
  3915. Group.cbSize = sizeof(LVGROUP);
  3916. Group.mask = LVGF_HEADER | LVGF_STATE | LVGF_GROUPID;
  3917. Group.iGroupId = MemoryPool->ProfilerMemoryType;
  3918. Group.pszHeader = HeaderString;
  3919. Group.cchHeader = wcslen(HeaderString) + sizeof(WCHAR);
  3920. Group.stateMask = LVGS_COLLAPSIBLE | LVGS_NORMAL;
  3921. Group.state = LVGS_COLLAPSIBLE | LVGS_NORMAL;
  3922. //
  3923. // Insert the group into the memory profiler's list view.
  3924. //
  3925. Result = TRUE;
  3926. MemoryProfiler = GetDlgItem(DialogWindow, IDC_MEMORY_PROFILER);
  3927. GroupIndex = ListView_InsertGroup(MemoryProfiler, -1, &Group);
  3928. if (GroupIndex == -1) {
  3929. DbgOut("Error: failed to create memory group for pool type "
  3930. "%d.\n",
  3931. MemoryPool->ProfilerMemoryType);
  3932. Result = FALSE;
  3933. goto CreateMemoryPoolListViewGroupEnd;
  3934. }
  3935. *GroupId = GetMemoryPoolGroupId(MemoryPool);
  3936. CreateMemoryPoolListViewGroupEnd:
  3937. return Result;
  3938. }
  3939. BOOL
  3940. DoesMemoryPoolListViewGroupExist (
  3941. PPROFILER_MEMORY_POOL MemoryPool,
  3942. PINT GroupId
  3943. )
  3944. /*++
  3945. Routine Description:
  3946. This routine returns whether or not a list view group already exists for
  3947. the given memory pool. If it exists, then it returns the ID of the group.
  3948. Arguments:
  3949. MemoryPool - Supplies a pointer to the memory pool whose list view group
  3950. status is to be tested.
  3951. GroupId - Supplies a pointer that receives the ID of the memory pool group,
  3952. if it exists.
  3953. Return Value:
  3954. Returns TRUE if a list view group exists for the memory pool, or FALSE if
  3955. if does not.
  3956. --*/
  3957. {
  3958. INT LocalGroupId;
  3959. HWND MemoryProfiler;
  3960. BOOL Result;
  3961. //
  3962. // Determine if there is already a group for this memory pool. The pool
  3963. // memory type is used as the group ID.
  3964. //
  3965. LocalGroupId = GetMemoryPoolGroupId(MemoryPool);
  3966. MemoryProfiler = GetDlgItem(DialogWindow, IDC_MEMORY_PROFILER);
  3967. Result = ListView_HasGroup(MemoryProfiler, LocalGroupId);
  3968. if (Result == FALSE) {
  3969. return FALSE;
  3970. }
  3971. *GroupId = LocalGroupId;
  3972. return TRUE;
  3973. }
  3974. BOOL
  3975. UpdateMemoryPoolListViewGroup (
  3976. PPROFILER_MEMORY_POOL MemoryPool,
  3977. INT GroupId
  3978. )
  3979. /*++
  3980. Routine Description:
  3981. This routine updates the memory pool list view group for the given group ID
  3982. with the given memory pool data.
  3983. Arguments:
  3984. MemoryPool - Supplies a pointer to the memory pool data to be used to
  3985. update the list view group.
  3986. GroupId - Supplies the ID of the list view group that is to be updated.
  3987. Return Value:
  3988. Returns TRUE on success, or FALSE on failure.
  3989. --*/
  3990. {
  3991. ULONGLONG FreePercentage;
  3992. LVGROUP Group;
  3993. INT GroupIndex;
  3994. LPWSTR GroupSubtitle;
  3995. HWND MemoryProfiler;
  3996. BOOL Result;
  3997. //
  3998. // Create the wide character string for the group's subtitle.
  3999. //
  4000. if (MemoryPool->TotalPoolSize != 0) {
  4001. FreePercentage = MemoryPool->FreeListSize * 100;
  4002. FreePercentage /= MemoryPool->TotalPoolSize;
  4003. GroupSubtitle = GetFormattedMessageW(L"Size: %1!#I64x!, "
  4004. L"Allocs: %2!I64u!, "
  4005. L"Frees: %3!I64u!, "
  4006. L"Failed: %4!I64u!, "
  4007. L"Percent Free: %5!I64u!%%, "
  4008. L"Free: %6!#I64x!",
  4009. MemoryPool->TotalPoolSize,
  4010. MemoryPool->TotalAllocationCalls,
  4011. MemoryPool->TotalFreeCalls,
  4012. MemoryPool->FailedAllocations,
  4013. FreePercentage,
  4014. MemoryPool->FreeListSize);
  4015. } else {
  4016. assert(MemoryPool->FreeListSize == 0);
  4017. GroupSubtitle = GetFormattedMessageW(L"Size: -, "
  4018. L"Allocs: %1!I64u!, "
  4019. L"Frees: %2!I64u!, "
  4020. L"Failed: %3!I64u!, "
  4021. L"Percent Free: -, "
  4022. L"Free: -",
  4023. MemoryPool->TotalAllocationCalls,
  4024. MemoryPool->TotalFreeCalls,
  4025. MemoryPool->FailedAllocations);
  4026. }
  4027. if (GroupSubtitle == NULL) {
  4028. DbgOut("Error: failed to create subtitle for group %d\n", GroupId);
  4029. Result = FALSE;
  4030. goto UpdateMemoryPoolListViewGroupEnd;
  4031. }
  4032. //
  4033. // Initialize the group with the new subtitle.
  4034. //
  4035. Group.mask = LVGF_SUBTITLE;
  4036. Group.cbSize = sizeof(LVGROUP);
  4037. Group.pszSubtitle = GroupSubtitle;
  4038. Group.cchSubtitle = wcslen(GroupSubtitle) + sizeof(WCHAR);
  4039. //
  4040. // Set the group information for the group with the given ID.
  4041. //
  4042. MemoryProfiler = GetDlgItem(DialogWindow, IDC_MEMORY_PROFILER);
  4043. GroupIndex = ListView_SetGroupInfo(MemoryProfiler, GroupId, &Group);
  4044. if (GroupIndex == -1) {
  4045. DbgOut("Error: failed to update the subtitle for group %d.\n", GroupId);
  4046. Result = FALSE;
  4047. goto UpdateMemoryPoolListViewGroupEnd;
  4048. }
  4049. Result = TRUE;
  4050. UpdateMemoryPoolListViewGroupEnd:
  4051. if (GroupSubtitle == NULL) {
  4052. LocalFree(GroupSubtitle);
  4053. }
  4054. return Result;
  4055. }
  4056. INT
  4057. GetMemoryPoolGroupId (
  4058. PPROFILER_MEMORY_POOL MemoryPool
  4059. )
  4060. /*++
  4061. Routine Description:
  4062. This routine gets the group ID for the given memory pool. This should
  4063. return a unique value for each pool type.
  4064. Arguments:
  4065. MemoryPool - Supplies a pointer to the memory pool for which the group ID
  4066. is to be returned.
  4067. Return Value:
  4068. Returns the group ID.
  4069. --*/
  4070. {
  4071. //
  4072. // The group ID is simply the memory pool type.
  4073. //
  4074. return MemoryPool->ProfilerMemoryType;
  4075. }
  4076. BOOL
  4077. CreateMemoryPoolTagListViewItem (
  4078. ULONG Tag,
  4079. INT GroupId,
  4080. PINT ItemIndex
  4081. )
  4082. /*++
  4083. Routine Description:
  4084. This routine creates a new item in the memory profiler's list view. The new
  4085. item is added to the given group with the provided tag. The index of the
  4086. item is return.
  4087. Arguments:
  4088. Tag - Supplies the pool tag of the new memory list item.
  4089. GroupId - Supplies the ID of the group to which this item will belong.
  4090. ItemIndex - Supplies a pointer that receives the list view index of the new
  4091. item.
  4092. Return Value:
  4093. Returns TRUE on success, or FALSE on failure.
  4094. --*/
  4095. {
  4096. INT Index;
  4097. LPTSTR ItemString;
  4098. LVITEM ListItem;
  4099. HWND MemoryProfiler;
  4100. BOOL Result;
  4101. ItemString = GetFormattedMessageA("%1!c!%2!c!%3!c!%4!c!",
  4102. (UCHAR)Tag,
  4103. (UCHAR)(Tag >> 8),
  4104. (UCHAR)(Tag >> 16),
  4105. (UCHAR)(Tag >> 24));
  4106. if (ItemString == NULL) {
  4107. Result = FALSE;
  4108. goto CreateNewListItemEnd;
  4109. }
  4110. //
  4111. // Initialize the new list item to set the first column text and group ID.
  4112. //
  4113. RtlZeroMemory(&ListItem, sizeof(LVITEM));
  4114. ListItem.mask = LVIF_TEXT | LVIF_GROUPID;
  4115. ListItem.iItem = INT_MAX;
  4116. ListItem.iSubItem = 0;
  4117. ListItem.iGroupId = GroupId;
  4118. ListItem.pszText = ItemString;
  4119. ListItem.cchTextMax = strlen(ItemString) + 1;
  4120. //
  4121. // Insert the item in the list view.
  4122. //
  4123. MemoryProfiler = GetDlgItem(DialogWindow, IDC_MEMORY_PROFILER);
  4124. Index = ListView_InsertItem(MemoryProfiler, &ListItem);
  4125. if (Index == -1) {
  4126. DbgOut("Error: failed to insert memory item: %s\n", ItemString);
  4127. Result = FALSE;
  4128. goto CreateNewListItemEnd;
  4129. }
  4130. //
  4131. // Adjust the column width to make sure the new text fits.
  4132. //
  4133. ListView_SetColumnWidth(MemoryProfiler, 0, LVSCW_AUTOSIZE);
  4134. *ItemIndex = Index;
  4135. Result = TRUE;
  4136. CreateNewListItemEnd:
  4137. if (ItemString != NULL) {
  4138. LocalFree(ItemString);
  4139. }
  4140. return Result;
  4141. }
  4142. VOID
  4143. DeleteMemoryPoolTagListViewItem (
  4144. INT ListViewIndex
  4145. )
  4146. /*++
  4147. Routine Description:
  4148. This routine deletes a single memory list view item at the given index.
  4149. Arguments:
  4150. ListViewIndex - Supplies the index of the list view item that is to be
  4151. deleted.
  4152. Return Value:
  4153. None.
  4154. --*/
  4155. {
  4156. HWND MemoryProfiler;
  4157. MemoryProfiler = GetDlgItem(DialogWindow, IDC_MEMORY_PROFILER);
  4158. ListView_DeleteItem(MemoryProfiler, ListViewIndex);
  4159. return;
  4160. }
  4161. BOOL
  4162. DoesMemoryPoolTagListViewItemExist (
  4163. PPROFILER_MEMORY_POOL_TAG_STATISTIC Statistic,
  4164. INT GroupId,
  4165. PINT ListViewIndex
  4166. )
  4167. /*++
  4168. Routine Description:
  4169. This routine determines whether or not a list view item exists for the
  4170. given tag statistic within the given group. If the list view item is found,
  4171. then the routine returns the item's index.
  4172. Arguments:
  4173. Statistic - Supplies a pointer to the pool tag's statistics.
  4174. GroupId - Supplies the ID of the group to which the list item belongs.
  4175. ListViewIndex - Supplies a pointer that receives the index of the list view
  4176. item if it is found.
  4177. Return Value:
  4178. Returns TRUE if a list view item does exist for the provided tag and group.
  4179. Returns FALSE if no such list view item can be found.
  4180. --*/
  4181. {
  4182. PLIST_ENTRY CurrentEntry;
  4183. LVFINDINFO FindInformation;
  4184. ULONG Index;
  4185. LPARAM LParam;
  4186. PMEMORY_POOL_ENTRY MemoryPoolEntry;
  4187. HWND MemoryProfiler;
  4188. INT PoolGroupId;
  4189. PLIST_ENTRY PoolListHead;
  4190. ULONG Tag;
  4191. ULONG TagCount;
  4192. INT ViewIndex;
  4193. //
  4194. // Determine which list to use. If no lists are available, then there is
  4195. // nothing on the screen and no items exist, return FALSE.
  4196. //
  4197. if (MemoryDeltaListHead != NULL) {
  4198. PoolListHead = MemoryDeltaListHead;
  4199. } else if (MemoryPoolListHead != NULL) {
  4200. PoolListHead = MemoryPoolListHead;
  4201. } else {
  4202. return FALSE;
  4203. }
  4204. //
  4205. // Search through the previously displayed pool statistics for an entry
  4206. // that has the same tag as the given statistics and the same group ID.
  4207. //
  4208. Tag = Statistic->Tag;
  4209. CurrentEntry = PoolListHead->Flink;
  4210. while (CurrentEntry != PoolListHead) {
  4211. MemoryPoolEntry = CONTAINING_RECORD(CurrentEntry,
  4212. MEMORY_POOL_ENTRY,
  4213. ListEntry);
  4214. //
  4215. // Skip to the next memory pool if the group IDs do not match.
  4216. //
  4217. CurrentEntry = CurrentEntry->Flink;
  4218. PoolGroupId = GetMemoryPoolGroupId(&(MemoryPoolEntry->MemoryPool));
  4219. if (PoolGroupId != GroupId) {
  4220. continue;
  4221. }
  4222. //
  4223. // Search through the memory pool for the correct tag.
  4224. //
  4225. TagCount = MemoryPoolEntry->MemoryPool.TagCount;
  4226. for (Index = 0; Index < TagCount; Index += 1) {
  4227. //
  4228. // If the tags are equal, try to find a list item with the LParam
  4229. // for the current tag. The LParam is the pointer to the tag
  4230. // statistic.
  4231. //
  4232. if (MemoryPoolEntry->TagStatistics[Index].Tag == Tag) {
  4233. MemoryProfiler = GetDlgItem(DialogWindow, IDC_MEMORY_PROFILER);
  4234. LParam = (LPARAM)&(MemoryPoolEntry->TagStatistics[Index]);
  4235. RtlZeroMemory(&FindInformation, sizeof(LVFINDINFO));
  4236. FindInformation.flags = LVFI_PARAM;
  4237. FindInformation.lParam = LParam;
  4238. ViewIndex = ListView_FindItem(MemoryProfiler,
  4239. -1,
  4240. &FindInformation);
  4241. if (ViewIndex == -1) {
  4242. return FALSE;
  4243. } else {
  4244. *ListViewIndex = ViewIndex;
  4245. return TRUE;
  4246. }
  4247. }
  4248. }
  4249. }
  4250. return FALSE;
  4251. }
  4252. BOOL
  4253. UpdateMemoryPoolTagListViewItem (
  4254. INT ItemIndex,
  4255. INT GroupId,
  4256. PPROFILER_MEMORY_POOL_TAG_STATISTIC Statistic
  4257. )
  4258. /*++
  4259. Routine Description:
  4260. This routine updates a memory list view item and the given index for the
  4261. given group. The given statistics are used to update the columns for the
  4262. item.
  4263. Arguments:
  4264. ItemIndex - Supplies the list view index of the item.
  4265. GroupId - Supplies the ID of the group to which the item belongs.
  4266. Statistic - Supplies the profiler memory statistics to use for updating the
  4267. item.
  4268. Return Value:
  4269. Returns TRUE on success, or FALSE on failure.
  4270. --*/
  4271. {
  4272. LPTSTR Format;
  4273. PGETCOLUMNVALUE GetColumnValueRoutine;
  4274. ULONG Index;
  4275. LPTSTR ItemString;
  4276. LVITEM ListItem;
  4277. PMEMORY_COLUMN MemoryColumn;
  4278. HWND MemoryProfiler;
  4279. BOOL Result;
  4280. ULONGLONG Value;
  4281. //
  4282. // Zero the list item and set any fields that will not change throughout
  4283. // the duration of this routine.
  4284. //
  4285. ItemString = NULL;
  4286. RtlZeroMemory(&ListItem, sizeof(LVITEM));
  4287. ListItem.iItem = ItemIndex;
  4288. ListItem.iGroupId = GroupId;
  4289. //
  4290. // Get the profiler memory window.
  4291. //
  4292. MemoryProfiler = GetDlgItem(DialogWindow, IDC_MEMORY_PROFILER);
  4293. //
  4294. // Update the LParam for the item to point to the latest statistics. This
  4295. // is used when sorting on column clicks.
  4296. //
  4297. ListItem.mask = LVIF_PARAM;
  4298. ListItem.iSubItem = 0;
  4299. ListItem.lParam = (LPARAM)Statistic;
  4300. Result = ListView_SetItem(MemoryProfiler, &ListItem);
  4301. if (Result == FALSE) {
  4302. DbgOut("Error: failed to set LParam for pool tag %c%c%c%c.\n",
  4303. (UCHAR)Statistic->Tag,
  4304. (UCHAR)(Statistic->Tag >> 8),
  4305. (UCHAR)(Statistic->Tag >> 16),
  4306. (UCHAR)(Statistic->Tag >> 24));
  4307. goto UpdateListViewItemEnd;
  4308. }
  4309. //
  4310. // Update the value of each subitem.
  4311. //
  4312. ListItem.mask = LVIF_TEXT;
  4313. for (Index = 1; Index < MEMORY_STATISTICS_COLUMN_COUNT; Index += 1) {
  4314. MemoryColumn = &(MemoryStatisticsColumns[Index]);
  4315. //
  4316. // Get the new string based on the size of the column and the format.
  4317. // Skip the column is there is no data to display.
  4318. //
  4319. if (MemoryDeltaModeEnabled != FALSE) {
  4320. Format = MemoryColumn->DeltaFormat;
  4321. } else {
  4322. Format = MemoryColumn->Format;
  4323. }
  4324. GetColumnValueRoutine = MemoryColumn->GetColumnValueRoutine;
  4325. Value = GetColumnValueRoutine(Statistic, MemoryColumn->Offset);
  4326. if ((MemoryBaseListHead != NULL) && (Value == 0)) {
  4327. ListViewSetItemText(MemoryProfiler, ListItem.iItem, Index, "");
  4328. continue;
  4329. }
  4330. ItemString = GetFormattedMessageA(Format, Value);
  4331. if (ItemString == NULL) {
  4332. Result = FALSE;
  4333. DbgOut("Error: failed to allocate string for pool tag "
  4334. "statistics.\n");
  4335. goto UpdateListViewItemEnd;
  4336. }
  4337. //
  4338. // Set the new string at the correct subitem index.
  4339. //
  4340. ListItem.iSubItem = Index;
  4341. ListItem.pszText = ItemString;
  4342. ListItem.cchTextMax = strlen(ItemString) + 1;
  4343. Result = ListView_SetItem(MemoryProfiler, &ListItem);
  4344. if (Result == FALSE) {
  4345. DbgOut("Error: failed to insert memory subitem (%d, %d): %s\n",
  4346. ItemIndex,
  4347. Index,
  4348. ItemString);
  4349. goto UpdateListViewItemEnd;
  4350. }
  4351. LocalFree(ItemString);
  4352. }
  4353. UpdateListViewItemEnd:
  4354. if (ItemString != NULL) {
  4355. LocalFree(ItemString);
  4356. }
  4357. return Result;
  4358. }
  4359. INT
  4360. CALLBACK
  4361. MemoryProfilerListViewCompare (
  4362. LPARAM LParamOne,
  4363. LPARAM LParamTwo,
  4364. LPARAM LParamSort
  4365. )
  4366. /*++
  4367. Routine Description:
  4368. This routine compares two memory profiler list view rows by the values in
  4369. the column by which they are being sorted. It returns a negative value if
  4370. the first parameter should be before the second, zero if they are equal,
  4371. and a positive value if the first parameter should be after the second. It
  4372. accounts for whether or not the sort is ascending or descending.
  4373. Arguments:
  4374. LParamOne - Supplies the LParam value for the first list item. This is a
  4375. pointer to the item's memory statistics.
  4376. LParamTwo - Supplies the LParam value for the second list item. This is a
  4377. pointer to the item's memory statistics.
  4378. LParamSort - Supplies an LParam value for the entire sort operation. This
  4379. is not used.
  4380. Return Value:
  4381. Returns -1 if LParamOne should be before LParamTwo, 0 if they are equal,
  4382. and 1 if LParamOne should be after LParamTwo.
  4383. --*/
  4384. {
  4385. PNTDBGCOMPAREROUTINE CompareRoutine;
  4386. PGETCOLUMNVALUE GetColumnValueRoutine;
  4387. PMEMORY_COLUMN MemoryColumn;
  4388. INT Result;
  4389. PPROFILER_MEMORY_POOL_TAG_STATISTIC StatisticOne;
  4390. PPROFILER_MEMORY_POOL_TAG_STATISTIC StatisticTwo;
  4391. ULONGLONG ValueOne;
  4392. ULONGLONG ValueTwo;
  4393. assert(CurrentSortColumn < MEMORY_STATISTICS_COLUMN_COUNT);
  4394. //
  4395. // Compare the list view items based on the compare routine and field value
  4396. // for the current sort column.
  4397. //
  4398. StatisticOne = (PPROFILER_MEMORY_POOL_TAG_STATISTIC)LParamOne;
  4399. StatisticTwo = (PPROFILER_MEMORY_POOL_TAG_STATISTIC)LParamTwo;
  4400. MemoryColumn = &(MemoryStatisticsColumns[CurrentSortColumn]);
  4401. if (MemoryBaseListHead != NULL) {
  4402. CompareRoutine = MemoryColumn->DeltaCompareRoutine;
  4403. } else {
  4404. CompareRoutine = MemoryColumn->CompareRoutine;
  4405. }
  4406. GetColumnValueRoutine = MemoryColumn->GetColumnValueRoutine;
  4407. ValueOne = GetColumnValueRoutine(StatisticOne, MemoryColumn->Offset);
  4408. ValueTwo = GetColumnValueRoutine(StatisticTwo, MemoryColumn->Offset);
  4409. Result = CompareRoutine(ValueOne, ValueTwo);
  4410. if (SortAscending == FALSE) {
  4411. Result = 0 - Result;
  4412. }
  4413. return Result;
  4414. }
  4415. BOOL
  4416. TreeViewIsTreeItemVisible (
  4417. HWND TreeViewWindow,
  4418. HTREEITEM TreeItem
  4419. )
  4420. /*++
  4421. Routine Description:
  4422. This routine determines whether or not the given tree item is currently
  4423. visible in the provided Tree View window.
  4424. Arguments:
  4425. TreeViewWindow - Supplies a handle to a Tree View window.
  4426. TreeItem - Supplies a handle to a tree item.
  4427. Return Value:
  4428. Returns TRUE if the tree item is visible in the window, or FALSE otherwise.
  4429. --*/
  4430. {
  4431. HTREEITEM FirstVisible;
  4432. RECT FirstVisibleRect;
  4433. INT ItemHeight;
  4434. BOOL Result;
  4435. RECT TreeItemRect;
  4436. LONG VisibleBottom;
  4437. UINT VisibleCount;
  4438. if (TreeItem == NULL) {
  4439. return FALSE;
  4440. }
  4441. //
  4442. // If the given tree item is the first visible, then the job is simple. If
  4443. // not, then the position the the item needs to be analyzed.
  4444. //
  4445. FirstVisible = TreeView_GetFirstVisible(TreeViewWindow);
  4446. if (FirstVisible == NULL) {
  4447. return FALSE;
  4448. }
  4449. if (FirstVisible == TreeItem) {
  4450. return TRUE;
  4451. }
  4452. //
  4453. // Get the current position of the first visible item, the give tree item,
  4454. // and calculate the bottom of the visible items. Oddly,
  4455. // TreeView_GetLastVisible does not return the last visible item. It just
  4456. // returns the last expanded item.
  4457. //
  4458. ItemHeight = TreeView_GetItemHeight(TreeViewWindow);
  4459. VisibleCount = TreeView_GetVisibleCount(TreeViewWindow);
  4460. Result = TreeViewGetItemRect(TreeViewWindow,
  4461. TreeItem,
  4462. &TreeItemRect,
  4463. FALSE);
  4464. if (Result == FALSE) {
  4465. return FALSE;
  4466. }
  4467. Result = TreeViewGetItemRect(TreeViewWindow,
  4468. FirstVisible,
  4469. &FirstVisibleRect,
  4470. FALSE);
  4471. if (Result == FALSE) {
  4472. return FALSE;
  4473. }
  4474. //
  4475. // Compare the values to see if the given tree item is in or out of view.
  4476. //
  4477. VisibleBottom = (ItemHeight * VisibleCount) + FirstVisibleRect.top;
  4478. if ((TreeItemRect.top < FirstVisibleRect.top) ||
  4479. (TreeItemRect.bottom > VisibleBottom)) {
  4480. return FALSE;
  4481. }
  4482. return TRUE;
  4483. }
  4484. BOOL
  4485. TreeViewGetItemRect (
  4486. HWND Window,
  4487. HTREEITEM Item,
  4488. LPRECT Rect,
  4489. BOOL ItemRect
  4490. )
  4491. /*++
  4492. Routine Description:
  4493. This routine retrieves the bounding rectangle for a tree-view item and
  4494. indicates whether the item is visible.
  4495. Arguments:
  4496. Window - Supplies the window handle to the tree-view control.
  4497. Item - Supplies the handle to the tree-view item.
  4498. Rect - Supplies a pointer to the rect structure that receives the bounding
  4499. rectangle. The coordinates are relative to the upper-left corner of
  4500. the tree-view control.
  4501. ItemRect - Supplies a boolean indicating whether the bounding rectangle
  4502. includes only the text of the item (TRUE) or the entire line the item
  4503. occupies in the tree view (FALSE).
  4504. Return Value:
  4505. TRUE on success.
  4506. FALSE on failure.
  4507. --*/
  4508. {
  4509. //
  4510. // The input to the message is in the same parameter as the output
  4511. // rectangle. Don't use the commctrl.h macro as it violates strict-aliasing
  4512. // rules.
  4513. //
  4514. memcpy(Rect, Item, sizeof(HTREEITEM));
  4515. return SendMessage(Window, TVM_GETITEMRECT, ItemRect, (LPARAM)Rect);
  4516. }
  4517. LPTSTR
  4518. GetFormattedMessageA (
  4519. LPTSTR Message,
  4520. ...
  4521. )
  4522. /*++
  4523. Routine Description:
  4524. This routine takes a formatted message string with an argument list and
  4525. returns the expanded message string. This routine operates on ASCII
  4526. strings.
  4527. Arguments:
  4528. Message - Supplies a pointer to a formatted message string.
  4529. ... - Supplies any arguments needed to conver the formatted string.
  4530. Return Value:
  4531. Returns a pointer to the expanded message string.
  4532. --*/
  4533. {
  4534. va_list ArgumentList;
  4535. LPTSTR Buffer;
  4536. Buffer = NULL;
  4537. ArgumentList = NULL;
  4538. va_start(ArgumentList, Message);
  4539. FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  4540. FORMAT_MESSAGE_ALLOCATE_BUFFER,
  4541. Message,
  4542. 0,
  4543. 0,
  4544. (LPTSTR)&Buffer,
  4545. 0,
  4546. &ArgumentList);
  4547. va_end(ArgumentList);
  4548. return Buffer;
  4549. }
  4550. LPWSTR
  4551. GetFormattedMessageW (
  4552. LPWSTR Message,
  4553. ...
  4554. )
  4555. /*++
  4556. Routine Description:
  4557. This routine takes a formatted message string with an argument list and
  4558. returns the expanded message string. This routine operates on UNICODE wide
  4559. character strings.
  4560. Arguments:
  4561. Message - Supplies a pointer to a formatted message string.
  4562. ... - Supplies any arguments needed to conver the formatted string.
  4563. Return Value:
  4564. Returns a pointer to the expanded message string.
  4565. --*/
  4566. {
  4567. va_list ArgumentList;
  4568. LPWSTR Buffer;
  4569. Buffer = NULL;
  4570. ArgumentList = NULL;
  4571. va_start(ArgumentList, Message);
  4572. FormatMessageW(FORMAT_MESSAGE_FROM_STRING |
  4573. FORMAT_MESSAGE_ALLOCATE_BUFFER,
  4574. Message,
  4575. 0,
  4576. 0,
  4577. (LPWSTR)&Buffer,
  4578. 0,
  4579. &ArgumentList);
  4580. va_end(ArgumentList);
  4581. return Buffer;
  4582. }
  4583. VOID
  4584. ListViewSetItemText (
  4585. HWND Window,
  4586. int Item,
  4587. int SubItem,
  4588. LPTSTR Text
  4589. )
  4590. /*++
  4591. Routine Description:
  4592. This routine changes the text of a list-view item or subitem.
  4593. Arguments:
  4594. Window - Supplies the window handle to the list-view control.
  4595. Item - Supplies the zero-based index of the list-view item.
  4596. SubItem - Supplies the one-based index of the subitem. To set the item
  4597. label, supply zero here.
  4598. Text - Supplies a pointer to a null-terminated string containing the new
  4599. text. This parameter can be LPSTR_TEXTCALLBACK to indicate a callback
  4600. item for which the parent window stores the text. This parameter can
  4601. be NULL.
  4602. Return Value:
  4603. None.
  4604. --*/
  4605. {
  4606. ListView_SetItemText(Window, Item, SubItem, Text);
  4607. return;
  4608. }
  4609. INT
  4610. ComparePoolTag (
  4611. ULONGLONG ValueOne,
  4612. ULONGLONG ValueTwo
  4613. )
  4614. /*++
  4615. Routine Description:
  4616. This routine compares two pool tag values. It is given the numeric value of
  4617. the pool tags, converts them to a string of four characters and the
  4618. compares them alphabeticall, ignoring case.
  4619. Arguments:
  4620. ValueOne - Supplies the numeric value of the first pool tag to compare.
  4621. ValueTwo - Supplies the numeric value of the second pool tag to compare.
  4622. Return Value:
  4623. Returns -1 if ValueOne is less than ValueTwo, 0 if they are equal, and 1
  4624. if ValueOne is greater than ValueTwo.
  4625. --*/
  4626. {
  4627. INT Result;
  4628. CHAR TagOne[5];
  4629. CHAR TagTwo[5];
  4630. sprintf(TagOne,
  4631. "%c%c%c%c",
  4632. (UCHAR)ValueOne,
  4633. (UCHAR)(ValueOne >> 8),
  4634. (UCHAR)(ValueOne >> 16),
  4635. (UCHAR)(ValueOne >> 24));
  4636. sprintf(TagTwo,
  4637. "%c%c%c%c",
  4638. (UCHAR)ValueTwo,
  4639. (UCHAR)(ValueTwo >> 8),
  4640. (UCHAR)(ValueTwo >> 16),
  4641. (UCHAR)(ValueTwo >> 24));
  4642. Result = strcasecmp(TagOne, TagTwo);
  4643. if (Result < 0) {
  4644. Result = -1;
  4645. } else if (Result > 0) {
  4646. Result = 1;
  4647. } else {
  4648. Result = 0;
  4649. }
  4650. return Result;
  4651. }
  4652. INT
  4653. CompareUlong (
  4654. ULONGLONG ValueOne,
  4655. ULONGLONG ValueTwo
  4656. )
  4657. /*++
  4658. Routine Description:
  4659. This routine compares two ULONG values, casting the input parameters to
  4660. ULONGs.
  4661. Arguments:
  4662. ValueOne - Supplies the first ULONG to compare.
  4663. ValueTwo - Supplies the second ULONG to compare.
  4664. Return Value:
  4665. Returns -1 if ValueOne is less than ValueTwo, 0 if they are equal, and 1
  4666. if ValueOne is greater than ValueTwo.
  4667. --*/
  4668. {
  4669. INT Result;
  4670. if ((ULONG)ValueOne < (ULONG)ValueTwo) {
  4671. Result = -1;
  4672. } else if ((ULONG)ValueOne == (ULONG)ValueTwo) {
  4673. Result = 0;
  4674. } else {
  4675. Result = 1;
  4676. }
  4677. return Result;
  4678. }
  4679. INT
  4680. CompareLong (
  4681. ULONGLONG ValueOne,
  4682. ULONGLONG ValueTwo
  4683. )
  4684. /*++
  4685. Routine Description:
  4686. This routine compares two LONG values, casting the input parameters to
  4687. LONGs.
  4688. Arguments:
  4689. ValueOne - Supplies the first LONG to compare.
  4690. ValueTwo - Supplies the second LONG to compare.
  4691. Return Value:
  4692. Returns -1 if ValueOne is less than ValueTwo, 0 if they are equal, and 1
  4693. if ValueOne is greater than ValueTwo.
  4694. --*/
  4695. {
  4696. INT Result;
  4697. if ((LONG)ValueOne < (LONG)ValueTwo) {
  4698. Result = -1;
  4699. } else if ((LONG)ValueOne == (LONG)ValueTwo) {
  4700. Result = 0;
  4701. } else {
  4702. Result = 1;
  4703. }
  4704. return Result;
  4705. }
  4706. INT
  4707. CompareUlonglong (
  4708. ULONGLONG ValueOne,
  4709. ULONGLONG ValueTwo
  4710. )
  4711. /*++
  4712. Routine Description:
  4713. This routine compares two ULONGLONG values.
  4714. Arguments:
  4715. ValueOne - Supplies the first ULONGLONG to compare.
  4716. ValueTwo - Supplies the second ULONGLONG to compare.
  4717. Return Value:
  4718. Returns -1 if ValueOne is less than ValueTwo, 0 if they are equal, and 1
  4719. if ValueOne is greater than ValueTwo.
  4720. --*/
  4721. {
  4722. INT Result;
  4723. if (ValueOne < ValueTwo) {
  4724. Result = -1;
  4725. } else if (ValueOne == ValueTwo) {
  4726. Result = 0;
  4727. } else {
  4728. Result = 1;
  4729. }
  4730. return Result;
  4731. }
  4732. INT
  4733. CompareLonglong (
  4734. ULONGLONG ValueOne,
  4735. ULONGLONG ValueTwo
  4736. )
  4737. /*++
  4738. Routine Description:
  4739. This routine compares two LONGLONG values, casting the input parameters to
  4740. LONGLONGs.
  4741. Arguments:
  4742. ValueOne - Supplies the first LONGLONG to compare.
  4743. ValueTwo - Supplies the second LONGLONG to compare.
  4744. Return Value:
  4745. Returns -1 if ValueOne is less than ValueTwo, 0 if they are equal, and 1
  4746. if ValueOne is greater than ValueTwo.
  4747. --*/
  4748. {
  4749. INT Result;
  4750. if ((LONGLONG)ValueOne < (LONGLONG)ValueTwo) {
  4751. Result = -1;
  4752. } else if ((LONGLONG)ValueOne == (LONGLONG)ValueTwo) {
  4753. Result = 0;
  4754. } else {
  4755. Result = 1;
  4756. }
  4757. return Result;
  4758. }
  4759. ULONGLONG
  4760. GetUlonglongValue (
  4761. PVOID Structure,
  4762. ULONG Offset
  4763. )
  4764. /*++
  4765. Routine Description:
  4766. This routine returns a ULONGLONG value at the given offset from within the
  4767. given structure.
  4768. Arguments:
  4769. Structure - Supplies a pointer to the structure that contains the ULONGLONG
  4770. value.
  4771. Offset - Supplies the offset within the structure where the value is stored.
  4772. Return Value:
  4773. Returns a ULONGLONG value.
  4774. --*/
  4775. {
  4776. return *(PULONGLONG)((PBYTE)Structure + Offset);
  4777. }
  4778. ULONGLONG
  4779. GetUlongValue (
  4780. PVOID Structure,
  4781. ULONG Offset
  4782. )
  4783. /*++
  4784. Routine Description:
  4785. This routine returns a ULONG value at the given offset from within the
  4786. given structure.
  4787. Arguments:
  4788. Structure - Supplies a pointer to the structure that contains the ULONGLONG
  4789. value.
  4790. Offset - Supplies the offset within the structure where the value is stored.
  4791. Return Value:
  4792. Returns a ULONG value.
  4793. --*/
  4794. {
  4795. return *(PULONG)((PBYTE)Structure + Offset);
  4796. }
  4797. VOID
  4798. UiGetWindowPreferences (
  4799. HWND Dialog
  4800. )
  4801. /*++
  4802. Routine Description:
  4803. This routine saves the given window's current rect information so that it
  4804. can be written to the preferences file on exit.
  4805. Arguments:
  4806. Dialog - Supplies the dialog window handle.
  4807. Return Value:
  4808. None.
  4809. --*/
  4810. {
  4811. RECT WindowRect;
  4812. //
  4813. // Only save the window rect if it has a non-zero height and width.
  4814. //
  4815. GetWindowRect(Dialog, &WindowRect);
  4816. if ((WindowRect.left != WindowRect.right) &&
  4817. (WindowRect.top != WindowRect.bottom)) {
  4818. memcpy(&CurrentWindowRect, &WindowRect, sizeof(RECT));
  4819. }
  4820. return;
  4821. }
  4822. VOID
  4823. UiLoadPreferences (
  4824. HWND Dialog
  4825. )
  4826. /*++
  4827. Routine Description:
  4828. This routine attempts to load up the previously saved debugger preferences.
  4829. Arguments:
  4830. Dialog - Supplies the dialog window handle.
  4831. Return Value:
  4832. None.
  4833. --*/
  4834. {
  4835. DEBUGGER_UI_PREFERENCES Preferences;
  4836. BOOL Result;
  4837. Result = UiReadPreferences(&Preferences);
  4838. if (Result == FALSE) {
  4839. return;
  4840. }
  4841. if (Preferences.Version < DEBUGGER_UI_PREFERENCES_VERSION) {
  4842. return;
  4843. }
  4844. if ((Preferences.WindowWidth != 0) && (Preferences.WindowHeight != 0)) {
  4845. MainPaneXPosition = Preferences.MainPaneXPosition;
  4846. MainPaneXPositionWidth = Preferences.MainPaneXPositionWidth;
  4847. ProfilerPaneYPosition = Preferences.ProfilerPaneYPosition;
  4848. ProfilerPaneYPositionHeight = Preferences.ProfilerPaneYPositionHeight;
  4849. SetWindowPos(Dialog,
  4850. HWND_TOP,
  4851. Preferences.WindowX,
  4852. Preferences.WindowY,
  4853. Preferences.WindowWidth,
  4854. Preferences.WindowHeight,
  4855. 0);
  4856. WindowSizesInitialized = TRUE;
  4857. }
  4858. //
  4859. // Save the initial UI preferences. In case the window is never moved.
  4860. //
  4861. UiGetWindowPreferences(Dialog);
  4862. return;
  4863. }
  4864. VOID
  4865. UiSavePreferences (
  4866. HWND Dialog
  4867. )
  4868. /*++
  4869. Routine Description:
  4870. This routine attempts to save the current UI features into the preferences
  4871. file.
  4872. Arguments:
  4873. Dialog - Supplies the dialog window handle.
  4874. Return Value:
  4875. None.
  4876. --*/
  4877. {
  4878. DEBUGGER_UI_PREFERENCES Preferences;
  4879. memset(&Preferences, 0, sizeof(DEBUGGER_UI_PREFERENCES));
  4880. Preferences.Version = DEBUGGER_UI_PREFERENCES_VERSION;
  4881. Preferences.WindowX = CurrentWindowRect.left;
  4882. Preferences.WindowY = CurrentWindowRect.top;
  4883. Preferences.WindowWidth = CurrentWindowRect.right - CurrentWindowRect.left;
  4884. Preferences.WindowHeight = CurrentWindowRect.bottom - CurrentWindowRect.top;
  4885. Preferences.MainPaneXPosition = MainPaneXPosition;
  4886. Preferences.MainPaneXPositionWidth = MainPaneXPositionWidth;
  4887. Preferences.ProfilerPaneYPosition = ProfilerPaneYPosition;
  4888. Preferences.ProfilerPaneYPositionHeight = ProfilerPaneYPositionHeight;
  4889. UiWritePreferences(&Preferences);
  4890. return;
  4891. }
  4892. BOOL
  4893. UiReadPreferences (
  4894. PDEBUGGER_UI_PREFERENCES Preferences
  4895. )
  4896. /*++
  4897. Routine Description:
  4898. This routine attempts to open and read the preferences file.
  4899. Arguments:
  4900. Preferences - Supplies a pointer where the preferences will be returned on
  4901. success.
  4902. Return Value:
  4903. TRUE on success.
  4904. FALSE on failure.
  4905. --*/
  4906. {
  4907. DWORD BytesRead;
  4908. HANDLE File;
  4909. BOOL Result;
  4910. memset(Preferences, 0, sizeof(DEBUGGER_UI_PREFERENCES));
  4911. Result = FALSE;
  4912. File = UiOpenPreferences();
  4913. if (File == INVALID_HANDLE_VALUE) {
  4914. goto ReadPreferencesEnd;
  4915. }
  4916. Result = ReadFile(File,
  4917. Preferences,
  4918. sizeof(DEBUGGER_UI_PREFERENCES),
  4919. &BytesRead,
  4920. NULL);
  4921. if (Result == FALSE) {
  4922. goto ReadPreferencesEnd;
  4923. }
  4924. ReadPreferencesEnd:
  4925. if (File != INVALID_HANDLE_VALUE) {
  4926. CloseHandle(File);
  4927. }
  4928. return Result;
  4929. }
  4930. BOOL
  4931. UiWritePreferences (
  4932. PDEBUGGER_UI_PREFERENCES Preferences
  4933. )
  4934. /*++
  4935. Routine Description:
  4936. This routine attempts to open and write the preferences file.
  4937. Arguments:
  4938. Preferences - Supplies a pointer to the preferences to write.
  4939. Return Value:
  4940. TRUE on success.
  4941. FALSE on failure.
  4942. --*/
  4943. {
  4944. DWORD BytesWritten;
  4945. HANDLE File;
  4946. BOOL Result;
  4947. Result = FALSE;
  4948. File = UiOpenPreferences();
  4949. if (File == INVALID_HANDLE_VALUE) {
  4950. goto WritePreferencesEnd;
  4951. }
  4952. Result = WriteFile(File,
  4953. Preferences,
  4954. sizeof(DEBUGGER_UI_PREFERENCES),
  4955. &BytesWritten,
  4956. NULL);
  4957. if (Result == FALSE) {
  4958. goto WritePreferencesEnd;
  4959. }
  4960. WritePreferencesEnd:
  4961. if (File != INVALID_HANDLE_VALUE) {
  4962. CloseHandle(File);
  4963. }
  4964. return Result;
  4965. }
  4966. HANDLE
  4967. UiOpenPreferences (
  4968. VOID
  4969. )
  4970. /*++
  4971. Routine Description:
  4972. This routine attempts to open the preferences file.
  4973. Arguments:
  4974. None.
  4975. Return Value:
  4976. Returns an open handle to the preferences file on success.
  4977. INVALID_HANDLE_VALUE on failure.
  4978. --*/
  4979. {
  4980. HANDLE File;
  4981. TCHAR Path[MAX_PATH];
  4982. HRESULT Result;
  4983. Result = SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, Path);
  4984. if (!SUCCEEDED(Result)) {
  4985. return INVALID_HANDLE_VALUE;
  4986. }
  4987. PathAppend(Path, TEXT("\\Minoca"));
  4988. CreateDirectory(Path, NULL);
  4989. PathAppend(Path, TEXT("\\DebugUI"));
  4990. CreateDirectory(Path, NULL);
  4991. PathAppend(Path, TEXT("prefs"));
  4992. File = CreateFile(Path,
  4993. GENERIC_WRITE | GENERIC_READ,
  4994. 0,
  4995. NULL,
  4996. OPEN_ALWAYS,
  4997. FILE_ATTRIBUTE_NORMAL,
  4998. NULL);
  4999. return File;
  5000. }