Przewodnik przenoszenia: narzędzie Spy modelu COM
Ten temat jest drugim w serii artykułów, które pokazują proces uaktualniania starszych projektów visual Studio C++ do najnowszej wersji programu Visual Studio. Przykładowy kod w tym temacie został ostatnio skompilowany za pomocą programu Visual Studio 2005.
COMSpy
COMSpy to program, który monitoruje i rejestruje aktywność składników usługi na maszynie. Składniki usługi to składniki COM+, które działają w systemie i mogą być używane przez komputery w tej samej sieci. Są one zarządzane przez funkcje usług składników w systemie Windows Panel sterowania.
Krok 1. Konwertowanie pliku projektu
Plik projektu jest łatwo konwertowany i tworzy raport migracji. W raporcie znajduje się kilka wpisów, które poinformują nas o problemach, z którymi możemy sobie poradzić. Oto jeden z zgłoszonych problemów (należy pamiętać, że w tym temacie komunikaty o błędach są czasami skracane do czytelności, na przykład w celu usunięcia pełnych ścieżek):
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).
Jednym z częstych problemów podczas uaktualniania projektów jest to, że może być konieczne przejrzenie ustawienia Linker OutputFile w oknie dialogowym właściwości projektu. W przypadku projektów przed programem Visual Studio 2010 plik OutputFile jest jednym z ustawień, z którymi kreator konwersji automatycznej ma problemy, jeśli jest ustawiona na wartość nietypową. W tym przypadku ścieżki plików wyjściowych zostały ustawione na folder inny niż standardowy, XP32_DEBUG. Aby dowiedzieć się więcej na temat tego błędu, skonsultowaliśmy się z wpisem w blogu dotyczącym uaktualnienia projektu programu Visual Studio 2010, który był uaktualnieniem, które obejmowało zmianę z vcbuild na msbuild, znaczącą zmianę. Zgodnie z tymi informacjami wartość domyślna ustawienia Plik wyjściowy podczas tworzenia nowego projektu to $(OutDir)$(TargetName)$(TargetExt)
, ale nie jest ustawiona podczas konwersji, ponieważ nie jest możliwe przekonwertowanie projektów w celu sprawdzenia, czy wszystko jest poprawne. Spróbujmy jednak umieścić to w pliku OutputFile i sprawdzić, czy działa. To robi, więc możemy przejść dalej. Jeśli nie ma konkretnego powodu używania niestandardowego folderu wyjściowego, zalecamy użycie standardowej lokalizacji. W takim przypadku wybraliśmy pozostawienie lokalizacji wyjściowej jako niestandardowej podczas procesu przenoszenia i uaktualniania; $(OutDir)
rozpoznaje folder XP32_DEBUG w folderze Debugowanie konfiguracji i folderu ReleaseU dla konfiguracji wydania .
Krok 2. Uzyskiwanie go do kompilacji
Kompilowanie przeniesionego projektu występuje wiele błędów i ostrzeżeń.
ComSpyCtl
nie kompiluje się jednak z powodu tego błędu kompilatora:
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
Błąd odwołuje się do Save
metody w IPersistStreamInitImpl
klasie w pliku 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());
}
Problem polega na tym, że konwersja, że starsza wersja kompilatora została zaakceptowana, nie jest już prawidłowa. Aby zachować zgodność ze standardem języka C++, kod, który wcześniej był dozwolony, nie jest już dozwolony. W takim przypadku nie można bezpiecznie przekazać wskaźnika innego niż const do funkcji, która oczekuje wskaźnika const. Rozwiązaniem jest znalezienie deklaracji IPersistStreamInit_Save
klasy CComSpy
i dodanie modyfikatora const do trzeciego parametru.
HRESULT CComSpy::IPersistStreamInit_Save(LPSTREAM pStm, BOOL /* fClearDirty */, const ATL_PROPMAP_ENTRY* pMap)
Podobna zmiana to IPersistStreamInit_Load
.
HRESULT IPersistStreamInit_Load(LPSTREAM pStm, const ATL_PROPMAP_ENTRY* pMap);
Następny błąd dotyczy rejestracji.
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.
Nie potrzebujemy już tego polecenia rejestracji po kompilacji. Zamiast tego po prostu usuniemy niestandardowe polecenie kompilacji i określimy w ustawieniach konsolidatora , aby zarejestrować dane wyjściowe.
Obsługa ostrzeżeń
Projekt generuje następujące ostrzeżenie konsolidatora.
warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification
Opcja kompilatora /SAFESEH
nie jest przydatna w trybie debugowania, co jest /EDITANDCONTINUE
przydatne, dlatego poprawka w tym miejscu jest wyłączona /SAFESEH
tylko dla konfiguracji debugowania . Aby to zrobić w oknie dialogowym właściwości, otwieramy okno dialogowe właściwości dla projektu, który generuje ten błąd, a najpierw ustawiliśmy opcję Konfiguracja na Debuguj (faktycznie Debuguj Unicode), a następnie w sekcji Zaawansowane konsolidatora zresetujemy właściwość Image Has Safe Exception Handlers na Wartość Nie (/SAFESEH:NO
).
Kompilator ostrzega nas, że PROP_ENTRY_EX
jest przestarzały. Nie jest bezpieczny, a zalecanym zamiennikiem jest 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()
Odpowiednio zmieniamy kod w pliku ccomspy.h, dodając odpowiednio typy 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()
Przechodzimy do kilku ostatnich ostrzeżeń, które są również spowodowane bardziej rygorystycznymi sprawdzaniem zgodności kompilatora:
\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'
Ostrzeżenie C4018 pochodzi z tego kodu:
for (i=0;i<lCount;i++)
CoTaskMemFree(pKeys[i]);
Problem polega na tym, że i
jest zadeklarowany jako UINT
i lCount
jest zadeklarowany jako long
, stąd niezgodność podpisanych/niepodpisanych. Byłoby niewygodne, aby zmienić typ lCount
na UINT
, ponieważ pobiera wartość z IMtsEventInfo::get_Count
, która używa typu long
, i nie jest w kodzie użytkownika. Dlatego dodajemy rzutowanie do kodu. Rzutowanie w stylu C zrobiłoby dla rzutu liczbowego, takiego jak ten, ale static_cast
jest zalecanym stylem.
for (i=0;i<static_cast<UINT>(lCount);i++)
CoTaskMemFree(pKeys[i]);
Ostrzeżenia te to przypadki, w których zmienna została zadeklarowana w funkcji, która ma parametr o tej samej nazwie, co prowadzi do potencjalnie mylącego kodu. Rozwiązaliśmy ten problem, zmieniając nazwy zmiennych lokalnych.
Krok 3. Testowanie i debugowanie
Najpierw przetestowaliśmy aplikację, uruchamiając różne menu i polecenia, a następnie zamykając aplikację. Jedynym zanotowaniem problemu było potwierdzenie debugowania po zamknięciu aplikacji. Problem pojawił się w destruktorze dla CWindowImpl
klasy bazowej CSpyCon
obiektu głównego składnika COM aplikacji. Błąd asercji wystąpił w poniższym kodzie w pliku 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
}
Parametr hWnd
jest zwykle ustawiony na zero w WindowProc
funkcji, ale tak się nie stało, ponieważ zamiast domyślnego WindowProc
program obsługi niestandardowej jest wywoływany dla komunikatu systemu Windows (WM_SYSCOMMAND), który zamyka okno. Niestandardowa procedura obsługi nie ustawiała wartości zero hWnd
. Spojrzenie na podobny kod w klasie MFC pokazuje, że gdy okno jest niszczone, jest wywoływane, OnNcDestroy
a w dokumentacji MFC CWnd
zaleca się, aby podczas zastępowania CWnd::OnNcDestroy
baza NcDestroy
była wywoływana, aby upewnić się, że występują odpowiednie operacje czyszczenia, w tym oddzielenie uchwytu okna od okna lub innymi słowy, ustawienie hWnd
wartości zero. To twierdzenie mogło zostać wyzwolone w oryginalnej wersji przykładu, ponieważ ten sam kod asercji był obecny w starej wersji pliku atlwin.h.
Aby przetestować funkcjonalność aplikacji, utworzyliśmy składnik serviced przy użyciu szablonu projektu ATL, wybraliśmy opcję dodania obsługi modelu COM+ w kreatorze projektu ATL. Jeśli wcześniej nie pracowaliśmy ze składnikami usługi, nie jest trudno utworzyć go i uzyskać jeden zarejestrowany i dostępny w systemie lub sieci, aby inne aplikacje korzystały. Aplikacja COM Spy została zaprojektowana tak, aby monitorować aktywność składników serwisowanych jako pomoc diagnostyczną.
Następnie dodaliśmy klasę, wybraliśmy obiekt ATL i określiliśmy nazwę obiektu jako Dog
. Następnie w pliku dog.h i dog.cpp dodaliśmy implementację.
STDMETHODIMP CDog::Wag(LONG* lDuration)
{
// TODO: Add your implementation code here
*lDuration = 100l;
return S_OK;
}
Następnie skompilowaliśmy go i zarejestrowaliśmy (musisz uruchomić program Visual Studio jako administrator) i aktywowaliśmy go przy użyciu aplikacji Składnik usługi w Panel sterowania systemu Windows. Utworzyliśmy projekt formularzy systemu Windows w języku C#, przeciągnął przycisk do formularza z przybornika i dwukrotnie kliknął go do procedury obsługi zdarzeń kliknięcia. Dodaliśmy następujący kod, aby utworzyć Dog
wystąpienie składnika.
private void button1_Click(object sender, EventArgs e)
{
ATLProjectLib.Dog dog1 = new ATLProjectLib.Dog();
dog1.Wag();
}
Zostało to uruchomione bez problemów, a program COM Spy jest uruchomiony i skonfigurowany do monitorowania Dog
składnika, wiele danych jest wyświetlanych z wyświetlonymi działaniami.
Zobacz też
Przenoszenie i uaktualnianie: Przykłady i analizy przypadków
Następny przykład: Spy++
Poprzedni przykład: MFC Scribble