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