Dela via


Portningsguide: COM Spy

Det här avsnittet är det andra i en serie artiklar som visar hur du uppgraderar äldre Visual Studio C++-projekt till den senaste versionen av Visual Studio. Exempelkoden i det här avsnittet kompilerades senast med Visual Studio 2005.

COMSpy

COMSpy är ett program som övervakar och loggar aktiviteten för servicekomponenter på en dator. Servicekomponenter är COM+-komponenter som körs på ett system och kan användas av datorer i samma nätverk. De hanteras av komponenttjänsternas funktioner i Windows-kontrollpanelen.

Steg 1. Konvertera projektfilen

Projektfilen konverterar enkelt och skapar en migreringsrapport. Det finns några poster i rapporten som meddelar oss om problem som vi kan behöva hantera. Här är ett problem som rapporteras (observera att felmeddelanden i hela det här avsnittet ibland förkortas för läsbarhet, till exempel för att ta bort de fullständiga sökvägarna):

ComSpyAudit\ComSpyAudit.vcproj: MSB8012: $(TargetPath) ('C:\Users\UserName\Desktop\spy\spy\ComSpyAudit\.\XP32_DEBUG\ComSpyAudit.dll') does not match the Librarian's OutputFile property value '.\XP32_DEBUG\ComSpyAudit.dll' ('C:\Users\UserName\Desktop\spy\spy\XP32_DEBUG\ComSpyAudit.dll') in project configuration 'Unicode Debug|Win32'. This may cause your project to build incorrectly. To correct this, please make sure that $(TargetPath) property value matches the value specified in %(Lib.OutputFile).

Ett av de vanligaste problemen med att uppgradera projekt är att inställningen Linker OutputFile i dialogrutan projektegenskaper kan behöva granskas. För projekt före Visual Studio 2010 är OutputFile en inställning som guiden för automatisk konvertering har problem med, om den är inställd på ett värde som inte är standard. I det här fallet har sökvägarna för utdatafilerna angetts till en mapp som inte är standard, XP32_DEBUG. För att ta reda på mer om det här felet har vi konsulterat ett blogginlägg om projektuppgradering i Visual Studio 2010, som var uppgraderingen som innebar ändringen från vcbuild till msbuild, en betydande förändring. Enligt den här informationen är standardvärdet för inställningen $(OutDir)$(TargetName)$(TargetExt) när du skapar ett nytt projekt , men detta anges inte under konverteringen eftersom det inte är möjligt för konverterade projekt att kontrollera att allt är korrekt. Men nu ska vi prova att lägga in det för OutputFile och se om det fungerar. Det gör det, så vi kan gå vidare. Om det inte finns någon särskild anledning till att använda en icke-standardiserad utdatamapp rekommenderar vi att du använder standardmappen. I det här fallet valde vi att lämna utdataplatsen som icke-standardiserade under portnings- och uppgraderingsprocessen; $(OutDir) pekar på mappen XP32_DEBUG i Debug-konfigurationen och på mappen ReleaseU i Release-konfigurationen.

Steg 2. Få det att kompilera

När du skapar det porterade projektet uppstår ett antal fel och varningar.

ComSpyCtl kompileras dock inte på grund av det här kompilatorfelet:

atlcom.h(611): error C2664: 'HRESULT CComSpy::IPersistStreamInit_Save(LPSTREAM,BOOL,ATL::ATL_PROPMAP_ENTRY *)': cannot convert argument 3 from 'const ATL::ATL_PROPMAP_ENTRY *' to 'ATL::ATL_PROPMAP_ENTRY *'atlcom.h(611): note: Conversion loses qualifiersatlcom.h(608): note: while compiling class template member function 'HRESULT ATL::IPersistStreamInitImpl<CComSpy>::Save(LPSTREAM,BOOL)'\spy\spy\comspyctl\ccomspy.h(28): note: see reference to class template instantiation 'ATL::IPersistStreamInitImpl<CComSpy>' being compiled

Felet refererar till Save metoden för IPersistStreamInitImpl klassen i atlcom.h.

STDMETHOD(Save)(_Inout_ LPSTREAM pStm, _In_ BOOL fClearDirty)
{
     T* pT = static_cast<T*>(this);
     ATLTRACE(atlTraceCOM, 2, _T("IPersistStreamInitImpl::Save\n"));
     return pT->IPersistStreamInit_Save(pStm, fClearDirty, T::GetPropertyMap());
}

Problemet är att en konvertering som en äldre version av kompilatorn accepterade inte längre är giltig. För att överensstämma med C++-standarden tillåts inte längre kod som tidigare var tillåten. I det här fallet är det inte säkert att skicka en icke-const-pekare till en funktion som förväntar sig en const-pekare. Lösningen är att hitta deklarationen av IPersistStreamInit_SaveCComSpy klassen och lägga till const-modifieraren i den tredje parametern.

HRESULT CComSpy::IPersistStreamInit_Save(LPSTREAM pStm, BOOL /* fClearDirty */, const ATL_PROPMAP_ENTRY* pMap)

Och en liknande ändring till IPersistStreamInit_Load.

HRESULT IPersistStreamInit_Load(LPSTREAM pStm, const ATL_PROPMAP_ENTRY* pMap);

Nästa fel handlar om registrering.

error MSB3073: The command "regsvr32 /s /c "C:\Users\username\Desktop\spy\spy\ComSpyCtl\.\XP32_DEBUG\ComSpyCtl.lib"error MSB3073: echo regsvr32 exec. time > ".\XP32_DEBUG\regsvr32.trg"error MSB3073:error MSB3073: :VCEnd" exited with code 3.

Vi behöver inte längre det här registreringskommandot efter bygget. I stället tar vi helt enkelt bort kommandot custom build och anger i Linker-inställningarna för att registrera utdata.

Hantera varningar

Projektet skapar följande länkvarning.

warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification

Kompilatoralternativet /SAFESEH är inte användbart i felsökningsläge, vilket är när /EDITANDCONTINUE det är användbart, så korrigeringen här är att inaktivera /SAFESEH endast för felsökningskonfigurationer . För att göra detta i egenskapsdialogrutan öppnar vi egenskapsdialogrutan för projektet som genererar det här felet, och vi ställer först in konfigurationenFelsöka (faktiskt Felsöka Unicode) och återställer sedan egenskapen Image Has Safe Exception Handlers till Nej (i avsnittet /SAFESEH:NO).

Kompilatorn varnar oss att PROP_ENTRY_EX är inaktuellt. Det är inte säkert och den rekommenderade ersättningen är PROP_ENTRY_TYPE_EX.

BEGIN_PROPERTY_MAP(CComSpy)
     PROP_ENTRY_EX( "LogFile", DISPID_LOGFILE, CLSID_ComSpyPropPage, IID_IComSpy)
     PROP_ENTRY_EX( "ShowGridLines", DISPID_GRIDLINES, CLSID_ComSpyPropPage, IID_IComSpy)
     PROP_ENTRY_EX( "Audit", DISPID_AUDIT, CLSID_ComSpyPropPage, IID_IComSpy)
     PROP_ENTRY_EX( "ColWidth", DISPID_COLWIDTH, CLSID_ComSpyPropPage, IID_IComSpy)
     PROP_PAGE(CLSID_StockFontPage)
END_PROPERTY_MAP()

Vi ändrar koden i ccomspy.h i enlighet med detta och lägger till COM-typer efter behov.

BEGIN_PROPERTY_MAP(CComSpy)
     PROP_ENTRY_TYPE_EX( "LogFile", DISPID_LOGFILE, CLSID_ComSpyPropPage, IID_IComSpy, VT_BSTR)
     PROP_ENTRY_TYPE_EX( "ShowGridLines", DISPID_GRIDLINES, CLSID_ComSpyPropPage, IID_IComSpy, VT_BOOL)
     PROP_ENTRY_TYPE_EX( "Audit", DISPID_AUDIT, CLSID_ComSpyPropPage, IID_IComSpy, VT_BOOL)
     PROP_ENTRY_TYPE_EX( "ColWidth", DISPID_COLWIDTH, CLSID_ComSpyPropPage, IID_IComSpy, VT_UINT)
     PROP_PAGE(CLSID_StockFontPage)
END_PROPERTY_MAP()

Vi kommer till de senaste varningarna, som också orsakas av strängare efterlevnadskontroller för kompilatorer:

\spy\comspyctl\usersub.h(70): warning C4457: declaration of 'var' hides function parameter\spy\comspyctl\usersub.h(48): note: see declaration of 'var'\spy\comspyctl\usersub.h(94): warning C4018: '<': signed/unsigned mismatch  ComSpy.cpp\spy\comspyctl\comspy.cpp(186): warning C4457: declaration of 'bHandled' hides function parameter\spy\spy\comspyctl\comspy.cpp(177): note: see declaration of 'bHandled'

Varning C4018 kommer från den här koden:

for (i=0;i<lCount;i++)
    CoTaskMemFree(pKeys[i]);

Problemet är att i deklareras som UINT och lCount deklareras som long, vilket leder till en signerat/osignerat missmatch. Det skulle vara obekvämt att ändra typen av lCount till UINT, eftersom den hämtar sitt värde från IMtsEventInfo::get_Count, som använder typen longoch inte finns i användarkoden. Så vi lägger till en cast i koden. En C-stils-konvertering skulle fungera för en numerisk typkonvertering som denna, men static_cast är den rekommenderade stilen.

for (i=0;i<static_cast<UINT>(lCount);i++)
    CoTaskMemFree(pKeys[i]);

Dessa varningar är fall där en variabel deklarerades i en funktion som har en parameter med samma namn, vilket leder till potentiellt förvirrande kod. Vi har åtgärdat det genom att ändra namnen på de lokala variablerna.

Steg 3. Testa och felsöka

Vi testade appen först genom att köra igenom de olika menyerna och kommandona och sedan stänga programmet. Det enda problem som noterades var ett felsökningspåstående när appen stängdes. Problemet uppstod i destruktor för CWindowImpl, en basklass för CSpyCon objektet, programmets huvudsakliga COM-komponent. Fel vid påstående inträffade i följande kod i atlwin.h.

virtual ~CWindowImplRoot()
{
     #ifdef _DEBUG
     if(m_hWnd != NULL)// should be cleared in WindowProc
     {
          ATLTRACE(atlTraceWindowing, 0, _T("ERROR - Object deleted before window was destroyed\n"));
          ATLASSERT(FALSE);
     }
     #endif //_DEBUG
}

hWnd Är normalt inställt på noll i WindowProc funktionen, men det hände inte eftersom i stället för standard WindowProcanropas en anpassad hanterare för Windows-meddelandet (WM_SYSCOMMAND) som stänger fönstret. Den anpassade hanteraren angav hWnd inte till noll. En titt på liknande kod i MFC:s CWnd-klass visar att när ett fönster förstörs, anropas OnNcDestroy, och i MFC rekommenderar dokumentationen att när du åsidosätter CWnd::OnNcDestroy bör basen NcDestroy anropas för att säkerställa att rätt städåtgärder inträffar, inklusive att separera fönsterhandtaget från fönstret, eller med andra ord ställa in hWnd till noll. Den här kontrollen kan också ha utlösts i den ursprungliga versionen av exemplet, eftersom samma kontrollkod fanns i den gamla versionen av atlwin.h.

För att testa appens funktioner skapade vi en tjänstkomponent med hjälp av ATL-projektmallen och valde att lägga till COM+-stöd i ATL-projektguiden. Om du inte har arbetat med servicekomponenter tidigare är det inte svårt att skapa en och få en registrerad och tillgänglig i systemet eller nätverket för andra appar att använda. COM Spy-appen är utformad för att övervaka aktiviteten hos servicekomponenter som ett diagnostiskt stöd.

Sedan lade vi till en klass, valde ATL-objekt och angav objektnamnet som Dog. Sedan lade vi till implementeringen i dog.h och dog.cpp.

STDMETHODIMP CDog::Wag(LONG* lDuration)
{
    // TODO: Add your implementation code here
    *lDuration = 100l;
    return S_OK;
}

Sedan skapade och registrerade vi det (du måste köra Visual Studio som administratör) och aktiverade det med hjälp av servicekomponentprogrammet på Windows Kontrollpanel. Vi skapade ett C# Windows Forms-projekt, drog en knapp till formuläret från verktygslådan och dubbelklickade på det till en klickhändelsehanterare. Vi har lagt till följande kod för att instansiera komponenten Dog .

private void button1_Click(object sender, EventArgs e)
{
    ATLProjectLib.Dog dog1 = new ATLProjectLib.Dog();
    dog1.Wag();
}

Detta kördes utan problem, och med COM Spy igång och konfigurerad för att övervaka komponenten Dog , visas massor av data som visar aktiviteten.

Se även

Portning och uppgradering: Exempel och fallstudier
Nästa exempel: Spy++
Föregående exempel: MFC Scribble