Udostępnij za pośrednictwem


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 wartość Konfiguracja na Debuguj (faktycznie Debuguj Unicode), a następnie w sekcji Zaawansowane konsolidatora zresetujemy właściwość Image Has Sejf Exception Handlers właściwości na 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 CWindowImplklasy 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 WindowProcprogram 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::OnNcDestroybaza 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 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 (należy uruchomić program Visual Studio jako Administracja istrator) i aktywować go przy użyciu aplikacji Serviced Component 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