“Novas Tecnologias” nas II Jornadas ESEIG do Politécnico do Porto em Vila do Conde

Nos próxmos dias 28 e 29 de Abril terão lugar as II Jornadas ESEIG, este ano sob o lema das Novas Tecnologias, para as quais fui honrado com o convite para apresentar uma sessão onde aflore os temas da mobilidade.

Ao contrário das minhas participações habituais noutros eventos, esta não contará com a vertente técnica ligada ao desenvolvimento, privilegiando antes a apresentação do Windows Mobile como plataforma por excelência no domínio profissional e empresarial.

.NET CF e gestão de memória virtual – Será esta uma descoberta revolucionária?

Um título destes seria digno de abertura de um qualquer telejornal nacional!

Recebi hoje pelo Newsgator/FeedDemon um artigo que a confirmar-se pode revolucionar tudo quanto tem sido escrito sobre as limitações de memória na execução de aplicações .NET Compact Framework. Eu assumo, não sou um entendido em arquitectura de computadores, mas em traços gerais, o panorama é mais ou menos este:  o Windows CE desde a versão 1.0 até à sua versão 5.0 permitem até 32 processos simultâneos, alocando até 32mb de memória a cada um, o que significa que cada aplicação vive dentro de um cubículo de até 32mb, independentemente da quantidade de memória que o equipamento tenha. Na prática, esses 32mb acabam por nunca estarem disponíveis… muitos equipamentos não dispoem sequer de 32mb livres de RAM e quando dispoem, a forma como são carregadas as DLL em código nativo leva a que o seu espaço seja reservado transversalmente entre todos os processos já criados, e mesmo os criados a posteriori.

No caso das aplicações .NET CF, a limitação da memória virtual mantém-se, embora existam algumas diferenças para as aplicações em código nativo, nomeadamente as DLL não ocuparem ‘transversalmente’ o tal espaço em todos os processos referido acima. Posso dizer-vos que já dispendi mais do que um dia a ler blogs, artigos no CodeProject e documentação na MSDN sobre este assunto da gestão em memória em Windows CE e pela .NET CF e sobre o Garbage Collector [1].

Muitas vezes só nos lembramos destas questões da memória quando nos rebenta uma OutOfMemory (OOM) exception – oh p’ra mim a assobiar para o ar – e se algumas situações são facilmente contornáveis, outras podem levar a uma restruturação de trabalho já feito. Uma situação em particular que pode conduzir a estas OOM e potencialmente cada vez mais fre quente, é a realização de operações gráficas com manipulação em memória de bitmaps do tamanho do ecrã em equipamentos de alta resolução, como os VGA e o WVGA, onde um bitmap que num equipamento normal conta com as dimensões de 240×320 vê o seu tamanho multiplicado por 4 no caso do VGA e por 5 no caso do WVGA. A conta é fácil de fazer, se a cada pixel corresponderem dois bytes, de 153kb passamos a 614kb e 768kb respectivamente [2].

Até chegar um sistema operativo Windows Mobile baseado em Windows CE 6.0 ou posterior, o que se espera que aconteça com a próxima grande versão do Windows Mobile, onde cada processo poderá contar com até 2GB para um máximo de 32000 processos a correr em simultâneo(!), pouco parecia haver a fazer para além de optimizar as aplicações actuais e usar as ferramentas das Powertoys (.NET CF CLR Profiler e .NET CF Remote Performance Monitor) para estudar o impacto das modificações efectuadas.

O tal artigo que mencionei no início do tópico é este…

http://blogs.msdn.com/robtiffany/archive/2009/04/09/memmaker-for-the-net-compact-framework.aspx

…e segundo o Rob Tifany, no caso das aplicações .NET CF, se criarmos um exe ‘vazio’ e colocarmos todo o resto do código numa DLL referenciada conseguimos ultrapassar a tal limitação dos 32mb por processo para a nossa aplicação! O artigo é um pouco extenso e técnico, mas é seguramente um must-read por parte de quem desenvolve para .NET CF! A única limitação que a técnica sugerida não contornará será mesmo a limitação crónica de alguns equipamentos com 64mb e menos de RAM, que mesmo após um soft-reset apresentam uma quantidade de RAM disponível abaixo dos 32MB.

Oportunamente testarei esta abordagem numa aplicação onde sei como provocar estes OOM :D

———————

[1] Para os interessados, a partir da seguinte série de artigos do Raffael poderão seguir outros links muito interessantes sobre esta temática:

http://blogs.msdn.com/raffael/archive/tags/Memory/default.aspx

[2 – Correcção]

O João Paulo Figueira alertou-me que cada pixel no ecrã representam dois bytes, e no caso de imagens do tipo PNG e alpha blending, cada pixel chega aos 4 bytes por pixel, ou seja, num equipamento como o HTC Touch HD, até 1,5mb!!!

[3 – Actualização em 2009/04/11 – 8:50]

VirtualMemory[1] A pedido de algumas famílias, a ferramenta que o Rob usa para visualizar a memória virtual ocupada por processo pode ser obtida a partir do seguinte artigo no CodeProject:

 http://www.codeproject.com/KB/mobile/VirtualMemory.aspx

É mais um artigo de leitura recomendada!

SQL Server Compact 3.5: Será que já está *mesmo* a usar a versão SP1?

Como referi em posts anteriores, tenho estado de volta do upgrade de uma aplicação para .NET CF 3.5 e SQL Compact 3.5. A aplicação já vem do tempo da .NET CF 1.0 e SSCE 2.0, altura em que não havia suporte por parte do SSCE a parâmetros com nomes, utilizando-se o ponto de interrogação para identificar o local dos parâmetros, por ex. …

UPDATE Clientes SET saldo = ? WHERE nrcliente = ?

…passando-se os parâmetros ao objecto SqlCeCommand pela ordem pela qual apareciam na instrução SQL. O suporte a parâmetros com nomes veio com o SQL Server 2005 Mobile Edition (3.0), passando-se a poder utilizar a notação mais comum:

UPDATE Clientes SET saldo = @novoSaldo WHERE nrcliente = @cliente

Ao executar a aplicação já em .NET CF 3.5 com SSC 3.5, dou-me conta de um erro numa instrução que ainda vinha do tempo do SSCE 2.0 e como tal ainda tinha os tais pontos de interrogação. Alguma investigação em torno da situação leva-me a experimentar substituír os ? por parâmetros com nomes, e ao executar de novo a instrução, a dita já funcionava… a causa? O SQL Compact 3.5 não estava a respeitar a ordem pela qual os parâmetros eram passados ao comando. A alternativa era óbvia, substituír as potenciais dezenas de instruções com os ditos pontos de interrogação por parâmetros com nomes.

No entanto, isto para mim não fazia sentido, não me lembro de ter lido nada que me alertasse para este novo comportamento, e após alguma investigação fui confirmar se já estaria a utilizar o SP1 do SQL Compact 3.5…

Nesta minha nova máquina de desenvolvimento, um Toshiba Tecra S10-128, pensava ter instalado tudo quanto há de mais recente, nomeadamente o Visual Studio 2008 SP1 e o SQL Server 2008 (o SP1 do SQL Server 2008 só saíu esta semana e ainda não o instalei) e com essas ferramentas, veio também o SQL Compact 3.5 SP1. O que eu me tinha esquecido é que essas actualizações não incluem o SP1 para os devices, e ao copiar o ficheiro sqlceca35.dll da pasta \Programas\Microsoft SQL Server Compact Edition\3.5 do PDA para o PC e com o explorer aberto as suas propriedades, constatei que estava a usar a build 3.5.5386, que de acordo com esta tabela, corresponde à versão RTM, portanto sem o SP1:

image

Rapidamente descarreguei o SP1 para Windows Mobile/Windows CE desta página e instalei-o. Actualizei a referência ao System.Data.SqlServerCe.dll para ter a certeza que estava tudo bem, e fiz o deploy da aplicação. Ao testar de novo a situação descrita no início do post, o problema deixou de ser reproduzido, ou seja, foi resolvido no SP1, ainda que não tenha obtido confirmação nas listas de fixes que verifiquei. Pode confirmar no PDA/emulador se está a usar a versão SP1 comparando no registry a chave [HKEY_LOCAL_MACHINE\Software\Microsoft\Microsoft SQL Server Compact Edition\v3.5] com o seguinte ecrã:

image

Outro stress ultrapassado… NEXT!!!

Problemas após conversão de projecto .NET CF 2.0 para .NET CF 3.5: A aplicação deixou de funcionar e pendura o debugger do VS2008

Ao converter um projecto em .NET CF 2.0, que tinha uma série de dependências de outros projectos igualmente em CF 2.0 que foram sendo igualmente convertifos, consegui executar a aplicação nos emuladores umas quantas vezes, à medida que ia actualizando os projectos com as últimas versões de alguns controlos de terceiros. Subitamente, depois de um conjunto de alterações mais extensas, deixo de conseguir correr a aplicação a partir do Visual Studio 2008 num dos emuladores. Testo noutro, a mesma coisa. Tento correr em modo release, nada feito. Executar a aplicação a partir do PDA? Uma ampulheta que só desaparecia com um soft reset ao equipamento. Depois de ter reiniciado o VS umas quantas vezes, tal como os emuladores, pensei, o PC tá a precisar de um reboot, mas um download demorado impedia-me de o fazer, pelo que fui testanto diferentes coisas sempre sem sucesso.

Após reiniciar o PC o problema mantem-se integralmente, e constato que se interromper a execução da aplicação – que ainda nem tinha arrancado – obtinha algo do género:

The debugger was unable to terminate one or more processes: The debugger may be unstable.

A coisa estava a ficar preta, todos os meus outros projectos em .NET CF 2.0 e 3.5 continuavam a funcionar como sempre à excepção deste (as leis de Murphy encarregam-se de deixar à V/ imaginação a importância deste projecto…).

Dado que pelo método da tentativa/erro não ia lá, e as pesquisas na internet se revelaram infrutíferas, passei para uma análise mais ‘científica’! No grupo dos Power Toys da .NET CF 3.5 no menu Iniciar, tem lá o .NET CF Logging Configuration…

image

…que permite escolher os logs que queremos activar:

image

No caso, escolhi apenas o Loader porque era aquele fazia mais sentido, afinal a aplicação não chegava a arrancar e cliquei em Apply. De seguida, no PDA arranquei a aplicação e esperei uns minutos, e abri o log que foi gerado na pasta da aplicação (netcf_m2uMobileSalesV3_Loader_2463895866.log), e posicionei-me no final onde encontrei uma referência a uma DLL que… não era de todo suposto lá estar, dado destinar-se a uma versão anterior da .NET CF. Voltei a verificar e corrigir todas as dependências e suas dependências, de modo a garantir que todas elas estavam a destinar-se à .NET CF 3.5 e que tinham as referências correctas aos controlos de terceiros. Nova tentativa e… a aplicação já corre de novo nos emuladores e PDAs!!!

Mais um stress resolvido fora-de-horas… NEXT!!!

Executar um ficheiro batch como Administrador sem mudar a pasta de trabalho…

No meu PC com Windows Vista e o UAC ligado, ao instalar uma actualização de uns controlos da Resco, executando o ficheiro .bat fornecido, obtinha a mensagem ‘Access Denied’ por cada ficheiro que o ficheiro batch tentava copiar para uma subpasta abaixo da pasta C:\Program Files. Este é o ficheiro:


@ECHO OFF

REM This script copies the libraries to default folders.
REM Change the paths in case the programs are installed in non-default folders.

REM Compact Framework libraries:

SET MFT2008=%programfiles%\Resco MobileForms Toolkit 2009\NET35
SET PRODUCT=AdvancedList

copy .\Full\CF\Resco.%PRODUCT%.CF3.dll “%MFT2008%\CF\Resco.%PRODUCT%.CF3.dll”
copy .\Full\CF\Resco.%PRODUCT%.CF3.XML “%MFT2008%\CF\Resco.%PRODUCT%.CF3.XML”

copy .\Full\CF\DesignerMetadata\Resco.%PRODUCT%.Design.dll “%MFT2008%\CF\DesignerMetadata\Resco.%PRODUCT%.Design.dll”
copy .\Full\CF\DesignerMetadata\Resco.%PRODUCT%.CF3.PocketPC.asmmeta.dll “%MFT2008%\CF\DesignerMetadata\Resco.%PRODUCT%.CF3.PocketPC.asmmeta.dll”
copy .\Full\CF\DesignerMetadata\Resco.%PRODUCT%.CF3.WindowsCE.asmmeta.dll “%MFT2008%\CF\DesignerMetadata\Resco.%PRODUCT%.CF3.WindowsCE.asmmeta.dll”
copy .\Full\CF\DesignerMetadata\Resco.%PRODUCT%.CF3.Smartphone.asmmeta.dll “%MFT2008%\CF\DesignerMetadata\Resco.%PRODUCT%.CF3.Smartphone.asmmeta.dll”

copy .\Full\Desktop\Resco.%PRODUCT%.dll “%MFT2008%\Desktop\Resco.%PRODUCT%.dll”
copy .\Full\Desktop\Resco.%PRODUCT%.XML “%MFT2008%\Desktop\Resco.%PRODUCT%.XML”

copy .\Full\Desktop\%PRODUCT%Designer.exe “%MFT2008%\Desktop\%PRODUCT%Designer.exe”

rem “%ProgramFiles%\Microsoft SDKs\Windows\v6.0A\bin\gacutil.exe” /i “%MFT2008%\CF\DesignerMetadata\Resco.%PRODUCT%.Design.dll” /f

PAUSE


Fácil, pensei eu, basta correr isto como administrador e resolve-se o problema.


Ao fazê-lo, cada uma daquelas mensagens ‘Access Denied’ transformou-se em ‘File not found’… humpf… verifiquei o ficheiro .bat, fiz umas modificações, mas o problema persistia e não havia razão para os ficheiros não serem encontrados. Aí lembrei-me de acrescentar um simples ‘dir’ ao início do ficheiro batch e ao corrê-lo normalmente devolveu-me o conteúdo da pasta de onde eu chamava o ficheiro, mas ao corrê-lo como administrador, o dir devolveu-me o conteúdo da pasa C:\Windows\System32!!! Aqc… (tradução livre do inglês wtf…)


Experimentei o RunAs entre outras coisas, sempre sem sucesso, até que me lembrei que não devia ser a primeira alma a encontrar este problema e uma pesquisa na internet devolveu-me esta preciosa página…


http://www.vistax64.com/vista-security/79008-elevate-without-losing-working-directory.html


…após o que alterando o ficheiro inicial para este novo (alterações destacadas), passei a poder executá-lo como administrador, mantendo como ‘working folder’ a pasta de onde o ficheiro batch é chamado:


@ECHO OFF

REM This script copies the libraries to default folders.
REM Change the paths in case the programs are installed in non-default folders.

REM Compact Framework libraries:

SET MFT2008=%programfiles%\Resco MobileForms Toolkit 2009\NET35
SET PRODUCT=AdvancedList

setlocal enableextensions
cd /d “%~dp0″

copy .\Full\CF\Resco.%PRODUCT%.CF3.dll “%MFT2008%\CF\Resco.%PRODUCT%.CF3.dll”
copy .\Full\CF\Resco.%PRODUCT%.CF3.XML “%MFT2008%\CF\Resco.%PRODUCT%.CF3.XML”

copy .\Full\CF\DesignerMetadata\Resco.%PRODUCT%.Design.dll “%MFT2008%\CF\DesignerMetadata\Resco.%PRODUCT%.Design.dll”
copy .\Full\CF\DesignerMetadata\Resco.%PRODUCT%.CF3.PocketPC.asmmeta.dll “%MFT2008%\CF\DesignerMetadata\Resco.%PRODUCT%.CF3.PocketPC.asmmeta.dll”
copy .\Full\CF\DesignerMetadata\Resco.%PRODUCT%.CF3.WindowsCE.asmmeta.dll “%MFT2008%\CF\DesignerMetadata\Resco.%PRODUCT%.CF3.WindowsCE.asmmeta.dll”
copy .\Full\CF\DesignerMetadata\Resco.%PRODUCT%.CF3.Smartphone.asmmeta.dll “%MFT2008%\CF\DesignerMetadata\Resco.%PRODUCT%.CF3.Smartphone.asmmeta.dll”

copy .\Full\Desktop\Resco.%PRODUCT%.dll “%MFT2008%\Desktop\Resco.%PRODUCT%.dll”
copy .\Full\Desktop\Resco.%PRODUCT%.XML “%MFT2008%\Desktop\Resco.%PRODUCT%.XML”

copy .\Full\Desktop\%PRODUCT%Designer.exe “%MFT2008%\Desktop\%PRODUCT%Designer.exe”

rem “%ProgramFiles%\Microsoft SDKs\Windows\v6.0A\bin\gacutil.exe” /i “%MFT2008%\CF\DesignerMetadata\Resco.%PRODUCT%.Design.dll” /f

PAUSE


Mais um stress resolvido… NEXT!!!



[Corrigdo em 8/Abril conforme comentários abaixo]