Páginas

segunda-feira, 26 de outubro de 2009

GAC vs. <codeBase>

Onde trabalho, não faz muito tempo que fomos questionados sobre boas práticas e o bom uso de assemblies .NET no GAC (Global Assembly Cache). Sinceramente, o melhor uso do GAC é não utilizá-lo, pelo menos no contexto que as aplicações aqui são desenvolvidas.

As aplicações, com excessão das que são utilitários e frameworks, geralmente são isoladas no que se diz respeito a reaproveitamento de código. Então, o uso do GAC não se justifica para essa maioria dos casos.

Umas das coisas legais que aprendi enquanto dava uma olhada no assunto foi utilizar melhor as configurações de runtime, mais especificamente em relação ao codebase.

Com esta configuração, é possível indicar o caminho de um assembly que deve ser utilizado pela aplicação, estando ela onde estiver na máquina ou inclusive na rede! Com isso, acabou a desculpa de colocar um componente no GAC apenas porque a aplicação, seja ela windows ou web, não consegue encontrá-lo. GAC é para compartilhar, como o próprio nome diz.

E como fazer com que o runtime encontre o assembly? Simplesmente adicionando a seguinte configuração ao arquivo config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <dependentAssembly>
                <assemblyIdentity name="CodebaseLib"
                                    publicKeyToken="72eb1259ccc328b1"
                                    culture="neutral" />
                <codeBase version="1.0.0.0"
                          href="file://D:\Projetos\Testes\TesteCodebase\CodebaseLib\bin\Debug\CodebaseLib.dll"/>
            </dependentAssembly>
        </assemblyBinding>
    </runtime>
</configuration>

Cada assembly que precisa ser encontrado deve ter uma tag dependentAssembly, contendo as tags assemblyIdentity e codeBase. A própria configuração é auto-explicativa: com assemblyIdentity, se identifica o componente, informando o nome, a cultura, o publicKeyToken (no caso de componentes com strong name); codeBase informa a versão e onde encontrar o componente.

Aqui cabem duas observações:

  1. href pode fazer referência tanto a um arquivo em disco como um arquivo na internet (poderia ser http://www.meusite.com.br/CodebaseLib.dll).
  2. Caso o componente tenha strong name, então ele pode se localizar em qualquer lugar; já se ele não possuir então deve estar em um subdiretório do diretório da aplicação.

[]’s

sexta-feira, 16 de outubro de 2009

Detection of product '{0}', feature 'Base_And_Client', component '{1}' failed

Finalmente consegui resolver o problema que estava tendo com o Windows Installer e o COM+ de uma forma aceitável.

O Problema

O problema inicial foi quando criei um instalador, utilizando o InstallShield, para empacotar um aplicativo que utilizamos onde trabalho, que havia sido migrado de .NET 1.1 para .NET 2.0. Este aplicativo é composto de componentes .NET 1.1 (para manter a compatibilidade com versões de sistemas que usam esse runtime), .NET 2.0 com windows services e COM+ (também para manter compatibilidade).

A compatibilidade foi necessária para fazer com que os sistemas que utilizassem a versão em .NET 1.1 desse aplicativo pudessem passar a utilizar a nova, sem a necessidade de alterações em código. E para manter a compatibilidade, foi criado o pacote COM+ citado acima, para ser o divisor de águas entre os runtimes 1.1 e 2.0, permitindo as chamadas.

Quando este pacote foi instalado nos servidores de componentes, a cada chamada do aplicativo, eram logados os eventos abaixo, além de ter um pequeno atraso na execução do mesmo.



Detection of product '{3FAE8AAC-EA51-4060-8181-7FC647E5EC9E}', feature 'Base_And_Client', component '{5A249606-8811-4E9D-BF61-36E04087B112}' failed. The resource '' does not exist.

Detection of product '{3FAE8AAC-EA51-4060-8181-7FC647E5EC9E}', feature 'Base_And_Client' failed during request for component '{3E87C314-FFBB-4F7F-A52C-609189873BB3}'
O produto {3FAE8AAC-EA51-4060-8181-7FC647E5EC9E} é o aplicativo que foi migrado para .NET 2.0. O componente {3E87C314-FFBB-4F7F-A52C-609189873BB3} é o componente que fica no COM+, responsável por rotear as chamadas vindas do .NET 1.1 para .NET 2.0 e o componente {5A249606-8811-4E9D-BF61-36E04087B112} é o componente que fez a chamada ao componente do COM+. Ambos também são instalados no GAC.

Obs. Utilizar COM+ para fazer a interoperabilidade entre as versões do runtime do .NET talvez não tenha sido a melhor idéia. Poderia ter utilizado .NET Remoting ou mesmo Web Services para isso, mas esse questionamento fica para depois.

A Análise

Como introduzimos o instalador MSI para empacotar o aplicativo, minha desconfiança caiu sobre o Windows Installer. Após algumas pesquisas, descobri que existe uma funcionalidade no Windows Installer chamada “resiliency” (comentei sobre ela neste post http://galorebr.blogspot.com/2009/09/windows-installer-application.html). Basicamente ela restaura uma instalação caso um componente não seja encontrado.

Parecia que a CLR do .NET 1.1 não encontrava o componente COM+ em .NET 2.0, mesmo estando no GAC. Talvez isso seja porque existem diferenças de localização do GAC entre essas versões do runtime: a primeira coloca as dll’s em C:\WINDOWS\assembly\GAC, a segunda em C:\WINDOWS\assembly\GAC_MSIL.

Utilizando o Filemon (http://technet.microsoft.com/en-us/sysinternals/bb896642.aspx), não encontrei evidências de não se encontrar o arquivo.
A próxima análise foi utilizando o log do Fusion (http://blogs.msdn.com/suzcook/archive/2003/05/29/57120.aspx). Com isso, conseguimos identificar que o CLR tomava um capote quando tentava achar o componente do COM+. Um detalhe que percebemos é que o erro acontecia quando o sistema que chamava nosso aplicativo se encontrava no COM+ também (.NET 1.1). Um dos locais onde era pesquisada a existência da dll era o diretório C:\Windows\System32\.

Como o CLR não encontrava a dll, chamava o Windows Installer para perguntar por que ela não existia (http://msdn.microsoft.com/en-us/library/15hyw9x3(VS.71).aspx, If Assembly2 is not found at either of those locations, the runtime queries the Windows Installer ). Isso é que provavelmente ocasionava a tentativa de reparação.

A Solução

Talvez não seja a solução ideal, nem a mais bela, mas funcionou. Simplesmente colocamos uma cópia do componente que vai no COM+ dentro do diretório C:\Windows\System32\. Com isso, o CLR localiza o componente e não pergunta nada ao Windows Installer.

Somente uma ressalva, não fizemos testes quando os sistemas que chamam nosso aplicativo possuem a propriedade Application Root Directory ajustada no COM+. Com isso, teoricamente o CLR não iria procurar no System32. Mas se isso acontecer, podemos ajudar no arquivo de config desse sistema para localizar o componente no lugar certo, através do Probing (http://msdn.microsoft.com/en-us/library/4191fzwb(VS.71).aspx.

Ufa, finalmente este problema foi resolvido!

[]’s

domingo, 20 de setembro de 2009

Windows Installer & Application Resiliency

Atualmente tenho lido sobre uma característica do Windows Installer, que é a "resiliência" (não encontrei ainda tradução para resiliency).

Trata-se de uma característica onde o próprio serviço do Windows Installer consegue identificar se um produto instalado está saudável, ou seja, se todos seus componentes estão instalados e configurados, etc etc.

O seguinte artigo é muito bom, pois explica um pouco mais sobre a forma que a resiliência funciona: (Application Resiliency: Unlock the Hidden Features of Windows Installer) http://msdn.microsoft.com/en-us/library/aa302344.aspx.

[]'s

quinta-feira, 20 de agosto de 2009

Erro ao executar o SGEN

Hoje encontrei um problema ao tentar compilar um projeto dentro do Source Control (no caso, Rational Clear Case).

Este projeto em C# faz referência a um web service, e por isso no processo de compilação é chamado o SGEN para gerar dll *.XmlSerializers.dll. Pra ser sincero não sei porque esta dll deve existir, isso fica para um próximo post. A questão agora é como resolver o problema.

O erro que ocorreu foi o abaixo. Deu access denied na hora de executar o SGEN em cima do meu projeto, sendo que este projeto faz referência a componentes (FxCopSdk.dll) que fazem parte do Source Control (ou seja, estão com o atributo read-only).

------ Rebuild All started: Project: bba.fwk.vbp.rules.code, Configuration: Release Any CPU ------c:\WINDOWS\Microsoft.NET\Framework\v3.5\Csc.exe /noconfig /nowarn:1701,1702 /errorreport:prompt /warn:4 /define:TRACE /reference:..\..\04_referencias\FxCop\FxCopSdk.dll /reference:..\..\04_referencias\FxCop\Microsoft.Cci.dll /reference:c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Data.dll /reference:c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll /reference:c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.EnterpriseServices.dll /reference:c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Web.Services.dll /reference:c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll /debug:pdbonly /filealign:512 /optimize+ /out:obj\Release\bba.fwk.vbp.rules.code.dll /resource:FwkCodeRuleResource.xml,bba.fwk.vbp.rules.code.FwkCodeRuleResource.xml /target:library DoNotExceedMaximumLinesOfCode.cs FwkCodeConfig.cs FwkCodeRule.cs MayExceedSomeNumberOfLinesOfCode.cs Properties\AssemblyInfo.cs Properties\Settings.Designer.cs "Web References\webReferences\Reference.cs"
Compile complete -- 0 errors, 0 warningsC:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\sgen.exe /assembly:Z:\bf_view_C02_BBACQ00298239\ADMCOMP_01\BBA_Framework\02_FWK.Net_v2\16_Validador_BP_SQL\00_Implementacao\00_Fontes\bba.fwk.vbp.rules.code\obj\Release\bba.fwk.vbp.rules.code.dll /proxytypes /reference:..\..\04_referencias\FxCop\FxCopSdk.dll /reference:..\..\04_referencias\FxCop\Microsoft.Cci.dll /reference:c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Data.dll /reference:c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll /reference:c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.EnterpriseServices.dll /reference:c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Web.Services.dll /reference:c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll SGEN : error : Could not load file or assembly 'FxCopSdk, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. Access is denied.Done building project "bba.fwk.vbp.rules.code.csproj" -- FAILED.


A solução que encontrei foi desativar ("Off") a opção "Generate serialization assembly". Com isso, a dll *.XmlSerializers.dll não é gerada, e não passamos pelo problema.

Agora a questão é pra que serve "Generate serialization assembly"..., mas isso fica pra depois...


quinta-feira, 23 de julho de 2009

Windows Installer CleanUp Utility - MSIZap

Vira e mexe temos um problema onde não conseguimos desinstalar um MSI instalado, onde não temos mais o arquivo .msi. Quando tentamos remover, aparece uma mensagem informando que a referência ao arquivo não é encontrada.

Para remediar, existe uma aplicação (Windows Installer CleanUp Utility) que faz a limpeza da instalação MSI no registry.

Note que ela não desinstala o produto, só remove as referências. A desinstalação fica por sua parte...

A aplicação lista todos os produtos instalados, e remove os que você selecionou usando o MSIZAP.

[]'s

quinta-feira, 21 de maio de 2009

Apertem os cintos, o Configuration Manager sumiu!

Por algum motivo que foge a razão, o Configuration Manager havia sumido do meu Visual Studio 2008. Não conseguia encontrar a opção para compilar as minhas soluções em Debug ou Release. Coisa de louco.

Reparei a instalação do Visual Studio, e nada... até que encontrei o seguinte post: http://blogs.msdn.com/saraford/archive/2008/03/05/did-you-know-how-to-pretend-the-configuration-manager-doesn-t-exist-besides-closing-your-eyes-and-saying-i-don-t-see-you-165.aspx

Nele se explica que para que o Configuration Manager apareça, é necessário que uma opção esteja marcada nas configurações do Visual Studio. É a “Show advanced build configuration”, e fica em Tools --> Options --> Project and Solutions --> General.



Após selecionar (e ainda quero descobrir com certeza o que aconteceu para ela ser removida), o Configuration Manager voltou. O Solution Configuration (aquela combo que mostra Debug e Release) voltou a aparece, agora posso configurar opções para compilações em Debug e Release, criar novas configurações, etc.

Fica essa dica se isso acontecer com você...



UPDATE: Se ele ainda assim não aparece, adicione-o através da customização da barra de ferramentas, clicando com o botão direito sobre ela e clicando em "Customize", no menu de contexto. Abaixo está uma imagem para ficar mais fácil de achar.

quarta-feira, 13 de maio de 2009

Add-in VSTO Excel não carregando em Terminal Service

Há algum tempo trabalhei em um projeto que precisou de uma solução envolvendo add-in VSTO para Excel. Era uma aplicação onde o usuário informava alguns dados na planilha e executava determinada funcionalidade no add-in que enviava esses dados para processamento no servidor.

Esta solução foi implantada há aproximadamente um ano, e funciona ok. Ela costuma ser instalada diretamente nas máquinas dos usuários.

Recentemente nos deparamos com um problema. Essa solução, quando instalada em servidores de terminal service, não funciona. O add-in não aparece para os usuários. Nem mesmo ele aparece nos “Disabled Items”, que é o local onde são exibidos os add-ins que não conseguiram carregar no Excel.



(Excel 2003 – no menu “Help”, clicar em “About Microsoft Office Excel” e depois em “Disabled Items”)

Obs. Há algum tempo, coloquei no blog como verificar erros no carregamento de add-ins (http://galorebr.blogspot.com/2008/03/nas-ltimas-semanas-estive-trabalhando.html).

Ao tentar adicionar o add-in “na unha”, através da janela COM Add-in e selecionando a respectiva dll, aparecia uma mensagem dizendo que meu add-in não era um add-in válido (".dll is not a valid Office add-in".).



Resolvi ir atrás de como o Excel e o VSTO carregam os ad-ins. Encontrei um artigo no site do MSDN, que explica que o Excel busca algumas chaves no registry para fazê-lo (http://msdn.microsoft.com/en-us/library/bb386106.aspx).

Nele, vi que as seguintes chaves precisam existir:
Para o Office 2003:

  • HKEY_CURRENT_USER\Software\Microsoft\Office\application name\Addins\add-in ID
  • HKEY_CURRENT_USER\Software\Classes\add-in ID\CLSID
  • HKEY_CURRENT_USER\Software\Classes\CLSID\{add-in CLSID}
  • HKEY_CURRENT_USER\Software\Classes\CLSID\{add-in CLSID}\InprocServer32
  • HKEY_CURRENT_USER\Software\Classes\CLSID\{add-in CLSID}\ProgID
  • HKEY_CURRENT_USER\Software\Classes\CLSID\{add-in CLSID}\Programmable
  • HKEY_CURRENT_USER\Software\Classes\CLSID\{add-in CLSID}\VersionIndependentProgID
Para o Office 2007:

  • HKEY_CURRENT_USER\Software\Microsoft\Office\application name\Addins\add-in ID
Mais detalhes estão no artigo, mas o problema que acontecia era que essas chaves não estavam cadastradas para o usuário (cada usuário que loga no servidor possui sei HKEY_CURRENT_USER).

A solução que tivemos foi fazer a importação dessas chaves através de um script que é executado quando o usuário loga no servidor pela primeira vez. Dessa maneira, foi possível com que o add-in aparecesse! Inclusive ela funcionou com o Citrix!

Uma observação: escolhemos essa solução de importar as chaves para cada usuário ao invés de importá-las no HKEY_LOCAL_MACHINE (que é acessível a todos os usuários) por causa do Office 2007, que só enxerga os add-ins que estão no HKCU.

[]’s

UPDATE

Em tempo, abaixo estão os scripts utilizados na importação das chaves. Veja que tanto Reg_XYZ.vbs quanto XYZ.reg devem estar localizados na pasta c:\Reg_XYZ\.
Para fazer com que cada usuário execute o script ao efetuar o login, devemos colocar a linha o call “c:\Reg_XYZ\Reg_XYZ.vbs no arquivo c:\windows\system32\usrlogon.cmd (pode ser logo após o primeiro comando @Echo Off).

A idéia dos scripts é primeiro verificar se existe um arquivo txt. Se já existir, então quer dizer que as chaves já foram importadas. Se não existir, então quer dizer que as chaves devem ser importadas. Logo após a importação, é gerado um txt para marcar que o usuário está ok com as chaves.

Obs. Royalties ao Luiz Alberto Valente, que escreveu esta parte.

Reg_XYZ.vbs

Call verificatxt
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "reg import C:\Reg_XYZ\XYZ.reg"
Call criatxt
'Funcao Verifica se o Script Ja foi executado
Function Verificatxt
Const USER_PROFILE = &H28&
Set objShell = CreateObject("Shell.Application")Set objFolder = objShell.Namespace(USER_PROFILE)Set objFolderItem = objFolder.Self
caminho = objFolderItem.Path & "\XYZ.txt"
Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FileExists(caminho) Then Wscript.quitEnd If End Function
'Funcao cria o log de validacao
Function criatxt
Const USER_PROFILE = &H28&
Set objShell = CreateObject("Shell.Application")Set objFolder = objShell.Namespace(USER_PROFILE)Set objFolderItem = objFolder.Self
caminho = objFolderItem.Path & "\XYZ.txt"
Set objFSO = CreateObject("Scripting.FileSystemObject")Set objFile = objFSO.CreateTextFile(caminho)
End Function

XYZ.reg

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Office\Excel\Addins\galore.XYZ.ui.XYZExcel2003Addin]"Manifest"="C:\\Program Files\\FABRICANTE\\XYZ\\galore.XYZ.ui.XYZExcel2003Addin.dll.manifest""LoadBehavior"=dword:00000003"Description"="galore XYZ - Addin para Excel 2003""FriendlyName"="galore XYZ - v. Excel 2003""CommandLineSafe"=dword:00000001
[HKEY_CURRENT_USER\Software\Classes\CLSID\{AD211889-FBA9-4CEB-A92B-9569B9FDAB76}]@="galore.XYZ.ui.XYZExcel2003Addin -- an addin created with VSTO technology"
[HKEY_CURRENT_USER\Software\Classes\CLSID\{AD211889-FBA9-4CEB-A92B-9569B9FDAB76}\InprocServer32]"ManifestLocation"="C:\\Program Files\\FABRICANTE\\XYZ\\""ThreadingModel"="Both"@=hex(2):25,00,43,00,6f,00,6d,00,6d,00,6f,00,6e,00,50,00,72,00,6f,00,67,00,72,\ 00,61,00,6d,00,46,00,69,00,6c,00,65,00,73,00,25,00,5c,00,4d,00,69,00,63,00,\ 72,00,6f,00,73,00,6f,00,66,00,74,00,20,00,53,00,68,00,61,00,72,00,65,00,64,\ 00,5c,00,56,00,53,00,54,00,4f,00,5c,00,38,00,2e,00,30,00,5c,00,41,00,64,00,\ 64,00,69,00,6e,00,4c,00,6f,00,61,00,64,00,65,00,72,00,2e,00,64,00,6c,00,6c,\ 00,00,00"ManifestName"="galore.XYZ.ui.XYZExcel2003Addin.dll.manifest"
[HKEY_CURRENT_USER\Software\Classes\CLSID\{AD211889-FBA9-4CEB-A92B-9569B9FDAB76}\ProgID]@="galore.XYZ.ui.XYZExcel2003Addin"
[HKEY_CURRENT_USER\Software\Classes\CLSID\{AD211889-FBA9-4CEB-A92B-9569B9FDAB76}\Programmable]
[HKEY_CURRENT_USER\Software\Classes\CLSID\{AD211889-FBA9-4CEB-A92B-9569B9FDAB76}\VersionIndependentProgID]@="galore.XYZ.ui.XYZExcel2003Addin"
[HKEY_CURRENT_USER\Software\Classes\galore.XYZ.ui.XYZExcel2003Addin]@=""
[HKEY_CURRENT_USER\Software\Classes\galore.XYZ.ui.XYZExcel2003Addin\CLSID]@="{AD211889-FBA9-4CEB-A92B-9569B9FDAB76}"

terça-feira, 28 de abril de 2009

COM+ e problemas de memória

Onde trabalho possuímos uma infra que serve de façade para a chamada de componentes dos sistemas de forma remota. Esta forma remota pode ser desde estações de usuário para servidores de componentes, servidores web para servidores de componentes e mesmo entre servidores de componentes. Ela é um pacote COM+, e chama componentes que estão também hospedados no COM+ (temos muito legado aqui, vai demorar para sair dessa tecnologia).

Recentemente deparamos com um problema de alto consumo de memória do nosso processo dllhost.

Pedimos um dump deste processo (windbg), e ao analisar, encontrei muitas entradas de System.OutOfMemoryException. Ao verificar a causa dessas exceptions, encontrei a seguinte mensagem: Retrieving the COM class factory for component with CLSID {6DEDD69A-065F-4781-90A7-35E0E0DEB4BA} failed due to the following error: 8007000e. O componente que está se querendo instanciar é um DTO nosso, e não está sendo possível pelo erro de falta de memória.

Encontrei um KB que fala sobre isso: http://support.microsoft.com/?scid=kb%3Ben-us%3B890425&x=7&y=14, e talvez possa relacionado. Ele diz que o RPC tem uma limitação de quantidade de interfaces únicas que podem ser chamadas entre processos no COM+. Como resultado, erros de OutOfMemory (8007000e) podem aparecer.

Ainda não temos um parecer final, mas estamos seguindo por essa linha para resolver a questão.

[]'s

UPDATE - 21/05/2009:

Desistimos de tentar descobrir o problema neste servidor (acontece em apenas uma máquina, outros executam o mesmo sistema sem o erro)...

sábado, 14 de março de 2009

Cakewalk Sonar 7 sem som

Fiquei muito feliz nesses últimos dias ao instalar uma nova versão do Cakewalk (agora chamado Sonar) e abrir alguns projetos de músicas da época qu eeu era um aspirante a músico.


Um ar nostalgico pairou ao meu redor...

No entanto, não consegui ouvir as músicas de primeira. Não tenho equipamento profissional, e para usar esse tipo de programa me valho de MIDI, só que não estava configurado para utilizá-lo, e ao executar a música nada se ouvia.


Bom, tive que seguir os passos abaixo, que foram tirados do seguinte post: http://74.125.95.132/search?q=cache:Ps7c-BK3QyQJ:forum.cakewalk.com/fb.asp%3Fgo%3Dnext%26m%3D1304455%26viewType%3Dtm+sonar+7+no+sound&hl=en&ct=clnk&cd=1



Primeiro, fui até o menu Options --> Audio...



Na tab Advanced, alterei o Driver Mode para MME (32-bit). Depois disso reiniciei o aplicativo.



Quando a nova instância foi aberta, me foi alertado que não havia nenhum output selecionado.



Foi então que pude escolher o wavetable da Microsoft para simular os sons dos instrumentos musicais!



Com isso, foi possível ouvir as músicas dos projetos sem problemas!

[]'s

quarta-feira, 4 de fevereiro de 2009

Como excluir pacotes COM+ que não possuem a opção Delete no menu de contexto

Olá!


Estou trabalhando em um projeto de uma aplicação que faz instalações de componentes em servidores de forma automatizada. Uma das funcionalidades é criar diretórios virtuais no IIS. Acontece que durante alguns testes dessa funcionalidade, notei que cada vez que os diretórios virtuais eram criados, também era criado um pacote COM+ de nome IIS-{//ROOT/}. O engraçado é que mesmo excluindo os diretórios virtuais, esses pacotes permaneciam, e não podiam ser excluídos (não havia a opção Delete no menu de contexto).



O problema foi corrigido na aplicação de instalação (não cheguei a ver a causa e correção, mas prometo fazê-lo), no entanto restava a necessidade de remover esses pacotes COM+, já que eram inúteis. Procurei no Google e encontrei uma solução para permitir que os pacotes fossem "excluíveis": usando o COMAdmin, alterar uma propriedade do pacote que libera esta opção.

O código utilizado é o abaixo:

ICOMAdminCatalog catalogo;
ICatalogCollection pacotesCOMPlus;
ICatalogObject pacoteCOMPlus = null;
catalogo = (ICOMAdminCatalog)Microsoft.VisualBasic.Interaction.CreateObject("COMAdmin.COMAdminCatalog", string.Empty);
pacotesCOMPlus = (ICatalogCollection)catalogo.GetCollection("Applications");
pacotesCOMPlus.Populate();
foreach (ICatalogObject pacote in pacotesCOMPlus)
if (pacote.Name.ToString().ToLower().Equals(textBox1.Text.ToLower().Trim()))
{
pacoteCOMPlus = pacote;
break;
}
if (pacoteCOMPlus != null)
{
pacoteCOMPlus.set_Value("Deleteable", true);
pacotesCOMPlus.SaveChanges();
MessageBox.Show(string.Format("Pode apagar o pacote {0} agora...", textBox1.Text));
}

Obs. textBox1 é o controle da telinha que fiz para rodar o código, e que é preenchida com o nome do pacote COM+.

Com isso, a opção de excluir o pacote COM+ foi habilitada!



[]'s