Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este tópico é o segundo de uma série de artigos que demonstra o processo de atualização de projetos mais antigos do Visual Studio C++ para a versão mais recente do Visual Studio. O código de exemplo neste tópico foi compilado pela última vez com o Visual Studio 2005.
COMSpy
COMSpy é um programa que monitora e registra a atividade de componentes atendidos em uma máquina. Os componentes com manutenção são componentes COM+ que são executados em um sistema e podem ser usados por computadores na mesma rede. Eles são gerenciados pela funcionalidade Serviços de Componentes no Painel de Controle do Windows.
Passo 1. Convertendo o arquivo de projeto
O arquivo de projeto converte facilmente e produz um relatório de migração. Há algumas entradas no relatório que nos dão a conhecer questões com as quais poderemos ter de lidar. Aqui está um problema relatado (observe que, ao longo deste tópico, as mensagens de erro às vezes são encurtadas para legibilidade, por exemplo, para remover os caminhos completos):
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).
Um dos problemas frequentes na atualização de projetos é que a configuração Linker OutputFile na caixa de diálogo de propriedades do projeto pode precisar ser revista. Para projetos anteriores ao Visual Studio 2010, o OutputFile é uma configuração com a qual o assistente de conversão automática tem problemas, se estiver definido como um valor não padrão. Nesse caso, os caminhos para os arquivos de saída foram definidos para uma pasta não padrão, XP32_DEBUG. Para saber mais sobre esse erro, consultamos uma postagem de blog relacionada à atualização do projeto Visual Studio 2010, que foi a atualização que envolveu a mudança de vcbuild para msbuild, uma alteração significativa. De acordo com essas informações, o valor padrão para a configuração Arquivo de saída quando você cria um novo projeto é $(OutDir)$(TargetName)$(TargetExt), mas isso não é definido durante a conversão, pois não é possível para os projetos convertidos verificar se tudo está correto. No entanto, vamos tentar colocar isso para OutputFile e ver se funciona. Sim, já podemos seguir em frente. Se não houver nenhum motivo específico para usar uma pasta de saída não padrão, recomendamos usar o local padrão. Neste caso, optámos por deixar o local de saída como não padrão durante o processo de migração e atualização; $(OutDir) é direcionado para a pasta XP32_DEBUG na configuração Debug e para a pasta ReleaseU na configuração Release.
Passo 2. Fazer com que funcione
Construindo o projeto portado, uma série de erros e avisos ocorrem.
ComSpyCtl não compila devido a este erro do compilador:
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
O erro faz referência ao método Save na classe IPersistStreamInitImpl em 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());
}
O problema é que uma conversão que uma versão mais antiga do compilador aceitou não é mais válida. Para estar em conformidade com o padrão C++, alguns códigos que anteriormente eram permitidos não são mais permitidos. Nesse caso, não é seguro passar um ponteiro não-constante para uma função que requisita um ponteiro constante. A solução é encontrar a declaração de IPersistStreamInit_Save na classe CComSpy e adicionar o modificador const ao terceiro parâmetro.
HRESULT CComSpy::IPersistStreamInit_Save(LPSTREAM pStm, BOOL /* fClearDirty */, const ATL_PROPMAP_ENTRY* pMap)
E uma mudança semelhante para IPersistStreamInit_Load.
HRESULT IPersistStreamInit_Load(LPSTREAM pStm, const ATL_PROPMAP_ENTRY* pMap);
O próximo erro diz respeito ao registo.
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.
Não precisamos mais desse comando de registro pós-compilação. Em vez disso, simplesmente removemos o comando build personalizado e especificamos nas configurações do Linker para registrar a saída.
Lidar com os avisos
O projeto produz o seguinte aviso de vinculação.
warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification
A /SAFESEH opção do compilador não é útil no modo de depuração, que é quando /EDITANDCONTINUE é útil, então a correção aqui é desabilitar /SAFESEH apenas para configurações de depuração . Para fazer isso na caixa de diálogo de propriedade, abrimos a caixa de diálogo de propriedade para o projeto que produz esse erro e primeiro definimos a Configuração como Debug (na verdade, Debug Unicode) e, em seguida, na seção Linker Advanced , redefinimos a propriedade Image Has Safe Exception Handlers para No (/SAFESEH:NO).
O compilador nos avisa que PROP_ENTRY_EX está preterido. Não é seguro e o substituto recomendado é 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()
Alteramos o código em ccomspy.h adequadamente, adicionando tipos de COM conforme apropriado.
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()
Estamos chegando aos últimos avisos, que também são causados por verificações de conformidade mais rigorosas do compilador:
\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'
Aviso C4018 vem deste código:
for (i=0;i<lCount;i++)
CoTaskMemFree(pKeys[i]);
O problema é que i é declarado como UINT e lCount é declarado como long, daí a incompatibilidade entre com sinal/sem sinal. Seria inconveniente alterar o tipo de lCount para UINT, uma vez que obtém o seu valor de IMtsEventInfo::get_Count, que usa o tipo long, e não está no código do utilizador. Assim, adicionamos um elenco ao código. Uma conversão estilo C seria suficiente para uma conversão numérica como esta, mas static_cast é o estilo recomendado.
for (i=0;i<static_cast<UINT>(lCount);i++)
CoTaskMemFree(pKeys[i]);
Esses avisos são casos em que uma variável foi declarada em uma função que tem um parâmetro com o mesmo nome, levando a um código potencialmente confuso. Corrigimos isso alterando os nomes das variáveis locais.
Passo 3. Teste e depuração
Testamos o aplicativo primeiro executando os vários menus e comandos e, em seguida, fechando o aplicativo. O único problema observado foi uma declaração de depuração ao fechar o aplicativo. O problema apareceu no destrutor do CWindowImpl, uma classe base do objeto CSpyCon, o principal componente COM da aplicação. A falha de asserção ocorreu no seguinte código em 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
}
O hWnd é normalmente definido como zero na WindowProc função, mas isso não aconteceu porque, em vez do padrão WindowProc, um manipulador personalizado é chamado para a mensagem do Windows (WM_SYSCOMMAND) que fecha a janela. O manipulador personalizado não estava definindo o hWnd como zero. Uma análise do código similar na classe MFC CWnd mostra que, quando uma janela está a ser destruída, OnNcDestroy é chamada, e na documentação do MFC, aconselha-se que, ao substituir CWnd::OnNcDestroy, a base NcDestroy deve ser chamada para garantir que as operações de limpeza corretas ocorram, incluindo separar o identificador da janela ou, em outras palavras, definir o hWnd para zero. Esta afirmação também pode ter sido acionada na versão original do exemplo, uma vez que o mesmo código de asserção estava presente na versão antiga do atlwin.h.
Para testar a funcionalidade do aplicativo, criamos um componente de serviço usando o modelo de projeto ATL, optamos por adicionar suporte COM+ no assistente de projeto ATL. Se nunca trabalhou com componentes de serviço antes, não é difícil criar um e registar um, tornando-o disponível no sistema ou na rede para outras aplicações usarem. O aplicativo COM Spy é projetado para monitorar a atividade de componentes atendidos como um auxílio de diagnóstico.
Em seguida, adicionamos uma classe, escolhemos ATL Object e especificamos o nome do objeto como Dog. Então, em dog.h e dog.cpp, adicionamos a implementação.
STDMETHODIMP CDog::Wag(LONG* lDuration)
{
// TODO: Add your implementation code here
*lDuration = 100l;
return S_OK;
}
Em seguida, nós o criamos e registramos (você precisará executar o Visual Studio como Administrador) e o ativamos usando o aplicativo Serviced Component no Painel de Controle do Windows. Criámos um projeto Windows Forms em C#, arrastámos um botão da caixa de ferramentas para o formulário e clicámos duas vezes nele para associar a um manipulador de eventos de clique. Adicionamos o seguinte código para instanciar o Dog componente.
private void button1_Click(object sender, EventArgs e)
{
ATLProjectLib.Dog dog1 = new ATLProjectLib.Dog();
dog1.Wag();
}
Isso funcionou sem problemas, e com o COM Spy instalado e funcionando e configurado para monitorar o Dog componente, muitos dados aparecem mostrando a atividade.
Ver também
Portabilidade e atualização: exemplos e estudos de caso
Próximo exemplo: Spy++
Exemplo anterior: MFC Scribble