Sdílet prostřednictvím


Průvodce přenosem: COM Spy

Toto téma je druhé v řadě článků, které demonstruje proces upgradu starších projektů visual Studio C++ na nejnovější verzi sady Visual Studio. Ukázkový kód v tomto tématu byl naposledy zkompilován pomocí sady Visual Studio 2005.

COMSpy

COMSpy je program, který monitoruje a protokoluje aktivitu obsluhovaných komponent na stroji. Obsluhované komponenty jsou komponenty modelu COM+, které běží v systému a mohou je používat počítače ve stejné síti. Spravuje je funkce Služby komponent ve Windows Ovládací panely.

Krok 1. Převod souboru projektu

Soubor projektu se snadno převede a vytvoří sestavu migrace. V sestavě je několik položek, které nám můžou dát vědět o problémech, se kterými se můžeme zabývat. Tady je jeden problém, který je nahlášený (všimněte si, že v tomto tématu se chybové zprávy někdy zkracují kvůli čitelnosti, například k odebrání úplných cest):

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).

Jedním z častých problémů při upgradu projektů je, že je potřeba zkontrolovat nastavení Linker OutputFile v dialogovém okně vlastností projektu. U projektů před sadou Visual Studio 2010 je výstupní soubor jedním z nastavení, se kterým má průvodce automatickým převodem potíže, pokud je nastavený na nestandardní hodnotu. V tomto případě byly cesty pro výstupní soubory nastaveny na nestandardní složku XP32_DEBUG. Další informace o této chybě jsme se seznámili s blogovým příspěvkem souvisejícím s upgradem projektu sady Visual Studio 2010, což byl upgrade, který zahrnoval změnu z nástroje vcbuild na msbuild, což byla významná změna. Podle těchto informací je výchozí hodnota pro nastavení Výstupní soubor při vytváření nového projektu $(OutDir)$(TargetName)$(TargetExt), ale tato hodnota není nastavena během převodu, protože není možné převést projekty ověřit, zda je všechno správné. Zkusme to ale vložit do výstupního souboru a zjistit, jestli funguje. To dělá, abychom mohli pokračovat. Pokud neexistuje žádný konkrétní důvod pro použití nestandardní výstupní složky, doporučujeme použít standardní umístění. V tomto případě jsme se rozhodli ponechat výstupní umístění jako nestandardní během procesu přenosu a upgradu; $(OutDir) přeloží do složky XP32_DEBUG v Ladění konfigurace a složky ReleaseU pro konfiguraci vydané verze .

Krok 2. Získání sestavení

Při sestavování přestavovaného projektu dochází k řadě chyb a upozornění.

ComSpyCtl kvůli této chybě kompilátoru se ale nekompiluje:

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

Chyba odkazuje na metodu Save třídy IPersistStreamInitImpl v 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());
}

Problémem je, že převod, který starší verze kompilátoru přijal, už není platný. Aby bylo možné splňovat standard C++, není už povolený nějaký kód, který byl dříve povolený. V tomto případě není bezpečné předat nekonstanční ukazatel na funkci, která očekává ukazatel const. Řešením je najít deklaraci IPersistStreamInit_Save CComSpy třídy a přidat modifikátor const do třetího parametru.

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

A podobná změna na IPersistStreamInit_Load.

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

Další chyba se týká registrace.

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.

Tento příkaz pro registraci po sestavení už nepotřebujeme. Místo toho jednoduše odebereme vlastní příkaz sestavení a zadáme v nastavení linkeru pro registraci výstupu.

Práce s upozorněními

Projekt vytvoří následující upozornění linkeru.

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

Možnost kompilátoru /SAFESEH není užitečná v režimu ladění, což je užitečné /EDITANDCONTINUE , takže oprava zde je zakázána /SAFESEH pouze pro konfigurace ladění . Chcete-li to provést v dialogovém okně vlastností, otevřeme dialogové okno vlastnosti projektu, který způsobí tuto chybu, a nejprve nastavíme Konfiguraci na Ladění (ve skutečnosti Debug Unicode) a potom v části Rozšířené linkery resetujeme vlastnost Bezpeční obslužné rutiny výjimek na Ne (/SAFESEH:NO).

Kompilátor nás upozorní, že PROP_ENTRY_EX je zastaralý. Není bezpečné a doporučená náhrada je 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()

Kód v ccomspy.h odpovídajícím způsobem změníme přidáním typů COM.

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()

Dostáváme se k posledním několika upozorněním, která jsou způsobená přísnějšími kontrolami shody kompilátoru:

\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'

Upozornění C4018 pochází z tohoto kódu:

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

Problém je, že i je deklarován jako UINT a lCount deklarován jako long, a proto se neshoda se znaménky nebo bez znaménka. Bylo by nevhodné změnit typ na lCount UINT, protože získá jeho hodnotu z IMtsEventInfo::get_Count, která používá typ long, a není v uživatelském kódu. Takže do kódu přidáme přetypování. Přetypování ve stylu jazyka C by udělalo pro číselné přetypování, jako je toto, ale static_cast je doporučeným stylem.

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

Tato upozornění jsou případy, kdy byla proměnná deklarována ve funkci, která má parametr se stejným názvem, což vede k potenciálně matoucímu kódu. Opravili jsme to změnou názvů místních proměnných.

Krok 3. Testování a ladění

Nejdřív jsme aplikaci otestovali spuštěním různých nabídek a příkazů a zavřením aplikace. Jediným problémem, který jsme si poznamenali, byl při zavření aplikace kontrolní výraz ladění. Problém se objevil v destruktoru pro CWindowImpl, základní třída objektu CSpyCon , hlavní komponenta com aplikace. K chybě kontrolního výrazu došlo v následujícím kódu v 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
}

Obvykle hWnd je nastavena na nulu WindowProc ve funkci, ale to se nestalo, protože místo výchozí WindowProc, je volána vlastní obslužná rutina pro zprávu systému Windows (WM_SYSCOMMAND), která okno zavře. Vlastní obslužná rutina nenastavila hWnd hodnotu nula. Podívejte se na podobný kód ve třídě MFC CWnd , ukazuje, že když je okno zničeno, OnNcDestroy volána a v mfc, dokumentace doporučuje, aby při přepsání CWnd::OnNcDestroy, základ NcDestroy by se měl volat, aby se zajistilo, že dojde ke správným operacím čištění, včetně oddělení úchytu okna od okna, nebo jinými slovy, nastavení na nulu hWnd . Tento kontrolní výraz mohl být aktivován také v původní verzi ukázky, protože stejný kontrolní kód byl přítomný ve staré verzi atlwin.h.

K otestování funkčnosti aplikace jsme vytvořili obsluhu pomocí šablony projektu ATL a zvolili jsme přidání podpory modelu COM+ v průvodci projektem ATL. Pokud jste ještě nepracovali s obsluhovanými součástmi, není obtížné si ho vytvořit a zaregistrovat a zpřístupnit v systému nebo síti, aby je mohli používat jiné aplikace. Aplikace COM Spy je navržena pro monitorování aktivity obsluhovaných komponent jako diagnostická pomoc.

Pak jsme přidali třídu, zvolili ATL Object a zadali název objektu jako Dog. Pak jsme do dog.h a dog.cpp přidali implementaci.

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

Dále jsme ho vytvořili a zaregistrovali (budete muset spustit Visual Studio jako správce) a aktivovali ho pomocí aplikace Serviced Component ve Windows Ovládací panely. Vytvořili jsme model Windows Forms projektu jazyka C#, přetáhli jsme tlačítko do formuláře z panelu nástrojů a poklikli na popisovač události kliknutí. Přidali jsme následující kód pro vytvoření Dog instance komponenty.

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

To se spustilo bez problémů a s nástrojem COM Spy a spuštěným a nakonfigurovaným pro monitorování Dog komponenty se zobrazí spousta dat zobrazující aktivitu.

Viz také

Přenos a upgrade: Příklady a případové studie
Další příklad: Spy++
Předchozí příklad: MFC Scribble