Tudo que você sempre quis saber sobre SQL Injection e nunca teve coragem de perguntar:
http://www.devarticles.com/c/a/MySQL/SQL-Injection-Attacks-Are-You-Safe/
Excelente texto que trata o assunto de forma objetiva, sem alarmismos exagerados (será? hehee) e contendo exemplos claros . Traz exemplos usando ASP e SQL Server, mas a vulnerabilidade e as técnicas de proteção são igualmente aplicáveis aos programas escritos em Delphi.
Recomendo a leitura!
Saturday, November 29, 2008
Friday, November 21, 2008
Cache de DataSets em aplicações multi-camadas III
OK, você tem um mecanismo que faz o cache de seus dados localmente e não precisa trafegar os dados a todo momento, certo? Mas como descobre se o seu cache reflete fielmente os dados no banco de dados?
Bem, existem bancos de dados que permitem notificação de aplicações clientes. Mas meu objetivo é criar algo genérico que não esteja atrelado a uma implementação específica de SGBD, ou seja, não será possível usar mecanismos de notificação.
Então o que resta é fazer a consulta diretamente. Eu sempre usei, em todas as tabelas, um campo que me informa a data/hora de inclusão/alteração. Vamos supor então que este campo se chama dat_hor_edi, ou seja, "data/hora de edição". Sempre que eu incluo um novo registro na tabela eu atualizo este campo com a data/hora atual. Procedo da mesma forma quando eu altero o conteúdo do registro. Então uma consulta como
sempre me fornecerá a data/hora da última inclusão e/ou edição que ocorreu nesta tabela. Assim, se eu executei a minha consulta na hora "H" e a consulta acima me retornou um valor para "dat_hor_edi" MAIOR que "H", eu sei que a tabela sofreu inclusão e/ou edição, e meu cache não reflete mais a situação atual do banco de dados, e então o cache precisa ser atualizado.
Mas aí tem um problema: Esta consulta não me garante as exclusões. Claro, se eu excluir um registro desta tabela a consulta acima não me mostrará isto.
Então precisamos de um outro dado para garantir a integridade do cache. A data de última edição/inclusão funcionará perfeitamente junto com o número de registros da consulta. Estes dois números juntos me permitem saber com certeza se meu cache tem ou não uma cópia fiel da tabela no banco de dados. Eu precisaria de uma consulta do tipo:
O número de registros de uma consulta é prontamente obtido, mas o campo "dat_hor_edi" precisa ser atualizado de uma das duas formas:
1) Pela aplicação, sempre que houver inclusão e ou edição do registro;
2) Por um trigger no banco de dados, criado específicamente para isto;
Eu já usei e continuo usando ambas as formas. A primeira pode parecer mais complicada de se conseguir, mas não o é de fato. Basta que sua aplicação seja construída de forma metódica. Obviamente a primeira forma não prevê "marretas" feitas diretamente no banco de dados.
Então, o cache em si é composto por 3 diferentes "dados": O DataSet, o número de registros deste DataSet, e a data/hora de última inclusão/edição neste conjunto de dados. Está aí uma classe desenhada, que num pseudo-código pascal seria:
Algumas pessoas já me perguntaram: "mas então para eu saber se preciso fazer uma consulta, eu precisarei fazer antes outra consulta?". A resposta é: Sim, precisará!
Mas você em geral troca uma consulta que retorna, digamos, 1000 registros e que leva 10 segundos e uma enormidade de processamento do seu SGBD por uma outra consulta que trafega meia dúzia de bytes e leva milésimos de segundos para ser executada.
O primeiro objetivo do cache é diminuir o tráfego de dados!
Na próxima vamos implementar a classe acima, o TDataSetCache! :)
Bem, existem bancos de dados que permitem notificação de aplicações clientes. Mas meu objetivo é criar algo genérico que não esteja atrelado a uma implementação específica de SGBD, ou seja, não será possível usar mecanismos de notificação.
Então o que resta é fazer a consulta diretamente. Eu sempre usei, em todas as tabelas, um campo que me informa a data/hora de inclusão/alteração. Vamos supor então que este campo se chama dat_hor_edi, ou seja, "data/hora de edição". Sempre que eu incluo um novo registro na tabela eu atualizo este campo com a data/hora atual. Procedo da mesma forma quando eu altero o conteúdo do registro. Então uma consulta como
SELECT MAX(dat_hor_edi) FROM minha_tabela
sempre me fornecerá a data/hora da última inclusão e/ou edição que ocorreu nesta tabela. Assim, se eu executei a minha consulta na hora "H" e a consulta acima me retornou um valor para "dat_hor_edi" MAIOR que "H", eu sei que a tabela sofreu inclusão e/ou edição, e meu cache não reflete mais a situação atual do banco de dados, e então o cache precisa ser atualizado.
Mas aí tem um problema: Esta consulta não me garante as exclusões. Claro, se eu excluir um registro desta tabela a consulta acima não me mostrará isto.
Então precisamos de um outro dado para garantir a integridade do cache. A data de última edição/inclusão funcionará perfeitamente junto com o número de registros da consulta. Estes dois números juntos me permitem saber com certeza se meu cache tem ou não uma cópia fiel da tabela no banco de dados. Eu precisaria de uma consulta do tipo:
SELECT
COUNT(*) as TblRecCount,
MAX(dat_hor_edi) as TblTimeStamp
FROM minha_tabela
O número de registros de uma consulta é prontamente obtido, mas o campo "dat_hor_edi" precisa ser atualizado de uma das duas formas:
1) Pela aplicação, sempre que houver inclusão e ou edição do registro;
2) Por um trigger no banco de dados, criado específicamente para isto;
Eu já usei e continuo usando ambas as formas. A primeira pode parecer mais complicada de se conseguir, mas não o é de fato. Basta que sua aplicação seja construída de forma metódica. Obviamente a primeira forma não prevê "marretas" feitas diretamente no banco de dados.
Então, o cache em si é composto por 3 diferentes "dados": O DataSet, o número de registros deste DataSet, e a data/hora de última inclusão/edição neste conjunto de dados. Está aí uma classe desenhada, que num pseudo-código pascal seria:
TDataSetCache = class
public
TimeStamp: TDateTime;
RecordCount: Integer;
DataSet: TDataSet;
end;
Algumas pessoas já me perguntaram: "mas então para eu saber se preciso fazer uma consulta, eu precisarei fazer antes outra consulta?". A resposta é: Sim, precisará!
Mas você em geral troca uma consulta que retorna, digamos, 1000 registros e que leva 10 segundos e uma enormidade de processamento do seu SGBD por uma outra consulta que trafega meia dúzia de bytes e leva milésimos de segundos para ser executada.
O primeiro objetivo do cache é diminuir o tráfego de dados!
Na próxima vamos implementar a classe acima, o TDataSetCache! :)
Labels:
DataSet cache,
DataSetProvider,
DataSnap,
Delphi
Grupo Intraweb no Yahoo Groups
Está aí o link:
http://br.groups.yahoo.com/group/iwbr/
quem usa Intraweb para desenvolvimento web deveria se associar e ajudar a disseminar o conhecimento sobre esta excelente ferramenta!
http://br.groups.yahoo.com/group/iwbr/
quem usa Intraweb para desenvolvimento web deveria se associar e ajudar a disseminar o conhecimento sobre esta excelente ferramenta!
Friday, October 17, 2008
Cache de DataSets em aplicações multi-camadas II
Identificando unicamente um DataSet.
Bem, quanto aos requisitos do post anterior:
1) O gerenciador do cache deverá ser capaz de identificar unicamente um DataSet
Como identificar unicamente um DataSet obtido a partir de uma consulta a um banco de dados? Bem, no meu caso os DataSets são resultado de uma consulta SQL. Independentemente do mecanismo de acesso a dados, quando se utiliza SQL em geral temos um objeto de query (TADOQuery, TSQLQuery, etc.) e o mesmo possui uma propriedade SQL do tipo TStrings. A string que contém o comando SQL é, com certeza, um identificador único de uma query certo? Mas a string não é ideal para um identificador único, pois pode ser gigante, difícil de comparar, etc. O melhor mesmo é gerar um hash a partir do SQL. SQL diferentes deverão gerar hashes únicos com uma probabilidade de colisão suficientemente baixa.
No meu caso, optei por usar não um hash propriamente dito, mas um CRC. Optei por usar o CRC64 que me possibilita ter uma probabilidade de colisão suficientemente baixa para uma aplicação de qualquer tamanho.
Quem tiver curiosidade sobre esta probabilidade, segue um teste com o algoritmo:
http://apollo.backplane.com/matt/crc64.html
Resumindo, a probabilidade de colisão é da ordem de 1 colisão para 2E12 mensagens, o que acredito que seja mais do que satisfatório.
Então, passamos nossa expressão SQL por um algoritmo de geração de CRC64 e obtemos um identificador único para nossa sentença SQL, como por exemplo CF247CF5C26FF896.
Mas temos ainda um problema: A mesma sentença de SQL executada contra dois bancos de dados diferentes (mas com estrutura compatível, como por exemplo um banco de dados de produção e outro de homologação) possuem identificadores idênticos porém o DataSet resultante é diferente.
Neste caso devemos incorporar ao identificador único do DataSet um identificador do banco de dados. O banco de dados geralmente é identificado pelo servidor, nome do banco ou schema, usuário, etc. Muitas formas são possíveis para se gerar um identificador do banco de dados. No meu caso, usando ADO como mecanismo de acesso a dados, optei por seguir a mesma linha de raciocínio e usar o CRC64 da string de conexão (propriedade ConnectionString do TADOConnection).
Então, com o CRC do SQL mais o CRC da string de conexão eu tenho um identificador único para meu DataSet que funcionará como índice. Mais sobre isto em breve.
Bem, quanto aos requisitos do post anterior:
1) O gerenciador do cache deverá ser capaz de identificar unicamente um DataSet
Como identificar unicamente um DataSet obtido a partir de uma consulta a um banco de dados? Bem, no meu caso os DataSets são resultado de uma consulta SQL. Independentemente do mecanismo de acesso a dados, quando se utiliza SQL em geral temos um objeto de query (TADOQuery, TSQLQuery, etc.) e o mesmo possui uma propriedade SQL do tipo TStrings. A string que contém o comando SQL é, com certeza, um identificador único de uma query certo? Mas a string não é ideal para um identificador único, pois pode ser gigante, difícil de comparar, etc. O melhor mesmo é gerar um hash a partir do SQL. SQL diferentes deverão gerar hashes únicos com uma probabilidade de colisão suficientemente baixa.
No meu caso, optei por usar não um hash propriamente dito, mas um CRC. Optei por usar o CRC64 que me possibilita ter uma probabilidade de colisão suficientemente baixa para uma aplicação de qualquer tamanho.
Quem tiver curiosidade sobre esta probabilidade, segue um teste com o algoritmo:
http://apollo.backplane.com/matt/crc64.html
Resumindo, a probabilidade de colisão é da ordem de 1 colisão para 2E12 mensagens, o que acredito que seja mais do que satisfatório.
Então, passamos nossa expressão SQL por um algoritmo de geração de CRC64 e obtemos um identificador único para nossa sentença SQL, como por exemplo CF247CF5C26FF896.
Mas temos ainda um problema: A mesma sentença de SQL executada contra dois bancos de dados diferentes (mas com estrutura compatível, como por exemplo um banco de dados de produção e outro de homologação) possuem identificadores idênticos porém o DataSet resultante é diferente.
Neste caso devemos incorporar ao identificador único do DataSet um identificador do banco de dados. O banco de dados geralmente é identificado pelo servidor, nome do banco ou schema, usuário, etc. Muitas formas são possíveis para se gerar um identificador do banco de dados. No meu caso, usando ADO como mecanismo de acesso a dados, optei por seguir a mesma linha de raciocínio e usar o CRC64 da string de conexão (propriedade ConnectionString do TADOConnection).
Então, com o CRC do SQL mais o CRC da string de conexão eu tenho um identificador único para meu DataSet que funcionará como índice. Mais sobre isto em breve.
Wednesday, October 15, 2008
Cache de DataSets em aplicações multi-camadas
Há algum tempo utilizo mecanismos diversos de cache para melhorar a performance das aplicações que desenvolvo, principalmente com relação a acesso a bancos de dados. De uma forma mais aplicada venho desenvolvendo um mecanismo genérico de cache, para ser utilizado em aplicações multi-camadas. Problemas simples como o do post anterior (campos de lookup) podem ser muito beneficiados com mecanismo deste tipo.
É interessante ver que a maioria das aplicações mais "avançadas" ou elaboradas possuem algum mecanismo de cache mas no "mundo real" das aplicações comerciais que possuem acesso pesado a bancos de dados, o uso de um mecanismo de cache, por mais precário que seja, é raro ou inexistente.
O Delphi tem, out of the box, tudo que é necessário para fazer um cache nem tão rudimentar assim. Na verdade, depois de pronto, acho que poderia bater em muita coisa comercial que tem por aí... O ClientDataSet é um candidato perfeito para ser um DataSet genérico utilizado em um cache: é um DataSet em memória, pode ser gravado em disco em formato nativo (binário) e está presente na maioria das aplicações multi-camadas escritas em Delphi.
De forma geral e bem simplificada, o cache deveria funcionar da seguinte forma:
- Um DataSet é requerido à aplicação servidora (middle-tier) pela aplicação cliente;
- O mecanismo de cache (iremos chamá-lo de "gerenciador do cache") verifica se este DataSet já está no cache. Caso não esteja, a consulta normal ao banco de dados é efetuada. Caso o DataSet exista no cache, o gerenciador do cache deverá determinar se o cache está atualizado. Se estiver, o DataSet no cache é retornado à aplicação cliente sem necessidade de acesso ao banco de dados. Se não estiver, a consulta normal ao banco de dados é efetuada;
- Caso a consulta normal ao banco seja efetuada (o DataSet não está no cache ou o mesmo se encontra desatualizado) o gerenciador do cache se encarrega de incluir/atualizar o cache com este DataSet recém obtido do banco de dados;
- O gerenciador do cache deve prever acessos simultâneos de diferentes threads;
- O gerenciador do cache deve persistir e recuperar o cache em um repositório local (disco).
Simples não? Bem... não é tão simples mas também não é ciência de foguetes. Veremos que existem vários requisitos que deverão ser satisfeitos para que o mecanismo de cache seja viável. Entre eles, podemos citar:
1) O gerenciador do cache deverá ser capaz de identificar unicamente um DataSet;
2) O gerenciador do cache deverá ser capaz de identificar de forma inequívoca se o cache reflete a situação atual do banco de dados, ou seja, se o cache está atualizado no que tange àquele DataSet específico.
O requisito número 1 exige que o banco de dados se adapte à aplicação, ou seja, ao uso do cache. Os sistemas que desenvolvo já possuíam uma estrutura adequada, mesmo antes de um mecanismo de cache ser pensado. Então neste caso foi fácil.
Irei detalhar como satisfazer os requisitos 1 e 2 no próximo post.
É interessante ver que a maioria das aplicações mais "avançadas" ou elaboradas possuem algum mecanismo de cache mas no "mundo real" das aplicações comerciais que possuem acesso pesado a bancos de dados, o uso de um mecanismo de cache, por mais precário que seja, é raro ou inexistente.
O Delphi tem, out of the box, tudo que é necessário para fazer um cache nem tão rudimentar assim. Na verdade, depois de pronto, acho que poderia bater em muita coisa comercial que tem por aí... O ClientDataSet é um candidato perfeito para ser um DataSet genérico utilizado em um cache: é um DataSet em memória, pode ser gravado em disco em formato nativo (binário) e está presente na maioria das aplicações multi-camadas escritas em Delphi.
De forma geral e bem simplificada, o cache deveria funcionar da seguinte forma:
- Um DataSet é requerido à aplicação servidora (middle-tier) pela aplicação cliente;
- O mecanismo de cache (iremos chamá-lo de "gerenciador do cache") verifica se este DataSet já está no cache. Caso não esteja, a consulta normal ao banco de dados é efetuada. Caso o DataSet exista no cache, o gerenciador do cache deverá determinar se o cache está atualizado. Se estiver, o DataSet no cache é retornado à aplicação cliente sem necessidade de acesso ao banco de dados. Se não estiver, a consulta normal ao banco de dados é efetuada;
- Caso a consulta normal ao banco seja efetuada (o DataSet não está no cache ou o mesmo se encontra desatualizado) o gerenciador do cache se encarrega de incluir/atualizar o cache com este DataSet recém obtido do banco de dados;
- O gerenciador do cache deve prever acessos simultâneos de diferentes threads;
- O gerenciador do cache deve persistir e recuperar o cache em um repositório local (disco).
Simples não? Bem... não é tão simples mas também não é ciência de foguetes. Veremos que existem vários requisitos que deverão ser satisfeitos para que o mecanismo de cache seja viável. Entre eles, podemos citar:
1) O gerenciador do cache deverá ser capaz de identificar unicamente um DataSet;
2) O gerenciador do cache deverá ser capaz de identificar de forma inequívoca se o cache reflete a situação atual do banco de dados, ou seja, se o cache está atualizado no que tange àquele DataSet específico.
O requisito número 1 exige que o banco de dados se adapte à aplicação, ou seja, ao uso do cache. Os sistemas que desenvolvo já possuíam uma estrutura adequada, mesmo antes de um mecanismo de cache ser pensado. Então neste caso foi fácil.
Irei detalhar como satisfazer os requisitos 1 e 2 no próximo post.
Labels:
DataSet cache,
Delphi Programming
Sunday, September 21, 2008
Campos Lookup - Uma abordagem racional
Ando lendo em sites e publicações especializadas em Delphi muita coisa sobre campos de lookup e sua utilização em sistemas, como por exemplo as famosas LookupComboBoxes. Geralmente fala-se mal, recomenda-se não utilizar campos lookup. Ou então que sua utilização é muito restrita, e só serve para tabelas contendo poucos registros (exatamente quantos, ninguém fala, certo?).. Coisas que eu ouvia há 10 ou 15 anos, quando quase todo mundo desenvolvia usando os velhos e bons conceitos Cliente/Servidor.
De lá prá cá muita coisa mudou. A banda de internet que a alguns têm em casa é mais rápida que algumas redes que usavam cabos coaxiais em 1995.
Mesmo assim, alguns teimam em usar o mesmo conceito antigo que "não se pode trafegar pela rede meia dúzia de registros que possivelmente não serão usados"... AFFFFF!!!!
Então vejamos... O que é mesmo AJAX? Quanta informação desnecessária é trafegada para dar a uma aplicação web AJAX o seu comportamento?
De qualquer forma, nada melhor que fatos e dados para basear o meu ponto de vista.
Suponha uma tabela "cliente", com a seguinte estrutura:
e uma tabela "profissao" com a seguinte estrutura:
Em 99% dos casos eu ouço que não se deveria fazer lookup na tabela de profissões, e sim um JOIN.
Imagine uma consulta típica para um relatório, onde irei listar os clientes e suas profissões. O SQL típico seria:
Com alguma simplificação podemos assumir um tamanho de registro de dados igual a 84 bytes. Se eu tivesse 2000 clientes, teríamos um pacote de dados de aproximadamente 165 kbytes.
Agora, vejamos por outro lado. Imagine que eu tivesse 2 consultas separadas:
Vamos supor ainda que minha tabela de profissão contivesse 500 registros. O total de dados trafegados seria 58*2000 (clientes) + 34*500 (profissões) = 130 kbytes.
Oras! Eu fiz duas consultas, trouxe toda a tabela de profissões para o lado cliente e ainda tive um tráfego menor???? Hum... Agora, imagine que este relatório é chamado por várias vezes durante a execução do programa?
Quantas vezes por dia a tabela de profissões muda? Talvez sejam adicionadas novas profissões, de vez em quando, mas em sistemas do mundo real isto ocorre muito no início do ciclo de vida do sistema e tende a diminuir - e muito - com o tempo. Não poderíamos carregar a tabela de profissões, digamos na inicialização do sistema, e usá-la como Lookup durante o ciclo de vida da aplicação?
Um mecanismo mais inteligente poderia checar as alterações da tabela de profissões antes de sua utilização (como lookup no relatório, por exemplo) e fazer um refresh sempre que necessário. O ganho de performance tanto de banco de dados quanto da aplicação é significativo.
Alguns conceitos de C/S estão certamente ultrapassados. O pessoal que os usa sem parar para pensar (e principalmente MEDIR) o que acontece de fato em sistemas do mundo real deveria revê-los.
De lá prá cá muita coisa mudou. A banda de internet que a alguns têm em casa é mais rápida que algumas redes que usavam cabos coaxiais em 1995.
Mesmo assim, alguns teimam em usar o mesmo conceito antigo que "não se pode trafegar pela rede meia dúzia de registros que possivelmente não serão usados"... AFFFFF!!!!
Então vejamos... O que é mesmo AJAX? Quanta informação desnecessária é trafegada para dar a uma aplicação web AJAX o seu comportamento?
De qualquer forma, nada melhor que fatos e dados para basear o meu ponto de vista.
Suponha uma tabela "cliente", com a seguinte estrutura:
codigo_cliente - Integer
nome_cliente - varchar(50)
codigo_profissao - Integer
e uma tabela "profissao" com a seguinte estrutura:
codigo_profissao - Integer
descricao_profissao - varchar(30)
Em 99% dos casos eu ouço que não se deveria fazer lookup na tabela de profissões, e sim um JOIN.
Imagine uma consulta típica para um relatório, onde irei listar os clientes e suas profissões. O SQL típico seria:
SELECT c.codigo_cliente, c.nome_cliente, p.descricao_profissao
FROM cliente c
INNER JOIN profissao p
ON c.codigo_profissao = p.codigo_profissao
ORDER BY c.codigo_cliente ASC
Com alguma simplificação podemos assumir um tamanho de registro de dados igual a 84 bytes. Se eu tivesse 2000 clientes, teríamos um pacote de dados de aproximadamente 165 kbytes.
Agora, vejamos por outro lado. Imagine que eu tivesse 2 consultas separadas:
SELECT codigo_cliente, nome_cliente, codigo_profissao
FROM cliente
ORDER BY codigo_cliente ASC
SELECT codigo_profissao, descricao_profissao
FROM profissao
Vamos supor ainda que minha tabela de profissão contivesse 500 registros. O total de dados trafegados seria 58*2000 (clientes) + 34*500 (profissões) = 130 kbytes.
Oras! Eu fiz duas consultas, trouxe toda a tabela de profissões para o lado cliente e ainda tive um tráfego menor???? Hum... Agora, imagine que este relatório é chamado por várias vezes durante a execução do programa?
Quantas vezes por dia a tabela de profissões muda? Talvez sejam adicionadas novas profissões, de vez em quando, mas em sistemas do mundo real isto ocorre muito no início do ciclo de vida do sistema e tende a diminuir - e muito - com o tempo. Não poderíamos carregar a tabela de profissões, digamos na inicialização do sistema, e usá-la como Lookup durante o ciclo de vida da aplicação?
Um mecanismo mais inteligente poderia checar as alterações da tabela de profissões antes de sua utilização (como lookup no relatório, por exemplo) e fazer um refresh sempre que necessário. O ganho de performance tanto de banco de dados quanto da aplicação é significativo.
Alguns conceitos de C/S estão certamente ultrapassados. O pessoal que os usa sem parar para pensar (e principalmente MEDIR) o que acontece de fato em sistemas do mundo real deveria revê-los.
Labels:
Delphi Programming,
Lookup Fields
Saturday, August 9, 2008
Novo!! Fórum de discussão da CodeGear
Está funcionando desde 7 de agosto o fórum de discussão da Embarcadero (e CodeGear) no endereço https://forums.codegear.com
Ainda é beta, mas parece bom.
Pelo visto, eu fui o primeiro a registrar e postar do Brasil! :)
O lançamento do fórum foi adiantado pelo crash do servidor de newsgroups, que pelo visto foi feio - há dois dias o newsgroup da CodeGear está offline.
Ainda é beta, mas parece bom.
Pelo visto, eu fui o primeiro a registrar e postar do Brasil! :)
O lançamento do fórum foi adiantado pelo crash do servidor de newsgroups, que pelo visto foi feio - há dois dias o newsgroup da CodeGear está offline.
Thursday, July 10, 2008
Então... Aplicações desktop estão acabadas?
Hum... Interessante. Cada um que me diz esta frase pagou algumas centenas de dinheiros para comprar uma cópia de Windows, certo? Bem provavelmente, pagou mais algumas centenas para ter a última versão do MS Office, right? Bem, não entendo é o seguinte: Se as aplicações desktop estão acabadas porquê continuar pagando por um sistema operacional SE a sua única função é servir como plataforma para um browser - que muitas vezes nem é o Internet Explorer, como no meu caso (Firefox rules!). Não seria mais fácil jogar no lixo todas as suas caixinhas com o logo "Windows compatible" e fazer o download do Linux?
Os mesmos "otimistas" que costumam falar este tipo de coisa devem estar doidos para comprar - se é que já não compraram - um processador Quad Core, e viajam pensando como será o dia que a Intel ou a AMD lançará um processador com 16, 32, 256 núcleos para desktops. Me pergunto para quê serviriam 256 núcleos num processador, se a única aplicação vivente no computador do cidadão será um web browser...
Eu me lembro quando era criança... Eu via um Puma GTB - um carrão na época - e falava com meu pai: "quero comprar um destes quando crescer". E meu pai respondia "quando você crescer os carros voarão, como no desenho Jettinsons". Alguns anos antes, a gerra fria estava no auge. Jonh Kennedy torrava notas de mil dólares para mostrar aos russos que os EUA dominavam o avanço tecnológico e podiam enviar homens à lua. Este era o objetivo. Obviamente meu pai foi levado a superestimar nosso futuro tecnológico devido ao oba-oba que arrebatou o mundo.
Hoje a história é outra. Avanço tecnológico é uma coisa. Ignorar os objetivos das forças econômicas que financiam todo este avanço tecnológico é estupidez.
Think about it.
Os mesmos "otimistas" que costumam falar este tipo de coisa devem estar doidos para comprar - se é que já não compraram - um processador Quad Core, e viajam pensando como será o dia que a Intel ou a AMD lançará um processador com 16, 32, 256 núcleos para desktops. Me pergunto para quê serviriam 256 núcleos num processador, se a única aplicação vivente no computador do cidadão será um web browser...
Eu me lembro quando era criança... Eu via um Puma GTB - um carrão na época - e falava com meu pai: "quero comprar um destes quando crescer". E meu pai respondia "quando você crescer os carros voarão, como no desenho Jettinsons". Alguns anos antes, a gerra fria estava no auge. Jonh Kennedy torrava notas de mil dólares para mostrar aos russos que os EUA dominavam o avanço tecnológico e podiam enviar homens à lua. Este era o objetivo. Obviamente meu pai foi levado a superestimar nosso futuro tecnológico devido ao oba-oba que arrebatou o mundo.
Hoje a história é outra. Avanço tecnológico é uma coisa. Ignorar os objetivos das forças econômicas que financiam todo este avanço tecnológico é estupidez.
Think about it.
Thursday, June 26, 2008
ExtPascal - Pascal para Ext JS
Legal! Wanderlan Anjos publicou uma versão inicial de seu framework ExtPascal, um wrapper para as bibliotecas Ext JS.
Está em seus estágios iniciais mas vale uma olhada. Baixei os fontes e irei criar um projeto piloto para verificar a possibilidade de utilização em algum projeto! Parabéns Wanderlan!
Está em seus estágios iniciais mas vale uma olhada. Baixei os fontes e irei criar um projeto piloto para verificar a possibilidade de utilização em algum projeto! Parabéns Wanderlan!
Thursday, June 12, 2008
G-Buster revisited. Batendo a praga em seu próprio jogo!
ATENÇÃO: Este procedimento não é válido para as versões mais recentes do G-Buster. Um novo post explica como bater esta praga novamente! Estou adorando brincar de gato & rato com a GAS Tecnologia! ;-)
A mais nova versão da praga pode ser removida mais fácil que antes, com o novo procedimento descrito neste link.
Pois bem amigos. Nunca pensei que o G-Buster Browser Defense fizesse tanto mal a tanta gente! Depois que fiz o post com o método que usei para remover a praga do meu PC recebo pelo menos dois e-mails por dia. Alguns agradecendo pela receita, outros se lamentando que não conseguiram, mesmo seguindo fielmente os passos para a remoção do G-Buster.
Bem, resolvi então fazer um segundo post sobre o assunto. Juntei inclusive opiniões e dicas de leitores, e apimentei um pouco a situação! Resolvi bater o G-Buster em seu próprio jogo SUJO de mudanças de permissão de acesso a chaves da registry, arquivos, etc. Então vamos lá! PS: Uso Windows XP, mas a receita funciona para todos!
1) Faça logon no Windows como administrador da máquina, sem permissões de administrador, nada feito.
2) Conforme explicado no meu outro post, o G-Buster é composto de uma DLL que é o controle ActiveX que interage com o Internet Banking do banco em questão, e também por um serviço, o abominável GbpSv.exe, ambos na pasta C:\Arquivos de programas\GbPlugin (ou C:\Program Files\GbPlugin se seu Windows for em Inglês).
O GbpSv.exe TEM que ser removido. Ele é o serviço que fica ativo 24x7 em sua máquina usando recursos sem a menor necessidade, e principalmente, SEM A SUA PERMISSÃO!
Você pode ver o G-Buster em execução teclando Ctrl+Alt+Del e abrindo o Task Manager (uso o DTaskManager), conforme a figura abaixo:
Não adianta tentar matar o G-Buster a partir do Task Manager. Ele entra em execução novamente. Mas... Se o arquivo executável do programa não pode ser acessado para leitura, então ele não pode ser executado, certo? CERTO! Então vamos remover a permissão de leitura do arquivo GbpSv.exe.
Siga os passos que constam nas figuras abaixo:
2.1) Removendo a propriedade de leitura do diretório C:\Arquivos de programas\GbPlugin:
Vá no Windows Explorer encontre a pasta C:\Arquivos de programas\GbPlugin. Clique com o botão direito do mouse e escolha no menu Propriedades:
Vá na aba Segurança. Clique em avançado e desmarque a opção "Herdar do Pai as entradas de permissão aplicáveis..."
Clique em OK. O Windows irá perguntar se é para copiar as permissões ou excluí-las. Para facilitar a demonstração, vou supor que você irá escolher COPIAR. Então teremos depois disso:
Remova, um por um, TODOS os usuários da lista de usuários com permissão. Deixe somente o usuário SYSTEM e o usuário LOCAL SERVICE. Se os usuários SYSTEM e LOCAL SERVICE não estiverem na lista adicione-os. A partir do momento que somente os dois usuários estiverem na lista você irá NEGAR acesso (TODOS) a estes usuários. Teremos então:
A partir daí, nem o Windows Explorer terá acesso aos arquivos e você não mais poderá vê-los no painel da direita.
3) Agora, reinicie a máquina. O G-Buster não conseguirá mais ser carregado, mesmo que as entradas na Registry ainda sejam mantidas, pois não terá mais acesso ao HD na pasta onde está o arquivo executável.
Após o boot, repare no Task Manager que o G-Buster não está mais na lista de processos em execução:
Agora vamos bater o G-Buster em seu próprio jogo sujo. Atenção: Estes passos são opcionais. Não é necessário fazer isto pois o G-Buster já foi removido, mas eu fiz para impedir que ele volte quando eu acessar novamente o Internet Banking.
4) Adicione acesso total ao seu usuário na pasta do G-Buster, conforme a figura:
Agora, renomeie o GbpSv.exe para um outro nome qualquer que não tenha extensão EXE (eu usei GbpSv_Ex_EXE.OLD) com esta extensão OLD ele é inofensivo, e não pode ser executado pois o Windows não reconhece esta extensão).
Crie então um arquivo com o nome que o serviço tinha antes. Pode ser um arquivo texto mesmo, de tamanho zero, com o nome GbpSv.exe. Deixe o arquivo sem permissão nenhuma de nenhum usuário como a figura:
Renomeando o GbpSv.exe:
Criando um novo arquivo GbpSv.exe, que não é executável:
Negando acesso ao arquivo GbpSv.exe:
Você pode estar se perguntando para quê eu fiz isto? Criar um arquivo vazio com o nome GbpSv.exe e bloquear o acesso a ele. Isto é uma forma simples de impedir que o instalador do plugin que roda quando você acessa o site consiga instalar de novo o GbpSv.exe original. Mesmo que as permissões da pasta sejam alteradas a existência de um arquivo de mesmo nome e sem permissão de acesso impede que um novo GbpSv.exe seja copiado para esta pasta. Ou seja, não tem mais como o instalador instalar a praga novamente. Será?
Bem a melhor forma é testar de novo, acessando o site do banco. Testei na Caixa Econômica Federal e olhem aí o Internet Banking totalmente funcional:
Será que o G-Buster não voltou? Hum... Desta vez não! ;-)
Bem, este segundo método de remover o G-Buster é mais fácil que o primeiro que fiz, mas mesmo assim é cheio de etapas. E pela extensão do processo, fica difícil explicar detalhadamente cada passo e usuários menos experientes em configurações avançadas do Windows podem ter alguma dificuldade. Mesmo não sendo difícil, é longo e passível de erros. Se você tentou e não conseguiu, respire fundo, acalme-se e comece de novo, passo-a-passo! ;-) Vale a pena! Depois disso você fica livre do G-Buster e continua acessando seu Internet Banking, sem problemas!
Você pode continuar o extermínio da praga até o fim, eliminando as entradas da registry pertinentes, conforme eu mostrei no primeiro post sobre o assunto. Não que seja necessário, mas eu gosto de começar e TERMINAR o serviço, então na minha máquina eu eliminei cada traço do G-Buster na registry!
Se quiser continuar até o fim, remova as DLL's que estão na pasta do G-Buster, ou, melhor ainda, renomeie-as usando um outro nome qualquer. Agora que o serviço GbpSv.exe não está mais ativo é fácil remover ou renomear as DLL's (após a reinicialização do sistema e ANTES de qualquer acesso ao Internet Banking do seu banco, caso contrário a DLL será carregada e estando em memória não pode ser excluída). Lembre-se somente que, excluindo a DLL, sempre que você acessar o Internet Banking será solicitado que você instale novamente o plugin ou ActiveX do banco, o que reinstalará a DLL (mas não o serviço GbpSv.exe, caso você tenha bloqueado o acesso ao arquivo de mesmo nome que está na pasta).
A mais nova versão da praga pode ser removida mais fácil que antes, com o novo procedimento descrito neste link.
Pois bem amigos. Nunca pensei que o G-Buster Browser Defense fizesse tanto mal a tanta gente! Depois que fiz o post com o método que usei para remover a praga do meu PC recebo pelo menos dois e-mails por dia. Alguns agradecendo pela receita, outros se lamentando que não conseguiram, mesmo seguindo fielmente os passos para a remoção do G-Buster.
Bem, resolvi então fazer um segundo post sobre o assunto. Juntei inclusive opiniões e dicas de leitores, e apimentei um pouco a situação! Resolvi bater o G-Buster em seu próprio jogo SUJO de mudanças de permissão de acesso a chaves da registry, arquivos, etc. Então vamos lá! PS: Uso Windows XP, mas a receita funciona para todos!
1) Faça logon no Windows como administrador da máquina, sem permissões de administrador, nada feito.
2) Conforme explicado no meu outro post, o G-Buster é composto de uma DLL que é o controle ActiveX que interage com o Internet Banking do banco em questão, e também por um serviço, o abominável GbpSv.exe, ambos na pasta C:\Arquivos de programas\GbPlugin (ou C:\Program Files\GbPlugin se seu Windows for em Inglês).
O GbpSv.exe TEM que ser removido. Ele é o serviço que fica ativo 24x7 em sua máquina usando recursos sem a menor necessidade, e principalmente, SEM A SUA PERMISSÃO!
Você pode ver o G-Buster em execução teclando Ctrl+Alt+Del e abrindo o Task Manager (uso o DTaskManager), conforme a figura abaixo:
Não adianta tentar matar o G-Buster a partir do Task Manager. Ele entra em execução novamente. Mas... Se o arquivo executável do programa não pode ser acessado para leitura, então ele não pode ser executado, certo? CERTO! Então vamos remover a permissão de leitura do arquivo GbpSv.exe.
Siga os passos que constam nas figuras abaixo:
2.1) Removendo a propriedade de leitura do diretório C:\Arquivos de programas\GbPlugin:
Vá no Windows Explorer encontre a pasta C:\Arquivos de programas\GbPlugin. Clique com o botão direito do mouse e escolha no menu Propriedades:
Vá na aba Segurança. Clique em avançado e desmarque a opção "Herdar do Pai as entradas de permissão aplicáveis..."
Clique em OK. O Windows irá perguntar se é para copiar as permissões ou excluí-las. Para facilitar a demonstração, vou supor que você irá escolher COPIAR. Então teremos depois disso:
Remova, um por um, TODOS os usuários da lista de usuários com permissão. Deixe somente o usuário SYSTEM e o usuário LOCAL SERVICE. Se os usuários SYSTEM e LOCAL SERVICE não estiverem na lista adicione-os. A partir do momento que somente os dois usuários estiverem na lista você irá NEGAR acesso (TODOS) a estes usuários. Teremos então:
A partir daí, nem o Windows Explorer terá acesso aos arquivos e você não mais poderá vê-los no painel da direita.
3) Agora, reinicie a máquina. O G-Buster não conseguirá mais ser carregado, mesmo que as entradas na Registry ainda sejam mantidas, pois não terá mais acesso ao HD na pasta onde está o arquivo executável.
Após o boot, repare no Task Manager que o G-Buster não está mais na lista de processos em execução:
Agora vamos bater o G-Buster em seu próprio jogo sujo. Atenção: Estes passos são opcionais. Não é necessário fazer isto pois o G-Buster já foi removido, mas eu fiz para impedir que ele volte quando eu acessar novamente o Internet Banking.
4) Adicione acesso total ao seu usuário na pasta do G-Buster, conforme a figura:
Agora, renomeie o GbpSv.exe para um outro nome qualquer que não tenha extensão EXE (eu usei GbpSv_Ex_EXE.OLD) com esta extensão OLD ele é inofensivo, e não pode ser executado pois o Windows não reconhece esta extensão).
Crie então um arquivo com o nome que o serviço tinha antes. Pode ser um arquivo texto mesmo, de tamanho zero, com o nome GbpSv.exe. Deixe o arquivo sem permissão nenhuma de nenhum usuário como a figura:
Renomeando o GbpSv.exe:
Criando um novo arquivo GbpSv.exe, que não é executável:
Negando acesso ao arquivo GbpSv.exe:
Você pode estar se perguntando para quê eu fiz isto? Criar um arquivo vazio com o nome GbpSv.exe e bloquear o acesso a ele. Isto é uma forma simples de impedir que o instalador do plugin que roda quando você acessa o site consiga instalar de novo o GbpSv.exe original. Mesmo que as permissões da pasta sejam alteradas a existência de um arquivo de mesmo nome e sem permissão de acesso impede que um novo GbpSv.exe seja copiado para esta pasta. Ou seja, não tem mais como o instalador instalar a praga novamente. Será?
Bem a melhor forma é testar de novo, acessando o site do banco. Testei na Caixa Econômica Federal e olhem aí o Internet Banking totalmente funcional:
Será que o G-Buster não voltou? Hum... Desta vez não! ;-)
Bem, este segundo método de remover o G-Buster é mais fácil que o primeiro que fiz, mas mesmo assim é cheio de etapas. E pela extensão do processo, fica difícil explicar detalhadamente cada passo e usuários menos experientes em configurações avançadas do Windows podem ter alguma dificuldade. Mesmo não sendo difícil, é longo e passível de erros. Se você tentou e não conseguiu, respire fundo, acalme-se e comece de novo, passo-a-passo! ;-) Vale a pena! Depois disso você fica livre do G-Buster e continua acessando seu Internet Banking, sem problemas!
Você pode continuar o extermínio da praga até o fim, eliminando as entradas da registry pertinentes, conforme eu mostrei no primeiro post sobre o assunto. Não que seja necessário, mas eu gosto de começar e TERMINAR o serviço, então na minha máquina eu eliminei cada traço do G-Buster na registry!
Se quiser continuar até o fim, remova as DLL's que estão na pasta do G-Buster, ou, melhor ainda, renomeie-as usando um outro nome qualquer. Agora que o serviço GbpSv.exe não está mais ativo é fácil remover ou renomear as DLL's (após a reinicialização do sistema e ANTES de qualquer acesso ao Internet Banking do seu banco, caso contrário a DLL será carregada e estando em memória não pode ser excluída). Lembre-se somente que, excluindo a DLL, sempre que você acessar o Internet Banking será solicitado que você instale novamente o plugin ou ActiveX do banco, o que reinstalará a DLL (mas não o serviço GbpSv.exe, caso você tenha bloqueado o acesso ao arquivo de mesmo nome que está na pasta).
Friday, May 30, 2008
Wednesday, April 23, 2008
Midas.dll and COM+ applications deployment issues
It is a known fact that Midas.dll needs to be registered before installing/registering a COM+ ActiveX Library under COM+, when deploying an ActiveX library written with Delphi, right?
Well... something interesting that I discovered this week: DON'T register a Midas.dll version 10.x (from BDS 2006 installation). Use a lower version - I'm using my D6 dll.
If you use version 10.x it will be registered without any errors, but when you try to install the ActiveX Library, boooom!!! You will get a "Error loading type library" error, as Midas.dll was not registered at all.
After ActiveX Library installation/registration, Midas.dll can be even deleted (If you are linking with MidasLib.dcu), as usual.
Well... something interesting that I discovered this week: DON'T register a Midas.dll version 10.x (from BDS 2006 installation). Use a lower version - I'm using my D6 dll.
If you use version 10.x it will be registered without any errors, but when you try to install the ActiveX Library, boooom!!! You will get a "Error loading type library" error, as Midas.dll was not registered at all.
After ActiveX Library installation/registration, Midas.dll can be even deleted (If you are linking with MidasLib.dcu), as usual.
Monday, April 14, 2008
ASP.NET Compilation Error
Another common error I've found installing ASP.NET web services in IIS:
"Compilation Error - The compiler failed with error code 128"
This error has a simple solution: Just go to your .NET subfolder, for instance,
%SystemRoot%\system32\Microsoft.NET\Framework\
and type
aspnet_regiis -i
This will re-install and configure ASP.NET for IIS.
"Compilation Error - The compiler failed with error code 128"
This error has a simple solution: Just go to your .NET subfolder, for instance,
%SystemRoot%\system32\Microsoft.NET\Framework\
and type
aspnet_regiis -i
This will re-install and configure ASP.NET for IIS.
Thursday, March 13, 2008
Arcana Intraweb Elite Suite is now Open Source!
Good news for Intraweb developers! Arcana has released its Intraweb Elite Suite to Open Source (MIT license). This is an excellent component pack for every Intraweb project!
Download here: http://code.google.com/p/iwelite/
Download here: http://code.google.com/p/iwelite/
Object Serialization in Delphi - Part II
This is a unit based on JVCL’s JvgXMLSerializer (from old Globus library). It has some basic classes used for object serialization:
The classes declared are:
TXMLSerializer: my direct descendant of TJvgXMLSerializer. It implements two new methods:
- Serialize: serialize an instance directly to a string, without the necessity of creating a TStream;
- DeSerialize: receive a string as a parameter containing the XML and deserialize the instance.
TSerializable: a TPersistent descendant that implements the Serialize and DeSerialize methods too.
They both work to serialize and deserialize itself.
All my "serializable" classes are direct descendants of TSerializable.
I've modified the original JVCL unit TvgXMLSerializer (using latest version 3.33) to make it compatible with VCL.NET (using BDS 2006).
Using that unit I can create serializable classes that can be shared - and used - among Win32 and .NET modules. Not only creating single sorced projects, but I can pass a XML to a ASP.NET WebService from my Win32 consumer client, in an easy and convinient way.
More about it later. ;-)
unit XMLSerializer;
interface
uses
SysUtils, Classes, JvgXMLSerializer;
type
TXMLSerializer = class(TJvgXMLSerializer)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
procedure Serialize(Component: TObject; var s: string); overload;
procedure DeSerialize(Component: TObject; s: string); overload;
published
{ Published declarations }
end;
TSerializable = class(TPersistent)
public
{ Public declarations }
function Serialize: string;
procedure DeSerialize(s: string);
end;
implementation
{ TXMLSerializer }
procedure TXMLSerializer.DeSerialize(Component: TObject; s: string);
var
Stream: TStringStream;
begin
Stream := TStringStream.Create(s);
try
DeSerialize(Component, Stream);
finally
Stream.Free;
end;
end;
procedure TXMLSerializer.Serialize(Component: TObject; var s: string);
var
Stream: TStringStream;
begin
Stream := TStringStream.Create('');
try
Serialize(Component, Stream);
s := Stream.DataString;
finally
Stream.Free;
end;
end;
{ TSerializable }
procedure TSerializable.DeSerialize(s: string);
var
Serializer: TXMLSerializer;
begin
Serializer := TXMLSerializer.Create(nil);
try
Serializer.DeSerialize(Self, s);
finally
Serializer.Free;
end;
end;
function TSerializable.Serialize: string;
var
Serializer: TXMLSerializer;
begin
Serializer := TXMLSerializer.Create(nil);
try
Serializer.Serialize(Self, Result);
finally
Serializer.Free;
end;
end;
end.
The classes declared are:
TXMLSerializer: my direct descendant of TJvgXMLSerializer. It implements two new methods:
- Serialize: serialize an instance directly to a string, without the necessity of creating a TStream;
- DeSerialize: receive a string as a parameter containing the XML and deserialize the instance.
TSerializable: a TPersistent descendant that implements the Serialize and DeSerialize methods too.
They both work to serialize and deserialize itself.
All my "serializable" classes are direct descendants of TSerializable.
I've modified the original JVCL unit TvgXMLSerializer (using latest version 3.33) to make it compatible with VCL.NET (using BDS 2006).
Using that unit I can create serializable classes that can be shared - and used - among Win32 and .NET modules. Not only creating single sorced projects, but I can pass a XML to a ASP.NET WebService from my Win32 consumer client, in an easy and convinient way.
More about it later. ;-)
Friday, March 7, 2008
G-Buster Browser Defense. O que os bancos instalam em seu PC sem você saber.
ATENÇÃO: Este procedimento não é válido para as versões mais recentes do G-Buster. Um novo post explica como bater esta praga novamente! Estou adorando brincar de gato & rato com a GAS Tecnologia! ;-)
A mais nova versão da praga pode ser removida mais fácil que antes, com o novo procedimento descrito neste link.
Este é o nome do vírus que os bancos instalam em seu computador sem você saber. Vírus sim, mesmo que benigno. Tem as mesmas características técnicas de um vírus qualquer que um hacker coloca no seu PC: invisível, furtivo, instalado sem seu conhecimento, impossível de ser removido por um leigo, rouba sua CPU fazendo coisas que você nem tem idéia.
Quem faz isto atualmente: Banco Real, Banco do Brasil, Caixa Econômica Federal, Unibanco. O desenvolvedor da praga é a GAS Tecnologia.
O mais nefasto dos efeitos deste "vírus" pode ser visto direto no seu TaskManager. Se você acessa um destes bancos via internet, poderá ver o serviço Gbpsv.exe na lista de processos em execução. Este serviço pode também ser visto acessando-se o controlador de serviços em Painel de Controle/Ferramentas Administrativas. O mais interessante é que nem o administrador local da máquina é capaz de parar, remover, pausar ou alterar as configurações deste serviço - o que na minha opinião é um abuso por parte dos bancos. Possivelmente em outro país isto seria considerado ilegal.
Pesquisei muito na internet para descobrir o que era e o que fazia o tal G-Buster, e a melhor fonte de informação está aqui.
O estranho é que tenho instaladas as versões do Banco Real e do Banco Mercantil do Brasil, mas somente a versão do Banco Real instala o serviço Gbpsv.exe. A versão do Banco Mercantil do Brasil é somente a DLL do controle ActiveX, mas não o serviço de monitoração.
Então, vamos separar o joio do trigo: A DLL que contém o controle ActiveX não é indesejável, uma vez que só é carregada sob demanda quando se acessa o Internet Banking do respectivo banco. Assim funciona por exemplo com o Banco Mercantil do Brasil que só usa a DLL gbiehbmb.dll.
A praga em si é o serviço Gbpsv.exe que roda para todos os usuários da máquina, 24 horas por dia, fazendo polling na registry, conforme demonstrou o Romulo Ceccon em seu blog.
Tentei removê-lo usando a técnica descrita no site acima, mas não tive sucesso. Então adicionei alguns passos e consegui resolver.
Identificando os módulos executáveis
Testei com o Internet Banking do Banco Real, e os módulos podem ser encontrados em:
C:\Arquivos de programas\GbPlugin\
Cada banco tem sua versão da DLL, a do Banco Real é: gbiehabn.dll, da Caixa é gbiehcef.dll, do BMB é gbiehbmb.dll e analogamente para outros bancos.
Existe ainda o serviço GbpSv.exe, na mesma pasta. Este é o serviço que está rodando 24x7 em seu computador, consumindo ciclos de sua CPU - sem você ter sido informado - e que será removido!
Ferramentas necessárias para remoção da praga:
1) SysInternals Process Explorer disponível aqui, ou para download direto aqui.
2) SysInternals Autoruns disponível aqui, ou para download direto aqui.
3) Login como administrador local da máquina. Sem estas credenciais, nada feito.
Desinstalando o servico Gbpsv.exe
A forma normal de remover um serviço é através do controlador de serviços no painel de controle. Infelizmente o abuso chegou ao ponto de impedir a parada ou desinstalação do serviço por este método.
O interessante é que antes testei a ferramenta de exclusão de serviços do aplicativo HijackThis que geralmente resolve este tipo de problema e não tive sucesso. Recebo a mensagem que o serviço não existe. Bem, isto porquê a praga muda as permissões na registry de sua própria entrada em:
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\GbpSv
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Services\GbpSv
OBSERVAÇÃO: Podem haver variações nos nomes da chave ControlSet001, ControlSet002, ControlSet003. Em todas elas deve-se verificar a existência da subchave GbpSv.
Então, vamos do início:
1) Abra o Regedit, para que possamos mudar as permissões nas chaves mostradas acima. Para abrir o Regedit vá em Iniciar -> Executar e digite Regedit e clique em OK, conforme a figura:
2) Vamos então procurar as chaves:
Expanda sucessivamente as chaves HKEY_LOCAL_MACHINE, depois SYSTEM, ControlSet001 e finalmente Services.
3) Mudando as permissões nas chaves:
As duas entradas têm permissões somente para as contas LOCAL e SYSTEM. Nem o Administrador possui permissões nesta chave. Mas não é possível conceder ao grupo de administradores a permissão diretamente. Primeiro temos que tornar o administrador o proprietário da chave. Siga os seguintes passos:
- Selecione a chave GbpSv e depois clique com o botão direito sobre ela (ao selecionar geralmente há mensagem de falta de permissão, ignore estas mensagens).
- Selecione no submenu a opção "Permissões". Poderá ser exibida uma mensagem como a que está abaixo. Ignore-a tb.
- Clique em Avançado e na nova janela que irá se abrir selecione a aba "Proprietário", conforme a figura abaixo:
Apesar do tema estar com cara da MacOS, é um legítimo Windows XP ;-)
Uma vez que a sua conta agora é o proprietário da chave, você poderá alterar as permissões. O que eu fiz: Neguei acesso a conta LOCAL e SYSTEM, a concedi acesso total a conta do administrador. Assim eu posso deletar os valores sem que a praga escreva os valores novamente (isto acontecerá se a conta SYSTEM continuar com permissão de acesso nesta chave). Eu mantive a chave GbpSv, somente excluí os valores e subchaves. Mantendo a chave com permissão negada à conta SYSTEM me garante que o GbpSv não irá escrever nada lá novamente.
Terminando a execução dos processos
Siga então os passos sugeridos pelo Romulo Ceccon: Abra o Process Explorer para que possamos terminar a DLL. A diferença é que além da DLL estar sob o processo WinLogon.exe, encontrei-a também sob o processo Explorer.exe. A melhor forma que encontrei foi procurar todas as ocorrências usando a opção FIND do Process Explorer, procurando pelo nome da DLL. Encontrei duas ocorrências e terminei ambas as threads. Terminei também o serviço GbpSv.exe, visível na lista de processos.
Agora que as pragas não estão em execução, temos que remover as entradas de autoexecução na Registry usando o Autoruns.
Desabilitando a inicialização automática
Execute o Autoruns e na aba EVERYTHING, percorra toda a lista e remova todas as entradas referentes a DLL e ao serviço GbpSv.exe. TODAS as entradas devem ser removidas. Na dúvida, tudo que tiver Gbieh como parte da descrição refere-se à praga e pode ser excluído.
Após remover todas as entradas efetue um boot no sistema. Quando o Windows iniciar, o serviço GbpSv.exe não deverá constar mais na lista de processos em execução no TaskManager. E nem a DLL gbiehabn.dll (ou gbiehcef.dll, etc.) deverá constar no Process Explorer.
Isto significa que, finalmente, poderemos excluir o arquivo GbpSv.exe!
Basta ir na pasta C:\Arquivos de programas\GbPlugin\ e teclar um majestoso DELETE e ver o arquivo ir pro lugar de onde não deveria ter saído.
Irei reinstalar a praga no meu sistema e monitorá-la um pouco mais para aumentar meu entendimento. Mais sobre isto depois.
A mais nova versão da praga pode ser removida mais fácil que antes, com o novo procedimento descrito neste link.
Este é o nome do vírus que os bancos instalam em seu computador sem você saber. Vírus sim, mesmo que benigno. Tem as mesmas características técnicas de um vírus qualquer que um hacker coloca no seu PC: invisível, furtivo, instalado sem seu conhecimento, impossível de ser removido por um leigo, rouba sua CPU fazendo coisas que você nem tem idéia.
Quem faz isto atualmente: Banco Real, Banco do Brasil, Caixa Econômica Federal, Unibanco. O desenvolvedor da praga é a GAS Tecnologia.
O mais nefasto dos efeitos deste "vírus" pode ser visto direto no seu TaskManager. Se você acessa um destes bancos via internet, poderá ver o serviço Gbpsv.exe na lista de processos em execução. Este serviço pode também ser visto acessando-se o controlador de serviços em Painel de Controle/Ferramentas Administrativas. O mais interessante é que nem o administrador local da máquina é capaz de parar, remover, pausar ou alterar as configurações deste serviço - o que na minha opinião é um abuso por parte dos bancos. Possivelmente em outro país isto seria considerado ilegal.
Pesquisei muito na internet para descobrir o que era e o que fazia o tal G-Buster, e a melhor fonte de informação está aqui.
O estranho é que tenho instaladas as versões do Banco Real e do Banco Mercantil do Brasil, mas somente a versão do Banco Real instala o serviço Gbpsv.exe. A versão do Banco Mercantil do Brasil é somente a DLL do controle ActiveX, mas não o serviço de monitoração.
Então, vamos separar o joio do trigo: A DLL que contém o controle ActiveX não é indesejável, uma vez que só é carregada sob demanda quando se acessa o Internet Banking do respectivo banco. Assim funciona por exemplo com o Banco Mercantil do Brasil que só usa a DLL gbiehbmb.dll.
A praga em si é o serviço Gbpsv.exe que roda para todos os usuários da máquina, 24 horas por dia, fazendo polling na registry, conforme demonstrou o Romulo Ceccon em seu blog.
Tentei removê-lo usando a técnica descrita no site acima, mas não tive sucesso. Então adicionei alguns passos e consegui resolver.
Identificando os módulos executáveis
Testei com o Internet Banking do Banco Real, e os módulos podem ser encontrados em:
C:\Arquivos de programas\GbPlugin\
Cada banco tem sua versão da DLL, a do Banco Real é: gbiehabn.dll, da Caixa é gbiehcef.dll, do BMB é gbiehbmb.dll e analogamente para outros bancos.
Existe ainda o serviço GbpSv.exe, na mesma pasta. Este é o serviço que está rodando 24x7 em seu computador, consumindo ciclos de sua CPU - sem você ter sido informado - e que será removido!
Ferramentas necessárias para remoção da praga:
1) SysInternals Process Explorer disponível aqui, ou para download direto aqui.
2) SysInternals Autoruns disponível aqui, ou para download direto aqui.
3) Login como administrador local da máquina. Sem estas credenciais, nada feito.
Desinstalando o servico Gbpsv.exe
A forma normal de remover um serviço é através do controlador de serviços no painel de controle. Infelizmente o abuso chegou ao ponto de impedir a parada ou desinstalação do serviço por este método.
O interessante é que antes testei a ferramenta de exclusão de serviços do aplicativo HijackThis que geralmente resolve este tipo de problema e não tive sucesso. Recebo a mensagem que o serviço não existe. Bem, isto porquê a praga muda as permissões na registry de sua própria entrada em:
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\GbpSv
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Services\GbpSv
OBSERVAÇÃO: Podem haver variações nos nomes da chave ControlSet001, ControlSet002, ControlSet003. Em todas elas deve-se verificar a existência da subchave GbpSv.
Então, vamos do início:
1) Abra o Regedit, para que possamos mudar as permissões nas chaves mostradas acima. Para abrir o Regedit vá em Iniciar -> Executar e digite Regedit e clique em OK, conforme a figura:
2) Vamos então procurar as chaves:
Expanda sucessivamente as chaves HKEY_LOCAL_MACHINE, depois SYSTEM, ControlSet001 e finalmente Services.
3) Mudando as permissões nas chaves:
As duas entradas têm permissões somente para as contas LOCAL e SYSTEM. Nem o Administrador possui permissões nesta chave. Mas não é possível conceder ao grupo de administradores a permissão diretamente. Primeiro temos que tornar o administrador o proprietário da chave. Siga os seguintes passos:
- Selecione a chave GbpSv e depois clique com o botão direito sobre ela (ao selecionar geralmente há mensagem de falta de permissão, ignore estas mensagens).
- Selecione no submenu a opção "Permissões". Poderá ser exibida uma mensagem como a que está abaixo. Ignore-a tb.
- Clique em Avançado e na nova janela que irá se abrir selecione a aba "Proprietário", conforme a figura abaixo:
Apesar do tema estar com cara da MacOS, é um legítimo Windows XP ;-)
Uma vez que a sua conta agora é o proprietário da chave, você poderá alterar as permissões. O que eu fiz: Neguei acesso a conta LOCAL e SYSTEM, a concedi acesso total a conta do administrador. Assim eu posso deletar os valores sem que a praga escreva os valores novamente (isto acontecerá se a conta SYSTEM continuar com permissão de acesso nesta chave). Eu mantive a chave GbpSv, somente excluí os valores e subchaves. Mantendo a chave com permissão negada à conta SYSTEM me garante que o GbpSv não irá escrever nada lá novamente.
Terminando a execução dos processos
Siga então os passos sugeridos pelo Romulo Ceccon: Abra o Process Explorer para que possamos terminar a DLL. A diferença é que além da DLL estar sob o processo WinLogon.exe, encontrei-a também sob o processo Explorer.exe. A melhor forma que encontrei foi procurar todas as ocorrências usando a opção FIND do Process Explorer, procurando pelo nome da DLL. Encontrei duas ocorrências e terminei ambas as threads. Terminei também o serviço GbpSv.exe, visível na lista de processos.
Agora que as pragas não estão em execução, temos que remover as entradas de autoexecução na Registry usando o Autoruns.
Desabilitando a inicialização automática
Execute o Autoruns e na aba EVERYTHING, percorra toda a lista e remova todas as entradas referentes a DLL e ao serviço GbpSv.exe. TODAS as entradas devem ser removidas. Na dúvida, tudo que tiver Gbieh como parte da descrição refere-se à praga e pode ser excluído.
Após remover todas as entradas efetue um boot no sistema. Quando o Windows iniciar, o serviço GbpSv.exe não deverá constar mais na lista de processos em execução no TaskManager. E nem a DLL gbiehabn.dll (ou gbiehcef.dll, etc.) deverá constar no Process Explorer.
Isto significa que, finalmente, poderemos excluir o arquivo GbpSv.exe!
Basta ir na pasta C:\Arquivos de programas\GbPlugin\ e teclar um majestoso DELETE e ver o arquivo ir pro lugar de onde não deveria ter saído.
Irei reinstalar a praga no meu sistema e monitorá-la um pouco mais para aumentar meu entendimento. Mais sobre isto depois.
Thursday, February 28, 2008
Usando Temas do Windows em programas Delphi 5 e 6
Recentemente me deparei com um problema bem incômodo ao usar temas através do Theme Manager de Mike Lischke.
O maior problema foram os TBitBtn's que não eram desenhados de acordo com o tema do Windows pelo Theme Service. Procurei várias soluções e não encontrei nada, nem no FAQ da soft-gems. Depois de mais alguma pesquisa, finalmente descobri!
A causa e a solução do problema são relativamente simples:
O problema só ocorria em forms que continham TBitBtn e cujo Owner não era o objeto Application. Uso frequentemente diálogos chamando uma função de classe que cuida de sua criação e destruição da seguinte forma:
Usado desta forma os TBitBtn's neste diálogo não serão desenhados de acordo com o tema do Windows. O melhor a fazer é substituir o código por:
Neste caso o TBitBtn é desenhado corretamente!
O maior problema foram os TBitBtn's que não eram desenhados de acordo com o tema do Windows pelo Theme Service. Procurei várias soluções e não encontrei nada, nem no FAQ da soft-gems. Depois de mais alguma pesquisa, finalmente descobri!
A causa e a solução do problema são relativamente simples:
O problema só ocorria em forms que continham TBitBtn e cujo Owner não era o objeto Application. Uso frequentemente diálogos chamando uma função de classe que cuida de sua criação e destruição da seguinte forma:
class function TMyDialog.Execute: boolean;
var
Dlg: TMyDialog;
begin
Dlg := TMyDialog.Create(nil);
try
Result := Dlg.ShowModal = mrOk;
finally
Dlg.Free;
end;
end;
Usado desta forma os TBitBtn's neste diálogo não serão desenhados de acordo com o tema do Windows. O melhor a fazer é substituir o código por:
class function TMyDialog.Execute: boolean;
var
Dlg: TMyDialog;
begin
Dlg := TMyDialog.Create(Application);
try
Result := Dlg.ShowModal = mrOk;
finally
Dlg.Free;
end;
end;
Neste caso o TBitBtn é desenhado corretamente!
Thursday, January 17, 2008
TIOBE index again!
Esta segue o link no blog do Marco Cantu. Como ele diz "está longe de ser científico" mas mostra bem o que está acontecendo! ;-)
http://blog.marcocantu.com/blog/delphi_tiobe_jan2008.html
http://blog.marcocantu.com/blog/delphi_tiobe_jan2008.html
Friday, January 11, 2008
Mais futurologia....
Entre meus colegas de trabalho sou reconhecido defensor do Delphi com relação a outras linguagens/IDE's. Hoje tive uma rápida conversa com um colega que me disse: "Lá para 2010 software nativo será como o DOS era para o Windows e rodará em modo de compatibilidade (mal e porcamente)".
Sendo assim teremos os seguintes "softwares" que rodarão "mal" e em "modo de compatibilidade":
- Todos os softwares servidores da M$ incluindo o SQL Server cujo core é 100% native code e deve ter um zilhão de linhas de C/C++ escritas!
- Aplicativos servidores da ORACLE, IBM...
- Java Virtual Machine
- .NET, FCL
- Todos os aplicativos MS-Windows - out of the box
Ou alguém acha que a ORACLE, por exemplo, irá reescrever os seus servidores de BD em VS.NET??? LOL!!!
O interessante é ver como a propaganda da MS é forte. Como é possível convencer tantos desenvolvedores que um software escrito para usar a API do sistema operacional pode ser forçado a rodar em "modo de compatibilidade" sendo que o próprio framework .NET da M$ usa exatamente a mesma API, da mesma forma???!!! Quem sabe o diagrama da arquitetura .NET ajude?
Talvez 90% dos desenvolvedores Windows que conheço precisem ler um pouco mais de informação e menos PROPAGANDA, tipo o site do Steve Trefethen AQUI.
Este camarada já passou longos anos na Borland e é um insider da M$. Então, com certeza, sabe muito mais o que fala do que os marketeiros da M$, é ou não é?
Saudações por hoje.
Sendo assim teremos os seguintes "softwares" que rodarão "mal" e em "modo de compatibilidade":
- Todos os softwares servidores da M$ incluindo o SQL Server cujo core é 100% native code e deve ter um zilhão de linhas de C/C++ escritas!
- Aplicativos servidores da ORACLE, IBM...
- Java Virtual Machine
- .NET, FCL
- Todos os aplicativos MS-Windows - out of the box
Ou alguém acha que a ORACLE, por exemplo, irá reescrever os seus servidores de BD em VS.NET??? LOL!!!
O interessante é ver como a propaganda da MS é forte. Como é possível convencer tantos desenvolvedores que um software escrito para usar a API do sistema operacional pode ser forçado a rodar em "modo de compatibilidade" sendo que o próprio framework .NET da M$ usa exatamente a mesma API, da mesma forma???!!! Quem sabe o diagrama da arquitetura .NET ajude?
Talvez 90% dos desenvolvedores Windows que conheço precisem ler um pouco mais de informação e menos PROPAGANDA, tipo o site do Steve Trefethen AQUI.
Este camarada já passou longos anos na Borland e é um insider da M$. Então, com certeza, sabe muito mais o que fala do que os marketeiros da M$, é ou não é?
Saudações por hoje.
Unicode no Tiburon
O blog da CodeGear está bem agitado estes dias. Allen Bauer comandando! Tudo sobre Unicode no Tiburon:
http://blogs.codegear.com/abauer/2008/01/09/38845/
http://blogs.codegear.com/abauer/2008/01/09/38846/
http://blogs.codegear.com/abauer/2008/01/10/38847/
http://blogs.codegear.com/abauer/2008/01/11/38848/
O Allen fala em um dos posts: "Mudança pior aconteceu quando houve a transição de 16 bits para 32 bits, Delphi 1 para Delphi 2."
Isto é fato. Foi muito mais drástico do que agora. E mesmo assim, mais fácil do que se previa. Ao contrário da M$, que não teve a menor piedade dos desenvolvedores VB6, a Borland (CodeGear) sempre primou pela manutenção da compatibilidade.
Eu vivi esta experiência de transição de sistemas 16/32 bits e posso afirmar: Valeu cada hora de trabalho recodificando a aplicação.
E outra: "Não se pode fazer uma omelete sem quebrar os ovos", já dizia minha vovozinha - que Deus a tenha!
http://blogs.codegear.com/abauer/2008/01/09/38845/
http://blogs.codegear.com/abauer/2008/01/09/38846/
http://blogs.codegear.com/abauer/2008/01/10/38847/
http://blogs.codegear.com/abauer/2008/01/11/38848/
O Allen fala em um dos posts: "Mudança pior aconteceu quando houve a transição de 16 bits para 32 bits, Delphi 1 para Delphi 2."
Isto é fato. Foi muito mais drástico do que agora. E mesmo assim, mais fácil do que se previa. Ao contrário da M$, que não teve a menor piedade dos desenvolvedores VB6, a Borland (CodeGear) sempre primou pela manutenção da compatibilidade.
Eu vivi esta experiência de transição de sistemas 16/32 bits e posso afirmar: Valeu cada hora de trabalho recodificando a aplicação.
E outra: "Não se pode fazer uma omelete sem quebrar os ovos", já dizia minha vovozinha - que Deus a tenha!
Tuesday, January 8, 2008
IIS e ASP.NET: +Erros no Deploy de WebServices
Mais alguns probleminhas com WebServices em ASP.NET no BDS 2006: Este pode tanto acontecer no deploy quanto no debug:
Unable to load type 'Global.TGlobal'.
ou em bom português
Não foi possível carregar o tipo 'Global.TGlobal'.
Vez por outra encontrei este erro quando tentei debugar o WebService rodando pelo IDE, usando o IIS, sendo que o diretório virtual no ISS foi criado no próprio IDE. Por default, o BDS cria uma pasta bin abaixo da pasta onde se encontra o projeto do WebService. Desta forma, ele "espera" que o assembly seja salvo nesta pasta bin.
Se não houver nenhuma configuração adicional do projeto, o assembly será salvo no mesmo diretório dos fontes, e neste caso poderá haver este erro.
Basta copiar, ou mover o assembly gerado para a subpasta bin e o aplicativo poderá ser debugado normalmente.
Convém alterar as opções do projeto direcionando o "Output dir" para esta subpasta bin.
Unable to load type 'Global.TGlobal'.
ou em bom português
Não foi possível carregar o tipo 'Global.TGlobal'.
Vez por outra encontrei este erro quando tentei debugar o WebService rodando pelo IDE, usando o IIS, sendo que o diretório virtual no ISS foi criado no próprio IDE. Por default, o BDS cria uma pasta bin abaixo da pasta onde se encontra o projeto do WebService. Desta forma, ele "espera" que o assembly seja salvo nesta pasta bin.
Se não houver nenhuma configuração adicional do projeto, o assembly será salvo no mesmo diretório dos fontes, e neste caso poderá haver este erro.
Basta copiar, ou mover o assembly gerado para a subpasta bin e o aplicativo poderá ser debugado normalmente.
Convém alterar as opções do projeto direcionando o "Output dir" para esta subpasta bin.
IIS e ASP.NET: Erros no Deploy de WebServices
Criando aplicações ASP.NET no BDS 2006, me deparei com alguns erros no momento do deploy. Um deles foi:
"Server cannot access application directory "APP_DIR". The directory does not exist or is not accessible because of security settings.
Por incrível que pareça a mensagem de erro é de fato informativa, e é bem plausível que seja realmente permissão insuficiente para acessar o diretório da aplicação (APP_DIR). Logo de início dei permissões de leitura a "Everyone" (Todos) e a aplicação funcionou corretamente. Obviamente esta não é a solução ideal e de fato não é necessário conceder esta permissão tão abrangente. Basta conceder permissão de leitura na pasta da aplicação ao usuário [Machine_Name]\ASPNET, e voilá!
Ambiente de referência:
- Windows 2000 Server - SP4
- .NET Framework V 1.1.4322
"Server cannot access application directory "APP_DIR". The directory does not exist or is not accessible because of security settings.
Por incrível que pareça a mensagem de erro é de fato informativa, e é bem plausível que seja realmente permissão insuficiente para acessar o diretório da aplicação (APP_DIR). Logo de início dei permissões de leitura a "Everyone" (Todos) e a aplicação funcionou corretamente. Obviamente esta não é a solução ideal e de fato não é necessário conceder esta permissão tão abrangente. Basta conceder permissão de leitura na pasta da aplicação ao usuário [Machine_Name]\ASPNET, e voilá!
Ambiente de referência:
- Windows 2000 Server - SP4
- .NET Framework V 1.1.4322
Friday, January 4, 2008
Futuro do DataSnap
Conversa rápida com o John Kaster (CodeGear Internet Services Architect) no newsgroup da CG hoje:
Pergunta minha a ele: "Will DataSnap remain tied to COM?"
Resposta: "I think it's safe to answer "no" for this one. Steve (Shaughnessy) is definitely
looking at eliminating COM as a required technology DataSnap solution,
and what he has in mind is 100% feasible and achievable."
...
"What I want to see is "native" (supported by the respective
platform/language frameworks) interop among data operations, REST,
"skins", user ids, tagging, commenting, searching, and so on for all
these platforms."
Fiquei feliz! :-) O DataSnap precisa se livrar da infraestrutura COM para então alcançar outro patamar.
Agora é esperar a revisão do roadmap do Delphi que o Nick Rodges disse que sai até o fim do mes de Janeiro, e ficar de olho no que o Tiburon trará de novidade.
Pergunta minha a ele: "Will DataSnap remain tied to COM?"
Resposta: "I think it's safe to answer "no" for this one. Steve (Shaughnessy) is definitely
looking at eliminating COM as a required technology DataSnap solution,
and what he has in mind is 100% feasible and achievable."
...
"What I want to see is "native" (supported by the respective
platform/language frameworks) interop among data operations, REST,
"skins", user ids, tagging, commenting, searching, and so on for all
these platforms."
Fiquei feliz! :-) O DataSnap precisa se livrar da infraestrutura COM para então alcançar outro patamar.
Agora é esperar a revisão do roadmap do Delphi que o Nick Rodges disse que sai até o fim do mes de Janeiro, e ficar de olho no que o Tiburon trará de novidade.
Thursday, January 3, 2008
As nuvens estão chegando
Amazon.com
Por US$ 288,00 / mês você pode "ter" um super servidor com
7.5 GB of memory
4 EC2 Compute Units (2 virtual cores with 2 EC2 Compute Units each)
850 GB of instance storage
64-bit platform
*One EC2 Compute Unit provides the equivalent CPU capacity of a 1.0-1.2 GHz 2007 Opteron or 2007 Xeon processor
Os pequenos DataCenters estão com os dias contados?
Por US$ 288,00 / mês você pode "ter" um super servidor com
7.5 GB of memory
4 EC2 Compute Units (2 virtual cores with 2 EC2 Compute Units each)
850 GB of instance storage
64-bit platform
*One EC2 Compute Unit provides the equivalent CPU capacity of a 1.0-1.2 GHz 2007 Opteron or 2007 Xeon processor
Os pequenos DataCenters estão com os dias contados?
Wednesday, January 2, 2008
Gerenciamento de memória e Garbage Colector no .NET
Três páginas muito interessantes que garimpei em newsgroups:
.NET Memory Management and Garbage Collection
Is .NET suitable for real-time projects?
Performance .NOT, a look at .NET Memory Performance Counters
O assunto tratado é sério e me preocuparia muito se eu estivesse no meio de um grande projeto corporativo usando .NET. Vejo um monte de aplicações ASP.NET simples, Client/Server, desktop, e até mesmo utilitários escritos em .NET. Mas pouco se fala de grandes aplicações distribuídas.
Os problemas relatados nas páginas acima: Qual a "verdade"? Qual o impacto disso neste tipo de aplicação?
Vale uma investigação...
.NET Memory Management and Garbage Collection
Is .NET suitable for real-time projects?
Performance .NOT, a look at .NET Memory Performance Counters
O assunto tratado é sério e me preocuparia muito se eu estivesse no meio de um grande projeto corporativo usando .NET. Vejo um monte de aplicações ASP.NET simples, Client/Server, desktop, e até mesmo utilitários escritos em .NET. Mas pouco se fala de grandes aplicações distribuídas.
Os problemas relatados nas páginas acima: Qual a "verdade"? Qual o impacto disso neste tipo de aplicação?
Vale uma investigação...
Subscribe to:
Posts (Atom)