Você está na página 1de 264

Padrões de implantação para o Microsoft .

NET Compact Framework


por Dan Fox e Jon Box
http://atomic.quilogy.com/
Data:
Novembro de 2004
Aplica-se a:

• Microsoft® .NET Compact Framework

• Microsoft® Visual Studio® .NET 2003


Baixe o Smart Application Updater.
Resumo: no fim do dia, os desenvolvedores de software bem-sucedidos precisam finalizar seus produtos. O exemplar
mais elegante de software, escrito com as ferramentas mais poderosas, não é capaz de reduzir a papelada, melhorar o
acesso às informações ou aumentar a precisão dos dados se os usuários a que se destina não puderem colocar suas
mãos nele. Isso se aplica aos desenvolvedores de software comercial assim como aos desenvolvedores corporativos que
trabalham tanto no ambiente de desktops quanto de dispositivos inteligentes. Neste white paper, estabeleceremos os
padrões que podem ser usados para implantar os aplicativos escritos com o Microsoft .NET Compact Framework. Este
documento é um conjunto completo sobre o tópico discutido no capítulo 10 de nosso livro Building Solutions with the
Microsoft .NET Compact Framework.
Embora o título desde white paper tenha como foco a idéia de implantação, este espaço realmente engloba vários
tópicos relacionados que podem ser divididos em três seções: Empacotamento (as etapas necessárias para se criar um
binário distribuível), Implantação (como o binário distribuível chega até o dispositivo inteligente e é instalado) e
Manutenção (como o aplicativo é atualizado à medida que é alterado). Neste white paper, discutiremos todos os três
aspectos e tentaremos fornecer uma visão geral e links para outros recursos que podem ser usados para empacotar,
implantar e manter seu aplicativo do .NET Compact Framework. Este artigo também contém links para páginas em
inglês. (22 páginas impressas)
Nesta página
Empacotamento
A primeira barreira para fazer com que um aplicativo de dispositivo inteligente chegue nas mãos de seus usuários é
empacotá-lo. Isso inclui a definição da pasta de saída e ações de criação nos arquivos dentro de seu projeto, a alteração
para o modo de liberação, a compreensão de como o GAC (cache de conjunto de módulos global) funciona no .NET
Compact Framework e finalmente o empacotamento do aplicativo em um arquivo .cab para uma implantação posterior.
Definindo a pasta de saída
A primeira etapa no empacotamento de seu aplicativo é definir a Pasta do Arquivo de Saída dentro da janela
Propriedades de seu projeto. Por padrão, isso será definido no diretório \Arquivos de Programas\nomedoprojeto do
dispositivo e será o caminho para o qual o aplicativo será implantado quando o arquivo .cab for executado.
Definindo ações de criação
Dentro de um projeto de dispositivo inteligente, cada arquivo pode ser marcado com uma Ação de criação definida na
janela Propriedades das formas a seguir.
Marcar um arquivo como Compile, que é o padrão para todos os formulários e arquivos de código, fará com que os

arquivos sejam compilados no conjunto de módulos (assembly) resultante.
Marcar um arquivo como Content permite que o arquivo seja empacotado no arquivo .cab e implantado junto com o

projeto, e é útil para a implantação de arquivos de configuração XML e bancos de dados SQL Server CE.
Marcar um arquivo como None simplesmente o ignora e é útil para a inclusão de documentação no projeto, como

diagramas do Visio que não devem ser implantados.
Marcar um arquivo como um Embedded Resource inclui o arquivo no conjunto de módulos (assembly) executável

como um recurso. Isso permite que o código escrito por você extraia o recurso com o uso de programação. Isso é
eficiente para o empacotamento de arquivos de script e imagens que podem ser usados posteriormente dentro do
aplicativo. Por exemplo, o seguinte trecho de código carrega uma imagem em um objeto System.Drawing.Bitmap e a
associa à propriedade Image de um controle PictureBox.
Dim a As [Assembly] = [Assembly].GetExecutingAssembly()

Dim b As Bitmap = New Bitmap(a.GetManifestResourceStream("logo.gif"))

pbLogo.Image = b

Alterando para o modo de liberação


Antes de realmente compilar seu aplicativo de dispositivo inteligente e criar arquivos .cab para implantação, é
importante não se esquecer de alterar o modo de criação de seu projeto de Debug para Release. Fazer isso reduzirá o
tamanho do executável no dispositivo (o que é importante em dispositivos inteligentes com restrições de
armazenamento) e também tornará a execução mais rápida. O modo de criação pode ser alterado tanto a partir da barra
de ferramentas Standard (Padrão) do Visual Studio .NET quanto da caixa de diálogo Configuration Manager (Gerenciador
de configurações), mostrada na Figura 1, que você pode acessar clicando em Configuration Manager, clicando com o
botão direito do mouse na solução na janela do Solution Explorer ou a partir do menu Build (Criar).

Figura 1. Definindo o modo de criação.


Agora, seu projeto pode ser criado usando o menu Build.
Usando o GAC
Do mesmo modo que no .NET Framework para desktops, o .NET Compact Framework dá suporte ao compartilhamento
de conjuntos de módulos (assemblies), colocando-os no cache do GAC. Embora na estrutura de desktop isso seja usado
principalmente para que os aplicativos permitidos usem de modo transparente versões atualizadas dos conjuntos de
módulos (assemblies), o .NET Compact Framework não oferece suporte à diretiva de versão configurável; portanto, seu
executável sempre se ligará à versão do conjunto de módulos com a qual foi compilado. Ao contrário, no .NET Compact
Framework o GAC é principalmente usado para permitir a implantação de uma cópia do conjunto de módulos (assembly)
no dispositivo para economia de espaço.
Em um dispositivo inteligente, o GAC está localizado no diretório \Windows e consiste simplesmente nos conjuntos de
módulos (assemblies) renomeados com o prefixo "GAC" e acrescidos do número da versão e da cultura, como
GAC_System.Data.Common_v1_0_5000_0_cneutral_1.dll. Os conjuntos de módulos (assemblies) são colocados no GAC
pelo utilitário Cgacutil.exe invocado pelo mecanismo de tempo de execução a cada vez que um aplicativo do .NET
Compact Framework é executado. Esse utilitário procura por arquivos de texto codificados como ANSI ou UTF-8 com
uma extensão .gac no diretório \Windows, que contém listas de conjuntos de módulos (assemblies) compartilhados a
serem movidos para o GAC.
Um arquivo .gac típico deve ser parecido com:
\Program Files\MyApp\AssemblyA.dll

\Program Files\MyApp\AssemblyB.dll

Se o arquivo .gac for excluído do diretório \Windows ou atualizado, as alterações apropriadas (inserções e exclusões)
serão feitas no GAC na próxima vez que um aplicativo do .NET Compact Framework for executado.
Os conjuntos de módulos (assemblies) podem ser diretamente instalados no GAC e removidos dele, iniciando
programaticamente o arquivo Cgacutil.exe de dentro de um aplicativo. Por exemplo, seu aplicativo poderia usar uma
classe que encapsula a função não gerenciada CreateProcess para invocar Cgacutil e usar as opções –i e –u para instalar
e desinstalar conjuntos de módulos (assemblies) no GAC, como mostrado aqui na Listagem 1, em sua forma
simplificada.
Public Structure PROCESS_INFORMATION

Public hProcess As UInt32

Public hThread As UInt32


Public dwProcessId As UInt32

Public dwThreadId As UInt32

End Structure

Public NotInheritable Class WindowsCE

Private Sub New()

End Sub

<DllImport("coredll.dll", EntryPoint:="CreateProcess", _

SetLastError:=True)> _

Private Shared Function CreateProcess( _

ByVal lpszImageName As String, _

ByVal lpszCmdLine As String, _

ByVal lpsaProcess As IntPtr, _

ByVal lpsaThread As IntPtr, _

ByVal fInheritHandles As Integer, _

ByVal fdwCreate As UInt32, _

ByVal lpvEnvironment As IntPtr, _

ByVal lpszCurDir As IntPtr, _

ByVal lpsiStartInfo As IntPtr, _

ByRef lppiProcInfo As PROCESS_INFORMATION) As Integer

End Function

Public Shared Function StartProcess(ByVal imageName As String, _

ByVal cmdLine As String) As Integer

Dim procInfo As New PROCESS_INFORMATION

CreateProcess(imageName, cmdLine, _

IntPtr.Zero, IntPtr.Zero, 0, Convert.ToUInt32(0), _

IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, procInfo)

Return Convert.ToInt32(procInfo.dwProcessId)

End Function

Public Shared Sub GACInstall(ByVal imageName As String)

WindowsCE.StartProcess("cgacutil.exe", "-i " & imageName)

End Sub

Public Shared Sub GACUninstall(ByVal imageName As String)

WindowsCE.StartProcess("cgacutil.exe", "-u " & imageName)

End Sub
End Class

Listagem 1. Modificando o GAC programaticamente.


Seu aplicativo cliente pode então adicionar um conjunto de módulos (assembly) ao GAC da seguinte forma:
WindowsCE.GACUInstall("\Program Files\My App\AssemblyA.dll")

Exatamente como no desktop, os conjuntos de módulos do Framework colocados no GAC devem ser assinalados com
nomes fortes usando os atributos AssemblyKeyFile ou AssemblyKeyName. O .NET Compact Framework não oferece
suporte à sinalização posterior usando o atributo AssemblyDelaySign.
Criando arquivos cab
A maneira mais simples de empacotar seu aplicativo de dispositivo inteligente antes de implantá-lo no dispositivo é criar
um arquivo .cab. Felizmente, o Visual Studio .NET facilita essa operação fornecendo um menu Build Cab File (Criar
arquivo cab) que pode ser invocado clicando com o botão direito do mouse no projeto. Um diretório cab será então
criado sob o diretório do modo de criação atual, que contém uma série de arquivos .cab que usam a convenção de
nomenclatura aplicativo_plataforma_cpu.CAB. Para exibir o diretório .cab, selecione a opção Show All Files (Mostrar
todos os arquivos), como mostrado na Figura 2.

Figura 2. Arquivos cab criados para implantação


Seguindo a convenção de nomenclatura, plataforma se refere a Windows CE (WCE) ou Pocket PC (PPC), enquanto CPU
identifica para que tipo de processador, ao qual o .NET Compact Framework oferece suporte, o arquivo .cab é destinado.
CPUs com suporte incluem ARM, ARMV4, MIPS, SH3 e X86.
Para personalizar o processo de instalação, os arquivos necessários para personalizar e recriar os arquivos .cab são
colocados no diretório obj\mododecriação. Nesse diretório, estão os arquivos BuildCab.bat, Dependencies_plataforma.txt
e Nomedoprojeto_plataforma.inf, junto com um arquivo de configuração para cada tipo de processador. Eles são usados
da seguinte forma:
O arquivo em lotes pode ser usado para iniciar a recriação dos arquivos .cab. O arquivo BuildCab.bat chama o

utilitário CabWiz.exe, que pode ser também invocado de maneira autônoma para personalizar ainda mais o processo
de criação. Para examinar a sintaxe para invocar o CabWizFor, consulte o tópico Cab wizard syntax (em inglês), no
SDK do Windows CE .NET 4.2.
O arquivo de dependências contém a lista de arquivos .cab dos quais este arquivo é dependente e sempre contém

uma referência ao arquivo .cab do .NET Compact Framework para cada tipo de processador. Durante a instalação,
essas dependências são verificadas por meio dos arquivos vsd_config.txt.plataforma, para garantir que a versão
correta do .NET Compact Framework foi instalada no dispositivo. É importante observar que o arquivo .cab do .NET
Compact Framework não está incluído no arquivo .cab criado para o projeto. Isso é verdade também para o
arquivo .cab do SQL Server CE específico da plataforma necessário para instalar o SQL Server CE no dispositivo.
O arquivo .inf contém as configurações de instalação a serem usadas quando o arquivo .cab é executado. Para

personalizar a instalação no dispositivo, o arquivo .inf pode ser modificado. Um exemplo simples de se fazer isso é
adicionar um atalho ao menu Iniciar em um Pocket PC. Isso pode ser realizado modificando as seguintes seções do
arquivo .inf, como a seguir:
[DefaultInstall]
CEShortcuts=Shortcuts

[Shortcuts]

My Application,0,myapp.exe,%CE17%

Aqui, a seção CEShorcuts é redirecionada à seção Shortcuts do arquivo e um atalho (na verdade, um arquivo com um
formato particular no Windows CE) é criado nesse local por meio da especificação do texto do atalho (vazio para
identificar o atalho como um arquivo), do arquivo para o qual o atalho está sendo criado e da pasta na qual o atalho
deve ser colocado. O identificador %CE17% especifica o diretório \Windows\StartMenu em um dispositivo Windows CE.
Para obter mais informações sobre as várias seções do arquivo .inf, consulte o tópico Creating an .INF (em inglês) no
SDK do Windows CE .NET 4.2
Outros exemplos de personalização do arquivo .inf podem incluir a adição de arquivos (por exemplo, bancos de dados
SQL Server CE e arquivos .gac) a serem distribuídos ao dispositivo por meio do arquivo .cab, ao se modificar a seção
SourceDiskFiles.
Depois que os arquivos .cab forem criados para seu projeto, eles poderão ser implantados no dispositivo inteligente. As
técnicas para se fazer isso serão discutidas na próxima seção. A simples execução do arquivo .cab no dispositivo iniciará
a instalação usando o executável WCELoad do Windows CE para efetuar a descompactação do arquivo .cab e a instalação
de seu conteúdo, assim como a exclusão automática do arquivo .cab quando a instalação for concluída. Além disso, esse
processo armazena informações no dispositivo de modo que o aplicativo possa ser desinstalado ao tocar em
Configurações, Remover Programas.
Início da página

Implantação
Na verdade, a instalação de seu aplicativo em um dispositivo inteligente pode ser feita de diversas maneiras, sendo que
uma delas nem mesmo precisa que você tenha criado qualquer arquivo .cab. Nesta seção, daremos uma olhada nas
técnicas disponíveis para implantar tanto o .NET Compact Framework quanto o seu aplicativo e discutiremos como e
quando cada uma delas deve ser empregada.
Implantando o .NET Compact Framework e o SQL Server CE
Antes que seu aplicativo possa ser executado, o .NET Compact Framework deve ser instalado no dispositivo. E, se seu
aplicativo necessitar do SQL Server CE, o arquivo .cab apropriado deverá ser também instalado. Embora ambos sejam
instalados automaticamente ao se implantar o aplicativo a partir do Visual Studio .NET usando o menu Deploy ou ao se
depurar o dispositivo, será necessário um mecanismo diferente para lidar com isso na produção.
Conforme mencionado anteriormente, os arquivos .cab criados para um projeto não incluem os arquivos .cab para
o .NET Compact Framework ou para o SQL Server CE. Embora o Pocket PC 2003 e dispositivos posteriores normalmente
incluam o .NET Compact Framework na memória ROM, para os dispositivos que não fizerem isso será necessário
implantar os arquivos .cab específicos da plataforma. Uma maneira simples de se fazer isso para o .NET Compact
Framework é baixar e executar o .NET Compact Framework 1.0 SP2 Redistributable (NETCFSetup.msi). A execução do
arquivo .msi quando o dispositivo estiver na base fará com que o .NET Compact Framework seja instalado. Se o
dispositivo não estiver na base quando o arquivo .msi for executado, o arquivo NCFSetup.exe poderá ser executado a
partir da pasta de instalação, estando o dispositivo conectado. Se o dispositivo não puder ser colocado na base, por
exemplo, por razões logísticas, os arquivos .cab específicos da plataforma poderão ser baixados para o dispositivo e
executados usando uma das técnicas discutidas no restante desta seção.
Uma segunda técnica mais complexa para implantar e até mesmo atualizar o .NET Compact Framework e o SQL Server
CE é trabalhar com o código de exemplo fornecido por Stan Adermann em seu artigo Creating an MSI Package that
Detects and Updates the .NET Compact Framework (em inglês). O código de exemplo mostra como pode ser detectada a
presença do .NET Compact Framework no dispositivo usando as APIs remotas (RAPI) CeRapiInitEx e CeFindAllFiles a
partir do desktop e como consultar o Registro no dispositivo para determinar o número da versão. É possível então
detectar o tipo de dispositivo chamando CeGetVersionEx e, em seguida, copiar o arquivo .cab apropriado para o
dispositivo e executá-lo usando WCELoad.
XCopy
A maneira mais simples de se ter um aplicativo do .NET Compact Framework em um dispositivo inteligente não requer
nem mesmo arquivos .cab, conforme mencionado na seção anterior. Exatamente como com o Framework do desktop,
um executável do aplicativo .NET Compact Framework e seus conjuntos de módulos (assemblies) dependentes (e o
próprio arquivo .cab do .NET Compact Framework) podem ser simplesmente copiados para uma pasta no dispositivo. Em
especial, esse é o caso se o aplicativo não depender de conjuntos de módulos (assemblies) no GAC, o que normalmente
acarretaria na inclusão de um arquivo .gac implantado no diretório \Windows. No entanto, mesmo um aplicativo
implantado por XCopy pode registrar seus próprios conjuntos de módulos (assemblies) na inicialização com o GAC,
usando a técnica mostrada acima.
Há duas maneiras principais de se realizar a implantação por XCopy.
Copiar seu aplicativo para o dispositivo usando o utilitário CECopy disponível por meio do Windows Mobile Developer

Power Toys. Este é um utilitário de linha de comando que depende da API remota (RAPI) para se conectar e copiar
arquivos.
Usar os recursos de sincronização de pastas do ActiveSync configurados, como mostrado na Figura 3. Com o uso

deste recurso, um aplicativo do .NET Compact Framework pode ser copiado para um diretório em um PC e, em
seguida, automaticamente copiado para o dispositivo quando uma conexão com o ActiveSync for feita.

Figura 3. Configurando a sincronização de arquivos no ActiveSync 3.7


Essa técnica pode ser útil durante o desenvolvimento e o teste iniciais em vários dispositivos ou quando o aplicativo for
muito simples e não necessitar da criação de atalhos ou outros eventos de instalação.
Site
Uma segunda maneira de implantar um aplicativo do .NET Compact Framework é colocar os arquivos .cab apropriados
em um site da intranet. Dessa maneira, um usuário precisa apenas navegar até o site usando o Pocket IE, como
mostrado na Figura 4, e clicar no hiperlink. Neste exemplo, os arquivos .cab destinados aos dispositivos que têm suporte
de uma organização específica estão listados. Certamente, esta técnica funciona melhor dentro do firewall, quando o
dispositivo inclui conectividade WLAN (rede local sem fio), e fora, quando ele dá suporte à conectividade WAN, como a
TDMA, CDMA, GSDM ou GPRS, como é o caso dos dispositivos Pocket PC Phone Edition.
Figura 4. Implantando a partir de um site
Para aumentar a segurança, o site pode ser configurado para exigir autenticação Básica e usar SSL (Secure Sockets
Layer) para proteger os arquivos .cab e o canal de comunicação, os quais têm suporte do Pocket IE no dispositivo.
Essa técnica é relativamente simples e seria mais útil em cenários de intranet com usuários avançados, experientes o
bastante para navegar até o site. O principal benefício dessa técnica é que ela libera o usuário de ter de colocar o
dispositivo na base em um PC. Para tornar esse processo mais simples, se os dispositivos forem usados para email por
meio do Pocket Outlook, por exemplo, os hiperlinks poderiam ser enviados em um email para que o usuário precise
apenas tocar no hiperlink para instalar o aplicativo.
Compartilhamento de arquivos
Uma terceira técnica, que é quase idêntica a usar um site, é postar os arquivos .cab em um compartilhamento em uma
rede local. Obviamente, essa técnica seria usada apenas dentro do firewall. O compartilhamento também pode ser
protegido, o que exibirá um aviso ao usuário quando o compartilhamento for aberto a partir do Gerenciador de Arquivos
no dispositivo inteligente, conforme mostrado na Figura 5.

Figura 5. Abrindo um compartilhamento de arquivos no Pocket PC.


Essa técnica é eficiente quando o dispositivo tem conectividade WLAN (rede local sem fio), pois, uma vez mais, ela libera
o usuário de ter de colocar o dispositivo na base, mas também pode ser usada quando o dispositivo estiver na base
usando o ActiveSync.
Aplicativos empacotados
Para empresas maiores que têm de gerenciar centenas de dispositivos, muitas vezes é mais vantajoso investir em uma
solução empacotada. Por exemplo, usando o Afaria da XcelleNet, um site central pode ser usado para monitorar e
gerenciar uma ampla variedade de dispositivos que incluem Pocket PCs, além de instalar e manter automaticamente
aplicativos nesses dispositivos.
Em cenários onde o dispositivo é usado dentro de uma WLAN (rede local sem fio) de uma maneira que leva o local em
consideração, o APS (Appear Provisioning Server) da Appear Network pode ser usado. Esse produto permite que os
administradores configurem "Click and Run Zones" para que, quando os dispositivos entrarem em uma zona específica, o
software seja instalado automaticamente e executado.
Além disso, a Microsoft está adicionando suporte para o gerenciamento de dispositivos móveis que executam o software
Windows CE 4.2 ou o Windows Mobile 2003 para o Pocket PC em um futuro Systems Management Server (SMS) 2003
Device Management Feature Pack (atualmente em beta).
Para obter mais informações sobre o gerenciamento de dispositivos móveis e links para outros fornecedores, consulte o
artigo Pocket PC Systems Management (em inglês).
Placa de armazenamento
Um dos cenários comuns é aquele em que seu aplicativo pode precisar ser implantado junto com um banco de dados
SQL Server CE muito grande ou outros extensos arquivos de dados. Como resultado, poderá ser muito demorado e
trabalhoso implantar tal aplicativo através de um site ou mesmo de uma conexão com o dispositivo na base.
Por essas razões, o aplicativo pode ser implantado em uma placa de armazenamento de memória, como uma placa
Compact Flash. Quando a placa for inserida no dispositivo, o usuário poderá então procurar pelo arquivo .cab e executá-
lo para instalar o aplicativo.
No entanto, em vez de o usuário ter que executar o arquivo .cab, após a placa de armazenamento ter sido inserida no
dispositivo, os dispositivos Pocket PC incluem um recurso de execução automática que pode ser utilizado.
Com esse recurso, quando uma placa de armazenamento é inserida no dispositivo, o Pocket PC procura por um
executável chamado Autorun.exe em um mapeamento de pasta para o tipo de processador do dispositivo (recuperado da
função não gerenciada GetSystemInfo). Por exemplo, se o tipo de processador for ARM, então ele procurará o arquivo \
Placa de armazenamento\2577\Autorun.exe na placa de armazenamento. Quando encontrado, o executável será copiado
para a pasta \Windows e executado com um parâmetro de instalação. Do mesmo modo, quando a placa é removida do
dispositivo, o mesmo executável é iniciado com um parâmetro de desinstalação.
Embora esses pontos estejam além do escopo deste white paper, o executável Autorun precisa executar as seguintes
etapas.
Determinar em que modo será executado. Como o parâmetro de linha de comando de instalação ou desinstalação é

passado ao aplicativo, o aplicativo Autorun deve primeiramente determinar em qual dos dois modos executar. Se o
parâmetro de desinstalação for detectado, o aplicativo Autorun deverá simplesmente ser encerrado ou verificará se o
aplicativo já está sendo executado.
Verificar se o aplicativo já não está instalado no dispositivo. Isso pode ser feito procurando pelo diretório de instalação

do aplicativo.
Localizar o nome da placa de armazenamento. Como os nomes de placas de armazenamento podem diferir em

dispositivos localizados (nem sempre serão chamados "Placa de armazenamento") e como os dispositivos podem
oferecer suporte a mais de uma placa de armazenamento, técnicas como o uso da função de API FindFirstFile do
Windows CE podem ser usadas conforme documentado no artigo Pocket PC Programming Tips (em inglês).
Localizar o arquivo .cab específico do processador. Se for necessário haver suporte a vários tipos de processadores,

isso pode ser feito primeiramente detectando o processador no dispositivo e, em seguida, mapeando-o para o
diretório apropriado na placa de armazenamento. O GetSystemInfo do Windows CE retorna a arquitetura do
processador em sua estrutura SYSTEM_INFO. Certamente, executáveis personalizados para cada tipo de processador
poderiam ser criados e colocados na placa de armazenamento e, portanto, esta etapa poderia ser de fato ignorada.
Executar o arquivo .cab. Isso pode ser feito usando uma API do Windows CE, como ShellExecuteEx ou CreateProcess e

pode ser usado para executar tanto o arquivo .cab do .NET Compact Framework quanto o arquivo .cab do aplicativo,
pois o .NET Compact Framework talvez não tenha sido anteriormente instalado no dispositivo. Por essa razão, o
aplicativo Autorun.exe é normalmente escrito usando eMbedded Visual C.
Desktop
Talvez a técnica mais conhecida de se implantar aplicativos de dispositivos inteligentes seja usar o Gerenciador de
Aplicativos do ActiveSync, de modo que o aplicativo seja implantado automaticamente quando o dispositivo for colocado
na base e conectado a um computador desktop. Esse processo envolve passar ao Gerenciador de Aplicativos
(CeAppMgr.exe) o caminho para um arquivo .ini que especifica informações sobre o aplicativo a ser instalado no
dispositivo, como o mostrado aqui.
[CEAppManager]

Version = 1.0

Component = MyApp

[MyApp]

Description = My Application

Uninstall = MyApp

CabFiles = MyApp_PPC.ARM.cab, MyApp_PPC.SH3.cab, MyApp_PPC.ARMV4.cab

Uma das vantagens dessa técnica é que o Gerenciador de Aplicativos detecta o tipo de dispositivo e, portanto, copia e
instala o arquivo .cab correto no dispositivo. Entretanto, ela obviamente requer que o dispositivo seja trazido de volta
para um local central e conectado a um PC.
A melhor maneira de se invocar o Gerenciador de Aplicativos é criar um componente instalador personalizado que possa
ser incluído em um Projeto de Instalação dentro do Visual Studio .NET, como o que Ralph Arvesen discute em seu
excelente artigo Developing and Deploying Pocket PC Setup Applications (em inglês). O projeto de instalação pode, além
disso, invocar uma instalação de pré-criação para que o arquivo BuildCab.bat seja executado a fim de recriar os arquivos
.cab de seu projeto.
Embora bem documentado no artigo mencionado acima, o principal ponto dessa técnica é criar uma classe que herde de
System.Configuration.Install.Installer e que possa ser chamada pelo aplicativo de instalação para invocar o Gerenciador
de Aplicativos, como mostrado na Listagem 2.
<RunInstaller(True)> _

Public Class AppManagerInstaller : Inherits Installer

Private Sub AppManagerInstaller_AfterInstall(ByVal sender As Object, _

ByVal e As InstallEventArgs) _

Handles MyBase.AfterInstall

Dim ceApp As String = GetCeAppMgr()

If ceApp = String.Empty Then

Return

End If

Dim args As String = GetIniArgs()

' Start the application manager process

Process.Start(ceApp, args)

End Sub

Private Sub AppManagerInstaller_AfterUninstall( _

ByVal sender As Object, ByVal e As InstallEventArgs) _

Handles MyBase.AfterUninstall
Dim ceApp As String = GetCeAppMgr()

If ceApp = String.Empty Then

Return

End If

Dim args As String = GetIniArgs()

' Start the application manager process without parameters

Process.Start(ceApp, "")

End Sub

Private Function GetCeAppMgr() As String

' Get the key from the registry

Dim ceApp As String = KeyExists()

If ceApp = String.Empty Then

MessageBox.Show( _

"The Application Manager is not installed on this machine.", _

"Setup", MessageBoxButtons.OK, MessageBoxIcon.Error)

Return String.Empty

Else

Return ceApp

End If

End Function

Private Function GetIniArgs() As String

Return """" & _

Path.Combine([Assembly].GetExecutingAssembly().Location, _

"Setup.ini") & """"

End Function

Private Function KeyExists() As String

Dim key As RegistryKey = Registry.LocalMachine.OpenSubKey( _

"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\CEAPPMGR.EXE")

If key Is Nothing Then

Return String.Empty

Else
Return key.GetValue("", String.Empty).ToString

End If

End Function

End Class

Listagem 2. Um instalador personalizado para invocar o Gerenciador de Aplicativos do ActiveSync.


A classe na Listagem 2 é então compilada como um conjunto de módulos (assembly) separado da Biblioteca de Classes
e, em seguida, ela é adicionada como uma ação de Instalar e Desinstalar personalizada dentro de seu projeto de
instalação. Quando o evento AfterInstall é disparado, o código procura pela presença do Gerenciador de Aplicativos na
máquina por meio de consulta ao Registro. Se encontrado, ele constrói o argumento a ser passado para o Gerenciador
de Aplicativos, que inclui o caminho para o arquivo Setup.ini. Finalmente, ele inicia o Gerenciador de Aplicativos usando
Process.Start e o passando no argumento. Na desinstalação, o Gerenciador de Aplicativos é executado sem o argumento
para permitir que o usuário desinstale o aplicativo a partir do dispositivo.
Início da página

Manutenção
Uma coisa é implantar inicialmente um aplicativo de dispositivo inteligente, e outra bem diferente é mantê-lo atualizado
à medida que os problemas são resolvidos e suas necessidades comerciais mudam. Isso é especialmente difícil se os
dispositivos não são trazidos de volta regularmente para uma localidade central, onde eles podem ser colocados na base
e atualizados usando as técnicas de implantação de desktop discutidas na seção anterior. Para abordar esse cenário, é
possível criar um aplicativo de dispositivo inteligente de atualização automática, usando os mesmos conceitos
freqüentemente usados por desenvolvedores de Framework de desktop para implantar os chamados aplicativos de
"Cliente inteligente".
Criando aplicativos de auto-atualização
Uma abordagem para se criar aplicativos que possam atualizar a si mesmos sem o uso do ActiveSync foi documentada
por Alex Feinman em seu ótimo artigo Creating Self-Updating Applications With the .NET Compact Framework (em
inglês). Nesse artigo, um aplicativo Atualizador é implantado com seu aplicativo. Esse "miniaplicativo" lê um arquivo de
configuração e chama um serviço da Web para determinar se há atualizações disponíveis para o aplicativo. Se houver, o
arquivo .cab apropriado será baixado e executado no dispositivo.
A vantagem dessa abordagem é que ela baixa e instala uma versão inteiramente nova do aplicativo, sem ser necessário
colocar o dispositivo na base. Isso é feito com um único arquivo .cab, diminuindo o uso da largura de banda, e essa
abordagem dá suporte a várias plataformas. No entanto, a principal desvantagem é que esse método não permite a
interação de seu aplicativo com o processo de atualização.
Criando um Atualizador de aplicativo de dispositivo inteligente
Uma abordagem mais integrada é criar um componente que possa ser referenciado por seu aplicativo, de modo a
integrar o processo de atualizá-lo. Criamos um componente chamado SmartAppUpdater, que será descrito no restante
deste documento, e você pode se sentir à vontade para usar um ponto inicial para desenvolver seu próprio atualizador.
Antes de analisar a funcionalidade, no entanto, você pode ter uma idéia do que o componente faz dando uma olhada na
API pública mostrada na Tabela 1 e no diagrama de arquitetura na Figura 6.

Membro Descrição

FileDownloaded Evento disparado quando um arquivo pertencente à nova versão é atualizado

Updated Evento disparado quando a nova versão foi completamente baixada e aplicada

Updating Evento disparado quando uma nova versão do aplicativo é encontrada

AppName Propriedade que retorna o nome do aplicativo a ser atualizado

AppVersion Propriedade que retorna a versão atual do aplicativo

Downloader Propriedade que retorna o componente IDownloadable usado para baixar arquivos

DownloadLocation Propriedade que retorna o local do servidor de onde novas versões serão baixadas

KeepOld Propriedade que determina se a versão anterior do aplicativo deve ser retida

NewPath Propriedade que retorna o caminho da nova versão do aplicativo


Membro Descrição

RootPath Propriedade que retorna o caminho raiz do aplicativo

PostProcessors Propriedade que expõe uma coleção de objetos IPostProcessor usados para executar código
personalizado após o processo de atualização

BeginStartUpdate Método que executa o processo de atualização em um thread de segundo pla

LogMessage Método sobrecarregado usado para registrar mensagens em log

MoveFile Método que move um arquivo da pasta atual para a pasta nova quando uma atualização é feita

StartNewVersion Método que inicia a nova versão do aplicativo

StartUpdate Método que executa o processo de atualização sincronizadamente

Tabela 1. A interface do componente SmartAppUpdater.

Figura 6. Arquitetura do SmartAppUpdater


Para compreender como o componente SmartAppUpdater pode ser usado, quebraremos sua funcionalidade em várias
áreas principais, incluindo:

• Inicializar o processo de modo que a versão atual esteja sempre carregada

• Baixar arquivos a partir de um servidor que inclua um manifesto além de arquivos do aplicativo

• Detectar novas versões do aplicativo e baixar atualizações


Gerar notificações quando uma nova versão for encontrada, arquivos forem baixados e o aplicativo for

atualizado

• Executar o pós-processamento de modo que você possa introduzir seu próprio código no processo

• Iniciar a nova versão do aplicativo

• Limpar a versão antiga do aplicativo


Inicializando o processo
Para permitir que o usuário execute a versão mais atual do aplicativo, um processo de inicialização é necessário. Esse
pequeno aplicativo sem interface do usuário do .NET Compact Framework chamado SmartAppLoader é responsável por
ler o arquivo de configuração SmartAppLoader.xml mostrado abaixo e, em seguida, iniciar o aplicativo.
<?xml version="1.0" encoding="utf-8" ?>

<!-- Used by the SmartAppLoader to start the current application -->

<SmartAppLoader>

<AppPath>1.0.0.0</AppPath>

<AppImage>TestUpdater.exe</AppImage>

<AppName>Test Updater</AppName>

</SmartAppLoader>
Você perceberá que tudo o que precisa para iniciar é a pasta relacionada àquela na qual o aplicativo está sendo
executado, além do nome do executável. Em seguida, a classe SmartAppLoader mostrada na Listagem 3 é usada para
ler o arquivo de configuração e expor um método StartApp.
Namespace Atomic.CF.Deployment

Public Class SmartAppLoader

Private _appPath, _appImage, _appName, _curPath As String

Public Sub StartApp()

Atomic.CF.Utils.WindowsCE.StartProcess( _

_curPath & Path.DirectorySeparatorChar & _appPath & _

Path.DirectorySeparatorChar & _appImage, Nothing)

End Sub

Public Sub New()

_InitClass()

End Sub

Public ReadOnly Property AppName() As String

Get

Return _appName

End Get

End Property

Private Sub _InitClass()

Dim xnl As XmlNodeList

Dim xd As XmlDocument

' Get the current path

_curPath = Path.GetDirectoryName( _

[Assembly].GetExecutingAssembly().GetName().CodeBase)

xd = New XmlDocument

xd.Load(_curPath & "\SmartAppLoader.xml")

' Read the app name

xnl = xd.GetElementsByTagName("AppName")

_appName = xnl(0).FirstChild.Value

' Read the app path

xnl = xd.GetElementsByTagName("AppPath")
_appPath = xnl(0).FirstChild.Value

' Read the app image

xnl = xd.GetElementsByTagName("AppImage")

_appImage = xnl(0).FirstChild.Value

End Sub

End Class

End Namespace

Listagem 3. A classe SmartAppLoader.


O método StartApp se baseia na classe WindowsCE, mostrada na Listagem 1, para iniciar o processo. Como mostrado
abaixo, o ponto de entrada do aplicativo simplesmente cria uma instância do objeto SmartAppLoader e chama o método
StartApp antes de sair em modo silencioso. Se ocorrer um erro, uma caixa de mensagem será exibida. Para usar o
SmartAppLoader, qualquer atalho especificado no arquivo e criado no dispositivo deve apontar para o executável do
SmartAppLoader.
Public Sub Main()

Dim l As SmartAppLoader

Try

l = New SmartAppLoader

l.StartApp()

Catch ex As Exception

MsgBox("Could not start the application.", _

MsgBoxStyle.Critical, "Smart App Loader")

End Try

' The loader exits

End Sub

Baixando arquivos
Como o componente SmartAppLoader será usado para atualizar seu aplicativo sem a necessidade de colocar o
dispositivo na base, será necessário baixar arquivos de um servidor usando um dos diversos transportes possíveis, por
exemplo, a partir de um site, serviço da Web ou compartilhamento de arquivos. Para permitir que você se conecte em
sua própria classe para fazer o download, o conjunto de módulos (assembly) inclui a interface IDownloadable mostrada
aqui.
Public Interface IDownloadable

Sub GetServerFile(ByVal filePath As String, _

ByVal serverPath As String)

Property Credentials() As ICredentials

End Interface

Essa interface contém simplesmente um método GetServerFile, que será invocado pelo SmartAppUpdater para baixar um
arquivo e a propriedade Credentials usada para fornecer as credenciais para autenticação.
Como a recuperação de novas versões a partir de um site é a abordagem mais comum, o conjunto de módulos
(assembly) do SmartAppUpdater também inclui uma classe HttpDownloader que implementa a interface da Listagem 4.
Namespace Atomic.CF.Deployment

Public Class HttpDownloader

Implements IDownloadable
Private _creds As ICredentials

Public Sub New()

End Sub

Public Sub New(ByVal credentials As ICredentials)

_creds = credentials

End Sub

Public Sub GetServerFile(ByVal filePath As String, _

ByVal serverPath As String) _

Implements IDownloadable.GetServerFile

Dim hr As HttpWebRequest

Dim fs As FileStream

Dim s As Stream

Try

hr = CType(HttpWebRequest.Create(serverPath), _

HttpWebRequest)

If Not _creds Is Nothing Then

hr.Credentials = _creds

End If

' Delete file if it exists

If File.Exists(filePath) Then File.Delete(filePath)

' Get new file

fs = New FileStream(filePath, FileMode.Create)

s = hr.GetResponse.GetResponseStream

Dim buffer(4096) As Byte

Dim bytesRead As Integer = s.Read(buffer, _

0, buffer.Length)

While bytesRead > 0

fs.Write(buffer, 0, bytesRead)
bytesRead = s.Read(buffer, 0, buffer.Length)

End While

Catch e As Exception

Throw New ApplicationException( _

"Could not download file " & serverPath, e)

Finally

If Not s Is Nothing Then s.Close()

If Not fs Is Nothing Then fs.Close()

End Try

End Sub

Public Property Credentials() As ICredentials _

Implements IDownloadable.Credentials

Get

Return _creds

End Get

Set(ByVal Value As ICredentials)

_creds = Value

End Set

End Property

End Class

End Namespace

Listagem 4. A classe HttpDownloader.


Esta classe implementa o método GetServerFile e usa o objeto HttpWebRequest para baixar o arquivo usando a mesma
técnica mostrada por Jim Wilson em seu artigo Improving .NET Compact Framework HTTP Communications using
HttpWebRequest and Custom ASP.NET Providers (em inglês). Observe que o código primeiramente aplica o objeto
ICredentials, se houver, e exclui o arquivo existente, se presente, antes de baixar o novo arquivo.
O nome totalmente qualificado do componente IDownloadable que o SmartAppUpdater usará e, opcionalmente, o
conjunto de módulos (assembly) no qual ele está localizado são armazenados no arquivo SmartAppUpdater.xml. Além
disso, esse arquivo de configuração inclui o local do servidor a partir do qual serão baixadas as atualizações, um
sinalizador que determina se versões antigas do aplicativo devem ser salvas, o número da versão atual, o nome do
aplicativo e o caminho onde está o SmartAppLoader, como mostrado aqui.
<?xml version="1.0" encoding="utf-8" ?>

<!-- Configuration file used by the SmartAppUpdater component -->

<SmartAppUpdater>

<DownloadLocation>http://192.168.1.101/TestLoader</DownloadLocation>

<KeepOld>False</KeepOld>

<Downloader>Atomic.CF.Deployment.HttpDownloader</Downloader>

<AppVersion>1.0.0.0</AppVersion>

<AppName>Test Updater</AppName>

<RootPath>\Program Files\TestUpdater</RootPath>
</SmartAppUpdater>

Observação: Para dar suporte a várias plataformas, o elemento DownloadLocation em diferentes versões do aplicativo
seria configurado para apontar para sites específicos da plataforma.
Essa informação é lida de maneira direta por um método _InitClass privado (que pode ser inspecionado no código de
exemplo) chamado a partir do construtor do componente e colocado em campos privados (indicados por um sublinhado
usado como prefixo). A parte interessante desse código, no entanto, é a criação do componente IDownloadable,
mostrada na Listagem 5.
' Load the configuration file

_curPath = Path.GetDirectoryName( _

[Assembly].GetExecutingAssembly().GetName().CodeBase)

_xd = New XmlDocument

_xd.Load(_curPath & Path.DirectorySeparatorChar & "SmartAppUpdater.xml")

' Read the loader info and create the type

xnl = _xd.GetElementsByTagName("Downloader")

Dim downloader As String = xnl(0).FirstChild.Value.ToString

Dim split() As Char = {","c}

Dim d() As String = downloader.Split(split)

Dim t As Type

If d.Length = 2 Then ' Load from a different assembly

Dim a As [Assembly] = [Assembly].LoadFrom(_curPath &

Path.DirectorySeparatorChar & d(1))

t = a.GetType(d(0))

Else

t = [Assembly].GetExecutingAssembly.GetType(d(0))

End If

_loader = CType(Activator.CreateInstance(t), IDownloadable)

Listagem 5. Criação do componente IDownloadable.


Aqui, o arquivo de configuração é carregado e o elemento Downloader é recuperado do objeto XmlDocument. O nome do
tipo é extraído da seqüência de caracteres, junto com um nome de conjunto de módulos (assembly) opcional. Se o nome
do conjunto de módulos for encontrado, o conjunto será carregado usando o método LoadFrom (assumindo que o
conjunto de módulos esteja na pasta atual) e o tipo criado. Se um nome de conjunto de módulos (assembly) não for
encontrado, assume-se que o tipo poderá ser encontrado no conjunto de módulos em execução. De qualquer modo, é
criada uma instância do objeto usando o método CreateInstance da classe System.Activator e convertido de volta para
um objeto IDownloadable. A variável de referência _loader é, em seguida, usada pelo componente para baixar o
manifesto, assim como novas versões de arquivos.
Detectando e baixando novas versões
A primeira responsabilidade do componente SmartAppUpdater é detectar quando novas versões do aplicativo estão
disponíveis. Como com as abordagens para se fazer isso nos aplicativos de Framework de desktop, o SmartAppUpdater
procura por um arquivo de manifesto em um servidor. Por exemplo, a estrutura do arquivo de manifesto se parece com:
<?xml version="1.0" encoding="utf-8" ?>

<Application>
<Version>2.0.0.0</Version>

<Folder>2.0.0.0</Folder>

<Files>

<File>Atomic.CF.dll</File>

<File>NotificationOny.dll</File>

<File>SmartAppUpdater.dll</File>

<File>SmartAppUpdater.xml</File>

<File>TestUpdater.exe</File>

</Files>

</Application>

Esse arquivo especifica que a versão mais recente disponível no site é 2.0.0.0 e está localizada na pasta 2.0.0.0. Além
disso, o elemento Files contém os nomes dos arquivos a serem baixados.
Observação: Um aperfeiçoamento do componente SmartAppUpdater seria permitir que um único arquivo .cab, contendo
todos os novos arquivos, pudesse ser especificado e baixado. Em seguida, o componente IDownloadable executaria o
arquivo .cab para extrair os arquivos de um diretório específico. Isso economizaria largura de banda durante a execução
da atualização. Um segundo aperfeiçoamento seria adicionar um atributo aos arquivos que precisam ser instalados no
GAC. O SmartAppUpdater poderia, então, instalar os arquivos programaticamente conforme discutido anteriormente
neste documento.
Quando o método StartUpdate (ou BeginStartUpdate, que simplesmente executa o método StartUpdate em um novo
objeto System.Threading.Thread ) é chamado, o componente verifica primeiramente se há uma conexão de rede
presente usando o componente Network, discutido em nosso white paper Testing for and Responding to Network
Connections in the .NET Compact Framework (em inglês). Se uma conexão for encontrada, o método privado
CheckManifest mostrado na Listagem 6 será chamado.
Private Function CheckManifest() As XmlNodeList

Dim newVersion As String

Dim manifest As XmlDocument

Dim xnl As XmlNodeList

Try

' Try and download the manifest file if connected

_loader.GetServerFile(_curPath & Path.DirectorySeparatorChar _

& "manifest.xml", _downloadLocation & "/manifest.xml")

' Open the manifest

manifest = New XmlDocument

manifest.Load(_curPath & Path.DirectorySeparatorChar & _

"manifest.xml")

' Read the manifest

xnl = manifest.GetElementsByTagName("Version")

_newVersion = xnl(0).FirstChild.Value

' Check the versions


If String.Compare(_newVersion, _appVersion) > 0 Then

' Get the new folder

xnl = manifest.GetElementsByTagName("Folder")

_serverFolder = xnl(0).FirstChild.Value

Return manifest.GetElementsByTagName("File")

Else

Return Nothing

End If

Catch e As Exception

' Couldn't get manifest so just go quietly

Me.LogMessage(e)

Return Nothing

End Try

End Function

Listagem 6. Verificando o arquivo de manifesto no servidor.


O método mostrado na Listagem 6 usa a classe IDownloadable referenciada pelo _loader para baixar o manifesto e
carregá-lo em um objeto XmlDocument. Em seguida, ele extrai o número da versão e o compara com o número da
versão encontrado no arquivo de configuração SmartAppYpdater.xml. Embora as informações sobre a versão do conjunto
de módulos (assembly) de seu aplicativo possam ser obtidas interrogando a propriedade Version do objeto
AssemblyName recuperado do objeto System.Reflection.Assembly, escolhemos colocar o número da versão nos arquivos
de configuração por facilidade de uso e flexibilidade.
Se o método CheckManifest achar que a versão do aplicativo no servidor é superior à versão no dispositivo inteligente,
ele retornará o XmlNodeList que contém os arquivos a serem baixados. Nesse ponto, o SmartAppUpdater sabe que uma
nova versão do aplicativo está disponível; portanto, o método StartUpdate continua e deve criar uma nova pasta no
dispositivo e começar a baixar os arquivos, conforme mostrado na Listagem 7.
Try

' Create a new directory if needed

_newPath = _rootPath & Path.DirectorySeparatorChar & _newVersion

If Not Directory.Exists(_newPath) Then

Directory.CreateDirectory(_newPath)

End If

' Walk through the files and download

Dim node As XmlNode

For Each node In xnl

Dim newfile As String = _newPath & Path.DirectorySeparatorChar _

& node.FirstChild.Value

_loader.GetServerFile(newfile, _downloadLocation & "/" _

& _serverFolder & "/" & node.FirstChild.Value)

Next

Catch e As Exception

Me.LogMessage(e)
' Clean up

If Directory.Exists(_newPath) Then Directory.Delete(_newPath)

If File.Exists(_curPath & Path.DirectorySeparatorChar & _

"manifest.xml") Then File.Delete(_curPath & _

Path.DirectorySeparatorChar & "manifest.xml")

Throw New ApplicationException("Could not update application", e)

End Try

Listagem 7. Criando uma nova pasta e baixando arquivos.


Na Listagem 7, você observará que a nova pasta é criada primeiro e, em seguida, cada arquivo é baixado chamando o
método GetServerFile do componente IDownloadable. Se ocorrer algum erro, a exceção é registrada em um arquivo de
texto e a nova pasta e o arquivo de manifesto são excluídos.
Após uma nova versão ter sido baixada com êxito, o método privado UpdateConfigs será chamado, o qual poderá se
analisado no código de exemplo, para atualizar o arquivo SmartAppLoader.xml. Ele atualiza o elemento AppPath a fim de
apontar para a nova pasta do aplicativo e, em seguida, insere um elemento OldAppPath refletindo o diretório do
aplicativo atual, se a propriedade KeepOld estiver definida como Falsa.
Gerando notificações
Em todo o processo de detectar uma atualização e baixar arquivos, o componente SmartAppUpdater pode notificar seu
aplicativo por meio da geração de eventos. Ele dá suporte ao evento Updating, disparado quando uma nova versão é
detectada por meio do arquivo de manifesto, ao evento FileDownloaded, disparado após o método GetServerFile ter sido
chamado para baixar um arquivo e ao evento Updated, disparado quando uma nova atualização é aplicada com êxito.
Cada um desses eventos segue o Padrão de Eventos documentado no SDK do .NET Framework, passando o objeto que
iniciou o evento, nesse caso SmartAppUpdater, assim como um objeto UpdaterEventArgs herdado de System.EventArgs,
conforme mostrado aqui.
Public Class UpdaterEventArgs : Inherits EventArgs

Public ServerPath As String

Public AppPath As String

Public AppName As String

Public NewVersion As String

Public CurrentFile As String

End Class

Os argumentos de evento personalizados simplesmente expõem campos públicos que incluem o caminho a partir do qual
a atualização será baixada, o caminho do aplicativo da nova versão, o nome do aplicativo, o número da nova versão e,
no caso do evento FileDownloaded, o arquivo que acabou de ser baixado. Por exemplo, o método StartUpdate cria uma
instância de um objeto UpdaterEventArgs e, em seguida, gera o evento Updating após o manifesto ser verificado e uma
nova versão ser encontrada.
' Setup the event args

args = New UpdaterEventArgs

args.AppPath = _newPath

args.NewVersion = _newVersion

args.ServerPath = Me.DownloadLocation

args.AppName = Me.AppName

' Raise the updating event

RaiseEvent Updating(Me, args)


De dentro de seu aplicativo, é possível adicionar manipuladores para os eventos e fornecer notificação aos usuários ou
realizar alguma ação específica. Por exemplo, na Listagem 8, é criada uma instância de SmartAppUpdater no método
Sync chamado de maneira aparente a partir de um item de menu ou de um botão, e o SmartAppUpdater associa um
manipulador de eventos ao evento Updating. O manipulador de eventos cria um objeto Notification e adiciona uma
notificação de balão que fornece uma dica visual ao usuário de que uma nova versão do aplicativo foi encontrada e está
sendo baixada no momento, conforme mostrado na Figura 7.
Observação: a classe Notification usa a API de notificação do Pocket PC e foi compilada em um conjunto de módulos
(assembly) separado e mencionada pelo aplicativo cliente. Você encontrará este conjunto de módulos no código de
exemplo desse artigo.
Private _updater As SmartAppUpdater

Private Sub Sync()

_updater = New SmartAppUpdater()

AddHandler _updater.Updating, AddressOf UpdatingApp

' Do your synchronization first

' Now try to update the application asynchronously

_updater.BeginStartUpdate()

End Sub

Private Sub UpdatingApp(ByVal sender As Object, _

ByVal e As UpdaterEventArgs)

' Show a notification when the application is being updated

Dim n As New Notification

n.Add("Updates found for " & e.AppName & " at " _

& e.ServerPath, "Smart App Updater", UInt32.Parse(3))

End Sub

Listagem 8. Manipulando eventos do SmartAppUpdater.


Figura 7. Notificando o usuário
Executando o pós-processamento
Um dos requisitos mais interessantes do componente SmartAppUpdater é que ele permite que você insira código
personalizado no processo de aplicação de atualizações. Isso é muito importante nos aplicativos de dispositivo
inteligente, pois eles normalmente têm seu próprio armazenamento de dados, como um banco de dados SQL Server CE
ou um conjunto de arquivos XML no diretório do aplicativo. Com freqüência, esses dados precisam ser preservados e,
portanto, não seriam reimplantados em uma nova versão. Ao fornecer um local onde você possa executar um método
personalizado, é possível, por exemplo, permitir que seu aplicativo compacte e copie o banco de dados do SQL Server CE
para o novo diretório do aplicativo, após uma atualização ter sido aplicada.
Para ajudar nesse processo, o conjunto de módulos (assembly) do SmartAppUpdater inclui a interface simples
IPostProcessor, que expõe um único método Process que toma como seu único argumento um objeto do tipo
SmartAppUpdater.
Public Interface IPostProcessor

Sub Process(ByVal updater As SmartAppUpdater)

End Interface

É possível, em seguida, criar seus próprios pós-processadores personalizados implementando a interface IPostProcessor,
como no caso da classe mostrada na Listagem 8.
Public Class MyPostProcessor

Implements IPostProcessor

Public Sub Process(ByVal updater As _

Atomic.CF.Deployment.SmartAppUpdater) _

Implements Atomic.CF.Deployment.IPostProcessor.Process

' Perform other logic here

updater.MoveFile("MyOtherFile.xml")

updater.MoveFile("MyOtherFile2.xml")

updater.LogMessage("Hello from My Post Processor")

End Sub

End Class

Listagem 9. Implementando um componente IPostProcessor.


Como você pode ver, um pós-processador personalizado pode usar o componente SmartAppUpdater para chamar o
método auxiliar MoveFile, que move um arquivo da pasta antiga para a pasta nova e ainda registra mensagens no log do
SmartAppUpdater usando o método sobrecarregado LogMessage.
Para associar um pós-processador ao SmartAppUpdater, há um construtor sobrecarregado que pode ser usado para
passar o componente, da seguinte forma:
Dim updater As New SmartAppUpdater(New MyPostProcessor)

Se você precisa oferecer suporte a vários pós-processadores, o SmartAppUpdater expõe uma ArrayList por meio da
propriedade PostProcessors, que pode ser usada para adicionar objetos.
No SmartAppUpdater, após ter aplicado uma atualização e disparado o evento Updated, o método StartUpdate enumera
a ArrayList de PostProcessors transmitindo para a interface IPostProcessor e executando seus métodos Process passando
uma referência para si próprio.
' Invoke the post processors

Dim o As Object

For Each o In _processors

If TypeOf (o) Is IPostProcessor Then

Dim p As IPostProcessor = CType(o, IPostProcessor)

p.Process(Me)

Else

_processors.Remove(o)

End If

Next

Iniciando a nova versão


Ao usar um pós-processador ou lidar com o evento Updated, seu aplicativo pode determinar que uma nova versão foi
baixada e instalada com sucesso. Isso permite que seu código, por exemplo, solicite ao usuário que o aplicativo seja
reiniciado ou simplesmente habilite um item de menu ou botão indicando a opção para reiniciar.
Para ajudar a reiniciar o aplicativo, o SmartAppUpdater expõe um método StartNewVersion e usa a classe WindowsCE
mostrada na Listagem 1 para executar o executável SmartAppLoader. Como o arquivo SmartAppLoader.xml foi
atualizado, a nova versão do aplicativo será iniciada.
Public Sub StartNewVersion()

WindowsCE.StartProcess(Me.RootPath & _

Path.DirectorySeparatorChar & "SmartAppLoader.exe", "")

End Sub

Para usar esse recurso em seu código, você pode simplesmente chamar este método, imediatamente seguido de uma
chamada para Application.Exit, como a seguir:
_updater.StartNewVersion()

Application.Exit()

Limpando versões anteriores


A última tarefa restante é remover a versão anterior do aplicativo do dispositivo a fim de economizar espaço precioso.
Isso é realizado pelo construtor do SmartAppLoader na próxima execução do aplicativo, se o elemento OldAppPath for
encontrado no arquivo SmartAppLoader.xml. Isso é feito nesse momento, em vez de imediatamente após a atualização
pelo SmartAppUpdater, pois a versão anterior do aplicativo está sendo executada e, portanto, não seria possível excluir
os arquivos.
Se o elemento for encontrado, a pasta antiga será excluída usando a classe System.IO.Directory.
' Read the old path if present

xnl = xd.GetElementsByTagName("OldAppPath")

If xnl.Count > 0 Then

Dim oldAppPath As String = xnl(0).FirstChild.Value


Try

If Directory.Exists(oldAppPath) _

Then Directory.Delete(oldAppPath, True)

' Clean up the file

xd.GetElementsByTagName( _

"SmartAppLoader")(0).RemoveChild(xnl(0))

xd.Save(_curPath & Path.DirectorySeparatorChar & _

"SmartAppLoader.xml")

Catch ex As Exception

' Could not clean up

End Try

End If

Início da página

Resumo
O software criado com o .NET Compact Framework fornece vários desafios para desenvolvedores nas áreas de
empacotamento, implantação e manutenção. Esperamos que, com o uso das idéias e dos recursos expostos nesse white
paper, você seja capaz de conhecer mais claramente suas opções e empregar uma solução que se ajuste aos requisitos
de seu aplicativo.
© 2005 Microsoft Corporation. Todos os direitos reservados. Termos de uso.
Início da página
.NET Framework

Implantando aplicativos do Windows Forms com o ClickOnce


por Mauro Sant'Anna
Diretor regional da Microsoft
Dezembro de 2004
Resumo: examina a tecnologia ClickOnce, compara-a a outras tecnologias de implantação e mostra como utilizá-la em
seus aplicativos. Este artigo também contém links para páginas em inglês. (11 páginas impressas)

Introdução
O ClickOnce é uma nova tecnologia de implantação do Windows Forms que será fornecida com o Visual Studio 2005. Ela
permite a instalação e a atualização simples de aplicativos da Web com clientes inteligentes. A implantação de aplicativos
do Windows Forms por HTTP está disponível desde a primeira versão do .NET Framework, e vem evoluindo a partir de
então. Este artigo discute as vantagens dos aplicativos do Windows Forms e a evolução dessa tecnologia até o ClickOnce.
Também mostrarei um exemplo simples usando a versão Beta 1 pública do Visual Studio 2005.
Início da página

Por que o Windows Forms?


Desde o surgimento da World Wide Web, a maioria dos usuários e desenvolvedores tem tido mais interesse pelos
aplicativos da Web do que pelos aplicativos "regulares" do Windows. Além de ser mais atraente, um aplicativo da Web
possui algumas características interessantes e especiais:
Um aplicativo da Web pode ser acessado a partir de qualquer local do mundo em que haja uma conexão com a

Internet; o cliente nem mesmo precisa estar executando o Windows. Um aplicativo da Web "puro" é uma tecnologia
muito boa quando seu aplicativo precisa ser acessado exatamente de qualquer lugar.
Um aplicativo da Web é simples de implantar e atualizar: basta copiar os arquivos do aplicativo para um diretório de

um servidor Web e todos os seus clientes poderão começar a usar o novo aplicativo imediatamente. Nada de
problemas com DLLs, nada de entradas do Registro a serem manipuladas, nada de classes COM a serem registradas;
ele simplesmente funciona!
O foco deste artigo está no segundo item descrito acima: implantação. O protocolo HTTP usado pelos aplicativos da Web
tem várias vantagens em relação à implantação de aplicativos tradicionais do Windows.
Por outro lado, por mais que eu adore a Web, é difícil dizer que a experiência do usuário na Web é maravilhosa. Quando
comparada aos aplicativos do Windows, a Web possui diversas desvantagens:
Sua interface de usuário é bastante pobre. Tudo o que podemos esperar de um aplicativo do Windows, como o recurso

arrastar e soltar e o clique direito do mouse, é muito difícil ou mesmo impossível de se obter em um aplicativo da
Web.
E, mesmo quando conseguimos executar alguns truques mais complexos de interface, isso geralmente requer uma

quantidade insana de scripts no cliente, um tipo de código que é particularmente difícil de criar e depurar.
Um aplicativo da Web consome muitos recursos do servidor, muita largura de banda e uma boa dose da paciência do

usuário, porque a maioria das coisas precisa ser feita no servidor, envolvendo viagens de ida e volta e um pouco de
espera.
A impressão é limitada à "tecnologia print screen", fornecendo pouco controle sobre recursos como quebras de página,

devido às diferenças de fontes, margens e tamanhos de papel.
Alguns dos problemas acima podem ser minimizados com o uso de plug-ins ou controles ActiveX, mas eles, por sua

vez, tendem a apresentar os mesmos problemas de implantação que os velhos aplicativos não-Web costumavam ter.
E se pudéssemos combinar a facilidade de distribuição dos aplicativos da Web com a avançada funcionalidade de cliente
dos aplicativos do Windows? Bem, nós podemos. O .NET Framework, desde a sua primeira versão, permite a distribuição
de aplicativos do Windows Forms por HTTP e sem os costumeiros problemas.
Esse tipo de aplicativo é especialmente interessante nos cenários de intranet/extranet, nos quais o acesso onipresente
não é necessário e nos quais podemos supor que o computador do usuário terá tanto o Internet Explorer quanto o .NET
Framework instalados.
Início da página

.NET Framework 1.x: usando HREF para .EXEs


As versões 1.0 e 1.1 do .NET Framework foram fornecidas com o recurso de implantação de aplicativos do Windows
Forms por HTTP. Basicamente, você usa uma marca "HREF" para apontar para um .EXE gerenciado. O Internet Explorer
e o .NET Framework em tempo de execução podem baixar e executar não só o executável, mas também as DLLs que ele
possa exigir, por demanda. Esse tipo de implantação foi apelidado de "href para EXEs".
Este é um exemplo de uma dessas marcas:
<a href="MainProject.exe">Call MainProject</a>
Isso é bastante simples de se fazer e já foi discutido nos seguintes artigos recomendados:
Security and Versioning Models in the Windows Forms Engine Help You Create and Deploy Smart Clients (em inglês),

de Chris Sells; veja também um outro artigo de Chris, smart client Wahoo! sample (em inglês)

• Death of the Browser? (em inglês), de Billy Hollis

• Meu artigo, Deploy Windows Forms on the Web (em inglês)

• Using Windows Forms Controls in Internet Explorer (em inglês)


Como o módulo (assembly) do .NET (.EXE ou .DLL) é a unidade básica de implantação, convém dividir o aplicativo em
um .EXE principal e várias DLLs. Dessa forma, se você fizer alterações simples em uma única DLL, apenas essa DLL
precisará ser baixada.
Por mais simples que isso pareça, ainda são necessários alguns truques para que tudo funcione corretamente:
O .NET Framework deve ter sido instalado previamente no cliente (embora você possa baixar um plug-in do endereço

http://msdn.microsoft.com/vstudio/downloads/tools/bootstrapper/, que pode facilitar a instalação do Framework e de
seu aplicativo).
Seu aplicativo será executado no cliente como código parcialmente confiável. Por um lado, isso é bom porque o

aplicativo é executado em um modo seguro, e tem limites quanto ao que pode fazer no computador cliente. Por outro
lado, se você precisar de funcionalidades como a abertura de arquivos locais ou a chamada de um objeto COM, terá
que configurar, de alguma forma, uma diretiva de segurança no cliente, o que não é nada simples de se fazer.
Por padrão, é bastante provável que seu executável tente carregar várias DLLs com recursos de localização; devido a

alguns problemas da implementação atual, o desempenho será prejudicado, principalmente em conexões lentas com a
Internet.
As atualizações são feitas arquivo por arquivo. Não há como garantir, por exemplo, que todos os seus 10 arquivos

atualizados sejam realmente baixados; seu cliente pode acabar ficando com um aplicativo "meio atualizado".
Seu aplicativo só estará disponível offline se o usuário configurar manualmente a opção "Trabalhar Offline" no Internet

Explorer; o aplicativo em si não tem nenhum controle sobre isso.

• Por padrão, o arquivo .config associado ao programa não está disponível (veja aqui como fazer isso).

• Seu aplicativo não terá um atalho na área de trabalho ou no menu Iniciar.


Início da página

Updater Application Block


Para resolver alguns dos problemas descritos acima, a Microsoft criou o UAB (Updater Application Block). O bloco
atualizador é uma biblioteca que pode ser adicionada ao seu aplicativo para gerenciar o download de suas partes por
HTTP
Ele tem algumas vantagens sobre a implementação original do Framework:

• Ele é executado como um aplicativo local e está disponível o tempo todo, sem prejudicar o desempenho.
As atualizações são feitas por transação, ou seja, todos os arquivos de uma nova versão precisam ser baixados com

êxito para que a nova versão seja disponibilizada.

• Todos os arquivos do aplicativo são listados em um manifesto.

• Ele é executado como um aplicativo totalmente confiável; não é preciso mexer com a diretiva de segurança do cliente.

• Seu aplicativo pode ter atalhos no menu Iniciar.


Por outro lado, existem também algumas desvantagens:

• É preciso alterar substancialmente o aplicativo a fim de utilizá-lo.


Como ele usa BITS para baixar as partes do aplicativo, ele não é executado no Windows 98/ME; é necessário o

Windows 2000 ou posterior.
Ele é executado como um aplicativo local totalmente confiável, por isso, na maioria das vezes, ignora a segurança de

acesso a código.

• Ele não tem suporte da Microsoft.


Para obter mais informações sobre o UAB, consulte o artigo .NET Application Updater Component (em inglês), de Jamie
Cool. Você também pode verificar a home page do UAB na Gotdotnet. O UAB não tem suporte "oficial", embora haja um
fórum em http://www.gotdotnet.com/. De qualquer forma, o UAB é fornecido com o código-fonte completo, e você pode
alterá-lo para corrigir algumas de suas restrições, como a exigência de BITS e do Windows 2000.
Início da página

ClickOnce
O UAB é obviamente uma medida paliativa, enquanto a Microsoft desenvolve uma solução definitiva. Essa solução
chama-se ClickOnce. Basicamente, o ClickOnce tem todas as vantagens do UAB, com alguns de seus problemas, mais
alguns recursos adicionais. Na minha opinião, uma das principais vantagens do ClickOnce é que ele restaura a segurança
de acesso a código.
Quando comparado aos HREF EXEs, um aplicativo do ClickOnce tem as seguintes vantagens:

• As atualizações são feitas por transação (ou seja, são feitas por completo ou não são feitas).
O aplicativo não só funciona offline, como também tem um certo grau de controle sobre isso; existem APIs, para que

o aplicativo possa saber se está online ou offline; ele também pode controlar seu próprio processo de atualização.
Ele tem uma boa integração com o Visual Studio .NET, incluindo a capacidade de gerar os arquivos e as ferramentas

extras apropriadas que ajudam a descobrir de quais privilégios de segurança seu aplicativo necessita para ser
executado.
Ele vem com um executável de inicialização (bootstraper) Win32 capaz de baixar os componentes necessários, até

mesmo o próprio .NET Framework.

• Os arquivos do aplicativo podem ser baixados por demanda ou em lotes.

• Ele pode ter atalhos no menu Iniciar.


O ClickOnce é um recurso do Visual Studio 2005, antes conhecido pelo codinome "Whidbey", e do .NET Framework 2.0.
Vamos explorar um exemplo usando a versão "Community Preview Beta 1" (Framework versão 2.0.40607).
Início da página

Um aplicativo do ClickOnce
Vamos criar um aplicativo simples do ClickOnce seguindo estas etapas.
1. Inicie o Visual Studio 2005.
2. Selecione File (Arquivo) e clique em New Project (Novo projeto).
3. Escolha um idioma (C# ou Visual Basic .NET) e selecione Windows Application (Aplicativo do
Windows).
4. Dê ao projeto o nome MyClickOnceApp e clique em OK.
5. Adicione um botão ao formulário e altere sua propriedade Text para About.
6. Clique duas vezes no botão. Na janela de código, insira o código a seguir.
Visual Basic .NET:
MsgBox("My First ClickOnce Application")
C#:
MessageBox.Show("My First ClickOnce Application");

Pressione F5 para executar e testar o aplicativo.


Todos os aplicativos do Windows sob o Visual Studio 2005 têm uma página Publish (Publicar) sob Project |
MyClickOnceApp Properties (Projeto | Propriedades de MyClickOnceApp) para controlar detalhes da implantação:

Figura 1. Definindo as configurações de publicação


Publishing Location (Local de publicação) indica o local a partir do qual o aplicativo será implantado. Pode ser um local
em um servidor Web (HTTP), como mostrado acima, mas também pode ser um caminho de rede regular.
Install Mode and Settings (Modo de instalação e configurações) controla diversos detalhes da implantação, como:

• Se o aplicativo estará disponível online apenas ou também offline.

• Application Files (Arquivos do aplicativo): o local onde os arquivos individuais serão instalados.
Prerequisites (Pré-requisitos): o local onde o programa de instalação deve instalar outros componentes, como

Windows Installer 2.0, .NET Framework 2.0, J# Redistributable Package, SQL Server 2005 Express, Crystal Reports e
Microsoft Data Access Components 2.8.

Figura 2. Configurando os pré-requisitos


Updates (Atualizações): controla quando o aplicativo deve verificar se existem atualizações e como elas serão

transferidas para o cliente.
Figura 3. Configurando as atualizações
Options (Opções): ajusta detalhes como o idioma do aplicativo, o nome do recurso no menu Iniciar, a página HTML

usada para a implantação na Web e o tíquete da diretiva de implantação.

Figura 4. Configurando as opções de publicação


Publish Version (Versão da publicação) ajusta o número de versão do aplicativo; ele pode ser aumentado
automaticamente em cada implantação.
Publish Wizard (Assistente de publicação) permite configurar várias opções de publicação. Este assistente também é
chamado pelo menu Build | Publish (Compilar | Publicar). Todos os aplicativos do ClickOnce devem ter assinatura
criptografada; o assistente solicita uma chave existente (recomendável) ou pode gerar uma nova
Figura 5. Assinando seu aplicativo
Depois de executar o assistente uma vez, você pode clicar em Publish Now (Publicar agora) para publicar atualizações.
Em nosso exemplo, será mostrada uma página da Web.

Figura 6. Aplicativo publicado


Esta página da Web contém um script que verifica qual pacote de pré-requisitos extra precisa ser instalado antes da
instalação de nosso aplicativo. Se algum pré-requisito não estiver presente, ele será instalado na primeira vez em que o
aplicativo for executado.
Depois que o usuário clicar no link Install (Instalar), várias caixas de diálogo poderão exigir a intervenção do usuário,
pelo menos na primeira vez em que ele for executado:

• Avisos do Windows XP SP2

• Contratos de licenças de software


Falta de verificação da assinatura do

editor
Início da página

Detalhes da implantação
O Visual Studio .NET 2005 criará uma nova Web para nosso aplicativo, com vários arquivos e pastas.
Figura 7. Pastas da implantação
A pasta dotnetfx contém o redistribuível do .NET Framework, atualmente um executável de 25 MB.
Por padrão, o Visual Studio aumentará o número da versão sempre que o aplicativo for implantado; cada versão
receberá uma nova pasta com o número de versão maior correspondente.
O arquivo .application é o destino do link mostrado na página HTML publish.htm. Ele é um arquivo XML que contém
informações, como a pasta correspondente à versão atual do aplicativo e as assinaturas digitais.
Publish.htm é uma página da Web que contém não só o link para o arquivo .application, mas também um script de
cliente que faz a mesma verificação de versão e mostra as mensagens apropriadas. Por exemplo, se o seu computador
não tiver o .NET Framework, a mensagem mostrada sob Install MyClickOnceApp (Instalar MyClickOnceApp) será
diferente.
O Setup.exe é um executável Win32 capaz de instalar não apenas o seu aplicativo, como também todos os
componentes necessários, como o próprio .NET Framework e o MDAC 2.8 na ordem correta.
Cada pasta do aplicativo contém os arquivos do aplicativo e também um arquivo de manifesto. O manifesto é um arquivo
XML com, basicamente, as seguintes informações:
A identidade precisa de todos os arquivos do aplicativo. Essa identidade compreende o nome do arquivo, o número da

versão, a cultura e a arquitetura de processador ("msil", em nosso caso).

• Todas as permissões que o aplicativo requer.

• Assinaturas digitais.
Início da página

Executando o aplicativo
Após baixado o aplicativo, você pode executá-lo sem precisar baixá-lo novamente. Em nosso exemplo, o aplicativo pode
ser iniciado clicando-se no link para a página da Web ou no atalho do menu Iniciar. Em ambos os casos, a existência de
uma nova versão é verificada de acordo com as configurações das opções do projeto Application Updates (Atualizações
de aplicativo). É baixada uma nova versão, se necessário.
Para verificar esse recurso de atualização, execute o seguinte procedimento:
Faça uma alteração visível no aplicativo, como na localização do botão da parte inferior do

formulário.

• Compile-o e implante-o novamente.

• Execute o aplicativo e verifique o processo de download.


Por fim, é importante notar que essas informações baseiam-se na versão Beta 1 do Visual Studio .NET; as versões mais
recentes podem ter outros recursos.
Início da página

Gráfico comparativo
HREF .EXE UAB ClickOnce

Nenhuma alteração necessária no aplicativo X X

Isolamento do aplicativo X X

Suporte total X X
HREF .EXE UAB ClickOnce

Baixo impacto sobre o sistema X X

Segurança de acesso a código preservada X X

Download de arquivos por demanda X X

Manifestos para listar de forma declarativa os arquivos necessários X X

Manifestos assinados por criptografia X

Download de arquivos em lotes X X

Trabalhar offline (*) X X

Instalações por transação X X

Desempenho otimizado X X

Windows 2000 ou posterior necessário X (**)

Integração com o shell do Windows X X

Bom controle sobre o processo de atualização X X

API para trabalhar offline e controlar o processo de download X

Instalação automática de pacotes opcionais X

Início da página

Conclusão
O ClickOnce é uma tecnologia muito poderosa para a implantação de aplicativos. Ele é uma evolução natural dos
modelos de implantação anteriormente disponíveis, e une robustez, segurança, desempenho e flexibilidade à avançada
funcionalidade de cliente dos aplicativos do Windows Forms.
Início da página

.NET Framework
Desenvolvendo Código Seguro com .NET Framework
Deixe-me ser muito claro de início: se você escreve código que irá rodar em rede, ele
será atacado. Ponto. Portanto, não pense que este “tipo de coisa” só acontece com os Faça o download deste
outros. Se você ainda não parou para pensar nas consequências de ter seu código sendo documento:
Desenvolvendo Código
atacado, eu sugiro você “desplugar” seus micros da rede imediatamente, dar uma Seguro com .NET
pensada e resolver este problema. Framework

formato Word, 96 KB
Mas não fique tão preocupado. Nem tudo está perdido. Uma das melhores iniciativas
criadas pela Microsoft para endereçar estes problemas, chama-se SD3, ou Secure by
Design, by Default, and in Deployment – algo como “Seguro no Projeto, por Definição e
na Instalação”.

Esta iniciativa propõe que o software deve ser projetado desde seu início com a questão
de segurança em mente. Além disto, quando instalado, o software deve ter o mínimo de
funções ativadas automaticamente, pois se uma função está ativada e o usuário não
sabe, muito provavelmente ele terá dificuldades em fazer ajustes de segurança na
mesma. Seguro na instalação significa que o usuário poderá fazer a manutenção do
software de forma simples e constante.

Com o advento do .NET Framework, muitos dos desafios enfrentados por coders são
minimizados, mas longe de termos um ambiente totalmente seguro e livre de ataques.
Apesar das muitas facilidades, o .NET Framework ainda pode ser explorado por mentes
distorcidas e hackers a procura de mais um “troféu” para mostrar aos seus amigos (ou
seriam inimigos?)
De qualquer forma, escrever código seguro é escrever código com qualidade. Não pense que o tempo investido em
analisar rotinas, componentes e assemblies à busca de falhas e potenciais brechas, seja algo dispensável. Não o é.

Portanto, meu intuito é mostrar a vocês alguns dos recursos disponíveis no ambiente .NET e algumas técnicas para
fazer seu código mais seguro.

Informação na Ponta dos Dedos

A melhor forma de iniciar o aprendizado de como escrever código .NET seguro é saber onde obter informação. Aqui
estamos muito bem servidos. Além dos livros que começam a aparecer, há uma gama de sites que falam sobre este
assunto. O mais importante deles é o Security Developer Center, da Microsoft ( http://msdn.microsoft.com/security).
Neste site os desenvolvedores podem achar uma série de artigos, códigos, melhores práticas, chats e outros
dispositivos para fazer que sua vida de programador seja facilitada no que diz respeito ao desenvolvimento de código
seguro.

Participar das listas de distribuição e grupos de discussão (tais como microsoft.public.platformsdk.security,


microsoft.public.dotnet.security) é também mandatório.

O Modelo de Código Seguro do .NET

Mas se você é um daqueles programadores que gosta de colocar a mão na massa e não fica esperando para depois,
tenho algumas dicas imediatas que podem ser aplicadas imediatamente em seu código .NET .

Para ajudar no suporte ao desenvolvimento de código seguro, o .NET Framework possui um conjunto amplo de
características, capaz de fazer com que seu código rode em um ambiente controlado dentro de um contexto definido
pelo administrador de segurança.

Antigamente o modelo de segurança era somente baseado no nível de acesso (ou role) que um usuário possuia. Ou
seja, todo o código que era executado com a autenticação daquele usuário, rodava com os mesmos privilégios.
Portanto um administrador que executava um código, transferia para este todos os seus privilégios de administrador.
Com o advento do modelo COM+ isto começou a mudar.

Mas no .NET, este modelo foi estendido e foi criada uma arquitetura muito elegante de segurança de código, batizada
de Code Access Security.

O Code Access Security baseia-se em alguns conceitos abstratos, tais como evidências, políticas e permissões.

Este sistema de segurança trabalha em conjunto com os sistemas de segurança do sistema operacional,
posicionando-se como camada adicional de proteção do mesmo.

As camadas de segurança do .NET Framework são amplamente possíveis de personalização para atender às
demandas dos usuários, criando um ambiente poderoso e flexível ao alcance do programador.

Tudo gira em torno do conceito de permissões. Permissões são os direitos que um determinado código possui para
executar uma tarefa. Por exemplo, se um código deseja gravar um arquivo ele precisa de uma permissão de acesso a
arquivos. Se ele precisa acessar a rede, ele precisa de permissão de acesso a rede.

Um vez definidas as permissões, o sistema de segurança utiliza as informações contidas no assembly para determinar
se um determinado código tem direito ou não de realizar uma determinada tarefa.

Há vários tipos de permissões, tais como File I/O, UI, OleDB, Performance, Storage e outras. Sugiro uma olhada no
manual “Securing Applications” capítulo “Code Permissions” do conjunto de manuais online do .NET Framework para
maiores informações sobre permissões de códigos.
Uma vez entendido o conceito por trás da segurança, fica muito mais fácil escrever código seguro.

Requerendo Permissões para Executar

O requerimento de permissões é o mecanismo usado pelo CLR do .NET para definir os direitos de executação de um
determinado assembly. Na verdade o CLR não apenas checa o assembly que solicitou a permissão, mas toda a pilha
de chamada, ou seja, se um assembly A chama o assembly B que chama um assembly C que solicita uma
determinada tarefa que necessita de nível de segurança específico, o CLR checa se A, B e C possuem as permissões
necessárias. Se um deles não possuir permissão a chamada irá disparar uma exceção. Isto chama-se Stack Walk. Na
realidade, o Stack Walk checa todas as funções encadeadas para saber se as mesmas possuem autorização.

O pedido de permissões é o modo correto de fazer seu código ser um código seguro. O pedido permite que seu
código requeira o mínimo de permissões necessárias para ele rodar e garante que ele não receba mais do que
precisa, evitando que o mesmo seja usado por terceiros (usuários ou outros códigos) para realizar tarefas as quais
ele não foi projetado.

O exemplo abaixo descreve como definir o pedido de permissão de um código.


Entretanto, um número de barreiras deverá ser superado com o objetivo deste emergente mercado de aparelhos
conectados na realidade. Atualmente, existem diferentes Móbile CPUs e sistemas operacionais móveis que os
desenvolvedores tem um tempo para decidir como escrever aplicações e para que tipo de aparelho. As diversas
capacidades de CPUs e sistemas operacionais também aproximam como nunca a criação de aplicações para Smart
Device Applications.

Desenvolvedores também estão incertos quanto à criação para qual modelo de aplicação sem fio é mais apropriado.
Neste ponto, o Wireless Application Protocol (WAP) foi chamado de resposta “certa”, mas WAP requer conexão o
tempo todo e não precisa de uma rica ou interativa experiência para muitas aplicações finais. Entretanto, WAP não
tem as vantagens e recursos disponíveis nos PDA como conexão de rede sem fio.

Alguns destes problemas podem ser resolvidos por permitir aplicações cliente-servidor para rodar nos dispositivos
móveis, mas isso implica em mais obstáculos. Imagine a frustração dos usuários de telefones móveis que forçaram
um reboot nos telefones porque uma aplicação pobre escrita, ou o custo de rechamada do celular em função de uma
falha na aplicação. Uma barreira significativa é que as soluções do lado do cliente existentes atualmente são
diferentes do objetivo final do ambiente de programação que requer o retreinamento do desenvolvedor, o qual podem
ser os custos de muitas empresas.
[assembly:FileIOPermission (SecurityAction.RequestMinimum, Write="C:\\MeuArquivo.doc")]
[assembly:PermissionSet SecurityAction.RequestOptional, Unrestricted=false)]
Este exemplo diz ao sistema que o código não deve rodar se não obtiver permissão de escrita para o arquivo
MeuArquivo.doc. Se o código é impedido de rodar devido à política de segurança da máquina, uma exceção será
disparada (PolicyException) e o código não irá executar.

Este exemplo também informa ao sistema que não há necessidade adicional de permissões. Você pode fazer o
inverso, também, ou seja, dizer ao sistema as permissões que você não quer que seu código tenha. Este estilo
permite que examinadores/auditores da empresa tenham certeza imediata das limitações de segurança de seu
código.

O fato de seu código receber autorização para executar coisas que ele não executa, pode não causar danos
imediatos, mas se houver uma brecha no código e este puder ser alterado ou operar erraticamente, ele terá
permissões de realizar tarefas para as quais não foi projetado. E isto pode não ser o que você deseja.

Regras Básicas

Dentre as diversas técnicas para escrever código seguro eu selecionei algumas mais básicas e simples que você pode
utilizar imediatamente.

Sem dúvida nenhuma a primeira delas é transformar seus assemblies em strong-named assemblies, ou seja,
assemblies assinados com chaves criptografadas.

O conceito por trás desta técnica é simples. Como você sabe que o arquivo winword.exe realmente é o executável da
Microsoft que inicia o Word? Bom, você não sabe. E se o arquivo for um vírus que ativa cavalos de tróia que
permitem hackers acessarem seu micro remotamente e roubar suas senhas? Esta técnica de hacking é chamada de
spoofing. É exatamente para evitar este tipo de problema que existe o strong-named assembly.

Assemblies strong-named são assemblies assinados digitalmente por um par de chaves (pública e privada) mais seu
nome, versão e cultura.

Para assinar um assembly é necessário o uso da ferramenta sn.exe encontrada no .NET Framework.

A sintaxe para a criação de uma chave é simples. Basta digitar sn –k pardechaves.snk. Este comando cria um par de
chaves que devem ser referenciados no arquivo AssemblyInfo.cs com a seguinte tag [assembly:
AssemblyKeyFile("pardechaves.snk")].

Outra forma de assinar um assembly é usar o compilador no modo de linha de comando, conforme o exemplo abaixo:
sn -k pardechaves.snk
csc/r:MeuComponente.dll /keyfile:pardechaves.snk MeuComponente.cs
sn -p pardechaves.snk publica.snk
sn -tp publica.snk > publica-hex.txt
Neste exemplo, o compilador cria um assembly chamado MeuComponente.dll e utiliza o o par de chaves
pardechaves.snk como assinatura. Logo em seguida a chave pública é extraída e transformada em hexadecimal. Com
isto fica possível a distribuição da chave pública para eventuais usuários do componente.

Além de usar assemblies strong-named é possível o uso do Authenticode, mas somente depois do assembly ter sido
assinado.

Com este procedimento é garantido que o MeuComponente.dll é seu assembly e não apenas um arquivo com o
mesmo nome.

Lembre-se que strong-named assemblies, quando usados com ASP.NET, devem ser armazenados no Global Assembly
Cache (GAC) usando a ferramenta gacutil.exe.

Seguindo nossa lista, uma ferramenta que não pode faltar em nosso arsenal de escrita de código seguro é o FxCop,
que pode ser encontrado no site http://www.gotdotnet.com/team/fxcop/default.aspx. O FxCop é uma ferramenta de
análise de código que compara seu código contra as regras de design definidas pela Microsoft no .NET Framework
Design Guidelines. O FxCop inspeciona seu assembly em mais de 200 itens nas áreas de convenções de nomes,
projeto de bibliotecas, localização, segurança e performance.

Além de apresentar os potenciais erros, o FxCop também gera um arquivo XML que pode ser lido e impresso para
eventuais consultas.

Portanto, antes de partir para algo mais sofisticado, certifique-se que seu assembly passe no teste do FxCop sem
nenhum erro.

Arquivos de Configuração Protegidos

O .NET Framework utiliza arquivos de configuração XML em muitos de seus componentes. Muitas vezes o utilizamos
para guardar informações importantes, mas senhas, chaves e strings de conexão a banco de dados devem ser
armazenadas em outros locais, de preferência no Registry. Ops! Registry? Mas isto não acaba com o conceito de
instalação via Xcopy? Sim, acaba, mas este é o preço que temos que pagar para ter nosso código mais seguro.

Uma outra técnica presente a partir do ASP.NET 1.1 é o uso da Data Protection API (DPAPI). Ao utilizar esta técnica,
as tags , e apontam para uma chave no registry que contém os dados necessários. Você pode utilizar a ferramenta
aspnet_setreg para criar os dados protegidos. É verdade que esta técnica não elimina todos as ameaças de proteção
de senhas, mas dificulta muito o acesso às mesmas por pessoas (ou aplicações) não autorizadas.

Um exemplo da utilização da DPAPI seria:


< system.web>
< processModel enable="true"
userName="registry:HKLM\Software\MinhaChave.username"
password=" registry:HKLM\Software\MinhaChave.password"
Armazenando Dados Temporários

Deixar rastros é muito comum quando falamos de aplicações que rodam diversas vezes via diversos usuários. E é
exatamente este rastro que hackers utilizam para obter informações importantes sobre o sistema que desejam
penetrar.

Um dos fatores para a criação de rastros é a criação de arquivos de armazenamento temporário, que guardam desde
condições até datasets completos.

Para evitar que aplicações malignas possam acessar estes dados, o .NET disponibiliza o que chamamos de
armazenamento isolado (ou isolated storage). O armazenamento isolado garante que um arquivo seja protegido e
acessado pelo usuário e assembly que os criou. Ou ainda pelo usuário, no domínio e assembly que os criou.

O armazenamento isolado deve ser usado para substituir os acessos a arquivos utilizando classes simples de I/O (File
I/O).

Um exemplo do armazenamento isolado seria:


using System.IO.IsolatedStorage;
...
IsolatedStorageFile isoFile =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly |
IsolatedStorageScope.Domain ,
null,
null);
Neste exemplo, há uma garantia de que os dados serão acessados apenas pelo usuário, assembly e domínio que os
criou.

Desabilitar Tracing e Informações de Debug

Muitas vezes vi códigos serem colocados em produção sem antes terem sido feitas as devidas “limpezas” . Bibliotecas
com informações de debug foram encontradas nos mais diversos ambientes de produção.
No caso de DLLs e aplicações .EXE, basta dizer ao compilador para produzir o código Release e ele eliminará as
informações de debug.

No caso de aplicações ASP.NET há uma tarefa adicional que é bloquear a capacidade de tracing de sua aplicação.

Para fazer isto há diversas maneiras. A mais simples é adicionar no arquivo de configuração web.config (que também
deve ficar protegido de acesso indevido) as tags e . Outra forma é atribuir a propriedade HttpContext.Trace o valor
false.

Em Resumo

Não exploramos nem 10% das técnicas possíveis de serem usadas para a criação de código mais seguro em .NET,
mas com certeza temos um leque inicial de ferramentas que produzem resultados imediatos.

O .NET Framework nos propicia uma séria de vantagens em relação a outros ambientes de desenvolvimento no que
diz respeito a escrita de código seguro. Estude-as e incorpore-as em seu código.

Lembre-se, criar código seguro não é uma finalidade, mas uma missão, uma jornada. Técnicas cada vez mais
sofisticadas serão criadas e incorporadas à nossa realidade. Mas se não fizermos da prática de escrever código seguro
um hábito, em pouco tempo não precisaremos mais nos preocupar em escrever nada, pois teremos perdido a guerra
contra os hackers e eventualmente nosso emprego.

Evanndro Reis
é MCSD e Arquiteto-Chefe de Software da ideativa. Trabalhou em desenvolvimento de software em empresas como
IBM, Sun e Apple e desde 1999 utiliza como sua plataforma exclusiva de desenvolvimento, o .NET.

.NET Framework
Estratégia de acesso a dados para o Microsoft .NET Compact
Framework
Artigo original encontra-se em
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnppc2k2/html/dataaccessstrategy.asp

Se aplica para:
Microsoft® .NET Compact Framework 1.0
Microsoft SQL Server™ 2000 Windows® CE Edition
Microsoft Windows Powered Pocket PC
Resumo:

O .NET Compact Framework pode forçar a comunicação e aplicação de troca de dados entre dispositivos móveis e
servidores de diversas formas. Isto é importante para definir uma sólida estratégia de acesso a dados antes de
desenhar, desenvolver e instalar soluções em relação a cada método com características diferentes. Este artigo
aborda as opções chaves: os baseados em XML e aqueles baseados em SQL Server CE. A importância de definir
uma estratégia de acesso a dados é crítica uma vez que levará a mais eficiência e menor tendência a erros no
desenvolvimento e instalação.
Introdução

O principal foco deste artigo, do ponto de vista de dispositivos, é a plataforma Pocket PC. Entretanto, o
Microsoft® .NET Compact Framework também roda em dispositivos Microsoft Windows® CE .NET que implementa
outros fatores do que o Pocket PC. Os conceitos do .NET Compact Framework discutidos aqui podem ser aplicados
para estes dispositivos também.
Uma estratégia de acesso a dados no .NET Compact Framework lida com um número de aspectos. O aspecto chave,
com o qual outros dependem, é a conectividade. Assim como o Pocket PC pode ser usado tanto online como offline,
a estratégia de acesso a dados precisa definir:

• Administrar e usar dados nos servidores remotos quando online

• Armazenar e usar dados quando offline

• Trocar dados quando o Pocket PC fica online a partir de um estado offline anterior

No contexto da estratégia de acesso a dados é importante para estabelecer uma terminologia comum. Isto é
verdadeiro em se tratando de troca de dados entre Pocket PC e o servidor. A partir de uma perspectiva conceitual,
existem dois métodos para troca de dados: sincronização e transferência de dados.

Sincronização de dados suporta trocas de dados em ambas as direções e implementa funcionalidades para assegurar
que os dados são idênticos nos dois lados. Em muitos cenários, o mesmo dado pode ser modificado em múltiplos
lugares enquanto os Pocket PCs estão offline, e então, sincronizados com o servidor. Sincronização deverá funcionar,
entretanto, freqüentemente inclui resolver conflitos que lidam com concorrência que ocorre quando um Pocket PC
desconectado retorna online para sincronizar dados. Um exemplo deste cenário ocorre quando um comprador móvel
atualiza no pedido o campo enquanto inicia offline. Antes de o comprador sincronizar as trocas, um representante
atualiza o mesmo pedido. Um cuidadoso desenho da resolução de conflitos pode determinar quais ações tomar
quando ocorrer conflitos, por exemplo, o comprador móvel exceder o comprador baseado no cargo ou tempo de
sincronização, ou se o sistema deverá notificar um usuário ou grupo de usuários com uma mensagem que ocorreu
um conflito.

Transferência de dados também suporta trocar dados em ambas as direções, mas não existe funcionalidade para
assegurar que os dados em ambos lugares sejam idênticos e não existe resolução de conflitos. Existem muitos
cenários onde a transferência de dados é um método válido e onde a sincronização de dados não é necessária.
Exemplos de um cenário de transferência de dados inclui transferência estática de dados do servidor para o Pocket
PC, transferir somente novas coleções de dados do Pocket PC para o servidor, e transferir dados para e dos Pocket
PCs que explicitamente está checado dentro e fora ou segmentado para usuários individuais, marcando os dados
como somente leitura no servidor enquanto está checado fora ou selecionado para um usuário específico.

O uso dos termos “sincronização de dados” e “transferência de dados” irão refletir suas verdadeiras idéias e quando
ambos estão implícitos nos termos “troca de dados” serão usados.

Aspectos da Estratégia de Acesso a Dados

Fundamentalmente, a estratégia de acesso a dados do .NET Compact Framework se divide em dois aspectos:

1. Como armazenar dados no Pocket PC. Do ponto de vista da aplicação, dados podem ser persistidos em um banco
de dados relacional (como um Microsoft SQL Server™ CE), em arquivos locais (como arquivos XML muitas vezes
gerenciados através de DataSets), e dados estruturados na memória (Session) que estão dispostos quando a
aplicação é encerrada.

2. Como trocar dados com o servidor. Trocar dados entre o Pocket PC e o servidor pode ser implementado de
maneira distinta dependendo de qual camada de comunicação do Pocket PC com a camada do servidor. A figura 1
ilustra três camadas em tanto no Pocket PC como no servidor.
Figura 1. Camada de aplicação no dispositivo x servidor

As opções de troca de dados para o .NET Compact Framework são:

• Banco de dados no Pocket PC para o Servidor: O banco de dados no Pocket PC troca dados diretamente com o
banco de dados no servidor. Esta opção é válida em cenários com um pequeno ou sem lógica de negócio envolvida,
e em cenários com um grande volume de dados, é implementada usando características Remote Data Access e
Merge Replication do SQL Server CE. Se a sincronização de dados é solicitada e não somente transferência de dados,
então o Merge Replicacion fornece com um construtor a resolução de conflitos implementadas no lado do servidor
SQL Server Reconciler.

• Pocket PC Component para Server Component: A aplicação Pocket PC comunica com componentes no servidor.
Esta opção permite lógica de negócio, implementada em Web services , para ser parte da troca de dados. Esta
opção pode ser usada tanto para persistir dados locais em arquivos XML como no banco de dados SQL Server CE.

• Componente no Pocket PC para o banco de dados no servidor: O Pocket PC conecta diretamente com o banco de
dados do servidor. Esta opção é freqüentemente usada quando a aplicação Pocket PC necessita para gerenciar uma
grande quantidade de dados em um banco de dados remoto sem necessariamente ter que transferir os dados para o
Pocket PC e quando a lógica de negócio não é uma preocupação primária. Esta opção será implementada usando o
namespace System.Data.SqlClient e pode ser usada em ambos se os dados serão persistidos no arquivo XML local e
no banco de dados SQL Server CE.

• Somente no servidor: A interface da aplicação no Pocket PC pode conectar diretamente para o componente no lado
do servidor ou pode ser implementado como uma aplicação WEB utilizando o Web Browser do Pocket PC. Isto
obviamente requer o Pocket PC estar sempre conectado com o servidor, assim como suficiente largura de banda.
Um exemplo de quando isto é uma opção viável é num cenário geograficamente limitado onde uma rede WiFi pode
ser implementada como nas warehouses, instituições de saúde, escritórios e outros. Esta opção não requer nenhum
dado a ser armazenado localmente no Pocket PC.

Tem algumas condições básicas a ser conhecidas ou estabelecidas antes de determinar de que modo dirigir os dois
aspectos “Como armazenar dados” e “Como trocar dados”: Características dos dados, Conectividade e Arquitetura
do Sistema.

Características de Dados

A característica de dados da solução revela as seguintes informações chaves:

1. Quantidade de dados estáticos e transacionais a serem armazenados no Pocket PC: Se a quantidade de dados a
ser armazenada no Pocket PC é baixa, neste caso menor que 50 – 100 kb, então, os dados podem ser armazenados
em arquivo XML locais. Se a quantidade de dados é grande, então, SQL Server CE fornecerá melhor performance e
robustes. A razão disto é que os dados acessados com o SQL Server CE Query Engine com suporte para Structure
Query Language (SQL) que produz maior performance e manuseio.

2. Quantidade de dados transacionais a serem trocados com o servidor: Se a quantidade de dados transacionais a
serem trocados entre o servidor e Pocket PC é baixa, por exemplo, menor que 500 kb – 1 Mb, então, os dados
podem ser passados como XML usando Web Services. Se a quantidade de dados é grande, então, Remote Data
Access e Merge Replication é a primeira opção para melhorar a performance. As razões para isto são que o SQL
Server CE Client e Server Agents implementa uma eficiente compressão de dados e que os dados pegam para
transmitir um significante número menor de processos antes de atingir o destino final. Por exemplo, quando
transferir dados do Pocket PC para o servidor usando Remote Data Access, os dados não possuem as camadas do
OLEDB CE, CLR/NET CF, SQL Server CE Data Provider,e ADO.NET no Pocket PC, assim como no servidor equivale
uma vez que transferidos para o servidor.

É importante notar que enquanto isto é criado para usar Web Services para troca de dados, se os dados estão
armazenados em arquivos XML locais, é também possível usar Web Service se os dados estão armazenados no SQL
Server CE – especialmente no Remote Data Access e Merge Replication. A razão disto inclui a lógica de negócios e a
política de arquitetura de sistema existente a ser empregada.

Conectividade

O aspecto de conectividade da solução relaciona-se ao tamanho de banda disponível quando e o quanto o Pocket PC
ficará online. Estes fatores podem as vezes estar determinado como pré-requisitos e as vezes estarem definidos e
controlados.

Devido a natureza do XML, no qual muitas vezes guarda grandes quantidades de dados sem compressão, Web
Services requer mais largura de banda para o mesmo dado que SQL Server CE Remote, Data Access e Merge
Replication. Entretanto, a realidade mostra que isto é característica conjunta do Remote Data Access e Merge
Replication, que é chamado para quando grandes quantidades de dados precisam ser trocadas. Além disto, estes
dois métodos são freqüentemente usados quando a troca de dados é baixa, no qual a probabilidade de crescimento
da quantidade de dados que precisa ser trocada é grande. Isto significa que, na realidade, SQL Server CE Remote
Data Access e Merge Replication é usado freqüentemente em cenários de alta largura de banda e Web Services em
cenários de baixa largura de banda. Entretanto, quando usar Web Services, um maior desenho conversador (muitas
pequenas requisições) é preferido antes de um desenho volumoso (poucas grandes requisições).

Arquitetura do Sistema

Aspectos de arquitetura de sistema divide geralmente com assunto de integração de sistemas. Uma solução móvel
pode ser respeitada como uma solução isolada conectando com sistemas existentes ou extensões, parte central do
sistema existente. Sem levar em consideração, soluções Pocket PC não são muito freqüentemente isoladas desde
então, elas precisam se comunicar com outros sistemas.

Atualmente, muitos sistemas são implementados em relação a interface técnica induzindo soluções multi-camadas.
Aplicações do lado do servidor são componentizadas em ordem para permitir o reuso de código, separando a lógica
de negócio e dados, e para aumentar a administração. O uso de XML e Web Services continua induzindo os
desenvolvimentos e as aplicações .NET Compact Framework certamente compatíveis dentro da arquitetura porque
suporta XML e o consume de Web Services.

Outras duas comuns integrações de sistemas implementadas são Asynchronous Messaging (transferência de
mensagens) utilizando consultas e integração com o banco de dados. As aplicações .NET Compact Framework
podem aderir para estas implementações usando Web Service para a fila do servidor MSMQ ou através do uso de
Web Services entre Pocket C e o servidor e usar as filas entre o servidor e o sistema back-office. Bancos de dados
integrados é uma parte natural do SQL Server CE como já discutido.

Códigos

Vamos criar um exemplo simples de código que mostra como implementar as opções mencionadas acima. O código
fonte está em uma simples aplicação chamada Feedback Anyplace, o qual foi usada no WebCast chamado Architect
Webcast: Designing Integrated Pocket PC Applications with .NET Compact Framework e ilustra como um inspetor de
qualidade pode usar o Pocket PC para capturar e relatar defeitos em um cenário de produção. O exemplo requer o
controle RickInk do IntelliProg para trabalhar. Você pode fazer download do código fonte, o qual inclui a
aplicação .NET Compact Framework, Web Services e componentes de banco de dados. O projeto é um exemplo de
transferência de dados, não sincronização de dados.

Armazenando Dados no Pocket PC

O seguinte código mostra como persistir uma propriedade como Class no DataSet e arquivo XML local.
Public Sub Save()
' Save new or update existing item
' Dim ds as DataSet
Dim dr As DataRow
Dim expr As String = "ID = '" + m_ID + "'"

If m_NewItem Then
' Add new row to table
dr = frmMain.ds.Tables("Feedback").NewRow()
Else
' Find existing row
' Use the Select method to find row matching the filter
dr = frmMain.ds.Tables("Feedback").Select(expr)(0)
End If
' Set datarow properties
dr("ID") = m_ID
dr("PlantSection") = m_PlantSection
dr("Part") = m_Part
dr("DefectScope") = m_DefectScope
dr("ScopeID") = m_ScopeID
dr("DefectType") = m_DefectType
dr("RichInk") = m_RichInk

If m_NewItem Then
' Add row to dataset, if new item
frmMain.ds.Tables("Feedback").Rows.Add(dr)
End If

' Save data


frmMain.ds.AcceptChanges()
frmMain.ds.WriteXml(frmMain.dataSource)
End Sub
Trocando Dados com o Servidor

A figura abaixo mostra a troca de dados usando Web Services. Em uma solução corporativa, é comum usar uma
plataforma de integração como BizTalk Server.

Figura 2. XML Web Service trocando dados


O cliente do Pocket PC solicita o Web Service no servidor e passa o DataSet (ds) como dados:
Private Sub Synchronize()
Dim username As String = "JohnS"
Dim blnSuccess As Boolean
' Synchronize using XML Web Service
Cursor.Current = Cursors.WaitCursor
Dim wsFeedback As New wsFeedback.feedback
blnSuccess = wsFeedback.InsertFeedback(ds, username)
Cursor.Current = Cursors.Default
End Sub
O Web Service aceita o DataSet e passa o XML diretamente para Stored Procedure do SQL Server 2000, o qual usa
SQLXML e OPENXML para analisar o XML e inserir o novo dados na tabela apropriada.
< WebMethod()> _
Public Function InsertFeedback(ByVal ds As DataSet, ByVal username As String) As Boolean
Dim con As New SqlConnection(connectionstring)
Dim cmd As New SqlCommand("p_Feedback_i", con)
cmd.CommandType = CommandType.StoredProcedure

' Set parameters


Dim prmXML As SqlParameter = cmd.Parameters.Add("@XML", SqlDbType.NText)
prmXML.Direction = ParameterDirection.Input
prmXML.Value = ds.GetXml

Dim prmUsername As SqlParameter = cmd.Parameters.Add("@Username", SqlDbType.NVarChar)


prmUsername.Direction = ParameterDirection.Input
prmUsername.Value = username

Try
con.Open()
cmd.ExecuteNonQuery()
Catch ex As Exception
' Handle, log and re-throw error
Throw ex

Finally
con.Close()
End Try

Return True

End Function

The stored procedure inserts the new data:

CREATE PROCEDURE p_Feedback_i


@XML ntext,
@Username nvarchar(50)

AS

SET NOCOUNT ON

DECLARE @iDoc integer


DECLARE @Error integer
/* Create XML document. */
EXEC sp_xml_preparedocument @iDoc OUTPUT, @XML

/* Insert new records */


INSERT INTO Feedback
(
FeedbackID,
PlantSection,
Part,
DefectScope,
ScopeID,
DefectType,
RichInk,
Username
)

SELECT ID,
PlantSection,
Part,
DefectScope,
ScopeID,
DefectType,
RichInk,
@Username
FROM OPENXML (@iDoc, '/DataSet/Feedback',2) WITH
(
ID uniqueidentifier,
PlantSection int,
Part int,
DefectScope int,
ScopeID nvarchar(50),
DefectType int,
RichInk nvarchar(50)
)

SELECT @Error = @@ERROR


IF (@Error <> 0)
BEGIN
GOTO Errorhandler
END

/* Remove the XML document*/


EXEC sp_xml_removedocument @iDoc

RETURN

Errorhandler:

IF NOT @iDoc IS NULL


EXEC sp_xml_removedocument @iDoc

RAISERROR (@Error,16,1)

RETURN
A figura seguinte mostra troca de dados no SQL Server CE Remote Data Access e Merge Replication.
Figura 3. Troca de dados no banco de dados

Conclusão

Entendendo as características de dados, conectividade e arquitetura do sistema é preciso para determinar uma
estratégia de acesso a dados apropriada. É importante notar que uma solução Pocket PC pode ser oferecida através
de um número de diferentes cenários e situações. Entretanto, é completamente possível que uma estratégia de
acesso a dados possa definir o uso tanto de arquivos XML locais, banco de dados SQL Server CE, Web Services e
SQL Server CE Remote Data Access e Merge Replication dependendo da situação e características atuais.

O .NET Compact Framework deixa projetistas de sistemas e desenvolvedores escolher o método apropriado de
acesso a dados dependendo no número de diferentes cenários. Com uma estratégia de acesso a dados definidos e
novas ferramentas, é fácil ficar focado nos benefícios e projeto das soluções atuais.

Renato Haddad
rehaddad@msn.com
Microsoft Most Valuable Professional
Autor de diversos livros e ministra palestras e treinamentos sobre a tecnologia .NET.
Autor de diversos treinamentos multimídia de ASP.NET e Visual Studio .NET 2003.

Referências:
http://www.microsoft.com/windowsmobile/products/pocketpc/default.mspx
http://www.gotdotnet.com
http://www.smartdevelopers.microsoftdev.com
http://www.microsoft.com/windowsmobile/information/businesssolutions/wifi/default.mspx
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/guide_ppc/htm/
_ppc_writing_applications_for_pocket_pc_uwxf.asp

Visão Geral do .NET Compact Framework


Este artigo é uma tradução encontrado em http://msdn.microsoft.com/vstudio/device/compactfx.aspx
Introdução

O .NET Compact Framework é a plataforma de desenvolvimento para Smart Device da iniciativa Microsoft .NET e a
chave para atingir clientes com grandes experiências – a qualquer hora, qualquer lugar e em qualquer dispositivo.
O .NET Compact Framework trás ao mundo do código gerenciado e XML Web Services para Smart Devices, e habilita a
execução com segurança, download de aplicações em devices como Personal Digital Assistants (PDAs), telefones
celulares e outros afins.

Em função do .NET Compact Framework ser um sub-conjunto do .NET Framework, os desenvolvedores podem
facilmente usar os conhecimentos de programação e os códigos existentes através de dispositivos, desktop e
servidores. A Microsoft liberou no Visual Studio .NET 2003 o Smart Device Application, que contém o .NET Compact
Framework. Isso significa que qualquer um dos 4 milhões de desenvolvedores Visual Studio que tem experiência com o
NET Framework será capaz de desenvolver aplicações para qualquer dispositivo que rode .NET Compact Framework.
Devido a isso, muitos desenvolvedores Visual Studio .NET serão desenvolvedores para Smart Device, existirão
exponencialmente mais desenvolvedores para .NET Compact Framework que qualquer outro dispositivo ou plataforma
de programação móvel.
Além disso, pelo fato de compartilhar as ferramentas e modelos de programação do .NET Framework, o .NET Compact
Framework terá dramaticamente redução do custo, e acréscimo de eficiência para o desenvolvimento de aplicações para
Smart Devices. Companhias que acreditavam ter um alto custo no desenvolvimento de aplicações móveis, agora vão
experimentar a produtividade no processo de desenvolvimento sem precisar retreinar os desenvolvedores. Isto irá ajudar
as companhias futuramente a reduzir os custos para realizar negócios para desenvolver novas aplicações móveis que
aumentarão a eficiência dos empregados.

Oportunidades

Há alguns anos houve um enorme salto do número de dispositivos conectados. Mais de 10 milhões de PDAs foram
vendidos no ano de 2000. O mercado para telefones celulares está crescendo rapidamente. O uso da televisão digital
(digital TV) e os computadores em automóveis também cresceu, e será assim com o passar dos anos. A proliferação de
tecnologias sem fio, como 2.5G, 3G e 802.11b, significa que os dispositivos podem conectar você a rede a qualquer
momento. Desenvolvedores e comerciantes tem grande oportunidade de levar vantagem, mobilidade e conectar estes
novos dispositivos.
Entretanto, um número de barreiras deverá ser superado com o objetivo deste emergente mercado de aparelhos
conectados na realidade. Atualmente, existem diferentes Móbile CPUs e sistemas operacionais móveis que os
desenvolvedores tem um tempo para decidir como escrever aplicações e para que tipo de aparelho. As diversas
capacidades de CPUs e sistemas operacionais também aproximam como nunca a criação de aplicações para Smart
Device Applications.

Desenvolvedores também estão incertos quanto à criação para qual modelo de aplicação sem fio é mais apropriado.
Neste ponto, o Wireless Application Protocol (WAP) foi chamado de resposta “certa”, mas WAP requer conexão o tempo
todo e não precisa de uma rica ou interativa experiência para muitas aplicações finais. Entretanto, WAP não tem as
vantagens e recursos disponíveis nos PDA como conexão de rede sem fio.

Alguns destes problemas podem ser resolvidos por permitir aplicações cliente-servidor para rodar nos dispositivos
móveis, mas isso implica em mais obstáculos. Imagine a frustração dos usuários de telefones móveis que forçaram um
reboot nos telefones porque uma aplicação pobre escrita, ou o custo de rechamada do celular em função de uma falha
na aplicação. Uma barreira significativa é que as soluções do lado do cliente existentes atualmente são diferentes do
objetivo final do ambiente de programação que requer o retreinamento do desenvolvedor, o qual podem ser os custos de
muitas empresas.
O .NET Compact Framework

O .NET Compact Framework resolve muitos destes problemas e capacita grandes usuários experientes que irão dirigir a
próxima onda de aparelhos e serviços móveis. O .NET Compact Framework trouxe o modelo de programação do .NET
Framework para uma extensa quantidade de aparelhos, de PDAs para aparelhos novos, como um Smart Phone.

Código Compartilhado and Aumento da Eficiência

Em função do .NET Compact Framework entregar o mesmo modelo de programação através dos aparelhos, isto
simplifica o processo de desenvolvimento de aplicação que irão rodar em múltiplos aparelhos. Muitos dos códigos para
esta aplicação, como uma lógica de negócio, camada de acesso a dados, e camada de XML Web Service, podem ser
compartilhados através de múltiplos aparelhos e computadores. Isto aumenta consideravelmente a eficiência no
desenvolvimento de aplicações.

Grandes Experiências Adaptadas para Cada Aparelho

É importante notar que o .NET Compact Framework não pega um denominador comum baixo aproximado usado por
outras plataformas de programação de aparelhos. Desenvolvedores podem ter ampla portabilidade e reusabilidade para
escrever para o modelo de programação comum, mas o .NET Compact Framework é também desenhado para ser
extendido com Class Library que expõem características únicas para cada família de aparelhos, ou para um modelo de
aparelho específico. Isto permite aproximar desenvolvedores para criar as melhores possibilidades para cada aparelho,
enquanto usa novamente conhecimentos e códigos.
Características Enterprise-Class para capacitar mais Aparelhos

O sucesso dos aparelhos Microsoft Pocket PC está crescendo, em parte porque eles tem recursos computacionais
necessários para lidar com sofisticadas aplicações de negócios. O .NET Compact Framework tem vantagens para a
plataforma Pocket PC por prover avançados sistemas que tranqüilamente simplifica o processo de desenvolvimento de
aplicações para dispositivos Pocket PC.

Código Robusto, Execução Segura

O .NET Compact Framework oferece um robusto e seguro ambiente para executar código no lado do cliente. O modelo
de código gerenciado (managed code) suportado pelo .NET Compact Framework aumenta a confiabilidade do código,
assim reduz os defeitos no software. A execução do código gerenciado assegura que o comportamento da aplicação não
será capaz de travar o aparelho. Ao mesmo tempo, o modelo de segurança prova através do .NET Compact Framework
assegura que códigos maliciosos não permitirão obter acesso para os recursos de segurança do sistema. O modelo de
segurança também permite atualizar aplicações para serem distribuídas pela rede sem fio dentro de um caminho seguro,
diminuindo a possibilidade do custo do retorno do dispositivo.

Amplo Suporte para Aplicações Offline

Com o código seguro no cliente, o .NET Compact Framework capacita aplicações que podem ser executadas Offline. Isto
permite uma experiência tranqüila em relação a qualquer problema com conexão intermitente que possa ocorrer quando
se acessa uma rede. O desenvolvedor pode escolher a combinação certa de programação do lado cliente ou servidor
para interagir com a experiência do usuário.

Custos Reduzidos de Desenvolvimento na Criação de Novas Oportunidades

Esta novidade de desenvolvimento para o .NET Compact Framework irá liderar a ampla criação de aplicações e serviços
que irão ajudar empresas a vencerem grandes mercados para os dispositivos móveis e criar novas oportunidades para
desenvolvedores. Muitos negócios que não foram realizados por uma questão de custo ou o treinamento especial de
desenvolvedores para aplicações móveis serão capaz de desenvolver novas aplicações móveis muito mais eficientes, com
o objetivo de baixar os custos dos negócios, e aumentar as oportunidades de negócios.

Grande Experiência Móvel

O .NET Compact Framework facilitou para os desenvolvedores criar e instalar XML Web Services tão bem quanto
aplicações Smart Client sobre uma grande linha de dispositivos móveis.

• XML Web Services. Suporte para acessar XML Web Services está integrado para o .NET Compact Framework. Como o
desktop .NET Framework, invocando XML Web Service está tão simples como chamar uma função.

• Acesso a dados corporativos. Em Pocket PC, as aplicações podem usar a classe de acesso a dados Microsoft ADO.NET
para acessar os dados corporativos através de uma comunicação sem fio. Também de um Pocket PC, os e-mails estão
disponíveis através do Pocket Outlook®. Para pequenos dispositivos, como um telefone celular, dados corporativos e e-
mails podem estar disponíveis através da rede através de XML Web Services e o Microsoft Móbile Information Server.

• Comércio. O .NET Compact Framework inclui suporte para criptografia, então, as aplicações podem suportar operações
de e-commerce seguras através de redes públicas.

• Novidades e Informações. Aplicações podem entregar ou atualizar novidades e informações, como quantidade de
estoque em tempo real em dispositivos móveis que rodem o .NET Compact Framework.

• Jogos e Aplicações de Formulários. Par todos os dispositivos, incluindo pequenos dispositivos como um telefone celular,
desenvolvedores podem usar pequenos gráficos e desenhos suportados para escrever jogos e outros gráficos ou
aplicações de texto. Para dispositivos mais poderosos, como um Pocket PC, desenvolvedores tem acesso a todas as
capacidades gráficas e funcionalidades de formulários, incluindo controles que usam ADO.NET para acessar dados
corporativos.
O .NET Compact Framework não só provê acesso para todos estes serviços, mas permite aos desenvolvedores combiná-
los para fornecer aplicações e serviços para usuários finais.

Amplo Recurso Cliente

As características no .NET Compact Framework atrairão para uma grande faixa de clientes:
• Desenvolvedores. Pela primeira vez, desenvolvedores irão encontrar facilidade para desenvolver aplicações para
Smart Devices como é para criar aplicações para desktop. O .NET Compact Framework usa um subset do modelo de
programação do .NET Framework, no qual o objetivo que os desenvolvedores que estão criando aplicações .NET
conectadas para um desktop podem usar as mesmas ferramentas e modelo de programação para escrever aplicações
para estes dispositivos móveis.

• Usuários Finais. Os usuários finais terão o benefício de novas aplicações e serviços que irão rodar em PDAs, telefones
móveis, e outros dispositivos suportados pelo .NET Compact Framework. Estas aplicações e serviços conectam usuários
no mundo de dados que existem fora dos dispositivos com um robusto e seguro caminho. O mais importante, as
aplicações irão suportar a grande experiência do usuário com conexão de rede intermitente.

• Fabricantes de Dispositivos. Para suportar um consistente modelo de programação para desenvolvedores, o .NET
Compact Framework permite um mercado para aplicações e dispositivos que é muito grande que qualquer mercado de
dispositivos no mundo atualmente. Fabricantes de dispositivos o qual incluem o .NET Compact Framework nos
dispositivos terão acesso para este mercado e aumentarão as vendas. Em conseqüência, os fabricantes de dispositivos
facilitarão entregar bibliotecas de softwares especializados no .NET Compact Framework. Tais bibliotecas permitirão aos
desenvolvedores total uso de características especiais dos dispositivos.

• Operação em Rede. Companhias como telefonias celulares ou de operações de redes se beneficiarão da capacidade
de construir novos negócios com XML Web Services para entregar aplicações e serviços para dispositivos capacitados
com .NET.
Características Chaves e Benefícios

O .NET Compact Framework é um sub-conjunto do .NET Framework, e isto inclui muitas das mesmas características
chaves encontradas na versão completa do .NET Framework.
• Desenhado para dar base para XML Web Services. XML Web Services são um modelo de aplicação muito usado
para Smart Devices. Dispositivos conectados permitem a comunicação com uma gama de outros sistemas, e protocolos
padronizados de XML Web Services permitem aplicações se comunicarem com qualquer outra, sem levar em
consideração o sistema operacional ou a linguagem de programação. Como o .NET Framework, o .NET Compact
Framework é desenhado para uma base a ser a melhor plataforma de desenvolvimento para escrever e consumir XML
Web Services.

• Modelo de programação familiar para desenvolvedores desktop. O .NET Compact Framework usa o mesmo
modelo de programação do .NET Framework. Este tipo de familiaridade facilita para os desenvolvedores escreverem
novas aplicações para o .NET Compact Framework e criar com muita facilidade para aqueles que migrarão partes
existentes de aplicações no .NET Framework para o Smart Devices.

• Visual Studio .NET. É usada exatamente a mesma versão do Visual Studio .NET para criar aplicações desktop e
servidoras que rodarão com o .NET Compact Framework para escrever aplicações para estes dispositivos. Isso porque
o .NET Compact Framework usa o mesmo modelo de programação e ferramentas para versões desktop, os 4 milhões de
desenvolvedores Visual Studio que escrevem aplicações desktop atualmente experimentarão a facilidade para escrever
aplicações para Smart Devices rodando no .NET Compact Framework.

• Segurança. O .NET Compact Framework usa o mesmo modelo de segurança dos .NET Framework. Diferente de outros
dispositivos de programação de sistemas que tem um dos dois sem segurança, ou um rudimentar sistema próprio, o
modelo de segurança baseado em prova é flexível, permitindo aplicações com mais privilégios para acessar serviços
seguros. Por exemplo, uma aplicação compartilhada não assinada deveria acessar somente a os recursos básicos do
sistema, como a tela, mas a aplicação assinada e autenticada de um fabricante de dispositivo ou rede deveria controlar o
acesso aos recursos de segurança.

• Projetado para os recursos limitados dos dispositivos. O .NET Compact Framework é projetado para detalhar os
trabalhos em cima dos recursos limitados até dos pequenos dispositivos como telefones celulares. O próprio Framework
é eficiente e aproveita cuidadosamente os recursos do sistema para aplicações quando eles não são mais necessários.

• Alta performance. Aplicações rodando em cima do .NET Compact Framework serão executadas em códigos nativos, o
qual é produzido por um compilador Just-In-Time (JIT). O uso da tecnologia JIT fornece grande performance na
execução do código que outro dispositivo de sistema programado como um código interpretado.

• Eficiência elevada do desenvolvedor e redução dos custos de desenvolvimento. O modelo de programação


compartilhada e a caixa de ferramenta do Visual Studio .NET reduz drasticamente os custos do desenvolvimento de
aplicações móveis.
Sendo oportunista, declarar variáveis também deve apresentar uma preocupação com a visibilidade. Por exemplo, eu
não aconselho a seguinte declaração:

Renato Haddad
rehaddad@msn.com

Referências:
http://www.asp.net/mobile
http://www.gotdotnet.com
http://www.smartdevelopers.microsoftdev.com
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/guide_ppc/htm/
_ppc_writing_applications_for_pocket_pc_uwxf.asp

Chamando a API do Windows e DLLs

Por Mauro Sant'Anna (santanna@mas.com.br). Mauro é um "MSDN Regional Director" (www.microsoft.com/rd),


consultor e instrutor da MAS Informática (www.mas.com.br), tendo ministrado treinamentos na arquitetura .NET
desde outubro de 2000.
Chamar DLLs e funções (APIs) do Windows é algo necessário em algumas situações. Felizmente isto é
relativamente fácil de fazer em C# no .NET Framework®.

Os modelos de programação

O .NET Framework provê um modelo de programação muito superior ao que existia anteriormente no Windows:
 O .NET Framework usa classes e componentes ao invés de funções definidas em linguagem C;
 O .NET Framework tem tipagem forte; é muito mais difícil ou até impossível passar informações sem sentido
para uma função;
 O .NET Framework permite a criação de código gerenciado que não pode danificar o sistema de execução;
 O .NET Framework é muito mais simples produtivo.

Apesar de todas as vantagens, existem algumas (poucas) situações onde devemos "voltar" ao modelo antigo baseado
em funções:
 Um fornecedor de hardware, como de impressoras fiscais ou leitor de código de barras por exemplo, fornece
uma DLL não gerenciada (isto é, "comum do Windows") que devemos chamar de nossos aplicativos .NET;
 Precisamos de alguma funcionalidade do Windows que ainda não está disponível como classes gerenciadas.

É importante notar que um programa .NET perde várias de suas vantagens no momento que chama uma DLL não
gerenciada:
 Pode comprometer a integridade e segurança do sistema de runtime;
 Pela possibilidade de comprometer a segurança, o programa exige um privilégio de segurança alto para rodar;
 Deixa de ser portável para outras plataformas.

Chamar DLLs é uma das poucas coisas que o C# faz melhor que o VB.NET, já que o C# permite o uso de ponteiros,
muito convenientes e até necessários em várias chamadas de API.

De forma a mostrar como chamar as funções, vamos criar código para chamar três APIs do Windows: MessageBeep,
MessageBox e GetUserName. Não estou aqui dizendo que chamar APIs é a melhor forma de obter a funcionalidade
destas funções, as escolhi apenas porque mostram alguns conceitos importantes.

Criando uma DLL com declarações

A princípio, para acessar funções em DLL, devemos colocar algumas declarações em nosso programa. Por questões de
reuso de código e eventualmente de segurança, é muito interessante colocar estas declarações em uma DLL à parte.
Vamos então criar uma "Class Library" para conter estas declarações e, eventualmente, algum código de apoio. Peça a
criação de um novo projeto deste tipo no Visual Studio .NET®. Vamos dar o nome de DLLUtil:

CLSCompliant

É importante que você coloque o atributo CLSCompliant no arquivo AssemblyInfo.cs do projeto:

[assembly: System.CLSCompliant(true)]

Este atributo é opcional, mas garante que a sua DLL pode ser chamada por programas escritos em qualquer linguagem
do .NET Framework, incluindo Visual Basic .NET. Por exemplo, CLSCompliant impede o uso de inteiros sem sinal nos
elementos públicos, algo que o VB .NET teria dificuldades em usar.

Veremos a seguir alguns exemplos que mostram técnicas específicas.

MessageBeep

Esta é uma função bastante simples de chamar. Pesquisando no auxílio do próprio Visual Studio .NET,
achamos o protótipo da função em linguagem C:

BOOL MessageBeep(
UINT uType // sound type
);

A princípio, a tradução da função em .NET seria a seguinte:

bool MessageBeep(uint uType);

Infelizmente, os tipos inteiros sem sinal não são CLSCompliant. Por esta razão, iremos usar o tipo int, que
tem o mesmo tamanho mas com sinal:

bool MessageBeep(int uType);

Para chamar a função, precisamos adicionar alguns detalhes à função:


using System;
using System.Runtime.InteropServices;

namespace DLLUtil {
public class WinAPI {
[DllImport("User32.dll")]
public static extern bool MessageBeep(int uType);
}
}

Devemos colocar a linha "using System.Runtime.InteropServices" de forma a permitir o uso do atributo DllImport. Este
atributo indica que a função a seguir no código está em uma DLL. A função em questão deve obrigatoriamente ser static
extern, como imposição do .NET Framework. Além disto, public indica que a função pode ser chamada de outros
assemblies, que é justamente o que desejamos fazer neste caso.

MessageBeep

Esta função tem o seguinte protótipo em linguagem C:

int MessageBox(
HWND hWnd, // handle to owner
window
LPCTSTR lpText, // text in message
box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
);

Note que esta função aceita o tipo LPCSTR. Este tipo é uma constante string que será passada para o
Windows. A letra "C" indica que o Windows não irá alterar a string passada. Neste caso, o .NET
Framework automaticamente "converte" um tipo string para LPCSTR. O protótipo da em C# função é o
seguinte

[DllImport("User32.dll")]
public static extern int MessageBox(IntPtr
hWnd, string lpText, string lpCaption, int
uType);

Observe que o tipo correspondente a um Handle do Windows é o tipo System.IntPtr na plataforma .NET.

GetUserName

Esta função tem o seguinte protótipo em linguagem C:

BOOL GetUserName(
LPTSTR lpBuffer, // name buffer
LPDWORD nSize // size of name buffer
);

Agora temos algumas complicações a mais. Estamos passando um LPTSTR que representa o endereço de
um buffer para ser preenchido pelo Windows, além do endereço da variável que contém o tamanho da
string. Uma das saídas deste problema é usar ponteiros, em uma declaração semelhante à seguinte:

unsafe private static extern bool GetUserName(char * lpBuffer, int * nSize);


Observe que estamos declarando as funções como private. A razão disto é que vamos criar uma função sem o atributo
unsafe que chama as funções do Windows, escondendo a necessidade de manipular ponteiros das rotinas que usarem a
DLL. Esta outra função devolverá um tipo gerenciado, string.

Conjunto de caracteres

A maioria das funções que usam string no Windows NT/2000/XP vem em duas versões: uma para caracteres ANSI e
outra para caracteres Unicode. As funções ANSI têm um sufixo "A", enquanto as funções Unicode têm sufixo "W".
Podemos resolver este problema de duas formas:
 Declarar o nome da função com o sufixo;
 Usar o parâmetro CharSet no atributo DllImport, uma saída mais elegante.

Veja como ficam as declarações agora:

[DllImport("Advapi32.dll",
EntryPoint="GetUserName",
CharSet=CharSet.Unicode)]
unsafe private static extern bool
GetUserNameW(char * lpBuffer, int *
nSize);
[DllImport("Advapi32.dll",
EntryPoint="GetUserName",
CharSet=CharSet.Ansi)]
unsafe private static extern bool
GetUserNameA(sbyte * lpBuffer, int *
nSize);

Observe que os tipos passados variam conforme o uso da versão ANSI ou Unicode:
 "sbyte *" para as versões ANSI;
 "char *" para as versões Unicode.

Ambos os tipos podem ser convertidos para string usando construtores adequados de string, mostrado nos exemplos a
seguir.

Alocando o buffer

Observe que devemos alocar um "buffer" e passá-lo à API do Windows. Existem duas maneiras de fazer isso:
 Alocando o array na pilha com stackalloc;
 Alocando o array no heap gerenciado.

stackalloc

Esta é a maneira mais fácil. Neste caso, a variável é alocada na pilha da CPU e desalocada automaticamente
quando a função termina.

Veja o código usando stackalloc, em versões Unicode e ANSI:

public static string GetUserNameW1() {


unsafe {
// Ajusta o tamanho máximo
int Comp = 80;
// Aloca buffer
char * Nome = stackalloc
char[Comp];
// Chama função
GetUserNameW(Nome, &Comp);
// Converte para string e
volta o valor
return new string(Nome, 0,
Comp);
}
}
// Chama a versão ANSI de GetUserName,
alocando na pilha
public static string GetUserNameA1() {
unsafe {
// Ajusta o tamanho máximo
int Comp = 80;
// Aloca buffer
sbyte * Nome = stackalloc
sbyte[Comp];
// Chama função
GetUserNameA(Nome, &Comp);
// Converte para string e
volta o valor
return new string(Nome, 0,
Comp);
}
}

Heap gerenciado

No caso de alocarmos a variável no heap gerenciado, existe um truque a mais: devemos impedir que o
coletor de lixo mude a variável de lugar usando a instrução fixed. Esta instrução deve ser usada sempre que
pegamos o endereço de uma variável no heap e o atribuímos a uma variável ponteiro.

Veja o código alocando dados no heap gerenciado, em versões Unicode e ANSI:

// Chama a versão Unicode de GetUserName,


alocando no heap
public static string GetUserNameW2() {
unsafe {
// Ajusta o tamanho máximo
int Comp = 80;
// Aloca buffer e Impede o
GC de afetar esta variável
fixed (char * Nome = new
char[Comp]) {
// Chama função
GetUserNameW(Nome,
&Comp);
// Converte para
string e volta o valor
return new
string(Nome, 0, Comp);
}
}
}
// Chama a versão ANSI de GetUserName,
alocando no heap
public static string GetUserNameA2() {
unsafe {
// Ajusta o tamanho máximo
int Comp = 80;
// Aloca buffer e Impede o
GC de afetar esta variável
fixed (sbyte * Nome = new
sbyte [Comp]) {
// Chama função
GetUserNameA(Nome,
&Comp);
// Converte para
string e volta o valor
return new
string(Nome, 0, Comp);
}
}
}

Compilando
Para compilar código contendo uma instrução unsafe, devemos ajustar uma opção especial de compilação no projeto,
como mostrado a seguir:

Este ajuste impede que compilemos código inseguro sem querer, por exemplo, ao baixar um fonte na Internet.

Testando

Para testar as chamadas de API, criaremos um aplicativo do tipo "Windows Application" em VB. O formulário do
aplicativo será o seguinte:

O código acionado pelos eventos Click dos botões é o seguinte:

Private Sub Button1_Click(ByVal sender As


System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
DLLUtil.WinAPI.MessageBeep(0)
End Sub

Private Sub Button2_Click(ByVal sender As


System.Object, ByVal e As
System.EventArgs) Handles Button2.Click
DLLUtil.WinAPI.MessageBox(Handle,
"Ola", "ChamaDLL", 0)
End Sub

Private Sub Button3_Click(ByVal sender As


System.Object, ByVal e As
System.EventArgs) Handles Button3.Click
TextBox1.Text =
DLLUtil.WinAPI.GetUserNameA1()
End Sub
Private Sub Button4_Click(ByVal sender As
System.Object, ByVal e As
System.EventArgs) Handles Button4.Click
TextBox1.Text =
DLLUtil.WinAPI.GetUserNameA2()
End Sub

Private Sub Button5_Click(ByVal sender As


System.Object, ByVal e As
System.EventArgs) Handles Button5.Click
TextBox1.Text =
DLLUtil.WinAPI.GetUserNameW1()
End Sub

Private Sub Button6_Click(ByVal sender As


System.Object, ByVal e As
System.EventArgs) Handles Button6.Click
TextBox1.Text =
DLLUtil.WinAPI.GetUserNameW2()
End Sub

Questões de segurança

O programa de testes só rodará se ele tiver o privilégio "Full Trust". É possível modificar a DLL de forma que ela em si
exija "Full Trust", mas que os programas que a chamem não, mas isto está fora do escopo deste artigo.

Algumas restrições

Os três exemplos mostrados exigem técnicas que cobrem boa parte das funções que são encontradas na prática.
Existem algumas técnicas que não foram abordadas como o uso de structs e callbacks. Para quem estiver interessado
nestes casos, sugiro consulta ao Help no tópico "Consuming Unmanaged DLL Functions" ou observando os exemplos no
diretório do .NET Framework SDK \Samples\Technologies\Interop\PlatformInvoke.

Conclusão

Chamar DLLs e APIs do Windows requer alguns cuidados, mas não é uma tarefa difícil.

Usando Datasets Tipados

Por Mauro Sant'Anna. Mauro é um "MSDN Regional Director", consultor e instrutor da MAS Informática,
tendo ministrado treinamentos na arquitetura .NET desde outubro de 2000.

Uma das características da arquitetura de bancos de dados ADO.NET é a facilidade para trabalhar em modo
desconectado. Este modo é o que se adapta melhor ao uso em conjunto com servidores de aplicativos, onde
a manutenção de estado através de cursores ou conexões abertas é um sério empecilho a performance e
"escalabilidade".

O "Typed DataSet" é o tipo perfeito para ser usado como "moeda de troca" entre as diversas "camadas" do
seu aplicativo. Saber como criá-los e manipulá-los é essencial para utilizá-los bem.

Não pretendo neste artigo entrar em méritos de metodologia e projetos em múltiplas camadas, mas os
leitores que tiverem experiência com estas disciplinas com certeza conseguirão reconhecer nos Typed
Datasets grandes aliados.

Este artigo supõe uma certa familiaridade com o Visual Studio .NET e também que você tenha um servidor
de banco de dados SQL Server instalado para testes.

Criando um projeto de testes

Crie um novo projeto do tipo "Windows Application" em Visual Basic. Adicione ao formulário um componente
OleDbDataAdapter. A seguinte caixa de diálogo aparecerá:
Pressione "Next":

Vamos agora criar uma conexão à base "Northwind", um banco de dados de exemplo que acompanha o SQL
Server. Clique em "New Connection" e entre os dados abaixo (evidentemente o nome do servidor será
diferente, correspondendo a algum servidor na sua rede):
Clique "Ok"; você voltará à tela anterior, mas com a base "Northwind" selecionada:

Clique "Next":
Clique "Next" novamente e entre a query "select * from Products":

Clique em "Finish". Observe que foram criados vários componentes:


 OleDbConnection1: Visível no formulário; encapsula a conexão ao banco de dados;
 OleDbDataAdapter1: Visível no formulário; fará a comunicação entre o DataSet e a base de dados;
 OleDbSelectCommand1: Não visível no formulário; encapsula um comando SQL SELECT.
 OleDbInsertCommand1, OleDbUpdateCommand1, OleDbDeleteCommand1: Não visíveis no
formulário; encapsulam comandos SQL de atualização logicamente associados ao comando SQLECT.

É possível modificar os comandos SQL criados, caso eles não sejam adequados. Por exemplo, você pode
desejar omitir a atualização em um campo calculado ou de "autoincremento".

Criando Datasets tipados

Os DataSets tipados podem ser criados de diversas maneiras:

1. Adicionando um DataSet ao projeto em "Project | Add New Item | Dataset";

2. A partir de um componente DataAdapter, tendo como base o conjunto de resultado de uma consulta
a banco de dados.
No nosso caso, usaremos a segunda maneira. Clique com o botão direito no componente OleDbDataAdapter1
ou no formulário e selecione "Generate DataSet":

Criaremos um novo DataSet com o nome "Northwind_Products":

Entre o nome e clique "Ok". O projeto terá agora uma classe DataSet no "Solution Explorer" e uma instância
desta classe no formulário:
Observe que NorthWind_Products representa uma classe. Você pode pedir "Show All Files" no "Solution
Explorer" e abrir o arquivo "Northwind_Products.vb" que contém o código fonte desta classe. Este fonte foi
criado automaticamente e não faz sentido modificá-lo. Esta classe pode ser usada em diversas situações:

 Como tipo de parâmetro de uma função que implementa alguma regra de negócio ou atualiza a base
de dados;

 Como tipo de retorno de uma função que efetua uma consulta ao banco de dados;
 Como base para criação de instâncias não só do próprio DataSet, como também das tabelas e linhas
das tabelas que ele contém para manipulações em lote.

Já "Northwind_Products1" representa uma instância de NorthWind_Products. Esta instância pode ser usada
em diversas situações, por exemplo:

 Como argumento do método Fill de um componente DataAdapter para efetuar uma consulta e
preenchê-lo com o conjunto de resultado;

 Como argumento do método Update de um componente DataAdapter para atualizar a base de dados
usando os comandos de atualização associados ao DataAdapter;
 Para atualizações com código "batch" de programação;
 Como argumento de funções.

Preenchendo e atualizando

Veremos como usar o DataSet em um aplicativo. Acrescente componentes de forma a ter o formulário
parecido com o seguinte:
Note que o componente na parte inferior é um DataGrid. Ajustamos duas de suas propriedades, de forma a
se referir à tabela Products do nosso DataSet:

 DataSource: Northwind_Products1;

 DataMember: Products.

O preenchimento do DataSet é feito a partir do botão "Consulta" com o método Fill do DataAdapter:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button1.Click
' Preenche o DataSet com o resultado da consulta
OleDbDataAdapter1.Fill(Northwind_Products1)
End Sub

Note um detalhe importante: embora estejamos preenchendo o DataSet com o conteúdo completo de uma
tabela, este procedimento só deve ser feito com tabelas pequenas. O mais comum é usar uma query
parametrizada com uma cláusula WHERE, de forma a limitar o tamanho do conjunto de resultado.

Depois de preencher o DataSet, o usuário poderá atualizá-lo através de código ou diretamente no DataGrid.
Para enviar as atualizações de volta ao banco de dados (como no botão "Atualiza"), o código é o seguinte:

' Submete atualizações


Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles Button2.Click
' Verifica se existem mudanças
If Northwind_Products1.HasChanges() Then
' Pega as modificações
Dim Modificados As DataSet = Northwind_Products1.GetChanges()
' Existem mesmo?
If Not Modificados Is Nothing Then
' Submete modificações ao banco
OleDbDataAdapter1.Update(Modificados)
' Limpa buffer de dados modificados
Northwind_Products1.AcceptChanges()
End If
End If
End Sub

Observe que extraímos as mudanças para outro DataSet que por sua vez é passado como argumento do
método Update do DataAdapter. O método Update envia ao banco de dados as atualizações efetuadas em
uma instância em memória do DataSet.

Atualizações Batch

É possível atualizar a instância do DataSet em memória tanto através de controles vinculados, como no caso
do DataGrid, como também através de código. Veja alguns exemplos.

Atualiza um campo de todas as linhas

As tabelas dentro do DataSet não possuem "cursores" ou "registro corrente". Elas são acessadas como se
fossem arrays. Veja duas versões para código que aumenta preço de todos os produtos.

' Aumenta preço, versão 1


Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles Button3.Click
' Declara uma instância da linha
Dim Linha As Northwind_Products.ProductsRow
' Varre a tabela inteira
For Each Linha In Northwind_Products1.Products
' Modifica
Linha.UnitPrice *= 1 + (NumericUpDown1.Value / 100)
Next
End Sub

' Aumenta preço, versão 2


Private Sub Button8_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles Button8.Click
' Declara uma instância da linha
Dim Linha As Northwind_Products.ProductsRow
' Declara variável de controle de loop
Dim I As Integer
' Varre a tabela inteira
For I = 0 To Northwind_Products1.Products.Rows.Count - 1
' Pega a linha
Linha = Northwind_Products1.Products(I)
' Modifica
Linha.UnitPrice *= 1 + (NumericUpDown1.Value / 100)
Next
End Sub

Insere um registro

Podemos inserir registros passando os valores de cada campo. Estes valores podem vir de controles não-
vinculados, programas de importação ou qualquer outra fonte. Veja um exemplo que insere um registro com
valores fixos.

Private Sub Button6_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button6.Click
' Insere uma linha com valores específicos
Northwind_Products1.Products.AddProductsRow( _
"Cerveja Touro Azul", 10, 1, "12 12oz cans", 6.45D, 100, 250, 150, False)
End Sub

Pesquisa dada chave primária

Cada tabela de um DataSet tipado contém um método que pesquisa a existência de uma linha dada uma
chave primária. Veja um exemplo que pesquisa dado o código do produto.

Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button4.Click
' Converte ID para inteiro
Dim ID As Integer = Convert.ToInt32(TextBox1.Text)
' Declara uma instância da linha
Dim Linha As Northwind_Products.ProductsRow =
Northwind_Products1.Products.FindByProductID(ID)
' Achou alguma coisa?
If Linha Is Nothing Then
MsgBox("Não Achado")
Else
MsgBox("Achado: " & Linha.ProductName)
End If
End Sub

Filtros

Podemos criar um filtro com o componente DataView, mas uma alternativa é usar o método Select do
DataTable. Este método aceita uma expressão semelhante a uma cláusula WHERE do SQL e retorna um array
de linhas que atendem ao critério.

O exemplo abaixo efetua pesquisa dado o nome do produto.

Private Sub Button7_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button7.Click
' Declara um array de linhas
Dim Linhas() As DataRow
' Cria um filtro
Linhas = Northwind_Products1.Products.Select("ProductName
= '" & TextBox1.Text.Trim() & "'")
' Verifica se a pesquisa achou alguma coisa
If Linhas.Length > 0 Then
' Declara uma linha
Dim Linha As Northwind_Products.ProductsRow
' Pega a primeira linha
Linha = CType(Linhas(0), Northwind_Products.ProductsRow)
MsgBox(Linha.ProductID & " - " & Linha.ProductName)
Else
MsgBox("Não Achado")
End If
End Sub

Apagando

Para apagar um registro basta chamar o método Remove de DataTable. Usualmente efetuamos uma
pesquisa antes para saber que linha estamos apagando. O exemplo abaixo apaga um registro dado o código
do produto.

Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button5.Click
' Converte ID para inteiro
Dim ID As Integer = Convert.ToInt32(TextBox1.Text)
' Declara uma instância da linha
Dim Linha As Northwind_Products.ProductsRow =
Northwind_Products1.Products.FindByProductID(ID)
' Achou alguma coisa?
If Linha Is Nothing Then
MsgBox("Não Achado")
Else
Northwind_Products1.Products.Rows.Remove(Linha)
End If
End Sub

Conclusão

Os DataSets tipados facilitam bastante a manipulação de bancos de dados de forma desconectada.

Configuração com "Dynamic Properties"

Por Mauro Sant'Anna. Mauro é um "MSDN Regional Director", consultor e instrutor da MAS Informática,
tendo ministrado treinamentos na arquitetura .NET desde outubro de 2000.

Há algum tempo atrás eu escrevi um artigo mostrando como ler informações de configuração a partir de
arquivos XML implicitamente associados aos aplicativos. Existe outra maneira ligeiramente diferente de
manusear informações de configuração usando "Dynamic Properties", que descreverei neste artigo.

O que são Dynamic Properties

Uma "Dynamic Property" é uma propriedade de um componente cujo valor é armazenado no arquivo XML de
configuração associado ao aplicativo. Este arquivo de configuração pode ser:

 O arquivo "web.config" no caso dos aplicativos Web;

 O arquivo "aplicativo.exe.config" no diretório do próprio aplicativo.

O valor é armazenado na seção <configuration><appSettings> com o seguinte formato:

<add key="Nome" value="Valor"/>


Veja um exemplo de arquivo XML completo:
<?xml version="1.0" encoding="Windows-1252"?>
<configuration>
<appSettings>
<!-- User application and configured property settings go here.-->
<!-- Example: <add key="settingName" value="settingValue"/> -->
<add key="Form1.Text" value="<vazio>" />
<add key="textBox1.Text" value="Teste" />
</appSettings>
</configuration>
Até agora temos um esquema idêntico ao descrito anteriormente no outro artigo. No entanto, uma "Dynamic
Property" tem as seguintes diferenças:

 Ela é especificada na ferramenta RAD, em tempo de desenvolvimento;

 O esqueleto do arquivo XML é criado automaticamente em tempo de compilação, contendo os valores


presentes nas propriedades naquele momento;
 O código para ler o artigo é criado automaticamente.

Um exemplo

A título de exemplo, criaremos um novo aplicativo Windows com algumas "Dynamic Properties". Crie um
novo aplicativo "Windows Application" em Visual Basic.NET e dê o nome "DinProp":

Acrescente um TextBox, modifique a propriedade Text para "Teste" e as propriedades MaximizeBox e


MinimizeBox do Form1 para False:
Selecione o TextBox, clique no "+" à esquerda de "Dynamic Properties" e clique nas reticências ao lado de
"Advanced" para exibir a seguinte caixa de diálogo:

Selecione a propriedade "Text" e clique "Ok". Observe que "TextBox1.Text" aparecerá sob
"DynamicProperties":

Repita o que foi feito anteriormente para as propriedades MaximizeBox e MinimizeBox de Form1:
Compile o programa. Observe que duas coisas aconteceram:

1. O fonte criado pelo compilador inclui código para ler as propriedades:

Friend WithEvents TextBox1 As System.Windows.Forms.TextBox


<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Dim configurationAppSettings As System.Configuration.AppSettingsReader =
New System.Configuration.AppSettingsReader()
Me.TextBox1 = New System.Windows.Forms.TextBox()
Me.SuspendLayout()
'
'TextBox1
'
Me.TextBox1.Location = New System.Drawing.Point(16, 16)
Me.TextBox1.Name = "TextBox1"
Me.TextBox1.TabIndex = 0
Me.TextBox1.Text = CType(configurationAppSettings.GetValue("TextBox1.Text",
GetType(System.String)), String)
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(292, 266)
Me.Controls.AddRange(New System.Windows.Forms.Control() {Me.TextBox1})
Me.MaximizeBox = CType(configurationAppSettings.GetValue("Form1.MaximizeBox",
GetType(System.Boolean)), Boolean)
Me.MinimizeBox = CType(configurationAppSettings.GetValue("Form1.MinimizeBox",
GetType(System.Boolean)), Boolean)
Me.Name = "Form1"
Me.Text = "Form1"
Me.ResumeLayout(False)
End Sub

2. O arquivo XML foi automaticamente criado no diretório de saída:

Veja o arquivo XML gerado:


Você pode agora modificar o arquivo XML usando qualquer editor de texto como o Notepad:

Os novos valores serão automaticamente lidos ao executar o programa:

Tome cuidado, contudo, para não recompilar o programa e apagar os valores; execute-o a partir do Windows
Explorer.

Candidatos Naturais

As propriedades que são as maiores candidatas a serem "Dynamic Properties" são aquelas associadas a
recursos externos que podem mudar, como por exemplo ConnectionString de componentes de banco de
dados como sqlConnection.

Alguns componentes já vêm com "Dynamic Properties" sugerindo valores que podem ser armazenados
externamente, como por exemplo:

 Path de MessageQueue;

 Log, MachineName e Source de EventLog;


 Path de DirectoryEntry;
 CategoryName, CounterName, InstanceName e MachineName de PerformanceCounter.

Note que as propriedades cima, embora aparecendo sob "DynamicProperties", ainda não foram
efetivamente selecionadas. Elas são apenas uma sugestão, como no caso de MessageQueue, mostrado a
seguir:

Restrições

As "Dynamic Properties" são sempre armazenadas como strings e, portanto, podemos apenas armazenar
tipos que sejam facilmente conversíveis a partir de strings. Não podemos usar "Dymanic Properties" para
armazenar tipos complexos como instâncias de classes, listas e referências a outros componentes. A título
de exemplo, compare a lista de propriedades de um TextBox com suas possíveis "Dynamic Properties" e veja
como nem todas as propriedades estão disponíveis como "Dynamic Properties":
Conclusão

As "Dynamic Properties" permitem que se definam facilmente propriedades que devem ser lidas a partir de
arquivos de configuração, funcionando como uma espécie de "arquivo de resources" de fácil uso.
Questões de Tipos e Memória

Por Fabio Camara, MCP, desenvolvedor e autor de vários livros destacando "Projetos com Windows DNA
e .Net" e "Orientação a Objeto com .Net", ambos pela editora Visual Books.
Os programadores C/C++ sabem dos problemas de gerenciamento de memória, vide ponteiros / malloc /
free / new / delete e as instabilidades causadas pelos ponteiros não alocados, as questões de "vazamento"
de memória e etc.

Herdando dúvidas que não necessariamente nasceram com o C/C++ para o C#, mas que existem na maioria
das linguagens, vamos estudar a questão dos tipos e seu melhor (entenda por melhor: otimizado e
performático) armazenamento em memória. Estudaremos a principal diferença entre tipos valor e tipos
referência, como eles são armazenados em memória?

Corro o risco de você estar se questionando se é realmente necessário entender isto, pois com Garbage
Collector, Boxing e tipos primários derivados de objetos, repito o pensamento: _ É realmente necessário
entender sobre como os tipos estão armazenados em memória? - Nós sabemos que com o C/C++ a única
forma de fazer gerenciamento de memória é manualmente, e que o C# não tem este problema. Mesmo assim
espero que no final do texto consiga responder positivamente esta pergunta.

Questões sobre memória não devem ser preocupantes se você desenvolve aplicações pequenas (desktop),
que por mais utilizem mais e mais memória no caso de erros na liberação da mesma, são resolvidos quando
o computador é desligado. Entretanto, imagine servidores que não podem ser desligados, como liberar esta
memória reservada irregularmente e inutilmente? Como ficam estes tipos na memória? Como se
comportam? É uma questão assustadora.

A primeira coisa que me vem a cabeça para explicar, e bem óbvia por sinal, é que todos os dados,
independentemente do tipo, são simplesmente uma seqüência de bits, ou melhor, uma seqüência de "zeros"
e "uns". O exemplo mais simples disto é um tipo Char. Este tipo representa um caractere no conjunto de
caracteres Unicode usando um número, ou seja, ele é armazenado da mesma forma que um tipo uShort -
"unsigned short" (0 a 65535). Mas não pretendo explorar este assunto agora, vamos as questões de tipos
valor e tipos referência.

Tipos Valor

Os tipos valor são armazenados em pilha (stack) e podem ser número ou caractere. Pilha é um bloco de
memória que é incrementada cada vez que um método é chamado (entrada) e esvaziado quando o método
sai. Quando criamos uma variável local do tipo valor, usamos 8 bytes de memória stack. A variável local
armazena apenas (internamente no código do programa) o endereço de memória onde o conteúdo será
copiado - ou seja, 4 bytes para o segmento de memória e 4 bytes para o offset. Por isso o tipo é chamado de
"valor", ou seja, alteramos a cópia do conteúdo, mantendo o original intacto.

Tipos Referência

Este tipo (tipo referência) armazena na memória um endereço de um objeto na memória Heap. Enquanto o
objeto não é instanciado, o tipo referência será nulo em memória. Durante a execução do programa, o tipo
referência será associado na memória Heap e permanecerá nela sob o gerenciamento do Garbage Collector,
que no momento certo vai descarregar o objeto e limpar / esvaziar a memória. Quando criamos uma
variável local do tipo referência, usamos 4 bytes de memória stack. Neste caso, são necessários apenas 4
bytes porque o compilador já sabe que o conteúdo da variável está no heap e qualquer alteração feita na
referência, será feita diretamente na memória heap.

Na prática

Para criarmos tipos valor ou tipos referência, o construtor destes tipos pode ser chamado com a palavra
reservada (keyword) "new". A diferença entre a ação do construtor nos tipos é que no tipo valor
simplesmente inicializamos o objeto e no tipo referência criamos um novo objeto e então inicializamos o
objeto.

Vamos construir um exemplo elucidador sobre a questão:


using System;
namespace ConsoleApplication1
{
// Reference type
class typeR
{
public int a;
}
// Value type
struct typeV
{
public int a;
}
class Class1
{
static void Main(string[] args)
{
// Atribuindo valores com o construtor new
typeR x;
typeV y;
x = new typeR();
y = new typeV();
x.a = 5;
y.a = 5;
// Atribuindo valores por referencia
typeR w = x;
typeV z = y;
w.a = 8;
z.a = 8;
Console.WriteLine(x.a);
Console.WriteLine(y.a);
}
}
}

Se você é como eu que gosta de compilar o código na "cabeça" concluiu que o resultado impresso ao
executar o programa foi "8 e 8". Confira com a imagem:

Parece que nosso compilador por neurônios não esta funcionando corretamente?!? - Vamos entender passo a
passo o que acontece.

Apesar de declararmos as variáveis x e y, e as inicializarmos com o keyword "new", a variável do tipo valor
se comportou sutilmente diferente da variável do tipo referência. Quando atribuímos os valores das
variáveis w e z, a variável w conseguiu passar o novo valor para o endereço de memória aonde esta
armazenado o valor anteriormente designado a x, porém para z isto não foi possível.

Vou imprimir todos os valores agora e isso tornará mais simples a compreensão.

Adicionei as seguintes linhas ao código existente:


Console.WriteLine(w.a);
Console.WriteLine(z.a);

O resultado da execução foi o seguinte:

Agora realmente ficou mais fácil. Recorda quando expliquei que o tipo valor reserva espaço na pilha (stack)
previamente a ser inicializado? É por este motivo que o seu conteúdo armazenado só pode ser alterado pela
própria variável declarada, ou seja, apenas atribuindo o valor 8 para a variável y conseguiríamos uma
igualdade em todos os resultados. Ao contrário do objeto em memória Heap que pode ser "ponteirado" por
muitas variáveis.

Concluindo, seria muito precipitado afirmar que devemos utilizar apenas tipos referência, pois existem casos
e casos. Contudo, face a "economia" de memória e a comodidade do gerenciamento para destruição do
Garbage Collector, é este tipo que deverá ser usado na maioria dos casos.

Fabio Camara

Mais loucuras na ligação de dados do Avalon


Chris Sells
MSDN
11 de outubro de 2004
Resumo: Chris Sells aprimora seu aplicativo Paciência adicionando seletores de estilos ao mix existente de ligação de
dados. Este artigo também contém links para páginas em inglês. (12 páginas impressas)
Baixe o arquivo de exemplo lhsol6.msi.
Você deve se lembrar de que, no último artigo desta série (em inglês), tínhamos uma coleção de objetos Person ligados
a um controle ListBox e a dois controles TextBox, e estávamos testando a visualização para executar a filtragem e a
classificação e também um transformador de dados para fazer a ligação com a cor de primeiro plano, Foreground, no
estilo de um item, como mostra a Figura 1.

Figura 1. A ligação de dados do Avalon em ação


As partes do XAML mais importantes são mostradas no trecho de código abaixo:
<Window.Resources>

<TransformerSource

def:Name="AgeTransformer"

TypeName="PersonBinding.AgeTransformer" />

<Style def:Name="PersonStyle">

<Style.VisualTree>

<FlowPanel>

<Text TextContent="*Bind(Path=Name)" />

<Text TextContent=":" />

<Text

TextContent="*Bind(Path=Age)"

Foreground="*Bind(Path=Age;Transformer={AgeTransformer})" />
<Text TextContent=" years old" />

</FlowPanel>

</Style.VisualTree>

</Style>

</Window.Resources>

<GridPanel Columns="2">

<Text>Persons</Text>

<ListBox ItemStyle="{PersonStyle}" ItemsSource="*Bind()" />

...

</GridPanel>

Início da página

Seletores de estilos
A combinação de estilos e transformadores cria um par poderoso, que permite a geração de elementos da interface do
usuário e a definição dinâmica de suas propriedades com base nos dados. Entretanto, não existe uma instrução "if" no
XAML que permita decidir, com base nos dados, quais elementos da interface do usuário devem ser exibidos. Por
exemplo, se você quisesse ocultar a idade e mostrar uma seqüência de caracteres quando ela atingisse um determinado
valor, o melhor que poderia fazer seria definir o atributo Visibility nos elementos da interface do usuário, como a
seguir:
<Text

TextContent="*Bind(Path=Age)"

Visibility="*Bind(Path=Age;Transformer={YoungVisibleTransformer})" />

<Text

TextContent="Wow! That's old!"

Visibility="*Bind(Path=Age;Transformer={OldVisibleTransformer})" />

Nesse caso, estamos ligando a propriedade Age duas vezes: uma para o primeiro elemento Text e outra para o segundo.
Caso a idade não fosse muito avançada, digamos abaixo de 30, YoungVisibleTransformer retornaria um valor
verdadeiro e OldVisibleTransformer retornaria falso, mostrando o primeiro elemento Text, mas não o segundo. No
caso de uma pessoa com 30 anos ou mais, YoungVisibleTransformer retornaria falso e OldVisibleTransformer
retornaria verdadeiro, o que ocultaria o primeiro elemento Text e mostraria o segundo. Até mesmo para algo tão
simples, isso é difícil de acompanhar, e requer duas classes. Portanto, tenho certeza de que você está esperando que eu
lhe mostre uma maneira mais fácil. E eu vou mostrar: os seletores de estilos.
Um seletor de estilos é uma parte do código que decide qual estilo deve ser aplicado aos dados com base nos próprios
dados. Por exemplo, para resolver nosso problema de mostrar/ocultar a idade, imagine que tivéssemos dois estilos:
<Style def:Name="YoungPersonStyle">

<Style.VisualTree>

<FlowPanel>

<Text TextContent="*Bind(Path=Name)" />

<Text TextContent=":" />

<Text TextContent="*Bind(Path=Age)" />

<Text TextContent=" years old" />

</FlowPanel>

</Style.VisualTree>

</Style>

<Style def:Name="OldPersonStyle">
<Style.VisualTree>

<FlowPanel>

<Text TextContent="*Bind(Path=Name)" />

<Text TextContent=":" />

<Text TextContent="Wow! That's old!" />

</FlowPanel>

</Style.VisualTree>

</Style>

Aqui, a única diferença entre os estilos é a presença de dois elementos Text, mas você pode imaginar qualquer
quantidade de estilos, todos completamente diferentes, dependendo do que deseja mostrar.
Um seletor de estilos personalizado é implementado como uma classe, que é derivada da classe de base StyleSelector
e substitui o método único, chamado SelectStyle:
public class PersonStyleSelector : StyleSelector {

Style youngStyle;

Style oldStyle;

public PersonStyleSelector(Style youngStyle, Style oldStyle) {

this.youngStyle = youngStyle;

this.oldStyle = oldStyle;

public override

Style SelectStyle(object item, FrameworkElement container) {

Person person = (Person)item;

if( person.Age < 30 ) return this.youngStyle;

return this.oldStyle;

Lembre-se de que estávamos aplicando PersonStyle usando o atributo ItemStyle em ListBox:


<ListBox ItemStyle="{PersonStyle}" ... />

Para inserir o seletor de estilos, é preciso definir ItemStyleSelector. Entretanto, como eu precisava criar uma instância
do PersonStyleSelector com os estilos passados para o construtor, defini a propriedade ItemStyleSelector da caixa
de listagem no construtor da janela:
void Window1_Loaded(object sender, EventArgs e) {

Style personStyle = (Style)this.Resources["PersonStyle"];

Style oldPersonStyle = (Style)this.Resources["OldPersonStyle"];

// Assumes an ID attribute set on the <ListBox> XAML element:

// <ListBox ID="personsListBox" ItemsSource="*Bind()" />

personsListBox.ItemStyleSelector =
new PersonStyleSelector(personStyle, oldPersonStyle);

...

Com o seletor de estilos inserido, cada item obtém seu estilo em tempo de execução com base nos dados, como mostra
a Figura 2.

Figura 2. Um seletor de estilos personalizado em ação


Um ponto a ser observado é que os seletores de estilos são usados uma vez, quando os dados são mostrados pela
primeira vez, e também quando a visualização é atualizada, mas não quando os dados são alterados. Isso seria mais
eficaz se a nossa interface do usuário de amostra tornasse o campo Age somente leitura. Entretanto, as versões futuras
do "Avalon", codinome do subsistema de apresentação do Longhorn, deverão incluir um recurso de seleção no estilo de
seletor que leve em conta as alterações reais nos dados.
Início da página

Outras fontes de dados


A combinação dos estilos e da ligação de dados oferece toda a flexibilidade para determinar como os dados são exibidos
no Avalon. O Avalon é igualmente flexível quando se trata da fonte dos dados. Por exemplo, a coleção de objetos Person
foi criada no manipulador de eventos Loaded da classe da janela principal. Entretanto, se eu quisesse, poderia fazer
com que a lista fosse criada como resultado de uma operação declarativa no XAML:
<Window.Resources>

<ObjectDataSource

def:Name="PersonsSource"

TypeName="PersonBinding.PersonsSource" />

...

</Window.Resources>

<GridPanel

ID="gridPanel"

Columns="2"

DataContext="*Bind(DataSource={PersonsSource})">

...

</GridPanel>

O elemento ObjectDataSource, assim como o elemento TransformerSource, usa um TypeName para criar uma
instância de um tipo CLR. O elemento def:Name de ObjectDataSource é, então, usado como o valor do atributo
DataContext do elemento GridPanel. Isso faz com que seja criada uma instância de PersonsSource, que eu
implementei como uma extensão da classe ArrayListDataCollection que preenche a si própria no momento da criação:
public class PersonsSource : ArrayListDataCollection {

public PersonsSource() {
this.Add(new Person("John", 10));

this.Add(new Person("Tom", 8));

É muito útil poder fazer isso, mas, pelo menos na compilação WinHEC do Longhorn, não é possível definir DataContext
no mesmo escopo em que ObjectDataSource é declarado. Nesse caso, isso significa mover DataContext para
GridPanel em vez de deixá-lo na própria janela.
Assim como todos os elementos do XAML, ObjectDataSource é apenas uma classe CLR. Especificamente, essa é uma
classe que implementa a interface IDataSource. Além dos elementos gráficos de objetos CLR, a ligação de dados do
Avalon também suporta a ligação a dados SQL, WinFS e XML, todos com implementações similares de IDataSource
(SqlDataSource, WinFSDataSource e XmlDataSource, respectivamente).
Início da página

O que isso tem a ver com o aplicativo Paciência?


Como você deve se lembrar, em Another Step Down the Longhorn Road (em inglês), eu estava criando uma versão do
Solitaire em Longhorn. E deve estar se perguntando: o que todo esse papo de ligação de dados tem a ver com o jogo
Paciência? Antes de colocar a ligação de dados do Avalon em uso, eu estava populando o controle PileOfCards pela
enumeração da estrutura de dados da pilha subjacente e pela adição de itens a um controle de caixa de listagem:
public partial class PileOfCards : Canvas {

Stack stack;

public Stack StackOfCards {

get { return this.stack; }

set {

this.stack = value;

ShowStack();

void ShowStack() {

cardList.Items.Clear();

foreach( Card card in this.stack ) {

string cardValue =

string.Format("{0}{1}", card.Value, card.Suit);

if( !card.Flipped )

cardValue = "[" + cardValue + "]";

// Fills XAML ListBox declared like so:

// <ListBox ID="cardList" />

cardList.Items.Add(cardValue);

}
Isso produzia uma exibição somente leitura, como a da Figura 3.

Figura 3. Paciência em Longhorn antes da ligação de dados


Se eu continuasse com esse esquema, não só teria um visual sem graça, como também teria que atualizar manualmente
o controle de caixa de listagem sempre que os dados subjacentes do jogo Paciência fossem alterados. Com as técnicas
de ligação de dados demonstradas anteriormente neste artigo e no anterior, cheguei ao resultado que você vê na Figura
4 sem precisar mover manualmente os dados entre os controles e os dados subjacentes.

Figura 4. Paciência em Longhorn usando a ligação de dados


A implementação da propriedade PileOfCards.Stack torna-se uma simples questão de definir o DataContext do controle:
public partial class PileOfCards : Canvas {

public Stack StackOfCards {

get { return this.DataContext as Stack; }

set {

this.DataContext = value;

...

Para oferecer suporte à reflexão das alterações nos dados subjacentes, o mecanismo do jogo Paciência foi atualizado
para usar IPropertyChange e ICollectionChange:
public class Card : IPropertyChange {

public event PropertyChangedEventHandler PropertyChanged;


void RaisePropertyChanged(string propertyName) {

if( this.PropertyChanged != null )

PropertyChanged(this,

new PropertyChangedEventArgs(propertyName));

...

bool flipped;

public bool Flipped {

get { return this.flipped; }

set {

this.flipped = value;

RaisePropertyChanged("Flipped");

public abstract class Stack : IEnumerable, ICollectionChange {

public event CollectionChangeEventHandler CollectionChanged;

void

RaiseCollectionChanged(CollectionChangeAction action, object obj) {

if( this.CollectionChanged != null )

CollectionChanged(this,

new CollectionChangeEventArgs(action, obj));

internal Card[] GrabCards(int i) {

...

RaiseCollectionChanged(CollectionChangeAction.Refresh, null);

...

...

A definição do estilo das cartas da lista foi realizada por meio de estilos, da ligação de dados e da transformação:
<Canvas.Resources>

<TransformerSource
def:Name="FlippedTransformer"

TypeName="CardControls.FlippedTransformer,CardControls" />

<TransformerSource

def:Name="SuitTransformer"

TypeName="CardControls.SuitTransformer,CardControls" />

<TransformerSource

def:Name="ValueTransformer"

TypeName="CardControls.ValueTransformer,CardControls" />

<Style def:Name="*typeof(sol:Card)">

<Style.VisualTree>

<FlowPanel>

<Text

TextContent="["

Visibility="*Bind(Path=Flipped;Transformer={FlippedTransformer})"

Foreground="*Bind(Path=Suit;Transformer={SuitTransformer})" />

<Text

TextContent="*Bind(Path=Value;Transformer={ValueTransformer})"

Foreground="*Bind(Path=Suit;Transformer={SuitTransformer})" />

<Text

FontFamily="Symbol"

TextContent="*Bind(Path=Suit;Transformer={SuitTransformer})"

Foreground="*Bind(Path=Suit;Transformer={SuitTransformer})" />

<Text TextContent="]"

Visibility="*Bind(Path=Flipped;Transformer={FlippedTransformer})"

Foreground="*Bind(Path=Suit;Transformer={SuitTransformer})" />

</FlowPanel>

</Style.VisualTree>

</Style>

</Canvas.Resources>

<ListBox ID="cardList" ItemsSource="*Bind()" FontSize="24pt" />


Deixarei que você mesmo analise os pequenos detalhes do código-fonte, mas alguns pontos devem ser destacados. Por
exemplo, observe que ListBox não tem um atributo ItemsStyle. Em vez disso, a correspondência entre o estilo e as
cartas da lista é feita usando o atributo def:Name do elemento Style definido como *typeof(sol:Card). O prefixo "sol" é
um prefixo de espaço para nome XML definido para fazer referência à DLL na qual o tipo Card do jogo Paciência está
definido (como discutido em Another Step Down the Longhorn Road, artigo em inglês). Na compilação WinHEC do
Longhorn, esses mapeamentos para estilos com base em tipo só funcionam quando o tipo é definido em uma DLL
separada, mas eu prefiro que eles sejam mapeados manualmente para um estilo no controle de caixa de listagem,
principalmente porque pode haver listas heterogêneas, e porque tipos diferentes na mesma lista podem ter estilos
diferentes (mais uma forma de mapear estilos para itens em tempo de execução).
Outro ponto importante a realçar é o uso de um transformador Visibility para colocar ou não uma carta entre colchetes,
dependendo de ela estar aberta ou fechada. Ou seja, [A♣] é a carta fechada, enquanto A♣ é a carta aberta.
Finalmente, o transformador do qual tenho mais orgulho é o do baralho. Observe que o atributo FontFamily do
elemento Text é codificado para Symbol. É daí que vêm os elementos gráficos do baralho. A implementação do
SuitTransformer mapeia o valor de enumeração Suit para a cor e o caractere da fonte Symbol apropriados, dependendo
do atributo que está definindo:
public class SuitTransformer : IDataTransformer {

...

public object

Transform(object obj, DependencyProperty dp, CultureInfo culture) {

// Heart, Diamond, Spade and Club in Symbol font

string textContents = "©¨ª§";

Brush[] foregrounds = {

Brushes.Red, Brushes.Red, Brushes.Black, Brushes.Black };

switch( dp.Name ) {

case "Foreground":

return foregrounds[(int)obj];

break;

case "TextContent":

return textContents[(int)obj].ToString();

break;

default:

return obj;

break;

Outra coisa que preciso fazer é adicionar filtros para que apenas as três cartas de cima sejam exibidas na pilha de
descarte, apenas a carta de cima do baralho seja empilhada, e assim por diante. Mesmo assim, é impressionante ver até
onde consegui chegar com a ligação de dados do Avalon (além disso, Peter Stern forneceu alguns elementos gráficos
para que eu usasse quando descobrisse como inseri-los).
Início da página

Algumas questões
Mas nem tudo é perfeito no Avalon. Quer dizer, o Longhorn ainda vai levar alguns... anos para ser lançado. Além das
questões já mencionadas, veja aqui algumas outras que encontrei pelo caminho:
Embora eu estivesse usando a ligação de dados, ao remover uma pilha de cartas para executar um processo de

arrastar e soltar, eu tive que atualizar manualmente o controle para mostrar que as cartas haviam saído da pilha
durante esse processo. Espero que isso seja simplificado nas compilações futuras.
Até mesmo para iniciar o processo de arrastar e soltar, precisei fazer testes para descobrir qual carta estava sendo

clicada. Isso é algo difícil de descobrir e ainda mais difícil de fazer funcionar (especialmente porque não consegui fazer
com que o ItemUIGenerator.IndexFromUI funcionasse na compilação WinHEC). Com certeza isso deve ficar mais
simples no futuro.
O mecanismo do jogo Paciência implementou o IEnumerable<Card> em sua estrutura de dados Stack, o que era

exatamente a coisa certa a ser feita. No entanto, tive que trocá-lo por IEnumerable porque o IEnumerable genérico
ainda não tem o suporte da ligação de dados do Avalon (embora deva ter nas compilações futuras).
Por mais perfeito que o seletor de estilos parecesse ser para as cartas abertas x fechadas, não consegui usá-lo porque

ele não era dinâmico o bastante. Por exemplo, o método SelectStyle não foi chamado para alternar os estilos quando
a propriedade Flipped foi alterada. Por acaso, eu sei que isso é um problema do design, mas existe a esperança de
que haja uma forma mais dinâmica de alternar estilos com base em alterações de dados nas compilações futuras do
Longhorn, mas é tão difícil sentar e esperar!
Início da página

Em que ponto estamos?


Você precisa saber que a ligação de dados do Avalon não é apenas o que mencionamos aqui. Recursos e
aprimoramentos adicionais estão previstos para as versões futuras, e eles nem foram mencionados aqui: o uso de várias
visualizações dos mesmos dados, visualizações sem dados ou relacionamentos entre mestres e detalhes. Ainda assim, ao
adicionar a ligação de dados ao meu aplicativo, percebi que o modelo Avalon de ligação de dados era diferente daqueles
que usei no Windows Forms ou no ASP.NET.
No Windows Forms, a ligação de dados tem a ver com a definição de valores de propriedade ou o preenchimento de
coleções de propriedades (a coleção Items de um controle ListBox, por exemplo). No ASP.NET, a ligação de dados tem a
ver com a geração de HTML a ser retornado ao cliente (os elementos de opção de um controle de seleção HTML, por
exemplo).
O Avalon se parece com o ASP.NET devido à sintaxe XAML, mas não estamos gerando mais código XAML. O Avalon
também se parece com o modelo do Windows Forms, mas não estamos simplesmente adicionando seqüências de
caracteres à coleção Items. Em vez disso, a ligação de dados do Avalon é algo novo. O XAML em si é estático e, muitas
vezes, compilado no aplicativo, mas estamos gerando elementos de interface do usuário na árvore visual em tempo de
execução. Portanto, é pouco provável que os clientes inteligentes conectados a um servidor para gerar conteúdo
dinâmico coletem o XAML; em vez disso, eles coletarão os dados para estabelecer a ligação com os estilos do Avalon
definidos no XAML usando, nesse processo, estilos, seletores de estilos e transformadores para tomar decisões sobre
quais elementos devem ser gerados com base nos dados.
Início da página

Referências
• Crazy About Avalon Data Binding (em inglês)
Avalon Styles Overview, Longhorn SDK (em

inglês)

• Avalon Data Binding, Longhorn SDK (em inglês)

• Grupo de notícias do Avalon (em inglês)


Chris Sells é Estrategista de conteúdo do MSDN Online e atualmente trabalha com o Longhorn, o próximo sistema
operacional da Microsoft. Ele já escreveu diversos livros, inclusive Mastering Visual Studio .NET e Windows Forms
Programming in C# (em inglês). Nas horas vagas, Chris realiza várias conferências, dirige o projeto Genghis de código-
fonte disponível, brinca com o Rotor e, geralmente, passeia pelo mundo dos blogs. Mais informações sobre Chris e seus
vários projetos podem ser encontradas em http://www.sellsbrothers.com/.

Hospedando um controle de janelas nativo dentro de um controle de


formulários do Microsoft® .NET Compact Framework
Autor: Peter Foot
Microsoft Embedded MVP
In The Hand Ltd
Data: Outubro de 2004
Aplica-se a:
Microsoft® Windows® CE
Microsoft® Windows® CE .NET
Resumo: Este artigo demonstrará uma técnica para hospedagem de controles de janelas nativos, neste exemplo o
controle HTML WebBrowser, dentro de um controle personalizado do .NET Compact Framework. O método descrito
oferece suporte à comunicação bidirecional completa com o controle nativo, para que eventos possam ser gerados
imediatamente após a interação do usuário. (6 páginas impressas)
Nesta página
Introdução
Se você já investiu na criação de um controle de janela nativo e deseja usar isso em seu código do .NET Compact
Framework, ou se deseja tirar proveito de alguns dos controles intrínsecos do Windows CE aos quais o .NET Compact
Framework não oferece suporte direto, você precisará de um método de interoperação com controles de janela nativos.
O OpenNETCF Smart Device Framework tem uma nova implementação personalizada usando a classe MessageWindow.
Ele usa Platform Invoke para criar uma janela visível que é capaz de hospedar controles nativos. Este artigo tratará da
criação de um controle personalizado para hospedar um controle de janela nativo – meu exemplo será um controle
gerenciado baseado no controle HTML disponível no Windows CE.
Início da página

Sobre a classe ControlEx


Como em qualquer controle personalizado do .NET Compact Framework, o desenvolvimento começa com uma classe que
é derivada de System.Windows.Forms.Control. Entretanto, para manipular grande parte dos mínimos detalhes
necessários à hospedagem de um controle nativo, criei uma classe OpenNETCF.Windows.Forms.ControlEx.
A classe ControlEx funciona hospedando uma classe MessageWindow modificada. Devido ao fato de a MessageWindow
ser criada como uma janela nativa invisível de 0 por 0 pixels, ela é um coletor perfeito para mensagens de janelas, mas
não pode executar nenhuma função útil como parte da interface do usuário. Portanto, usamos Platform Invoke para
alterar as propriedades dessa janela a fim de torná-la visível e, assim, possibilitar que ela hospede outros controles.
A ControlEx cuida da criação de uma ControlMessageWindow e da criação do controle nativo de sua escolha. No tempo
de execução, ControlMessageWindow recebe todas as mensagens de notificação do controle nativo e as transmite ao
método OnNotifyMessage da classe derivada ControlEx, neste caso o WebBrowser. ControlEx responde automaticamente
a eventos como Redimensionar, mudanças de Foco, etc., e redimensiona o controle nativo automaticamente.
Quando uma nova ControlMessageWindow é criada, é atribuída uma referência ao controle gerenciado pai. Usando
Platform Invoke, o método de API SetWindowParent é chamado para tornar ControlMessageWindow um filho da classe
derivada ControlEx. A própria classe ControlEx cria o controle nativo como filho de ControlMessageWindow. Desta forma,
as mensagens de notificação do controle nativo são recebidas por MessageWindow e passadas de volta para a classe
derivada ControlEx gerenciada.

Figura 1 - Como os códigos nativo e gerenciado interagem


Funcionalidade padrão
A classe ControlEx tem uma implementação interna para várias propriedades que serão úteis para seu controle derivado,
inclusive BorderStyle, Handle, Name e Tag.
Início da página

Identificadores de janela
O .NET Compact Framework não expõe os identificadores de janela nativos para controles. A classe ControlEx
implementa a interface IWin32Window, que está presente no .NET Framework completo, mas é adicionada através do
OpenNETCF ao .NET Compact Framework. Essa interface define uma propriedade Handle – ela é usada para expor o
identificador de janela do próprio controle gerenciado. Isso é determinado definindo Capture no controle e usando a
função de API GetCapture para retornar o identificador de janela:
/// <summary>

/// Native Window Handle

/// </summary>

public IntPtr Handle

get

if(m_handle==IntPtr.Zero)

this.Capture = true;

m_handle = OpenNETCF.Win32.Win32Window.GetCapture();

this.Capture = false;

return m_handle;

De forma semelhante, ControlEx expõe uma propriedade ChildHandle para as classes derivadas; isso fornece acesso fácil
ao identificador de janela (HWND) do controle nativo que estamos hospedando. Ele é muito usado, porque as
propriedades e os métodos são invocados no controle nativo através do envio de mensagens para este identificador.
Início da página

Suporte ao designer
O código da classe ControlEx foi escrito para fornecer grande parte do suporte básico em tempo de criação. Ele trata do
controle que está sendo redimensionado no formulário e simplesmente o preenche com um retângulo nos controles
BackColor. Se BorderStyle for definido como FixedSingle ou Fixed3D, então uma caixa preta é desenhada ao redor do
perímetro do controle (não há suporte para uma aparência 3D para a borda). Controles individuais derivados de
ControlEx podem fornecer comportamento de tempo de projeto adicional, conforme apropriado, substituindo o método
OnPaint.
Início da página

O controle WebBrowser
Ao manipular este código genérico na classe ControlEx, estamos reduzindo a carga de trabalho necessária para criar o
próprio controle de invólucro gerenciado individual. O único código extra necessário no construtor para o controle
WebBrowser é para invocar a função de API InitHtmlControl a fim de preparar o controle para uso. Então, definimos
CreateParams da classe ControlEx com o nome de classe "DISPLAYCLASS".
public WebBrowser() : base(true)

//new stack (for history urls)

m_history = new Stack();

//load htmlview module

IntPtr module = Core.LoadLibrary("htmlview.dll");

//init htmlcontrol

int result = InitHTMLControl(Instance);


//set the html specific class name of the native control

this.CreateParams.ClassName = "DISPLAYCLASS";

O nome da classe para o controle nativo pode ser encontrado no arquivo de cabeçalho do controle – você precisará do
SDK para sua plataforma de destino, que deve conter htmlctrl.h. O arquivo de cabeçalho do controle nativo também será
necessário como referência para começar a conectar as propriedades, os métodos e os eventos para o controle
específico. No exemplo do WebBrowser, incluí todas as constantes e estruturas necessárias dentro do código do
WebBrowser.
Início da página

Comunicação com o controle nativo


Estando o construtor pronto para criar o controle nativo, um projeto de teste pode ser criado e o controle adicionado a
ele através de código (ainda não construímos um conjunto módulos do designer), e ele exibirá o WebBrowser.
Entretanto, ainda não há nenhuma funcionalidade para navegar ou definir outras propriedades para o controle.
A comunicação entre controles nativos é obtida por meio de mensagens de janelas enviadas para o controle ou
mensagens WM_NOTIFY recebidas dele. Em alguns casos, outras configurações são alteradas por meio da configuração
de um estilo de janela específico no controle. No mundo gerenciado, os controles têm propriedades para obter ou definir
valores, métodos para executar uma ação e eventos gerados quando algo acontece no controle. É assim que eles
geralmente são traduzidos quando estamos envolvendo um controle nativo em um invólucro:

Gerenciado Nativo

Property Envia uma mensagem específica para o controle e o valor é retornado. Para dados complexos, um ponteiro
Get para um buffer, que é preenchido com os dados necessários, é enviado com a mensagem.

Property Envia uma mensagem específica para o controle tendo o valor como um parâmetro. Para dados complexos,
Set é enviado um ponteiro de memória nativo que aponta para uma estrutura que contém os dados.
Ou define um estilo de janela específico para o controle nativo.

Method Envia uma mensagem específica para o controle, opcionalmente com parâmetros enviados para uma
estrutura através de um ponteiro.

Event Mensagem WM_NOTIFY recebida do controle. Contém um ponteiro para mais informações que descrevem o
evento, que podem ser empacotadas em uma estrutura gerenciada.

No caso do controle WebBrowser, provavelmente a mensagem fundamental a ser enviada é aquela para navegar para
uma URL específica. Uma cópia da URL atual é mantida no campo privado m_url. A seqüência de caracteres é
empacotada na memória nativa com um caractere nulo anexado ao final, para indicar o final da seqüência, e o ponteiro é
enviado para o controle com a mensagem DTM.NAVIGATE. O método de API do Windows SendMessage é usado para
enviar a mensagem para o controle hospedado. Depois disso, a memória nativa usada é liberada.
public void Navigate(string url)

//allocate temporary native buffer

IntPtr stringptr = MarshalEx.StringToHGlobalUni(url + '\0');

//send message to native control

Win32Window.SendMessage(ChildHandle,(int)DTM.NAVIGATE, 0, (int)stringptr);

//free native memory

MarshalEx.FreeHGlobal(stringptr);

}
Os valores das mensagens estão no arquivo de cabeçalho correspondente ao controle. Para torná-los acessíveis em seu
código, você pode armazená-los como uma enumeração (como fiz no código de exemplo), como constantes privadas ou
você pode embuti-los no código nos blocos aplicáveis (não é recomendável). Consulte os códigos para todas as
mensagens do Controle HTML e seus valores com suporte na enumeração DTM, no código.
Todas as implementações de propriedades e métodos seguirão o mesmo padrão básico. Quaisquer dados a serem
enviados com a mensagem devem ser posicionados na memória não-gerenciada, que deve ser liberada em seguida.
Você encontrará vários métodos úteis para alocar e liberar memória na classe
OpenNETCF.Runtime.InteropServices.MarshalEx.

Eventos
A implementação de ControlEx passa a funcionar com total eficiência ao reagir a eventos a partir do controle nativo. Os
controles nativos enviam suas notificações de volta para uma janela pai. A classe Control de base não expõe um método
WndProc para processar mensagens de entrada; portanto, você não consegue hospedar controles nativos diretamente e
receber seus eventos. A ControlMessageWindow é uma camada extra entre o controle gerenciado e o controle de janelas
nativo, e ela é capaz de receber e processar mensagens de entrada da janela.
Para capturar eventos no controle nativo, precisamos fornecer uma implementação para o método OnNotifyMessage. Na
prática, esse é o nosso WndProc para o controle. O SDK define várias mensagens de notificação enviadas pelo controle
HTML a seu pai. No código-fonte para WebBrowser, elas foram definidas na enumeração NM. As mensagens passadas
para o controle terão o valor de Msg igual a WM_NOTIFY. LParam aponta para uma estrutura de notificação específica do
controle. Ele será iniciado pelos membros de NMHDR identificando o remetente, uma identificação personalizada (não
usada) e o código da notificação. Definimos uma instrução switch para verificar o campo de código em relação às
mensagens de notificação conhecidas e realizar as ações apropriadas.
A primeira tarefa a ser feita é empacotar os dados anexados a esta notificação na memória gerenciada. Esses dados são
específicos do controle das janelas – no caso de WebBrowser, eles definem a URL e outras informações relacionadas ao
evento ocorrido. Mais uma vez, a estrutura desses dados é definida no arquivo de cabeçalho htmlctrl.h, na estrutura
NM_HTMLVIEW. Em seguida, usamos o campo "code" para determinar o tipo de notificação e a ação a ser executada.
//get html viewer specific data from the notification message

NM_HTMLVIEW nmhtml = (NM_HTMLVIEW)Marshal.PtrToStructure(m.LParam,typeof(NM_HTMLVIEW));

//marshal the Target string

string target = MarshalEx.PtrToStringAuto(nmhtml.szTarget);

//check the incoming message code and process as required

switch(nmhtml.code)

//hotspot click

case (int)NM.HOTSPOT:

case (int)NM.BEFORENAVIGATE:

OnNavigating(new WebBrowserNavigatingEventArgs(target));

break;

Essa parte do código mostra o evento mais comum a ser tratado – Navigating, que ocorre sempre que o controle começa
a navegar para uma nova página. Quando esse evento ocorre, um dos membros da estrutura de dados de notificação
aponta para uma seqüência de caracteres que fornece a URL da página solicitada. Esse ponteiro é empacotado em uma
seqüência de caracteres gerenciada e usado na criação de uma instância da classe personalizada
WebBrowserNavigatingEventArgs. Finalmente, isso é passado para o método OnNavigating, que fará o trabalho de gerar
o evento.
protected virtual void OnNavigating(WebBrowserNavigatingEventArgs e)

if(Navigating!=null)
{

Navigating(this,e);

Esse método primeiro verifica se há algum assinante para o evento. Se houver, o evento será gerado ao se passar os
argumentos do evento criados. Um consumidor desse evento pode usar a URL para determinar a ação a ser executada.
Se você estiver manipulando um evento no controle nativo que precise passar informações de volta, conforme foi feito
aqui com a URL, precisará de uma abordagem semelhante, em que uma classe EventArgs personalizada é passada com
o evento.
Início da página

Criando para o tempo de design


A capacidade de adicionar seu controle a um projeto e fazer o layout da interface do usuário usando o designer de
formulários do Visual Studio é muito importante. Entretanto, precisamos fornecer alguma funcionalidade adicional para
implementar isso. Em primeiro lugar, a moldura da área de trabalho não tem conhecimento do controle MessageWindow
ou dos P/Invokes específicos de nossa plataforma, como SendMessage. Portanto, é necessário ocultar a implementação
de nossa criação em tempo de design. Isso é feito com a adição de compilação condicional no código-fonte. Em todas as
nossas classes OpenNETCF, usamos a constante "DESIGN" ao criar versões de tempo de design de nossos controles.
Você encontrará um link muito bom para um excelente artigo sobre como fazer controles compatíveis em tempo de
criação, escrito por Alex Yakhnin, no final deste artigo. Portanto, nas seções do código em que o código específico da
plataforma é referenciado, como no construtor, adicionamos na instrução #if:
#if !DESIGN

//load htmlview module

IntPtr module = Core.LoadLibrary("htmlview.dll");

//init htmlcontrol

int result = InitHTMLControl(Instance);

//set the html specific classname of the native control

this.CreateParams.ClassName = "DISPLAYCLASS";

#endif

De fato, o designer não tem conhecimento algum sobre o funcionamento interno de nosso controle, embora ele ainda
liste as propriedades e eventos públicos que definimos. Para o WebBrowser, eu não implementei nenhum suporte extra
para o tempo de design; ele herdará seu desenho da classe ControlEx. Há pouca necessidade de se fornecer um controle
de processamento HTML totalmente operacional no designer de formulários; entretanto, é muito importante obter o
posicionamento correto do controle para o tempo de execução.
Início da página

Aplicativo de exemplo
Os projetos Pocket Browser (C#) e Pocket Browser VB são idênticos aos projetos Pocket PC que utilizam o controle
WebBrowser. Eles mostram como o controle é adicionado ao seu formulário no modo de exibição de design, exatamente
como qualquer outro controle. Os outros controles no formulário ilustram as propriedades e os métodos de chamada no
controle, para navegar para uma URL específica ou usar o método GoBack. O aplicativo manipula vários eventos gerados
pelo controle WebBrowser.
Quando o WebBrowser indica que está iniciando a navegação, o WaitCursor é exibido. Esse cursor é limpo quando o
evento DocumentCompleted é gerado. No evento DocumentTitleChanged, a propriedade Form.Text é atualizada para
refletir o título do documento HTML atual.
[C#]

//update the page title

private void webBrowser1_DocumentTitleChanged(object sender, EventArgs e)


{

this.Text = webBrowser1.DocumentTitle;

[VB]

Private Sub WebBrowser1_DocumentTitleChanged(ByVal sender As Object,

ByVal e As System.EventArgs) Handles WebBrowser1.DocumentTitleChanged

'update title to document title

Me.Text = WebBrowser1.DocumentTitle

End Sub

O controle WebBrowser não precisa ser usado simplesmente para navegar pelos sites da Internet. Devido ao fato de
você poder fornecer código-fonte HTML ao controle e capturar eventos quando os links são tocados, você poderia usá-lo
para fornecer uma interface do usuário HTML gerada dinamicamente para seu aplicativo.
Início da página

Conclusão
O WebBrowser implementa várias propriedades, métodos e eventos, todos seguindo o modelo básico descrito acima. O
código-fonte do controle está incluído nos projetos de exemplo de navegador da Web em C# e VB. É altamente
recomendável que você olhe todo o código de ControlEx e também de WebBrowser para compreender o processo e saber
como aplicá-lo em outros controles nativos. A classe ControlEx permite implementar vários controles que antes não
estavam disponíveis no .NET Compact Framework e permite dar suporte total a eventos gerados pelo controle nativo.
Usando essas mesmas técnicas, os controles InkX e MonthCalendar foram criados para o Smart Device Framework.
Início da página

Recursos adicionais
Para obter mais detalhes sobre a criação de controles de tempo de design para o .NET Compact Framework, consulte
este artigo: http://www.intelliprog.com/articles/index.html (em inglês)
Para obter mais detalhes sobre o Smart Device Framework, consulte http://www.opennetcf.org/sdf/ (em inglês)
© 2004 Microsoft Corporation. Todos os direitos reservados. Termos de uso.

.NET Collections - Interfaces


por Israel Aéce
Aplica-se:

• .NET Framework 1.1

• VB.NET e C#
As coleções são grupos de objetos similarmente iguais que são agrupados, quais podemos manipular, inserindo,
removendo, alterando e exibindo. No .NET Framework Class Library temos um Namespace específico qual contem
diversas coleções para serem utilizadas em nossas aplicações. Este Namespace chama-se System.Collections.
Dentro deste Namespace, encontramos várias classes e Interfaces para a criação dos mais diversos tipos de coleções. As
coleções estão definidas em três categorias: Coleções Ordenadas, Coleções Indexadas e Coleções baseadas em uma
"chave-e-valor". Veremos abaixo a finalidade de cada uma delas:
Coleções Ordenadas - São coleções que são distingüidas apenas pela ordem de inserção e assim controla a ordem

em que os objetos podem ser recuperados da coleção. System.Collections.Stack e System.Collections.Queue são
exemplos deste tipo de coleção.
Coleções Indexadas - São coleções que são distingüidas pelo fato de que seus valores e/ou objetos podem ser

recuperados/acessados através de um índice numérico (baseado em 0 (zero)). System.Collections.ArrayList é um
exemplo deste tipo de coleção.
Coleções "Chave-e-Valor" - Como o próprio tipo diz, é uma coleção que contém uma chave associado a algum tipo.

Seus índices são classificados no valor da chave e podem ser recuperados na ordem classificado pela enumeração.
System.Collections.HashTable é um exemplo deste tipo de coleção.
Além das coleções, temos dentro do Namespace System.Collections as interfaces genéricas - tema deste artigo - que

são implementadas nas várias classes de coleções que aqui temos. Estas Interfaces se fazem necessárias, pois são
implementadas para garantir um "contrato", obrigando assim, uma classe à implementar os métodos e propriedades
que são necessários àquelas funcionalidades.
Início da página

As Interfaces
As Interfaces que são utilizadas para serem implementadas nas coleções do Namespace System.Collections ou mesmo
criar nossas próprias coleções são: ICollection, IComparer, IDictionary, IDictionaryEnumerator, IEnumerable,
IEnumerator e IList.
Cada uma destas Interfaces contém métodos e propriedades que obrigatóriamente devem ser implementados nas
classes que as implementarem. Abaixo veremos a hierarquia de alguma destas Interfaces:

Figura 1 - Hierarquia entre algumas das Interfaces.


Como todas as Interfaces (consequentemente as coleções também, direta ou indiretamente) devem implementar a
Interface IEnumerable e extendendo a Interface ICollection, você pode escolher entre duas implementações: IList ou
IDictionary, onde IList é uma coleção onde podemos recuperar seus objetos dado um índice e IDictionary uma coleção
onde definimos chave-e-valor. Veremos um poucos mais sobre estas Interfaces um pouco mais abaixo.
Na Figura 1, vemos que as Interfaces IList e IDictionary são derivadas (herdam) de ICollection e esta por sua vez, herda
da Interface IEnumerable.
Veremos a partir de agora, um pouco mais sobre cada Interface que é utilizada para implementações das coleções
intrínsicas do .NET Framework e também podendo ser usadas para a criação de coleções customizadas.
Início da página

IEnumerable e IEnumerator
Como vemos na Figura 1, a Interface IEnumerable é a base direta e indiretamente para algumas outras Interfaces e
todas as coleções a implementam. Esta interface tem apenas um método a ser implementado, chamado GetEnumerator,
qual retorna um objeto do tipo da Interface de IEnumerator. Antes de falarmos deste método, primeiramente, veremos
do se trata a Interface IEnumerator.
A Interface IEnumerator é a base para todos os enumeradores. Composta por uma propriedade (Current) e dois métodos
(MoveNext e Reset), percorre e lê os elementos de uma determinada coleção, permitindo apenas ler os dados, não
podendo alterá-los. Vale levar em consideração que enumeradores não podem ser utilizados para a coleção subjacente.
A propriedade Current nos retorna o valor corrente que está sendo lido. Consequentemente deverá chamar o método
MoveNext para avançar o enumerador antes de ler o valor corrente. Já o método Reset restaura à posição inicial da
coleção.
Para ficar um pouco mais claro, veremos o exemplo em código:
Public Class TestIEnumerator

Implements IEnumerator

Private _categorias() As String = {"ASP.NET", "VB.NET", "C#", "SQL", "XML"}

Private _current As Integer = -1

Public ReadOnly Property Current() As Object Implements System.Collections.IEnumerator.Current

Get

If Me._current < 0 Then


Throw New InvalidOperationException

ElseIf Me._current > 4 Then

Throw New InvalidOperationException

Else

Return Me._categorias(Me._current)

End If

End Get

End Property

Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext

Me._current += 1

If Me._current > 4 Then

Return False

Else

Return True

End If

End Function

Public Sub Reset() Implements System.Collections.IEnumerator.Reset

Me._current = -1

End Sub

End Class

Como podemos ver, os métodos Reset e MoveNext e também a propriedade Current deve ser implementados para
conseguirmos a iteração na coleção, que neste caso está sendo feito em uma Array de String, denomidado _categorias.
E abaixo o código que é escrito no cliente para invocar esta classe:
Sub Main()

Dim test As New TestIEnumerator

While test.MoveNext()

Console.WriteLine(test.Current())

End While

End Sub

Pois bem, depois de entedermos a Interface IEnumerator, voltaremos agora a discutir o método GetEnumerator da
Interface IEnumerable, que retorna um objeto IEnumerator. A Interface IEnumerable é utilizado em classes onde poderá
ser recuperado seus elementos através do laço For Each.
Geralmente quando a Interface IEnumerable é implementada em uma determinada classe, o método GetEnumerator
retornará uma instância de uma classe privada que implementa a Interface IEnumerator. Mas claro que isso pode variar
de acordo com a sua necessidade. O código ficará da seguinte forma implementando a Interface IEnumerable:
Public Class TestIEnumerable
Implements IEnumerable

Public Function GetEnumerator() As System.Collections.IEnumerator Implements


System.Collections.IEnumerable.GetEnumerator

Return New TestIEnumerator

End Function

End Class

E no cliente, criamos a instância da classe e percorremos a coleção através do laço For Each:
Sub Main()

Dim testIEnumerable As New testIEnumerable

For Each categoria As Object In testIEnumerable

Console.WriteLine(categoria)

Next

End Sub

Pois bem, depois de entedermos a Interface IEnumerator, voltaremos agora a discutir o método GetEnumerator da
Interface IEnumerable, que retorna um objeto IEnumerator. A Interface IEnumerable é utilizado em classes onde poderá
ser recuperado seus elementos através do laço For Each.
Geralmente quando a Interface IEnumerable é implementada em uma determinada classe, o método GetEnumerator
retornará uma instância de uma classe privada que implementa a Interface IEnumerator. Mas claro que isso pode variar
de acordo com a sua necessidade. O código ficará da seguinte forma implementando a Interface IEnumerable:
Public Class TestIEnumerable

Implements IEnumerable

Public Function GetEnumerator() As System.Collections.IEnumerator Implements


System.Collections.IEnumerable.GetEnumerator

Return New TestIEnumerator

End Function

End Class

E no cliente, criamos a instância da classe e percorremos a coleção através do laço For Each:
Sub Main()

Dim testIEnumerable As New testIEnumerable

For Each categoria As Object In testIEnumerable

Console.WriteLine(categoria)

Next

End Sub
Início da página

ICollection
A Interface ICollection é a Interface base para todas as coleções que estão contidas dentro do Namespace
System.Collections. Direta ou indiretamente essas classes a implementa.
Ela por sua vez é composta por três propriedades (Count, IsSynchronized e SyncRoot) e um método (CopyTo). Abaixo
veremos a utilidade de cada um desses membros:

Membro Descrição

Count Quando implementado pela classe, retorna o número de elementos contidos na


ICollection.

IsSynchronized Quando implementado pela classe, indica se o acesso à ICollection é sincronizado


(thread-safe).

SyncRoot Quando implementado pela classe, retorna um objeto que pode ser usado para
sincronizar o acesso à ICollection.

CopyTo Quando implemento pela classe, copia os elementos da ICollection para um Array,
iniciando em um índice particular do Array.

Interfaces como IList e IDictionary derivam desta Interface ICollection, pois estas Interfaces são mais especializadas,
tendo também outros métodos a serem definidos além dos quais já estão contidos dentro da Interface ICollection. Já as
classes como System.Collections.Queue e System.Collections.Stack implementam a Interface ICollection diretamente.
Início da página

IList
Derivada das Interfaces ICollection e IEnumerable ela é a Interface base para todas as listas contidas no .NET
Framework. Listas quais podemos acessar individualmente por um índice seus objetos.
As implementações da Interface IList podem estar em das seguintes categorias: Read-Only, Fixed-Size e Variable-Size.
Uma IList Read-Only, não permite que você modifique os elementos. Já uma IList Fixed-Size, não permite adicionar ou
remover os elementos, mas permite que você altere os elementos existentes. E por fim, uma IList Variable-Size, qual
permite que você adicione, remova ou altere os elementos.
Claro que quando implementamos esta Interface em alguma classe, é necessário criarmos um membro privado que será
o responsável por armazenar os items. Este membro privado é manipulado pelos métodos e propriedades da Interface
IList que serão implementados nesta classe.
Abaixo um exemplo de como implementar a Interface IList em uma classe. Vale lembrar que muitos métodos e
propriedades foram ocultados por questões de espaço.
Public Class TestIList

Implements IList

Private _arr As ArrayList

Public Sub New()

Me._arr = New ArrayList

End Sub

Public ReadOnly Property Count() As Integer Implements System.Collections.ICollection.Count

Get

Return Me._arr.Count()

End Get

End Property
Public Function Add(ByVal value As Object) As Integer Implements System.Collections.IList.Add

Me._arr.Add(value)

End Function

'...

End Class

É importante ressaltar que no código acima é criado um membro privado chamado de _arr do tipo ArrayList e que sua
manipulação é feita através dos métodos que estão definidos na Interface IList e nesta classe implementados.
Com a Interface ILIst conseguimos criar uma coleção customizada, podendo inclusive criar uma coleção fortemente
tipada, onde seus tipos seriam únicos, ou seja, seria aceito apenas um objeto de um tipo definido e assim em design-
time já conseguiríamos detectar possíveis problemas de "cast".
O código acima é apenas um exemplo para entedermos o funcionamento da implementação da Interface IList, onde a
implementamos e criamos um membro privado do tipo ArrayList para armazenar os objetos, pois dentro do Namespace
System.Collections já temos um classe abstrata, chamada CollectionBase, que é a base para a criação de coleções
fortemente tipadas. Como a criação de coleções não é o foco deste artigo, fica aqui uma referência à um artigo que já
escrevi sobre a classe CollectionBase, que encontra-se publicado no Portal Linha de Código.
Início da página

IDictionary
A Interface IDictionary é a base para todas as coleções do tipo "chave-e-valor". Cada elemento inserido neste tipo de
coleção é armazenado em um objeto do tipo DictionaryEntry (Structure).
Cada associação deve ter um chave original que não seja uma referência nula, mas o seu respectivo valor de associação
pode incluir sem problemas uma referência nula.
Como já vimos um pouco acima quando falávamos sobre a Interface IList, as coleções "chave-e-valor" também estão
divididas em três categorias: Read-Only, Fixed-Size e Variable-Size.
Dentro da Interface IDictionary temos os seguintes membros:

Membro Descrição

Item Quando implementado pela classe, retorna ou define um elemento baseando-se em uma
"key".

Keys Quando implementado pela classe, retorna um objeto do tipo ICollection, contendo as
Keys do IDictionary.

Values Quando implementado pela classe, retorna um objeto do tipo ICollection, contendo os
Values do IDictionary.

Add Quando implementado pela classe, adiciona um novo elemento no IDictionary, baseando
na Key e Value que são informados como parâmetro para este método.

Ocultamos aqui alguns métodos e propriedades por questões de espaço, mas para uma lista completa, pode consultar o
seguinte endereço: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/
frlrfsystemcollectionsidictionarymemberstopic.asp.
Uma classe bastante conhecida no mundo .NET que implementa a Interface IDictionary é a classe chamada Hashtable,
que é uma coleção "chave-e-valor" e que organiza seus elementos baseando-se no código Hash da chave ("key").
Esta classe possui internamente uma Structure (estrutura de dados) que é composta pelos seguintes campos: hash_coll,
key e val. Neste caso, temos um membro privado chamado bucktes, que nada mais é que um Array desta estrutura,
onde são armazenados os valores que são inseridos através do método Add.
Como sabemos, para a utilização de um laço For Each para recuperar os valores de qualquer coleção, necessitamos
saber o tipo do elemento para que isso seja possível. Para isso, o objeto que temos que utilizar para percorrer a coleção
é a Structure DictionaryEntry. Abaixo, um exemplo de código, utilizando Hashtable:
Public Class TestIDictionary
Private _hastTable As Hashtable

Public Sub New()

Me._hastTable = New Hashtable

Me._hastTable.Add("qwert", "qwert")

Me._hastTable.Add("asdfg", "asdfg")

Me._hastTable.Add("zxcvb", "zxcvb")

End Sub

Public Sub PrintValues()

For Each dictionary As DictionaryEntry In Me._hastTable

Console.Write(dictionary.Key & " - " & dictionary.Value)

Console.WriteLine()

Next

End Sub

End Class

Para invocar a classe no cliente, o código fica:


Module Module1

Sub Main()

Console.WriteLine("Teste com a Interface IDictionary.")

Dim testIDictionary As New testIDictionary

testIDictionary.PrintValues()

Console.WriteLine()

End Sub

End Module

Só para constar, o "output" deste código será:


Teste com a Interface IDictionary.

qwert - qwert

zxcvb - zxcvb

asdfg - asdfg

Isto confirma o que vimos acima, onde não importa a ordem que o objeto foi inserido no Hashtable, pois internamente é
aplicado um algoritmo para recuperar o valor da Hash "Key" para ter uma ordenação eficiente dos elementos.
Início da página

IDictionaryEnumerator
Esta Interface tem a mesma finalidade da Interface IEnumerator, ou seja, percorrer e ler os elementos de uma
determinada coleção, mas esta em especial, trata de enumerar os elementos de um dicionário, ou seja, uma coleção que
contenha "chave-e-valor".
Para nos certificarmos disso, ao decompilar a propriedade "Current", qual está definida na Interface IEnumerator, pois
IDictionaryEnumertator herda desta Interface, teremos a seguinte código:
Public Overridable ReadOnly Property Current As Object

Get

'...

'...

Return New DictionaryEntry(Me.currentKey, Me.currentValue)

End Get

End Property

Como vimos anteriormente, cada elemento de uma coleção da classe Hashtable (qual está sendo discutida como
exemplo) é representado por um objeto do tipo DictionaryEntry e neste caso, vemos que a propriedade Current retorna
um objeto deste tipo, contendo seus respectivos valores (Key e Value).
Neste exemplo, a Interface IDictionaryEnumerator está implementanda dentro de uma classe privada chamada
HashtableEnumerator qual é responsável por gerar o enumerador do Hashtable corrente, e retornando através da
propriedade Current um objeto do tipo DictionaryEntry.
Início da página

IComparer
Esta Interface é utilizada para customizar uma ordenação ("sort") em uma determinada coleção. A Interface IComparer
é composta por um método chamado Compare, qual compara dois objetos e retorna um valor inteiro indicando se os
objetos são ou não iguais, ou seja, se os valores comparados forem iguais, 0 (zero) é retornado.
Há classes, como por exemplo o ArrayList, que já implementa o método Sort para tal ordenação, mas há casos em que
desejamos criar uma ordenação customizada, onde a ordenação padrão não nos interessa. É justamente neste momento
que a Interface IComparer é utilizada, inclusive o método Sort da classe ArrayList tem um Overload que recebe no
parâmetro um objeto do tipo da Interface IComparer.
Quando utilizamos o método Sort do ArrayList sem informar nenhum parâmetro, a ordenação é feita em ordem
ascendente, a padrão. Mas podemos querer ordenar em ordem descendente, e com isso teríamos que criar uma classe
que implementa a Interface IComparer, codificando assim o método Compare.
Vejamos abaixo um exemplo onde criamos duas classes chamadas: TestIComparerAscending e
TestIComparerDescending onde cada uma delas é responsável para ordernar em um formato específico (Ascendente ou
Descendente):
Public Class TestIComparerAscending

Implements IComparer

Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements


System.Collections.IComparer.Compare

Return String.Compare(x.ToString(), y.ToString())

End Function

End Class

Public Class TestIComparerDescending


Implements IComparer

Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements


System.Collections.IComparer.Compare

Return String.Compare(y.ToString(), x.ToString())

End Function

End Class

E para constatar as suas funcionalidades, veja como fica o código no cliente, onde informamos o tipo de ordenação que
desejamos:
Module Module1

Sub Main()

Console.WriteLine("Teste com a Interface IComparer.")

Dim arr As New ArrayList

arr.Add("NNNNN")

arr.Add("AAAAA")

arr.Add("ZZZZZ")

arr.Add("BBBBB")

arr.Add("IIIII")

Console.WriteLine("Ordem Ascendente")

arr.Sort(New TestIComparerAscending)

For i As Integer = 0 To arr.Count - 1

Console.WriteLine(arr.Item(i))

Next

Console.WriteLine()

Console.WriteLine("Ordem Descendente")

arr.Sort(New TestIComparerDescending)

For i As Integer = 0 To arr.Count - 1

Console.WriteLine(arr.Item(i))

Next

Console.WriteLine()

End Sub

End Module
Quando utilizamos coleções customizadas, como por exemplo coleções derivadas de CollectionBase, a forma de
implementação é um pouco diferente, ou seja, é criado uma(s) classe(s) privada dentro da classe que manipula a
coleção, onde implementamos a Interface IComparer e definimos por qual propriedade do objeto/elemento que
desejamos ordenar.
No caso do exemplo que mostrarei abaixo, apenas reescrevi o método ToString() na classe Usuário, retornando o nome
do Usuário apenas para o exemplo. Quando necessitarmos fazer mais de um tipo de ordenação é necessário fazer o
"cast" dos objetos x e y do método Compare para resgatarmos o valor da propriedade que desejamos ordenar, para que
seja possível efetuar a comparação. Com isso, devemos nos atentar para que na coleção seja inserido somente um
determinado tipo de objeto, pois se isso não ocorrer, uma Exception será atirada, pois não será possível efetuar o "cast".
Vejamos abaixo como ficaria a coleção para ordená-la:
Public Class UsuarioColecao

Inherits CollectionBase

Public Enum Ordenacao

NomeCrescente

NomeDecrescente

End Enum

Public Sub Sort(ByVal sorter As Ordenacao)

Select Case sorter

Case Ordenacao.NomeCrescente

InnerList.Sort(New AscendingNameSorter)

Case Ordenacao.NomeDecrescente

Innerlist.Sort(New DescendingNameSorter)

End Select

End Sub

Private Class AscendingNameSorter

Implements IComparer

Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements


System.Collections.IComparer.Compare

Return String.Compare(x.ToString(), y.ToString())

End Function

End Class

End Class

Como vemos no código acima, a classe UsuarioColecao herda de CollectionBase, que como já vimos, utilizamos para a
criação de uma coleção customizada. Vale lembrar que internamente (dentro da classe CollectionBase) existe um
membro, do tipo ArrayList, e como sabemos que ele comporta o método Sort, apenas criamos o mesmo método na
nossa coleção customizada de Usuários e passamos um instância da classe privada (que implementa a Interface
IComparer) para que a ordenação seja realizada.
E finalmente, o código no cliente que faz o uso desta classe:
Module Module1

Sub Main()

Dim coll As New UsuarioColecao

coll.Add(New Usuario("Joao"))

coll.Add(New Usuario("Maria"))

coll.Add(New Usuario("Willian"))

coll.Add(New Usuario("Anna"))

coll.Sort(UsuarioColecao.Ordenacao.NomeCrescente)

For i As Integer = 0 To coll.Count - 1

Console.WriteLine(coll.Item(i).Nome)

Next

Console.WriteLine()

coll.Sort(UsuarioColecao.Ordenacao.NomeDecrescente)

For i As Integer = 0 To coll.Count - 1

Console.WriteLine(coll.Item(i).Nome)

Next

Console.WriteLine()

End Sub

End Module

Ao chamarmos o método Sort da classe UsuarioColecao, informamos no parâmetro através de um enumerador qual é o
tipo de ordenação que deverá ser realizada, e após isso, a ordenação é efetuada baseando-se na instância da classe
privada que informamos no método Sort.
Início da página

Conclusão
Vimos neste artigo as Interfaces que estão contidas dentro do Namespace System.Collections, analisando suas
propriedades, métodos e entendendo suas funcionalidades nas classes que estão contidas dentro deste mesmo
Namespace. Elas são utilizadas por todo o .NET Framework e também estão à disposição para a criação de coleções
customizadas que possam vir a serem necessárias durante a construção de uma aplicação.
Início da página

Sobre o autor:
Israel Aéce (israel@projetando.net) é Desenvolvedor de aplicações .NET, Microsoft MVP (Most Valuable Professional),
Fundador e Líder do Grupo Projetando.NET (http://www.projetando.net), Colunista .NET e Moderador das Listas VB/ASP
do Portal Linha de Código e e INETA LatAm Volunteer.
Início da página
Usando vetores com o .NET Framework e C#
Por Fabio Galuppo

Arrays, ou simplesmente vetores, são elementos dimensionais de um determinado tipo de dados, sendo eles tipo
referência ou tipo valor, onde são agrupados num número fixo de elementos. Em C# os vetores são acessados através
de um índice (valor numérico) cujo sempre inicia-se com zero (0).

Um vetor pode ser criado na forma unidimensional, multidimensional ou jagged, em C#. Um vetor é um tipo referência.
Ele indica que a variável, a qual representa o vetor, aponta para os elementos em memória, bem como quando passada
através de métodos, seus elementos podem ser alterados. Os vetores de tamanho fixo são baseados na classe
System.Array do .NET Framework.

Declarar, criar, armazenar e acessar um vetor

Na declaração de um array (vetor) selecionamos o tipo de dado seguido de colchetes ([]). Após, coloca-se a
variável.

string[] Nomes; //array declarado, mas não criado ou inicializado

Antes do armazenamento dos valores, o operador new é obrigatório para a criação do número de elementos
desejado. O mesmo aparece entre conchetes ([]) após o tipo determinado.

Nomes = new string[10]; //array criado com 10 elementos

Para armazenar um valor num array use o indíce onde para o primeiro elemento do vetor é zero (0) e o
último elemento é o tamanho total do vetor menos um (1). Depois é colocado o sinal de igual com o valor
(constante ou uma variável) desejado a guardar naquela posição.

Nomes[0] = "Fabio Galuppo"; //armazenando uma string no primeiro elemento


Nomes[1] = "Vanclei Matheus"; //armazenando uma string no segundo elemento

Nomes[9] = "Wallace Santos"; //armazenando uma string no último elemento

Para acessar um valor do array basta usar o indíce juntamente a variável que representa o vetor, como
indicado anteriormente.

string nome = Nomes[0]; //Fabio Galuppo

Outro ponto interessante no ambiente do .NET Framework é as instâncias serem apenas criadas, ou seja, não
existe um operador delete para eliminar a memória consumida. O é trabalho é feito pelo coletor de lixo
(garbagge collector) o qual se responsabiliza pela liberação dos recursos gerenciados que foram consumido.
Portanto, como os vetores possuem um tamanho fixo determinado na sua criação, eles não poderão ser
redimensionados. Se um nova instrução new com um número maior de elementos for indicada num vetor já
existente, isto não quer dizer que o vetor antigo será redimensionado, de fato ocorrerá que um novo vetor
vai sobrescrever o antigo. A coleta de lixo libera o vetor antigo da memória desconsiderando seus valores no
novo vetor.

Nomes = new string(10);

Nomes[0] = "Fabio Galuppo"; //armazenando uma string no primeiro elemento


Nomes[1] = "Vanclei Matheus"; //armazenando uma string no segundo elemento

Nomes[9] = "Wallace Santos"; //armazenando uma string no último elemento


Nomes = new string(20); //novo vetor, o antigo foi liberado e sobrescrito

string nome = Nomes[0]; //Nome[0] é null

Se o vetor acessar um índice não existente, a exceção IndexOutOfRangeException será disparada.

string novonome = Nomes[21]; //Indíce não existente (IndexOutOfRangeException)

Tipos de vetor

Existem três (3) tipos de vetores em C#. Eles são alinhados linearmente na memória, mas quanto as suas dimensões
eles podem ser:
 Unidimensionais - uma (1) dimensão para acessar através de um indíce;
 Multidimensionais - duas (2) ou mais dimensões para acessar através de uma combinação de indíces. Eles são
conhecidos de matrizes, principalmente por aparecerem na sua forma mais comum como um array de duas
dimensões. São retangulares quanto suas dimensões;
 Jagged - array de arrays. São multidimensionais, mas não possuem a características retangulares de uma
matriz, onde numa dimensão o número de elementos contidos poderão ser diferentes.

Para declarar um array multidimensional, dentro dos colchetes ([]) separe por vírgulas (,) o número de
dimensões. Na criação passe os valores para cada uma das dimensões.

int[,] MatrizBidimensional; //array multidimensional declarado para 2 dimensões


int[,,] MatrizTridimensional; //array multidimensional declarado para 3 dimensões

MatrizBidimensional = new int[3,3]; //criado com 9 elementos.


MatrizBidimensional = new int[3,4,5]; //criado para 60 elementos.

MatrizBidimensional[0,0] = 50; //armazenando na matriz bidimensional

MatrizBidimensional[2,1] = 100;
MatrizBidimensional[2,2] = 200;

MatrizTridimensional[1,2,4] = 250; //armazenando na matriz tridimensional

MatrizTridimensional[4,3,2] = 500;
MatrizTridimensional[4,4,4] = 1000;

Para declarar um array jagged coloque o número de colchetes ([]) referente ao número de dimensões. Crie
cada uma das dimensões independetemente. Passe os valores para cada uma delas.

int[][] JaggedBidimensional; //array jagged declarado para 2 dimensões


int[][][] JaggedTridimensional; //array jagged declarado para 3 dimensões

JaggedBidimensional = new int[2][]; //2 linhas na primeira dimensão


JaggedBidimensional[0] = new int[3]; //na primeira linha, 3 colunas criadas
JaggedBidimensional[1] = new int[2]; //na segunda linha, 2 colunas criadas

JaggedTridimensional = new int[1][][]; //3 linhas na primeira dimensão


JaggedTridimensional[0] = new int[2]; //primeira linha, 2 elementos criadas
JaggedTridimensional[0][0] = new int[1]; //linha 1, coluna 1, 1 elemento criado
JaggedTridimensional[0][1] = new int[2]; //linha 1, coluna 2, 2 elementos criados
JaggedBidimensional[0][1] = 100;
JaggedBidimensional[0][2] = 400;

JaggedTridimensional[0][1][1] = 700;

JaggedTridimensional[0][0][0] = 900;

Os arrays retangulares e jagged podem ser misturados, por exemplo:

//jagged de 2 dimensões
double[][,][][,,] Misturado = new double[2][,][][,,];
//linha 1 - criada com uma matriz 3x3
Misturado[0] = new double[3,3][][,,];
//linha 2 - criada com uma matriz 1x1
Misturado[1] = new double[1,1][][,,];
//linha 1, matriz(1,1) - criada com um vetor de 2 elementos
Misturado[0][0,0] = new double[2][,,];
//linha 1, matriz(1,1), linha 2 - criada com uma matriz de 2x1x3

Misturado[0][0,0][1] = new double [2,1,3];


//e assim por diante...

Cada tipo de vetor possue uma representação lógica diferente na memória. Na figura 1, temos a
represantação dos vetores abaixo:

long[] ar1 = new long[5]; //vetor unidimensional

long[,] ar2 = new long[3,2]; //vetor multidimensional ou matriz

long[][] ar3 = new long[2][]; //vetor jagged

ar3[0] = new long[2];


ar3[1] = new long[4];

Figura 1: Representação dos vetores unidimensional, multidimensional e jagged

Inicializando os vetores

Os vetores podem ser inicializados durante a declaração. Os valores de inicialização são colocados entre chaves. É
normal e permitido neste caso a omissão do tamanho do array e do operador new.
//Array unidimensional
double[] ar1 = new double[4]{1.1, 2.2, 3.3, 4.4};
double ar1 = new double[]{1.1, 2.2, 3.3, 4.4};
double ar1 = {1.1, 2.2, 3.3, 4.4};

//Array multidimensional
int[,] ar2 = new int[2,3]{ {1,2,3}, {4,5,6} };
int[,] ar2 = new int[,]{ {1,2,3}, {4,5,6} };
int[,] ar2 = { {1,2,3}, {4,5,6} };

//Array Jagged
byte[][] ar3 = new byte[2][]{new byte[2]{0xE,0xF}, new byte[4]{0xA,0xB,0xC,0xD}};

byte[][] ar3 = new byte[][]{new byte[]{0xE,0xF}, new byte[]{0xA,0xB,0xC,0xD}};


byte[][] ar3 = {new byte[2]{0xE,0xF}, new byte[4]{0xA,0xB,0xC,0xD}};

//Array Jagged
byte[][] ar3 = new byte[2][];
ar3[0] = new byte[2]{0xE, 0xF};
ar3[1] = new byte[4]{0xA, 0xB, 0xC, 0xD};

Percorrendo os vetores

Os vetores podem ser percorridos de diversas formas. Por exemplo, usando comandos de iteração como for
e while. Eles são acessados por seus índices. Há duas possibilidades de obter-se o tamanho do array. A
primeira é guardá-lo numa variável durante sua criação. Já a outra forma, mais eficiente, é utilizar a
propriedade Length ou o método GetLength para obter o total de elementos dele.

//csc Vetores.cs

using System;

public class Unidimensional


{
string[] s = {"um", "dois", "três", "quatro"};

public void Processar()


{
Console.Write("\nUnidimensional = ");
for(int idx = 0, l = s.Length; idx < l; ++idx)
Console.Write("{0} ", s[idx]);
}
}

public class Multidimensional


{
string[,] s = { {"um", "dois"}, {"três", "quatro"} };

public void Processar()


{
Console.Write("\nMultidimensional = ");
for(int idx = 0, l = s.GetLength(0); idx < l; ++idx)
for(int idx2 = 0, l2 = s.GetLength(1); idx2 < l2; ++idx2)
Console.Write("{0} ", s[idx, idx2]);
}
}

public class Jagged


{
string[][] s = { new string[]{"um"}, new string[]{"dois", "três", "quatro"} };

public void Processar()


{
Console.Write("\nJagged = ");
for(int idx = 0, l = s.GetLength(0); idx < l; ++idx)
for(int idx2 = 0, l2 = s[idx].GetLength(0); idx2 < l2; ++idx2)
Console.Write("{0} ", s[idx][idx2]);
}
}

public class Vetores


{
public static void Main()
{
Unidimensional uni = new Unidimensional();
Multidimensional multi = new Multidimensional();
Jagged jag = new Jagged();

uni.Processar();
multi.Processar();
jag.Processar();
}
}

Nos casos acima a propriedade Length aplicada ao vetor unidimensional retorna a quantidade de elementos
dele. O método GetLength(0) aplicado ao vetor multimensional retorna a quantidade de elementos da
primeira dimensão, enquanto o método GetLength(1) retorna a quantidade de elementos da segunda
dimensão. O método GetLength(0) aplicado ao vetor jagged retorna ao número de arrays dentro do array. As
outras quantidades devem ser acessadas através de GetLength(0) da dimensão desejada. Por exemplo,
s[0].GetLength(0) retorna o número de elementos contido na primeira dimensão do array.

Para percorrer um vetor o comando foreach pode ser utilizado, principalmente porque um array possui o
método GetEnumerator que retorna a interface IEnumerator. Usando o comando foreach para iterar um
vetor o resultado é disposição sequencial dele, ou seja, o resultado final é a disposição dos elementos na
memória. Os jagged arrays não conseguem ser acessado somente pelo comando foreach devido suas
dimensões variáveis.

//csc Vetores.cs

using System;

public class Unidimensional


{
string[] s = {"um", "dois", "três", "quatro"};

public void Processar()


{
Console.Write("\nUnidimensional = ");
foreach(string e in s)
Console.Write("{0} ", e);
}
}

public class Multidimensional


{
string[,] s = { {"um", "dois"}, {"três", "quatro"} };

public void Processar()


{
Console.Write("\nMultidimensional = ");
foreach(string e in s)
Console.Write("{0} ", e);
}
}

public class Jagged


{
string[][] s = { new string[]{"um"}, new string[]{"dois", "três", "quatro"} };

public void Processar()


{
Console.Write("\nJagged = ");
for(int idx = 0, l = s.GetLength(0); idx < l; ++idx)
foreach(string e in s[idx])
Console.Write("{0} ", e);
}
}
public class Vetores
{
public static void Main()
{
Unidimensional uni = new Unidimensional();
Multidimensional multi = new Multidimensional();
Jagged jag = new Jagged();

uni.Processar();
multi.Processar();
jag.Processar();
}
}

A classe System.Array

Os vetores são baseados na classe System.Array do .NET Framework. É possível usar as propriedades e os
membros da classe para executar operações diversas. Os principais membros estão relacionados abaixo:

Length retorna a quantidade total de elementos em todas as dimensões.

int tamanho = elems.Length; //6

Rank retorna o rank ou o número de dimensões do array.

int dimensoes = elems.Rank; //1

BinarySearch faz uma busca usando o algoritmo de binary search apenas numa dimensão.

Array.BinarySearch(elems, 'F'); //2

GetLength retorna a quantidade de elementos de uma dimensão especificada.

int dim1 = elems.GetLength(0); //6

GetLowerBound retorna o limite inferior do vetor. Em C# é sempre zero (0).

int inferior = elems.GetLowerBound(0); //0

GetUpperBound retorna o limite superior do vetor.

int superior = elems.GetUpperBound(0); //5

GetValue obtém o valor de um elemento determinado do vetor.

char ch = (char)elems.GetValue(1); //A

SetValue define um valor para um elemento determinado do vetor.


elems.SetValue('C', 5);

IndexOf retorna o índice da primeira ocorrência de um valor numa dimensão do vetor.

Array.IndexOf(elems, 'A'); //0

LastIndexOf retorna o índice da última ocorrência de um valor numa dimensão do vetor.

Array.LastIndexOf(elems, 'A'); //5

Copy e CopyTo efetuam cópias de um array para outro.

elems.CopyTo(elems2, 0);

Array.Copy(elems, elems3, elems.Length);

Sort organiza um vetor na ordem crescente.

Array.Sort(elems2); //A B C D E F

Reverse inverte a ordem de um vetor.

Array.Reverse(elems3); //C D E F A B

Clear limpa os elementos do array com zero (0), falso (false) ou nulo (null), dependendo do tipo de dados.
Os parâmetros especificados são o vetor, o índice inicial e a quantidade de elementos a serem limpados.

Array.Clear(elems2, 0, elem2.Length); //Todos elementos


Array.Clear(elems2, 1, 2); //2 elementos a partir da 2ª posição

O exemplo a seguir usa todos os métodos discutidos anteriormente:

//csc Vetores.cs

using System;

public class Vetores


{
public static void Main()
{
//Criação
char[] elems = new char[6];

//Atribuição
elems[1] = 'A'; //A
elems[0] = (char)0x42; //B
elems[5] = "C# is COOL"[0]; //C
elems[4] = 'D'; //D
elems[3] = (char)69; //E
elems[2] = "EFI"[1]; //F
//Percorrer
foreach(char ch in elems) Console.WriteLine(ch);

//Usando os membros do System.Array


Console.WriteLine("Tamanho do array = {0}", elems.Length);

Console.WriteLine("Número de dimensões = {0}", elems.Rank);

Console.WriteLine("Índice do 'F' = {0}", Array.BinarySearch(elems, 'F'));

int dim1 = elems.GetLength(0);


Console.WriteLine("Número de elementos na 1ª dimensão = {0}", dim1);

Console.WriteLine("Lower Bound = {0}", elems.GetLowerBound(0));

Console.WriteLine("Upper Bound = {0}", elems.GetUpperBound(0));

Console.WriteLine("Valor na posição 2 = {0}", elems.GetValue(1));

elems[5] = 'A';

//Primeira ocorrência
Console.WriteLine("Índice de 'A' = {0}", Array.IndexOf(elems, 'A'));

//Última ocorrência
Console.WriteLine("Índice de 'A' = {0}", Array.LastIndexOf(elems, 'A'));

elems.SetValue('C', 5);

char[] elems2 = new char[6];


elems.CopyTo(elems2, 0);

char[] elems3 = new char[6];


Array.Copy(elems, elems3, elems.Length);

Array.Sort(elems2);
Console.WriteLine("Ordem Crescente");
foreach(char ch in elems2) Console.Write(ch);
Console.WriteLine("\nOrdem Decrescente");
for(int a = elems2.Length - 1; a > -1; --a) Console.Write(elems2[a]);

Console.WriteLine("\nOrdem Inversa do Vetor");


Array.Reverse(elems3);
foreach(char ch in elems3) Console.Write(ch);

Array.Clear(elems2, 0, elems2.Length);
Array.Clear(elems3, 0, elems3.Length);
}
}

Para compilar o exemplo acima no Command Prompt digite csc /nologo Vetores.cs. Execute o programa digitando
Vetores. A figura 2 mostra a compilação e execução da aplicação em C#.

Figura 2: Compilação e Execução do exemplo Vetores


Cálculos com Matrizes

O exemplo abaixo mostra um programa de cálculos com matrizes bidimensionais de inteiros. Ele cria uma
classe Matriz onde possui os métodos Somar, Subtrair e Imprimir para efetuar operações de soma com
matrizes, subtração com matrizes e exibir a matriz:

//csc /nologo Matrizes.cs

using System;

public class Matriz


{
private int[,] matriz;
private int linhas, colunas;

public Matriz(int linhas, int colunas)


{
this.linhas = linhas;
this.colunas = colunas;
matriz = new int[linhas, colunas];
}

public Matriz Somar(Matriz m)


{
Matriz res = new Matriz(this.linhas, this.colunas);

for(int a = 0, l = linhas; a < l; ++a)


for(int b = 0, l2 = colunas; b < l2; ++b)
res.SetValue(a + 1, b + 1, matriz[a, b] + m.GetValue(a + 1, b + 1));

return res;
}

public Matriz Subtrair(Matriz m)


{
Matriz res = new Matriz(this.linhas, this.colunas);

for(int a = 0; a < linhas; ++a)


for(int b = 0; b < colunas; ++b)
res.SetValue(a + 1, b + 1, matriz[a, b] - m.GetValue(a + 1, b + 1));

return res;
}

public void SetValue(int linha, int coluna, int valor)


{
matriz[linha - 1, coluna - 1] = valor;
}

public int GetValue(int linha, int coluna)


{
return matriz[linha - 1, coluna - 1];
}

public void Imprimir()


{
for(int a = 0; a < linhas; ++a)
{
Console.WriteLine("");
for(int b = 0; b < colunas; ++b)
Console.Write("{0} ", matriz[a, b]);
}
}

public static void Main()


{
Console.Write("\nM1");

Matriz m1 = new Matriz(3, 3);


m1.SetValue(1, 1, 50); m1.SetValue(1, 2, 100); m1.SetValue(1, 3, 1);

m1.SetValue(2, 1, 33); m1.SetValue(2, 2, 300); m1.SetValue(2, 3, 2);

m1.SetValue(3, 1, 22); m1.SetValue(3, 2, 700); m1.SetValue(3, 3, 5);


m1.Imprimir();

Console.Write("\n\nM2");

Matriz m2 = new Matriz(3, 3);


m2.SetValue(1, 1, 8); m2.SetValue(1, 2, 200); m2.SetValue(1, 3, 15);

m2.SetValue(2, 1, 9); m2.SetValue(2, 2, 700); m2.SetValue(2, 3, 1);


m2.SetValue(3, 1, 3); m2.SetValue(3, 2, 100); m2.SetValue(3, 3, 0);

m2.Imprimir();

Console.Write("\n\nSomar = M1 + M2");

Matriz resultado = m1.Somar(m2);

resultado.Imprimir();

Console.Write("\n\nSubtrair = M1 - M2");

resultado = m1.Subtrair(m2);

resultado.Imprimir();

Console.WriteLine("");
}
}

Para compilar o exemplo acima no Command Prompt digite csc /nologo Matrizes.cs. Execute o programa digitando
Matrizes. A figura 3 mostra a compilação e execução da aplicação em C#.

Figura 3: Compilação e Execução do exemplo Matrizes

Arrays dinâmicos

Os arrays dinâmicos em C# permite que sejam alteradas as dimensões de um vetor com operações como
Insert e Remove. Ele é utilizado através de uma instância da classe ArrayList que se encontra no namespace
System.Collections do .NET Framework.

//csc VetorDin.cs

using System;
using System.Collections;

public class VetorDin


{
public static void Main()
{
ArrayList arr = new ArrayList();
arr.Add("C#");
arr.Add("C++");
arr.Add("Perl");
arr.Add("Visual Basic");
int a = arr.Count; // 4
arr.RemoveAt(0); //Remove da 1ª posição

arr.Remove("Perl");
a = arr.Count; // 2
}
}

Um dos construtores da classe ArrayList possui um inteiro para indicar a capacidade (capacity) inicial do
vetor que será alocada. Procure sempre passar este valor para o construtor, mesmo que seja um valor
aproximado, para mais ou para menos onde a alocacão interna para o vetor passa a ser mais efícaz.

ArrayList arr = new ArrayList(10); //Já reserva 10 elementos antes de ser usado

Roadmap
Todos a bordo... É com grande satisfação que ínicio uma grande jornada dentro dos domínios do MSDN Brasil, o objetivo
é conquistar a plataforma .NET. Quem se habilita a fazer parte da tripulação?

Desbravando o .NET Framework será uma coluna mensal, onde irá explorar as classes que compõe o .NET Framework.
Nesta viagem, num mundo desconhecido para alguns, mas nem tanto para outros, teremos a oportunidade de conhecer,
utilizar e principalmente dominar as classes.

O que teremos pela frente? A respota é uma nova plataforma a ser explorada! Talvez o que o torna mais excitante é a
transição do velho mundo ao novo mundo. Porém nunca abrindo mão de nossa origem, que será útil e necessária em
algumas ocasiões. Um aviso importante: os dois mundos convivem! Cabe a nós entendermos e utilizarmos de maneira
mais eficiente.

Qual o requisito para tanto? É necessário o .NET Framework SDK como kit de sobrevivência! Contudo, novidades irão
surgindo nesta caminhada e na medida do possível os equipamentos serão de extrema utilidade.

O .NET Framework SDK utilizado é o Beta 2, que em breve estará disponível. O Beta 1 pode ser encontrado em
http://msdn.microsoft.com/code/sample.asp?url=/msdn-files/027/000/976/msdncompositedoc.xml. Instale-o e vamos
em frente...

O .NET Framework e a Plataforma

Figura 1: Plataforma .NET

Resumindo, o .NET Framework é um subconjunto de funcionalidades que faz parte do .NET Framework SDK e,
principalmente, forma a base da plataforma .NET. A figura1 representa a plataforma .NET. Onde nela encontra-se quatro
componentes principais, conforme demonstrado nas figuras internas, iniciando-se da direita no sentido horário elas
representam:
 .NET Enterprise Servers - servidores de missão crítica, tais como SQL Server 2000, Exchange Server 2000,
Biztalk Server 2000, entre outros;
 .NET Devices - dispositivos com suporte a plataforma, tais como Pocket PC, smart-phones, Tablet PC, Desktop
PC, entre outros;
 .NET Building Block Services - web services, software como serviço, tais como Passport, Hailstorm, serviços de
terceiros, entre outros;
 .NET Framework e o Visual Studio.NET - infraestrutura de execução e desenvolvimento e IDE de produtividade.

O .NET Framework divide-se em três partes principais:


 Common Language Runtime - ambiente de execução, formado pelos compiladores Just In Time, Class Loader,
Garbage Collector, segurança, entre outros;
 ASP.NET;
 .NET Framework Class Library;

No .NET, o suporte as linguagens de programação é vasto, quatro podem ser as linguagens fornecidas pela Microsoft
nesta primeira versão:Visual Basic.NET, C#, C++ Managed Extensions e JScript.NET. Estes compiladores geram um
código intermediário, MSIL, que compila-se em tempo de execução ou deploy, e o código nativo é executado. Linguagens
e compiladores de terceiro também são encontrados, como por exemplo, Cobol da Fugitsu( http://www.adtools.com),
Perl da Active State(http://www.activestate.com) e Eiffel da Interactive Software
Engineering(http://www.eiffel.com).

Maiores detalhes em: http://msdn.microsoft.com/net/ e http://www.microsoft.com/net/.


Nosso objetivo

Como citado anteriormente, nossa missão é explorar as bibliotecas de classes do .NET Framework. Sendo que elas são
um subconjunto do .NET Framework e o mais importante é que elas são compartilhadas entre todas as linguagens
suportadas pelo .NET. E com um pouco de esforço essas mesmas classes podem ser utilizadas em aplicações não
gerenciadas, como no caso do COM(+) e Win32.

Todas as classes do Framework são herdadas da classe básica System.Object, seguindo a mesma filosofia das interfaces
COM as quais oriundem da IUnknown. Quando uma classe .NET é criada, ela passa a fazer parte do Framework, ou seja
o Framework é extensível. Os principais grupos formadores da árvore hierárquica principal com raíz no namespace
System são:
 Base Class Library, ou BCL - encontradas na mscorlib.dll e System.dll, correspondem classes base como
threading, remoting, file I/O, messaging, networking, entre outros;
 ADO.NET - encontradas na System.Data.dll, comportam Data & XML;
 ASP.NET - encontradas na System.Web.Services.dll e System.Web.dll, comportam Web Services e Web Forms;
 Windows Forms - encontrada na System.Windows.Form.dll.

Descobrindo o Namespace

Inicialmente, iremos explorar uma parte do namespace System.IO. Onde nele contém classes que permitem ler e
escrever streams de dados e arquivos, na forma síncrona ou assíncrona. Através dele ocorre toda abstração do file
system.

O código abaixo, em C#, se compilado e executado, produz no console, através da classe System.Console uma saída
semelhante ao tradicional comando dir do DOS. A figura 2 mostra este resultado.

Figura 2: DirView em C#

//csc /out:DirView.exe DirView.cs

using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace Shell
{

public class DirViewCommand


{
[DllImport("kernel32.dll")]
extern private static bool GetDiskFreeSpaceEx(
string lpDirectoryName,
out long lpFreeBytesAvailable,
long lpTotalNumberOfBytes,
long lpTotalNumberOfFreeBytes);

public void DirView()


{

//Obtém o diretório atual e executa com o método DirView(string sPath)


this.DirView(Directory.GetCurrentDirectory());
}

public void DirView(string sPath)


{

//Se o diretório não existe e dispara uma excessão


if(Directory.Exists(sPath)==false)
throw new Exception("Directory not exists");

DirectoryInfo di = null;
try
{
di = new DirectoryInfo(sPath);
}
catch(Exception e)
{
throw e;
}

//Percorre e exibe os diretórios


foreach(DirectoryInfo dif in di.GetDirectories())
{
Console.WriteLine("[{0}]", dif.Name);
}

long lFilesInBytes = 0;

//Percorre e exibe os arquivos


foreach(FileInfo fi in di.GetFiles())
{
try
{
Console.WriteLine("{0,-20}{1,12:#,#} {2,-25}{3,20:G}", fi.Name, fi.Length, GetValueFromRegistry(fi.Extension),
fi.LastWriteTime);
lFilesInBytes+=fi.Length;
}
catch
{
Console.WriteLine("{0,-20}{1,12:#,#} {2,-25}", fi.Name, "???.???.???", GetValueFromRegistry(fi.Extension));
}
}

//Recebe o número de bytes livres


long lFreeBytes = 0;
GetDiskFreeSpaceEx(di.FullName, out lFreeBytes, 0, 0);

Console.WriteLine("{0,8} File(s){2,16:N0} bytes\r\n{1,8} Dir(s){3,17:N0} bytes free", di.GetFiles().Length,


di.GetDirectories().Length, lFilesInBytes, lFreeBytes);

private static string GetValueFromRegistry(string sExtension)


{

RegistryKey rk = Registry.ClassesRoot; //Percorre a hive HKCR


rk = rk.OpenSubKey(sExtension); //Abre a subkey a partir da extensão
if (rk!=null)
{
string sSubKey = (string)rk.GetValue(null); //Obtém o valor da default
rk = Registry.ClassesRoot; //Percorre novamente a hive HKCR
if(sSubKey!=null)
{
rk = rk.OpenSubKey(sSubKey); // Abre a subkey a partir do valor do reg.
if(rk!=null)
return (string)rk.GetValue(null); //Recebe a descrição da extensão
}
}

//Se a extensão não existe no Registry


if(sExtension.Length > 0)
return (sExtension.Substring(1).ToUpper() + " File");
else
return "File without Extension";

}
static void Main(string[] args)
{

DirViewCommand Dvcmd = new DirViewCommand();


//Verifica se o número de argumentos é maior que zeroif(args.Length>0){
//Verifica se o número de argumentos é diferente de "?" if(args[0]!="?")
Dvcmd.DirView(args[0]); //Executa DirView(string) na instância
else
{
//Exibe utilização do comando
Console.WriteLine("usage: DirView [path]");
Console.WriteLine("Ex:\r\nDirView c:\\");
Console.WriteLine("DirView");
}

}
else
Dvcmd.DirView(); //Executa DirView() na instância

A classe DirCommandView, dentro do namespace Shell, extende o Framework e manipula o file system através das
classes Directory, DirectoryInfo e FileInfo.
 Directory - herdeira da System.Object expõe as funcionalidades elementais de manipulação de dirétorio, tais
como, criar, apagar, mover, enumerar arquivos e diretórios e verificar atributos. Esta classe não pode ser
herdada, devido a característica sealed(ou NotInheritable). Seus métodos são estáticos sendo acessados
diretamente através da classe. Os principais são: CreateDirectory, GetCurrentDirectory, GetDirectories, GetFiles,
Exists, Move e Delete.
 DirectoryInfo - herdeira da System.IO.FileSystemInfo, nela expõe-se as mesmas funcionalidades da classe
Directory com alguns adicionais, porém a diferença ocorre cujo seus membros poderão ser acessados via uma
instância da classe e sendo distribuídos entre métodos. Os principais métodos são: Create, Delete, Move, Delete,
GetDirectories e GetFiles. Suas propriedades são Exists, Name, Parent e Root.
 FileInfo - herdeira da System.IO.FileSystemInfo esta classe expõe funcionalidades de manipulação de arquivos,
tais como, criar, copiar, apagar, mover e abrir. Ela não pode ser herdada, devido a característica sealed(ou
NotInheritable). Todos seus membros são utilizados através de uma instância da classe e seus principais
métodos: Open, OpenText, OpenRead, OpenWrite, Create, CopyTo, MoveTo, Delete e suas principais
propriedades são: Name, FullName, Length, Exists, Directory e DirectoryName.

Por definição um objeto é "trafegado" através de Remoting utilizando marshalling por valor, isto é, uma cópia do objeto
pode ser entregue. As classes DirectoryInfo e FileInfo suportam o marshalling por referência, ou seja, numa operação
Remoting o objeto é representado no cliente como um proxy o qual interage com o objeto na AppDomain(equivalente a
processo).

No ponto de entrada Main analiza-se os argumentos, dependendo do valor o método DirView é executado, caso contrário
uma pequena ajuda é exibida. O método DirView utiliza o conceito de sobrecarga, DirView() e DirView(string). A
implementação de DirView() é bem simples, onde na instância o método DirView(string) é executado com o diretório
atual, vindo de GetCurrentDirectory da classe Directory.

this.DirView(Directory.GetCurrentDirectory());

No caso de DirView(string), a partir de um objeto do tipo DirectoryInfo, as enumerações são efetuadas. Onde obtemos e
exibimos os diretórios através do método GetDirectories, seguido dos arquivos por GetFiles.

foreach(DirectoryInfo dif in di.GetDirectories())

foreach(FileInfo fi in di.GetFiles())

Nos detalhes do arquivo adquirimos seu nome, tamanho, descrição de extensão e último acesso, correspondendo as
propriedades Name, Length, Extension e LastWriteTime do objeto FileInfo.

Console.WriteLine(fi.Name, fi.Length, fi.Extension, fi.LastWriteTime);

Pela extensão do arquivo pode-se ler a descrição encontrada no Registry, onde essa tarefa é executada no método
GetValueFromRegistry.

GetValueFromRegistry(fi.Extension)

O método GetValueFromRegistry utiliza internamente a classe RegistryKey do namespace Microsoft.Win32, o objeto de


RegistryKey é uma instância fornecida pela classe Registry, com ClassesRoot. O código acima explora a hive
HKEY_CLASSES_ROOT do Registry. Outras possibilidades seriam CurrentUser, LocalMachine, Users, PerformanceData,
CurrentConfig e DynData. O método OpenSubKey abre a chave e seu valor é obtido com uma chamada ao método
GetValue, a partir desta sequência teremos a descrição da extensão.

No final, a quantidade de bytes é calculado e a quantidade de bytes livres da unidade pesquisada é exibida, no entanto
para este caso utilizamos a API Win32 , da kernel32.dll, GetDiskFreeSpaceEx, onde é importada através das regras de
P/Invoke pelo atributo DllImport encontrado em System.Runtime.InteropServices. Para o código acima, os únicos
valores interessados é a string com o nome do diretório e um long que receberá a quantidade livre. Perceba a utilização
de out indicando parâmetro de saída.

// Win32
BOOL GetDiskFreeSpaceEx(
LPCTSTR lpDirectoryName,
PULARGE_INTEGER lpFreeBytesAvailable,
PULARGE_INTEGER lpTotalNumberOfBytes,
PULARGE_INTEGER lpTotalNumberOfFreeBytes
);

// P/Invoke
[DllImport("kernel32.dll")]
extern private static bool GetDiskFreeSpaceEx(
string lpDirectoryName,
out long lpFreeBytesAvailable,
out long lpTotalNumberOfBytes,
out long lpTotalNumberOfFreeBytes
);

Continuando com diretórios e arquivos, o código abaixo, em C++ Managed Extensions, utiliza-se das classes de
System.IO para implementação de cópia de arquivo. Dois métodos sobrecarregados da instância FileCopy(String*,
String*) e FileCopy(String*) são implementados na classe FileCopyCommand abaixo. A figura 3 mostra este resultado.

Figura 3: FileCopy em C++ Managed Extensions

//cl /CLR FileCopy.cpp


//link /out:FileCopy.exe FileCopy.obj
#using

using namespace System;


using namespace System::IO;
namespace Shell{

__gc public class FileCopyCommand


{
public:
void FileCopy(String* sFullNameSrc, String* sFullNameDst){

//Se o arquivo origem existir


if (File::Exists(sFullNameSrc))
{
//Verifica se a string de destino corresponde a um diretório
if(sFullNameDst->EndsWith("\\"))
sFullNameDst = String::Concat(sFullNameDst,
this->GetFileNameFromSource(sFullNameSrc));

//Se o diretório destino existir copia o arquivo


if(Directory::Exists((newFileInfo(sFullNameDst))->DirectoryName))
File::Copy(sFullNameSrc, sFullNameDst, true); //Copia
else
throw new Exception("Directory not found");
}
else
throw new Exception("File not found");

return;
}

void FileCopy(String* sFullName)


{
String* psDest;
//Concatena o nome do arquivo e o diretório atual
psDest = String::Concat(Directory::GetCurrentDirectory(),
"\\", GetFileNameFromSource((String*)sFullName));

this->FileCopy(sFullName, psDest);

private:
String* GetFileNameFromSource(String* sFullName)
{

//Pega o índice da última barra


int a = sFullName->LastIndexOf("\\");
if(a!=-1)
returnsFullName->Substring(++a); //Retorna nome do arquivo
else
return sFullName; //Retorna nome do arquivo

}
};

void main(int argc, char* argv[])


{

//Verifica se o número de argumentos é maior que um


if(argc > 1)
{
Shell::FileCopyCommand* pFcmd = new Shell::FileCopyCommand();
//Se tiver apenas um argumento com excessão do nome do executável
if(argc==2)
pFcmd->FileCopy(argv[1]); //Executa FileCopy(string) na instância
else
//Executa FileCopy(string,string) na instância
pFcmd->FileCopy(argv[1],argv[2]);
}
else
{
//Exibe utilização do comando
Console::WriteLine("usage: FileCopy filesource|filepathsource [pathdestination|filedestination]");
Console::WriteLine("Ex:\r\nFileCopy c:\\bookmark.htm");
Console::WriteLine("FileCopy c:\\bookmark.htm d:\\");
Console::WriteLine("FileCopy c:\\bookmark.htm d:\\myfolder");
Console::WriteLine("FileCopy bookmark.htm d:\\myotherfolder");
}

A função main basicamente verifica o número de argumentos e executa o método da instância de acordo com esse valor
ou exibe uma pequena ajuda.

Na classe FileCopyCommand, o método FileCopy com um parâmetro, obtém o nome do arquivo de origem e o diretório
atual, concatena e executa o método FileCopy com dois parâmetros. O nome do arquivo de origem é obtido através do
método privado GetFileNameFromSource, que a partir de uma string procura uma substring a qual ocorre após a última
barra(\) , ou seja, feito com o método LastIndexOf do objeto string onde faz-se uma varredura do fim para o começo ao
pegar a primeira ocorrência e retornar o resultado e se não encontrado ele recebe a string completa.

Na implementação de FileCopy(String*, String* ), a primeira ação é verificar a existência do arquivo com o método
Exists da classe File.

 File - herdeira da System.Object, esta classe expõe as funcionalidades elementais de manipulação arquivo, tais como,
criar, copiar, apagar, mover e abrir. Ela não pode ser herdada, devido a característica sealed(ou NotInheritable) e seus
métodos estáticos. Sendo acessado diretamente através da classe. Seus principais métodos são: Open, OpenText,
OpenRead, OpenWrite, Create, Copy, Move, Delete, Exists e GetAttributes.

Na sequência verifica se a string de destino finaliza com uma barra indicando um diretório, isto é, feito com o método
EndsWith do objeto String cujo retorna um booleano. Caso verdadeiro o nome do arquivo de origem é concatenado com
o caminho de destino através do método Concat da classe String.
if(sFullNameDst->EndsWith("\\"))
sFullNameDst = String::Concat(sFullNameDst,
this->GetFileNameFromSource(sFullNameSrc));

A partir do caminho completo do arquivo é verificado se o diretório de destino existe, para tanto o método
DirectoryName de uma instância de FileInfo é utilizada juntamente ao método Exists da classe Directory que retorna um
booleano. Se a avaliação é verdadeira a cópia de arquivo é feita através do método Copy da classe File. Os parâmetros
de Copy são uma string com a origem do arquivo, uma string com o destino do arquivo e um booleano opcional para
sobreescrever o arquivo se existente.

if(Directory::Exists((newFileInfo(sFullNameDst))->DirectoryName))
File::Copy(sFullNameSrc, sFullNameDst, true);
else
throw new Exception("Directory not found");

O código seguinte, em JScript.NET, utiliza as classes de System.IO para implementação de mover arquivo. Os métodos
FileMove (String, String) e FileMove(String) são implementados na classe FileMoveCommand abaixo. Vale lembrar que o
método FileMove utiliza sobrecarga. Como demonstardo na figura 4 .

Figura 4: FileMove em JScript.NET

//jsc /out:FileMove FileMove.js

import System;
import System.IO;
import Microsoft.JScript;
package Shell
{

public class FileMoveCommand{

function FileMove(source : String)


{
//Com o nome do arquivo origem e o diretório atual executa FileMove
this.FileMove(source, Directory.GetCurrentDirectory());

}
function FileMove(source : String, destination : String)
{

//Verifica se o arquivo origem existe


if(File.Exists(source))
//Verifica se o diretório destino existe
if(Directory.Exists(destination))
File.Move(source, destination + new FileInfo(source).Name); //Move
else
throw new Exception("Destination Directory not Exists");
else
throw new Exception("Source File not Exists");

import Shell;

//Recebe a linha de comando


var sArgs:String[] = Environment.CommandLine.Split(" ");
if(sArgs.Length>1)
{
var Fmcmd:FileMoveCommand = new FileMoveCommand();

//Se tiver apenas um argumento com excessão do nome do executável


if(sArgs.Length==2)
Fmcmd.FileMove(sArgs[1]); //Executa FileMove(string) na instância
else
//Executa FileMove(string, string) na instância
Fmcmd.FileMove(sArgs[1], sArgs[2]);

}
else
{
//Exibe utilização do comando
print("usage: FileMove filesource|filepathsource [pathdestination]");
print("FileMove c:\bookmark.htm d:\myfolder");
print("FileMove bookmark.htm d:\myfolder");
print("FileMove c:\bookmark.htm");
print("FileMove bookmark.htm");
}

Na execução, a propriedade CommandLine da classe Environment encontrada em System retorna a linha de comando, a
qual é o nome do executável mais os argumentos adicionais separados por espaço. Estes valores são armazenados num
vetor de strings obtidos através do método Split que as separa por um determinado vetor de caractéres. Caso existam
argumentos adicionais, um dos métodos FileMove será invocado com os devidos parâmetros, senão uma pequena ajuda
é exibida no console.

Na classe FileMoveCommand o primeiro método implementado é o FileMove com um parâmetro, que simplesmente
executa o método FileMove passando o arquivo origem e o diretório atual.

this.FileMove(source, Directory.GetCurrentDirectory());

O método FileMove recebe duas strings, origem e destino do arquivo. Inicialmente verifica-se a existência do arquivo
origem e na sequência o diretório de destino, se qualquer um deles não existir uma excessão correspondente é
disparada, senão o arquivo é movido, através do método Move da classe File.

if(File.Exists(source))
if(Directory.Exists(destination))
File.Move(source, destination + new FileInfo(source).Name);
else
throw new Exception("Destination Directory not Exists");
else
throw new Exception("Source File not Exists");

Finalizando, o código abaixo, em Visual Basic.NET, utiliza as classes de System.IO para implementação de exibição de
arquivo, muito semelhante com o comando Type do DOS. Apenas um método Display é encontrado na classe
DisplayCommand. A figura 5 mostra este resultado

Figura 5: Display em Visual Basic.NET

'vbc /out:Display.exe Display.vb

Imports System
Imports System.IO
Imports Microsoft.VisualBasic
Module MainModule

Sub Main()

'Verifica se existe algum argumento


If (Interaction.Command() <> Constants.vbNullString) Then
Dim vbShell As New Shell.DisplayCommand()
vbShell.Display(Interaction.Command())

Else

'Exibe utilização do comando


Console.WriteLine("usage: Display filesource|filepathsource")
Console.WriteLine("Ex:" + Strings.Chr(10) + Strings.Chr(13) + "Display bookmark.htm")
Console.WriteLine("Display c:\mypages\bookmark.htm")
End If

End Sub

End Module

Namespace Shell

Public Class DisplayCommand

Public Sub Display(ByVal sFullName As String)

If (Not File.Exists(sFullName))

Then Throw New Exception("File not exists")

'Abre arquivo somente para leitura


Dim fs As New FileStream(sFullName, FileMode.Open, FileAccess.Read)
'Cria o reader para navegação
Dim sr As New StreamReader(fs)

'Executa a leitura completa do stream


Dim line As String = sr.ReadLine()
Do Until (line Is Constants.vbNullString)
Console.WriteLine(line) 'Exibe a linha lida
line = sr.ReadLine() 'Armazena o linha lida
Loop

sr.Close() 'Fecha o StreamReader


fs.Close() 'Fecha o FileStream

End Sub

End Class

End Namespace

Na procedure Main, encontrada no módulo MainModule, se existe algum argumento este é recuperado através do
método Command da classe Interaction, ele retorna a constante vbNullString caso nenhum argumento for passado. A
classe DisplayCommand possui o método Display onde verifica a existência do arquivo através do método Exists da
classe File. Para a exibição do conteúdo são criados os objetos FileStream para abertura do arquivo e StreamReader para
navegação.
 FileStream - herdeira da System.IO.Stream esta classe permite as tradicionais operações de leitura e escrita em
arquivos, podendo ser síncrona ou assíncrona. Seus principais métodos são: Read, ReadByte, Write, WriteByte,
Seek, Flush, Lock e Unlock e suas propriedades são: CanRead, CanSeek, CanWrite, Handle, IsAsync, Length,
Name e Position.
 StreamReader - herdeira da System.IO.TextReader esta classe lê caracteres de uma stream de bytes. O
encoding padrão é o UTF-8 que trata a Unicode, porém este comportamento pode ser alterado durante a
inicialização através dos construtores. Os principais métodos são:: Read, ReadLine, Peek e Close. Suas
propriedades são: BaseStream e CurrentEncoding.

O arquivo de origem é aberto através de FileStream, o qual utiliza um dos seus construtores passando uma string com o
nome e o caminho do arquivo, um valor inteiro dentro da enumeração FileMode e também um valor inteiro dentro da
enumeração FileAccess. No caso acima, o FileMode, para indicação do modo de operação do arquivo, é o Open e seus
outros valores possíveis são: Append, Create, CreateNew, OpenOrCreate e Truncate. E o FileAccess, para indicação de
acesso do arquivo, é o Read e seus outros valores são: Write e ReadWrite.

Dim fs As New FileStream(sFullName, FileMode.Open, FileAccess.Read)

O objeto para navegação da stream é da classe StreamReader que consome através de um de seus construtores o
objeto criado por FileStream.

Dim sr As New StreamReader(fs)

A leitura é feita através do método ReadLine do objeto da StreamReader. Ele lê uma linha por vez e retorna
null(vbNullString) ao final da leitura do arquivo. A linha lida é limitada por \n(NewLine) ou \r\n(CrLf). Esta operação pode
ser colocada dentro de um comando de iteração(loop) para leitura completa do arquivo. Devemos utilizar o método Close
quando o StreamReader e o FileStream não são necessários, ou seja, a boa prática continua se valendo, consumir tarde
e liberar cedo!

Onde estamos e onde queremos chegar

Nesta edição vimos alguns componentes, os quais formam a plataforma .NET e iniciamos a exploração do Framework.
Através de exemplos em C#, Visual Basic.NET, C++ Managed Extensions e JScript.NET, o ponto de partida foram
algumas classes do namespace System.IO que manipulam arquivos e diretórios, como: Directory, DirectoryInfo, File,
FileInfo, FileStream e StreamReader. Sendo que o .NET Framework contém a documentação essencial para o manuseio
da biblioteca de classes.
Como prática, é interessante implementar novas funcionalidades, tais como FileDelete, FileRename, MakeDir, entre
outros. Implemente-as na linguagem que se sentir mais confortável, ou até mesmo reescrever os exemplos em outras
linguagens.

Caça ao memory leak


Softwares num mundo não-gerenciado podem sofrer do conhecido problema do memory leak. Normalmente este
problema é detectado quando uma solução está em produção, ocasionando quebra do aplicativo e por vezes
desestabilizando a infraestrutura. Aplicações escritas de maneira correta podem não apresentar problemas, porém há
alguns fatores a considerar na ocorrência deste tipo de transtorno, como por exemplo, componentes e bibliotecas de
terceiros que ajudam no consumo de memória deliberada, bem como o simples ato de alocar e desalocar memória
dinamicamente, o qual atrapalha a vida de um programador desatento. Sem levar em conta, referências circulares e
contagem de referências.

Como a Common Language Runtime pode minimizar isto? Garbage Collector, ou o coletor de lixo, faz parte do
gerenciamento de memória da infraestrutura do .NET Framework responsável pelo tracing, liberação automática de
objetos e recursos da aplicação. Todo objeto ou reference type tratado pela Common Language Infrastructure, como
managed data, está alocado na managed heap cuja mesma é inspecionada pelo coletor de lixo, o qual tem a função de
liberar memória consumida desnecessariamente facilitando assim o trabalho do desenvolvedor. A alocação na managed
heap ocorre através do operador new onde possuí uma performance muito semelhante a alocação na stack. O coletor de
lixo contém threads no qual roda em baixa prioridade e verifica o comportamento da mémoria. Quando um objeto
implementa o método Finalize um thread é responsável pelo enfileiramento na finalization queue onde marca o objeto
como pronto para finalização e invoca o método Finalize. Para melhorar a performance, objetos maiores que 20K são
alocados numa heap de objetos grandes.

Ao iniciar um processo a CLR reserva uma região para alocação de memória dinâmica chamada de managed heap. Ela
por sua vez mantém um ponteiro chamado NextObjPtr cujo indica a área da próxima alocação. Quando o operador new é
utilizado, ele verifica se existe memória suficiente para essa operação, caso contrário uma excessão é disparada. Ao criar
o objeto o NextObjPtr aponta para ele e seu construtor se aciona, tendo por fim seu endereço retornado. Após isso o
NextObjPtr é incrementado passando para o próximo objeto que será colocado na heap.

Figura 1: Managed heap antes da coleta de lixo

A figura 1 mostra como se forma a managed heap antes de sua coleta. O coletor de lixo utiliza um algoritmo de "marca e
compactação". Onde o mesmo inicializa a busca pelos objetos da raíz e procura todos os objetos referenciados. Todo
objeto que não está sendo utilizado diretamente ou indiretamente é marcado para ser coletado. Ao final do processo de
coletagem os objetos da heap são reorganizados. Durante este trabalho qualquer processamento fica interrompido,
enquanto o garbage collector é executado. No caso de falta de memória, ou seja quando a managed heap a consome
completamente o operador new dispara a excessão OutOfMemoryException. A figura 2 em relação a figura 1 mostra
como ficará formada a managed heap após a coleta.
Figura 2: Managed heap após da coleta de lixo

Generations

A "mágica" por trás do Garbage Collector está na forma como a memória é coletada, ou seja, ele implementa uma
inteligência onde prioriza os objetos utilizando um conceito chamado generations, ou generational garbage collector. Este
conceito determina quanto mais novo é um objeto mais curto será seu tempo de vida, e quanto mais velho o objeto
torna-se, mais longo será seu tempo de vida, além de uma parte da heap ser compactada ao invés dela completamente.
Melhorando assim a performance da coleta de memória. Na atual versão do Garbage Collector, o valor 0 é atribuido para
o mais novo objeto, isto é, quando um objeto é criado ele nasce como Generation 0 ou Gen 0. A prioridade máxima é 2
indicando a permanência mais longa na heap. No entanto, o valor de prioridade máxima é retornado pela propriedade
MaxGeneration da classe GC, bem como o atual valor do objeto o qual é obtido através do método GetGeneration
também da classe GC. Normalmente quem vive(ou morre) na Gen 0 são objetos ou variáveis locais. Na Gen 1 objetos
que sobreviveram a primeira coleta, como por exemplo formulários e controles. E na Gen 2 objetos que sobreviveram a
várias coletas, como as aplicações.

A figura 3 exibe as etapas de alocação, coleta e generations. Na fase 1 da figura 3 vemos a alocação de três objetos na
heap, o valor inicial é atribuído a cada um deles(Gen 0). Na fase 2, após a primeira coleta, os objetos sobreviventes
passam para o próximo nível(Gen 1) e novos objetos são alocados(Gen 0). Na fase 3, objetos sobreviventes da Gen 1
passam para a Gen 2, da Gen 0 para Gen 1 e novos objetos são alocados. Em toda a coleta, a Gen 0 é verificada para
liberação de memória, caso não exista nenhum objeto na Gen 0, a Gen 1 passa a ser verificada, seguida da Gen 2, e
assim por diante, conforme apresentado nas etapas 4 e 5.

Figura 3: Processo de Alocação, coleta e generations

Clean Up

A limpeza de dados, mesmo que automática, é válida apenas para managed data. Entretanto, quando uma aplicação
utiliza recursos externos, tais como conexão com banco de dados, conexão com redes ou outros handles, duas
possibilidades são oferecidas pelo Framework: Finalize e Dispose. Outra possibilidade é estabelecer um método
personalizado, como por exemplo Close, assim como muitas classes do Framework utilizam para este tipo de limpeza. O
código a seguir exibe uma típica aplicação a qual se utiliza classes do Framework onde ela efetua liberação de recursos.
Perceba logo abaixo a utilização do método Close junto com uma instância de StreamReader:

'vbc /out:StreamReader.exe StreamReader.vb


Imports System
Imports System.IO
Imports Microsoft.VisualBasic

Module MyApp

Sub Main()

Dim separators as String = " "


'Lê os argumentos da linha de comando
Dim commands as String = Interaction.Command
'Separa-os em um array
Dim args() as String = commands.Split(separators.ToCharArray)
Dim sFileName as String = args(0)
Dim sView as String '/x Hexadecimal - /c Character
Dim lineCountMax as Integer

If args.Length <= 1 Then


sView = "/x"
Else
sView = args(1).ToLower()
If sView <> "/x" And sView <> "/c" _
Then Throw New Exception("View not defined")
If sView = "/x" Then lineCountMax = 16 Else lineCountMax = 64
End If

If Not File.Exists(sFileName) Then Throw New Exception("File not exists")

Dim strmReader as New StreamReader(sFileName)


Dim lineCount as Byte
Dim ch as Integer = strmReader.Read()

'Percorre e exibe toda a Stream


Do While(ch <> -1)
lineCount = lineCount + 1

If lineCount = lineCountMax Then


If sView = "/x" Then
Console.WriteLine("{0,3:X}",ch)
Else
Console.WriteLine("{0}",Convert.ToChar(ch))
End If

lineCount = 0
Else
If sView = "/x" Then
Console.Write("{0,3:X} ",ch)
Else
Console.Write("{0}",Convert.ToChar(ch))
End If
End If

ch = strmReader.Read() 'Lê um caracter


Loop

strmReader.Close() 'Fecha a StreamReader e libera os recursos do sistema

End Sub

End Module

O código acima se compilado e executado produz no prompt o resultado abaixo(figura 4):

Figure 4: Compilação e execução do programa StreamReader


Finalize e Destrutor

A utilização do método Finalize representa o equivalente ao destrutor de uma classe C++, porém possui comportamento
diferente. Uma classe em .NET somente aciona seu destrutor, ou o método Finalize implementado, quando passa pelo
processo de coleta de lixo, ou seja, não existe o processo de liberação do objeto de forma síncrona. O método Finalize,
ou destrutor em Managed C++ ou C#, possui um overhead relacionado a sua invocação, portanto utilize-o apenas
quando houver necessidade, mesmo porque isso pode impactar na performance da aplicação. Outro ponto é a liberação
de recursos. O método Finalize não libera esses recursos até sua invocação por causa da característica não-
determinísitica, não temos certeza do que e quando foram desalocados, neste caso o padrão proposto é utilizar uma
implementação de IDisposable(ou algo semelhante), onde o controle da liberação de recursos é requisitada. Abaixo
segue algumas regras de utilização do método Finalize:
 Somente implemente o método Finalize em classes que necessitem finalização. Existe algum overhead associado
a este procedimento.
 Não faça o método Finalize visível. Ele deve ser protected não public.
 Libere qualquer recurso externo consumido no seu finalizador
 Caso possua apenas recursos gerenciados ou managed references não há necessidade de implementar o método
Finalize.
 Não utilize outros objetos de seu finalizador quando eles já foram finalizados. Um finalizador deve somente
liberar recursos que são utilizados pelo seu objeto e ele não deve referenciar qualquer outro objeto.
 Nunca chame um finalizador que não seja diretamente da sua base.
 Chame base.Finalize() no seu finalizador. Isto será feito automaticamente com a síntaxe do destrutor em C#
depois da compilação.

Abaixo segue implementação de uma classe onde encapsula um recurso da Base Services da Win32 API chamado Atom,
maiores informações podem ser encontradas no Platform SDK. Note na implentação do destrutor em C# que ele é
prefixado com ~ e não possui nem argumentos e nem valor de retorno:

//csc /out:Atom.exe Atom.cs

using System.Runtime.InteropServices;

//Esta classe cria um wrapper simples para Global Atom Table


class CGlobalAtomWrapper
{

[DllImport("kernel32.dll", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)]


private static extern short GlobalAddAtom(
string lpString
);

[DllImport("kernel32.dll", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)]


private static extern short GlobalDeleteAtom(
short nAtom
);

[DllImport("kernel32.dll", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)]


private static extern uint GlobalGetAtomName(
short nAtom,
char[] lpBuffer,
int nSize
);

private int m_Size; //Armazena o tamanho da string para criar o buffer


private short m_Atom; //Armazena o Global Atom

//Construtor
public CGlobalAtomWrapper(string s)
{

m_Size = s.Length;
m_Atom = GlobalAddAtom(s); //Cria o Global Atom e armazena uma string na GAT

public string GetData()


{

char[] s = new char[m_Size];


GlobalGetAtomName(m_Atom,s,m_Size); //Recupera a string armazenada na GAT
return new string(s);

//Destrutor
~CGlobalAtomWrapper()
{
if(m_Atom!=0)
GlobalDeleteAtom(m_Atom); //Destroi o Global Atom
}
}

public class MyApp


{

public static void Main()


{

CGlobalAtomWrapper GlobalAtom = new CGlobalAtomWrapper("Hello World");


System.Console.WriteLine(GlobalAtom.GetData());

CGlobalAtomWrapper GlobalAtom2 = new CGlobalAtomWrapper("This is C#!!!");


System.Console.WriteLine(GlobalAtom2.GetData());

Utilizando o ildasm.exe(figura 5) com o executável produzido pelo compilador C# do código acima, verifica-se que
internamente na transformação para CIL(Common Intermediate Language) o destrutor é implementado como método
(protected(C#)/ family(CIL)) Finalize. Note que o compilador embute um bloco de tratamento de excessão .try/finally e
na linha IL_0017 o método Finalize do objeto base é acionado.

.method family hidebysig virtual instance void Finalize() cil managed


{
// Code size 30 (0x1e)
.maxstack 1
.try
{
IL_0000: ldarg.0
IL_0001: ldfld int16 CGlobalAtomWrapper::m_Atom
IL_0006: brfalse.s IL_0014
IL_0008: ldarg.0
IL_0009: ldfld int16 CGlobalAtomWrapper::m_Atom
IL_000e: call int16 CGlobalAtomWrapper::GlobalDeleteAtom(int16)
IL_0013: pop
IL_0014: leave.s IL_001d
} // end .try
finally
{
IL_0016: ldarg.0
IL_0017: call instance void [mscorlib]System.Object::Finalize()
IL_001c: endfinally
} // end handler
IL_001d: ret
} // end of method CGlobalAtomWrapper::Finalize
Figura 5: Implementação interna do destrutor
Dispose

O objetivo do método Dispose é liberar os recursos da classe que não são mais necessários de um forma síncrona.
Normalmente estes recursos não estão relacionados a memória gerenciada, mas a handles de banco de dados, gráficos,
network, entre outros. Através de propagação a classe implementada pode chamar o método Dispose da classe base até
que todos os recursos consumidos pelo objeto sejam liberados.

O .NET Framework utiliza uma interface padrão, IDisposable, para suporte a implementação do método Dispose e
verificação em tempo de execução através de polimorfismo. É conveniente que uma vez implementado o método
Dispose a chamada do método SuppressFinalize da classe GC a qual evita o processamento do destrutor até mesmo
quando ele estiver pronto para ser executado a partir da finalization queue.

A utilização do método Dispose é aconselhada na maioria dos casos, pois a medida que os recursos da aplicação ficam
escassos e a chamada do destrutor não é controlada é possível o domínio da liberação dos recursos. Abaixo são
apresentadas as regras de utilização de Dispose:
 Implemente a interface IDisposable.
 Forneça um método público para os usuários liberarem os recursos externos.
 Libere qualquer recurso externo no método Dispose.
 Utilize SuppressFinalize na instância onde o método Dispose foi chamado.
 Chame o método Dispose da classe base se ela implementar a interface IDisposable.
 Não assuma que o método Dispose será chamado. Recursos deverão ser liberados nos finalizadores apenas no
caso que o método Dispose não for chamado.
 Dispare a excessão ObjectDisposedException quando recursos já foram liberados através de Dispose(disposed).
 Propague através das hierárquias contidas.
 Considere não ter seu objeto utilizável após uma chamada do método Dispose. Recriar um objeto que fora
liberado através de Dispose é uma tarefa muito complexa.
 Permita seu método Dispose chamado mais que uma vez sem disparar uma excessão. Deixe-o apenas inativo
ápos a primeira chamada.

'vbc /out:Atom.dll /target:library Atom.vb

Imports System
Imports System.Runtime.InteropServices

'Esta classe cria um wrapper simples para Global Atom Table


Public Class CGlobalAtomWrapper
Implements IDisposable
<DllImport("kernel32.dll", CharSet:=CharSet.Unicode, _
CallingConvention:=CallingConvention.StdCall)> _
Private Shared Function GlobalAddAtom(lpString as String) as Short
End Function

<DllImport("kernel32.dll", CharSet:=CharSet.Unicode, _
CallingConvention:=CallingConvention.StdCall)> _
Private Shared Function GlobalDeleteAtom(nAtom as Short) as Short
End Function

<DllImport("kernel32.dll", CharSet:=CharSet.Unicode, _
CallingConvention:=CallingConvention.StdCall)> _
Private Shared Function GlobalGetAtomName(nAtom as Short, lpBuffer() as Char, _
nSize as Integer) as Long
End Function

Private m_Size as Integer 'Armazena o tamanho da string para criar o buffer


Private m_Atom as Short 'Armazena o Global Atom

'Construtor
Public Sub New(s as String)

m_Size = s.Length
m_Atom = GlobalAddAtom(s) 'Cria o Global Atom e armazena uma string na GAT

End Sub

'Método Dispose para liberação de recursos manualmente


Public Sub Dispose() Implements IDisposable.Dispose

If m_Atom <> 0 Then


GlobalDeleteAtom(m_Atom) 'Destroi o Global Atom
'Neste caso é opcional pois a classe não implementa o Finalize,
'porém isto é uma boa prática
GC.SuppressFinalize(Me)
End If

End Sub

Public Function GetData() as String

Dim s(m_Size) as Char


GlobalGetAtomName(m_Atom,s,m_Size) 'Recupera a string armazenada na GAT
Return New String(s)

End Function

End Class

Uma vez que o padrão do Framework para a implementação do Dispose seja através da interface IDisposable, é possível
para a aplicação consumidora identificá-la através da verificação em run-time. No código abaixo existe 3 situações para
verificação em run-time do suporte da interface. No primeiro trecho o consumidor tem certeza da existência da interface
IDisposable cuja utiliza um cast para execução do método Dispose. No segundo trecho o operador is é utilizado e no
terceiro trecho uma versão com menos overhead que a anterior utiliza o operador as.

//csc /r:Atom.dll /out:AtomConsumer.exe AtomConsumer.cs

using System;

public class MyApp


{

public static void Main()


{

//Trecho #1
CGlobalAtomWrapper ga1 = new CGlobalAtomWrapper("I have support to IDisposable interface!");
try
{
Console.WriteLine(ga1.GetData());
}
finally
{

if(ga1 != null)
((IDisposable)ga1).Dispose(); //Cast para obter a interface IDisposable

//Trecho #2
CGlobalAtomWrapper ga2 = new CGlobalAtomWrapper("Maybe I have support to IDisposable interface. I need check the
run-time type!");
try
{
Console.WriteLine(ga2.GetData());
}
finally
{

if(ga2 != null)
{
//Verifica se este tipo suporta a interface IDisposable.
//Neste caso o compilador emite um aviso que interface é suportada.
if(ga2 is IDisposable)
{
IDisposable disp = ga2;
disp.Dispose(); //Após verificação de suporte interface IDisposable
}
}

//Trecho #3
CGlobalAtomWrapper ga3 = new CGlobalAtomWrapper("Maybe I have support to IDisposable interface. I need check the
run-time type, but without overhead!");
try
{
Console.WriteLine(ga3.GetData());
}
finally
{

if(ga3 != null)
{
//Elimina o excesso do operador is
IDisposable disp = ga3 as IDisposable;
//Após verificação de suporte interface IDisposable
if(disp != null) disp.Dispose();
}

Dentro das opções acima apresentadas a melhor é a primeira. Adaptada e repetida logo abaixo. No caso de IDisposable
o código abaixo será utilizado, na maioria dos casos, pelas aplicações consumidoras que liberarão os recursos através de
Dispose e protegendo o bloco de utilização do objeto com try/finally é certeza de invocação do método. As outras opções
apresentadas anteriormente ficam para o tratamento de outras interfaces.

CGlobalAtomWrapper ga = new CGlobalAtomWrapper("Using IDisposable interface!");


try
{
Console.WriteLine(ga.GetData());
}
finally
{
if(ga != null)
((IDisposable)ga).Dispose();
}

Entretanto o código acima pode ser reescrito em C# com o comando using(não confunda com o using para utilização de
namespace) suportado pelo compilador. Sua função é exatamente proteger o código com o bloco try/finally e chamar a
interface IDisposable do objeto que está dentro do parenteses, como o código acima. Porém, ele elimina o excesso na
codificação. O comando using citado suporta aninhamento.

using(CGlobalAtomWrapper ga = new CGlobalAtomWrapper("Here We Go!"))


{
Console.WriteLine(ga.GetData());
}
Controlando o Garbage Collector

O Garbage Collector é processado em background onde threads em baixa prioridade executam a tarefa de inspecionar e
liberar os recurso da mémoria consumida, portanto não requer uma interação direta da aplicação. O coletor faz seu
trabalho automaticamente. No entanto, há casos que são importante controlar o comportamento dele, e para isso existe
a classe GC no namespace System da biblioteca mscorlib.dll.
 GC - herdeira da System.Object tem controle do coletor de lixo do sistema, e seus membros influenciam na
liberação de recursos consumidos forçando uma coleta e fornecendo informações sobre o total de memória
disponível do sistema, generation e memória alocada de um objeto. Esta classe não pode ser herdada, devido a
característica sealed(ou NotInheritable). Todos seus membros são estáticos, ou class-level, sendo acessados
diretamente através da classe. Seus métodos são Collect, GetGeneration, GetTotalMemory, KeepAlive,
ReRegisterForFinalize, SuppressFinalize e WaitForPendingFinalizers. Sua única propriedade é MaxGeneration.

//jsc /r:Atom.dll /out:GCSample.exe GCSample.js

import System;
import Microsoft.JScript;

var obj:CGlobalAtomWrapper = new CGlobalAtomWrapper("Have a nice day!!!");

Console.WriteLine(GC.GetGeneration(obj)); //Gen0

GC.Collect(); //Promove para proxima Gen

Console.WriteLine(GC.GetGeneration(obj)); //Gen1

GC.Collect(); //Promove para proxima Gen

Console.WriteLine(GC.GetGeneration(obj)); //Gen2

GC.Collect(); //Promove para proxima Gen

Console.WriteLine(GC.GetGeneration(obj)); //Gen2 = MaxGen

Console.WriteLine(GC.MaxGeneration);

obj.Dispose();

obj = null;

Console.WriteLine(GC.GetTotalMemory(false) + " bytes at " + DateTime.Now.ToString());

GC.Collect(0); //Coleta na Gen0

GC.WaitForPendingFinalizers();

Console.WriteLine(GC.GetTotalMemory(true) + " bytes");

GC.Collect(1); //Coleta na Gen1

GC.WaitForPendingFinalizers();

Console.WriteLine(GC.GetTotalMemory(true) + " bytes");

GC.Collect(2); //Coleta na Gen2

GC.WaitForPendingFinalizers();

Console.WriteLine(GC.GetTotalMemory(true) + " bytes");

Design Pattern

Implementar os métodos Finalize e Dispose numa única classe onde consome recursos externos é uma tarefa comum,
principalmente para oferecer ao consumidor a possibilidade de escolha entre liberar manualmente ou automaticamente,
garantido, inclusive, liberação desses recursos mesmo que a aplicação termine de maneira inesperada. Utilizar um
padrão comum de implementação é considerado uma boa prática de programação. O exemplo abaixo encontrado na
documentação do .NET Framework exibe a implementação através de um padrão bem definido.

class ResourceWrapper : BaseType, IDisposable


{

private IntPtr handle; //Ponteiro para um recurso externo.


private OtherResource otherRes; //Outro recurso.
//Variável que controla o comportamento da liberação de recursos.
private bool disposed = false;

public ResourceWrapper()
{
handle = //Aloca um recurso não-gerenciado.
otherRes = new OtherResource(…);
}

//Libera seu estado.


private void freeState()
{
if(!disposed)
{
CloseHandle(handle);
dispose = true;
}
}

//Libera seu estado, chama Dispose em todos os estados mantidos, e


//impende a chamada do destrutor na finalization queue.
public void Dispose ()
{
freeState ();
OtherRes.Dispose();
base.Dispose(); //Se a classe base implementa Dispose, deve ser chamado.
GC.SuppressFinalization(this);
}

//Libera seu estado(não os outros estados mantidos) e dá a chance


//do destrutor da base ser invocado.
~ResourceWrapper()
{
freeState();
}

//Sempre que algo for feito com esta classe, verifique se


//o estado é disposed, se sim, dispare a excessão ObjectDisposedException
public void SimpleDoStuff ()
{
if(disposed)
{
throw ObjectDisposedException ("ResWrapper");
}
}

Customizar o método Dispose também é uma boa prática. Possivelmente um nome mais amigável, ou mais apropriado,
deve ser utilizado, como por exemplo, o método Close o qual é encontrado na maioria das classes do Framework que
também tem por objetivo a liberação de recursos.

O exemplo abaixo implementa o design pattern proposto juntamente ao método Close:

//cl /CLR AtomC.cpp

#include <memory.h>
#using <mscorlib.dll>

using namespace System;


using namespace System::Runtime::InteropServices;

[DllImport("kernel32", CharSet=CharSet::Unicode, CallingConvention=CallingConvention::StdCall)]


extern "C" short GlobalAddAtom(
String* lpString
);

[DllImport("kernel32", CharSet=CharSet::Unicode, CallingConvention=CallingConvention::StdCall)]


extern "C" short GlobalDeleteAtom(
short nAtom
);

[DllImport("kernel32", CharSet=CharSet::Unicode, CallingConvention=CallingConvention::StdCall)]


extern "C" unsigned int GlobalGetAtomName(
short nAtom,
String* lpBuffer,
int nSize
);

__gc class CGlobalAtomWrapper:public IDisposable


{

private:
int m_Size; //Armazena o tamanho da string para criar o buffer
short m_Atom; //Armazena o Global Atom
bool m_disposed; //Armazena o status da limpeza de recursos

public:
//Construtor
CGlobalAtomWrapper(String* s):m_disposed(false)
{

m_Size = s->Length;
//Cria o Global Atom e armazena uma string na GAT
m_Atom = GlobalAddAtom(s);

};

String* GetData()
{

char* ch = new char[m_Size];


ch[m_Size] = '\0';
memset(ch,32,m_Size);
String* s = new String(ch);
//Recupera a string armazenada na GAT
GlobalGetAtomName(m_Atom,s,m_Size);
return s;

};

//Destrutor
~CGlobalAtomWrapper()
{

//Executa a liberação de recursos


FreeResources();

};

//Função Dispose
void Dispose()
{

//Executa a liberação de recursos


FreeResources();

//Quando uma classe possui a interface IDisposable


//e um destrutor para liberação de recursos
//é necessário utilizar o comando abaixo dentro do
//método Dispose para impedir a execução do destrutor
GC::SuppressFinalize(this);

};

//Função mais amigável para Dispose


void Close()
{

Dispose();

};

private:
//Função interna que libera os recursos consumidos
void FreeResources()
{

//Verifica se já ocorreu a limpeza


if(!m_disposed)
{
if(m_Atom != 0)
GlobalDeleteAtom(m_Atom); //Destroi o Global Atom
m_disposed = true; //Indica que já ocorreu a limpeza
}

};

};

void main()
{

//Deixando a liberação de recursos por conta do destrutor(Finalize)


CGlobalAtomWrapper* ga = new CGlobalAtomWrapper(S"Hello World!");
Console::WriteLine(ga->GetData());

//Deixando a liberação de recursos por conta do Close(Dispose)


CGlobalAtomWrapper* ga2 = new CGlobalAtomWrapper(S"Hello World, Again!");
Console::WriteLine(ga2->GetData());
ga2->Close();

Weak Reference

Toda referência a um objeto apontado direta ou indiretamente na memória é chamada de strong reference. Uma strong
reference não pode ser coletada pelo Garbage Collector até que essa referência seja eliminada. Porém, o Garbage
Collector suporta uma funcionalidade chamada de weak reference, representada no Framework pela classe
WeakReference do namespace System, onde ela oferece uma alternativa à strong reference. Weak references permite
que o Garbage Collector colete o objeto, mas caso isso não aconteça, ele possibilita a recuperação do mesmo através da
propriedade Target da classe WeakReference. Isto faz com que as estruturas ou classes onde mantém alto consumo de
memória fiquem ativas quando a memória consumida pelo sistema estiver baixa. A propriedade Target restaura a strong
reference ou null e é retornado se ela fora coletada. Por exemplo, é possível criar um componente que mantenha
dados(ou estado) para consulta em memória.
 WeakReference - herdeira da System.Object indica uma weak reference que contém a existência do objeto em
memória, porém ele pode ser coletado. Seus membros são de instância. Seu único método é GetObjectData
usado em serialização e suas propriedades são: IsAlive, Target e TrackResurrection.

//csc /out:WeakApp.EXE WeakRef.cs

using System;
using System.Collections;

class MyApp
{

public static ArrayList GetArrayList()


{

ArrayList al = new ArrayList();


al.Add("C#");
al.Add("VB");
al.Add("C++");
al.Add("JScript");
//Num exemplo real mais dados seriam necessarios

return al;

public static void Main()


{

//Strong reference
ArrayList al = GetArrayList(); //Cacheia o ArrayList

WeakReference wr = new WeakReference((object)al);

//Remove a strong reference


al = null;

object obj = wr.Target;


if(obj == null)
al = GetArrayList(); //GC ocorrido. Recarregar!
else
al = (ArrayList)obj; //GC não ocorrido. Obter da memória!
foreach(string s in al)
Console.WriteLine(s);

No ciclo de eliminação do objeto, quando suportar o método Finalize, ele não morre completamente. Primeiramente ele
morre, vive novamente para executar sua finalização e morre uma vez mais. Este processo é chamado de Resurrection.
Com a classe WeakReference é possível rastrear e até mesmo obter uma referência para uma classe já morta, porém
este comportamento pode ser imprevisível. Outro ponto interessante é o armazenamento de uma weak reference
podendo assim ser internamente dividida em duas tabelas: a primeira chamada de short weak reference para referências
que não suportam trackRessurection e a segunda chamada long weak reference para suportar trackRessurection(true,
indicado numa das assinaturas do construtor da classe). Para saber se a weak reference suporta Resurrection utilize a
propriedade TrackRessurection.

WeakReference(object target, bool trackRessurection); //C# Construtor da classe

bool fTrackRes = wk.TrackRessurection; //C# Acesso a propriedade

Monitorando a memória da CLR

Para refletir a atuação do Garbage Collector o .NET Framework instala alguns performance counters para medição da
memória e da CLR. Eles podem ser utilizados pelo Performance Monitor, ou perfmon.exe. A figura 6 exibe como acessar
o Perfomance Monitor através da opção Run... do menu Start.

Figure 6: Perfmon.EXE, ou Performance Monitor

De acordo com a figura 7 nota-se que é possível fazer medições diversas, incluindo o comportamento do .NET CLR
Memory o qual deve ser selecionada na combo box Performance object. A partir disso é possível selecionar os counters
desejados.

Figura 7: Performance Monitor e seleção de counters para .NET CLR Memory

A figura 8 exibe um relatório produzido com alguns counters selecionados. Pode-se verificar, por exemplo, a quantidade
de bytes na heap, promoção dos objetos para a próxima Gen e a quantidade de coleta ocorrida nas Gens . Na tabela 1
temos a descrição dos principais counters do objeto .NET CLR Memory.

Tabela 1: Principais counter do objeto .NET CLR Memory


Counter Descrição
#Bytes in all Heaps Total de bytes nas heaps para Gen0, Gen1 e Gen2 e na large object heap
#GC Handles Total de handles GC utilizados
#Gen 0 Número de vezes de coletas ocorridas na Gen0
#Gen 1 Número de vezes de coletas ocorridas na Gen1
#Gen 2 Número de vezes de coletas ocorridas na Gen2
#Induced GC Número de vezes que GC foi invocada através de uma chamada explícita
Finalization Survivors Classes coletadas que sobreviveram devido ao finalizor referencia-las
Gen 0 heap size Tamanho em bytes da Gen 0 heap
Gen 1 heap size Tamanho em bytes da Gen 1 heap
Gen 2 heap size Tamanho em bytes da Gen 2 heap
Large Object heap size Tamanho em bytes da Large Object heap
Promoted Memory Gen 0 Quantidade em byte de memória promovida da Gen0 para Gen1
Promoted Memory Gen 1 Quantidade em byte de memória promovida da Gen1 para Gen0
A Coluna .NET é publicada mensalmente em primeira mão na revista Developers' Magazine da Editora Axcel Books e tem
por intuito apresentar de forma clara a nova estratégia da Microsoft e como os desenvolvedores podem tirar proveito
desta tecnologia. Neste site estaremos publicando a coluna com um mês de atraso em relação a revista nas bancas, e a
atualização desta coluna acontecerá todo dia 30 de cada mês.

.NET Framework
Do ponto de vista dos programadores, a ".NET Framework" é o sistema operacional. É através dela que são invocadas
todas as funções necessárias ao funcionamento dos programas, sob qualquer sistema operacional. Neste artigo irei
analisar o seu papel e ver alguns exemplos de seu uso.

Funções do Sistema Operacional


Todo sistema operacional define as maneiras pelas quais os programas devem chamar suas funções.
O MS-DOS, por exemplo, define suas chamadas através de instruções assembly INT, passando e recebendo informações
através de registradores da CPU. Mesmo que você, como programador de MS-DOS, jamais tenha escrito uma instrução
INT, alguém fez isso para você.
Estas instruções estão no código gerado pelo compilador ou, mais provavelmente, dentro da "biblioteca de runtime" do
compilador "linkeditada" juntamente com seu programa. Isto vale para todas as linguagens como Clipper, C, Basic,
Pascal e COBOL.
Em 1985, a Microsoft introduziu uma grande novidade em seu novo "ambiente operacional" Windows: a maneira de
chamar o sistema era através de funções em linguagem de alto nível, a princípio usando a linguagem C. Não era mais
necessário se preocupar com registradores da CPU. O Uso de funções em C foi sem dúvida uma grande vantagem e
permitiu que o próprio Windows fosse bem mais complexo e capaz, visto que os programas poderiam usar seus recursos
com maior facilidade.
O esquema de usar funções, no entanto, foi logo mostrando seus problemas. Um deles é que as funções não estavam
formalmente agrupadas, nem mesmo para preservar o "handle" usado pelas mesmas funções. Por causa disto, logo
surgiram "bibliotecas de classe" em C++, como a OWL da Borland e a MFC da própria Microsoft, para facilitar a
programação. Lá por 1995, a MFC era de fato a "API do Windows", pelo menos para criar a estrutura principal dos
programas e suas janelas.
Por outro lado, ferramentas como Visual Basic e Delphi introduziram um modelo completamente diferente: o usuário
manipulava métodos, propriedades e eventos de componentes, que por sua vez se responsabilizavam por chamar a API
do Windows. Raramente o programador chamava a própria API diretamente.
Ficamos desta forma com três maneiras diferentes de programar para Windows:
 Diretamente na API, uma forma pouco usada pela alta complexidade e baixa produtividade;
 Com bibliotecas de classes como a MFC, um recurso disponível apenas em C++;
 Com componentes específicos como em Delphi e Visual Basic, uma maneira de alta produtividade, mas
incompatível com outras ferramentas.
Vantagens da .NET Framework
A principal idéia da .NET Framework é usar um modelo baseado em componentes como a única maneira de programar
para o sistema operacional. Este é o modelo mais flexível e produtivo entre todos os disponíveis, bastante semelhante do
Delphi ou Visual Basic. Isto, no entanto não é uma tarefa fácil:
 O modelo de objetos deve ser definido no próprio sistema operacional;
 Devem ser suportadas várias linguagens de programação;
 Diversos conceitos como propriedades e eventos devem ser suportados nativamente, não apenas os métodos e
campos, como normalmente ocorre;
 Criar instâncias e até mesmo herdar uma classe da outra deve ser permitido, mesmo que só tenhamos o código
binário disponível e não saibamos sequer a linguagem de desenvolvimento da classe original;
 Os objetos devem ser "autodocumentados"; ou seja, incluir informações detalhadas dos tipos que estão lá
dentro. Este recurso não só facilita a chamada das classes, mas também é fundamental para permitir a
validação do uso e manutenção da integridade do sistema de tipos em tempo de execução;
 O gerenciamento de memória deve ser feito pelo sistema operacional, para que um programa possa "passar um
objeto" para outro programa sem dificuldades e não precisar se preocupar com alocação de memória;
 Deve existir uma preocupação com controle de versões; um programa pode precisar de uma versão mais antiga
e outro de uma versão mais nova da mesma classe; ambos devem ser satisfeitos, mesmo que estejam rodando
ao mesmo tempo.

O esquema acima é muito semelhante ao implementado pelo Delphi da Borland quando usamos "pacotes de runtime".
Vale lembrar que os desafios acima foram enfrentados sem sucesso pela "Next", empresa fundada pelo Steve Jobs
depois que ele saiu da Apple e também pela Taligent, esforço comum da Apple e IBM para criar justamente um
"framework universal".
Para encarar o desafio, a Microsoft colocou à frente do projeto a pessoa que já tinha liderado um projeto semelhante
com sucesso: Anders Hejlsberg, o criador do Delphi. Pode-se dizer que os "pacotes de runtime" do Delphi são um
"protótipo" do que foi feito no ".NET Framework". No entanto o ".NET Framework" foi bem além do que existia no Delphi:
 São suportadas diversas linguagens de alto nível: é possível criar um componente em uma linguagem, C# por
exemplo, e uma classe derivada em outra, VB por exemplo;
 Os componentes e sua descrição (Metadata) estão sempre no mesmo arquivo e não podem ser separados;
 O gerenciamento de memória é feito pelo sistema operacional e não pelos programas;
 O sistema de tipos não pode jamais ser violado; os "casts inseguros" não existem;
 Os tipos "simples" com o inteiro e "double" podem ser "derivados" de um ancestral comum ter propriedades e
métodos. Isto é feito sem que haja um custo adicional para usos mais simples, como operações aritméticas;
 O suporte a "reflections" (informações de tipo em tempo de execução) foi bastante expandido;
 Todo "executável .NET" (na verdade chamados de "assemblies") possuem, além do nome, informações
detalhadas de versões em três partes: número principal, secundário e de manutenção. Existe um mecanismo
sofisticado de resolução de versões, baseado em regras (heurísticas) do sistema operacional, incluindo também
sugestões do programa, como por exemplo "use a versão de manutenção mais nova a não ser que o programa
exija uma versão específica";

Os executáveis .NET são independentes do sistema operacional. Basta que haja um "sistema de runtime" que possa
compilar os programas para a CPU real e que as classes necessárias estejam implementadas para o programa rodar.
Nada nos programas compilados os amarra a alguma CPU ou sistema operacional.

Usando as classes
Dadas às necessidades e complexidade dos programas atuais, existem literalmente centenas de classes oferecendo
diversos "serviços", como por exemplo:
 Acesso a arquivos;
 Conexões TCP/IP;
 Gerenciamento de janelas e controles;
 Segurança;
 Desenho e impressão;

Usar as classes da biblioteca é algo simples. Em geral, basta criar um objeto da classe desejada e chamar seus métodos
e propriedades. Veja no Quadro 1 um exemplo em C# para mostrar o conteúdo de um arquivo na console.
// Programa C# para exibir um arquivo na console
// Este programa usa uma classe da .NET FrameWork

// Indica que "namespaces" estào sendo referidos


using System;
using System.IO;
public class Class1 {
public static int Main(string[] args) {
// Cria objeto para ler arquivos. Note que não é necessário liberar o objeto criado
StreamReader L = new StreamReader(@"c:\odbcconf.log");
// lê o arquivo todo e coloca-o em uma variável com um métododa classe
string S = L.ReadToEnd();
// Exibe na console
Console.WriteLine(S);
return 0;
}
}

Quadro1: Exemplo de programa que usa a Framewwork

Conclusão
A .NET Framework é uma biblioteca de classes que reúne as todas as funções normalmente associadas ao sistema
operacional. Ele resolve muitos problemas da API do Windows, sendo baseada em um modelo de componentes utilizado
com sucesso no Visual Basic e no Delphi.
Se você deseja conhecer em maiores detalhes o Framework .NET e o Visual Studio.Net, a Microsoft estará realizando, em
parceria com a Brás & Figueiredo, um evento em São Paulo voltado para desenvolvedores onde estaremos detalhando
esta nova tecnologia. Para maiores informações sobre o evento e como paticipar, visite o site do Visual Studio.

Links
Página do Visual Studio.NET em inglês: http://msdn.microsoft.com/vstudio/nextgen/default.aspx
Outros artigos sobre .NET em português: http://www.mas.com.br/ms
Evento para Desenvolvedores do Visual Studio.NET: http://www.microsoft.com/brasil/vstudio

tem softwares publicados no Brasil e exterior e utiliza profissionalmente a ".NET


Mauro Sant'Anna
Framework" desde seu lançamento em julho de 2000.

Código compartilhado do Common Language Infrastructure

Leonardo Tolomelli é Gerente do Programa de Desenvolvedores da Microsoft Brasil e pode ser contactado pelo email
leotolo@hotmail.com

Na última semana de março eu tive a oportunidade de participar de um evento da Microsoft Research (braço de pesquisa
da Microsoft) em Cambridge na Inglaterra, onde foi apresentada a plataforma .NET para um grupo de 180 professores
universitários e PHD da Europa e da América Latina (incluindo oito brasileiros os quais fui acompanhando).

Durante o evento a Microsoft apresentou pela primeira vez o SSCLI (Shared Source Common Language Infrastructure),
conhecido internamente como projeto Rotor e que é na minha opinião o maior compromisso da empresa com o universo
acadêmico até hoje.

O Common Language Infrastructure (CLI) é a especificação dos principais serviços do .NET Framework e que
implementa a tecnologia que permite que um aplicativo seja desenvolvido em diversas linguagens de programação e
executados num mesmo ambiente de execução. O CLI foi enviado pela Microsoft a ECMA (European Computer
Manufacturing Association, Associação dos Fabricantes de Computadores da Europa), um órgão padronizador europeu
equivalente à ISO (International Standards Organization, Organização de Padronização Internacional) com cede nos
Estados Unidos.

No dia 13 de dezembro de 2001 a ECMA aprovou por unanimidade a especificação 1.0 do CLI e da linguagem C#,
tornando-os padrões abertos de mercado controlados agora pelo ECMA e não mais pela Microsoft. Qualquer empresa
pode visitar o site da organização (http://www.ecma.ch) e baixar o arquivo com a especificação, implementando a sua
própria versão do CLI, em qualquer sistema operacional.

Para acelerar a adoção destes padrões pelo mercado, a Microsoft, que já possui o código utilizado no .NET Framework
comercial, realizou duas implementações do CLI, uma delas para Windows XP e outra para FreeBSD, versão de Unix de
código aberto muito utilizada pelo mundo acadêmico.

Pelo anúncio realizado em Cambridge, a Microsoft tornou pública estas implementações através de seu web site MSDN
(http://msdn.microsoft.com/, pesquise por SSCLI) para ser utilizada em projetos de pesquisas de universidades ou
dentro do currículo programático dos cursos de tecnologia das mesmas. Trata-se aproximadamente de 1,9 milhão de
linhas de código disponíveis para download.

Mesmo que você não esteja no mundo acadêmico pode se beneficiar desta iniciativa. É uma ótima oportunidade para
entender como funciona o ambiente de execução do .NET Framework e seus recursos técnicos como o JIT (Just In Time)
Compiler ou o Garbagge Colector. Está presente no código também o código do compilador C# (escrito em linguagem C)
e outro de JScript (este já escrito em C#).

A licença da Microsoft permite a modificação e expansão do código disponível, mas não permite a comercialização das
novas versões.

Com esta iniciativa acreditamos acelerar o processo de adoção do CLI pelo mercado através destas primeiras
implementações e que em breve poderemos executar nossas aplicações em diversos sistemas distintos.

Professores aproveitem esta oportunidade de conhecer em detalhes a nova plataforma .NET e utilizar em seus projetos
de pesquisa e aulas. No site do evento de Cambridge você encontrará os slides usados durante o evento na
Inglaterra e poderá acelerar o seu conhecimento sobre o projeto.
Porque certificar?

Leonardo Tolomelli é Gerente do Programa de Desenvolvedores da Microsoft Brasil e pode ser contactado pelo email
leotolo@hotmail.com

Uma questão que surge durante o desenvolvimento profissional de um desenvolvedor é: devo me certificar? Que
vantagens eu levo com isso?

A Microsoft foi uma das primeiras empresas a apostar na certificação de profissionais, e o objetivo desta aposta sempre
foi em garantir uma mão de obra qualificada no mercado de trabalho e capaz de utilizar da melhor maneira nossos
produtos.

Para o profissional esta passou a ser a melhor maneira de comprovar, através de um órgão independente, sua
capacidade técnica em determinado produto ou tecnologia.

Para as empresas que estão contratando, o processo de certificação é a melhor forma de avaliar o conhecimento técnico
do profissional a ser contratado e ter certeza que o investimento que está sendo feito na contratação será recompensado
com o melhor uso da infra-estrutura de software disponível na empresa.

Quando falamos do processo de desenvolvimento, garantir a qualidade do que será produzido é de suma importância.
Principalmente quando temos uma equipe de desenvolvedores trabalhando num mesmo projeto, e compartilhando a
mesma base de código. É importante garantir que o trabalho de cada membro venha a agregar no projeto, e não
complicar.

Além da certificação de Microsoft Certified Professional de uma ferramenta de desenvolvimento específica como o Visual
Basic, Visual C++ ou Visual FoxPro, a Microsoft disponibiliza já a alguns anos a certificação de Microsoft Certified
Solution Developer (ou MCSD para encurtar). Este título é perseguido por muitos, mas alcançado por poucos.

O principal motivo do pequeno número de MCSDs no mercado é devido a extensão da certificação. Ela exige
conhecimentos deste o processo de projeto e análise da solução, passando pela codificação, teste e manutenção.

Essa abrangência consegue garantir que os profissionais certificados tenham um amplo conhecimento de todo o processo
de desenvolvimento do aplicativo.

Todavia a realidade é que grande parte dos desenvolvedores trabalha em uma parte específica do processo,
principalmente na parte de codificação, teste e manutenção da aplicação.

A parte de análise e projeto é normalmente realizada pelo gerente do projeto ou arquiteto da solução. Um profissional
dentro da equipe (ou um grupo seleto dentro de uma equipe maior). Desta forma parte das exigências da certificação
não faziam parte do dia a dia da maioria dos profissionais.

Para atender a necessidade desta grande maioria, a Microsoft está criando um novo título de certificação para
desenvolvedores, o Microsoft Certified Application Developer (MCAD). Esta nova certificação está focada no processo de
codificação da aplicação, não exigindo conhecimento específico nas partes de análise e projeto da aplicação. Assim
esperamos atingir um número bem maior de desenvolvedores com esta certificação.

Então o que você está esperando. Visite o site MSDN para conhecer as novidades da plataforma .NET, e seja um
dosprimeiros certificados MCADs do país. Conheça o site da certificação para maiores detalhes
(http://www.microsoft.com/brasil/certifique/certificacao/mcad/mcadreq.asp ) e certifique-se.

MSDN - Microsoft Developer Network

Leonardo Tolomelli é Gerente do Programa de Desenvolvedores da Microsoft Brasil e pode ser contactado pelo email
leotolo@hotmail.com

Existe muita dúvida toda vez que o nome MSDN aparece. O que é exatamente MSDN (Microsoft Developer Network)?

MSDN é a marca que a Microsoft utiliza para se relacionar com o público desenvolvedor. Toda vez que realizamos alguma
atividade para este público, o nome MSDN está relacionado.

Entre as atividades que temos hoje disponíveis podemos citar o site MSDN Online ( http://www.msdn.com.br no Brasil
ou http://msdn.microsoft.com nos Estados Unidos) onde são publicados regularmente informações técnicas relevantes
aos desenvolvedores interessados em utilizar a tecnologia Microsoft no seu dia a dia. Entre os recursos oferecidos na
versão brasileira do site estão artigos técnicos em português, colunas com os mais respeitados profissionais do mercado,
treinamentos online. Oferecemos ainda newsgroups para que os desenvolvedores visitantes possam trocar experiências
entre si sobre como melhor utilizar as tecnologias e ferramentas de desenvolvimento que colocamos no mercado. Entre
os temas discutidos estão ASP .NET, ADO .NET, Visual Basic, C#, Web Services e Visual FoxPro. Estão disponíveis ainda
links para recursos que possam complementar o conhecimento dos profissionais como livros sobre as tecnologias
discutidas, revistas voltadas para os produtos como a Visual Studio Programmers' Journal e mesmo sites de terceiros
que apresentam informações sobre estes assuntos.

Os profissionais que visitam o MSDN Online podem se registrar no mesmo e passam assim a receber uma newsletter
quinzenal informando das novidades do site naquele período, além de promoções especiais e exclusivas para quem faz
parte da comunidade de desenvolvedores Microsoft.

Outro recurso disponível com a marca MSDN são os Seminários Técnicos e Eventos para este público. Todos os eventos
Microsoft para desenvolvedores como o DevDays ou o PDC são oferecidos pelo MSDN, além de seminários técnicos com
duração de 3 horas geralmente e realizados regularmente nos auditórios da Microsoft. A divulgação do calendário de
eventos e os temas propostos podem ser encontrados no site MSDN Online ou na newsletter do mesmo. Portanto não
perca mais tempo, entre no site MSDN Online e cadastre-se para ficar atualizado.

O último recurso MSDN, e o mais reconhecido é a assinatura MSDN Subscription, que inclui, além de informações
técnicas para o profissional de desenvolvimento, os produtos necessários para montar um ambiente de desenvolvimento
e teste das aplicações. Todo profissional que desenvolvem aplicativos para o ambiente Microsoft deveria ter a sua própria
assinatura MSDN Subscription, mesmo aqueles que não utilizam nossas ferramentas, apesar da grande quantidade de
informações especificamente para os usuários do Visual Studio e suas linguagens.

A assinatura está disponível hoje em 5 versões para atender a todos os gostos. São elas: Library, Operating systems,
Professional, Enterprise e Universal. Para conhecer em detalhes os produtos que estão presentes em cada uma delas,
visite a página no endereço: http://www.microsoft.com/assinaturas/ass_msdn_1.asp
Bem pessoal, acho que já deu para dar uma idéia de como o MSDN está presente dentro da vida de vocês. Portanto da
próxima vez que vocês encontrarem a marca MSDN pela frente, podem esperar bastante informação relevante para o
seu dia a dia como desenvolvedor.

No mês que vem nos vemos novamente.

Como criar um aplicativo do .NET Compact Framework usando o Visual


Studio .NET 2003
Mike Hall
Janeiro de 2005
Aplica-se a:

• Microsoft® Visual Studio® .NET 2003

• Microsoft .NET Compact Framework

• Microsoft Windows® CE
Resumo: Este artigo fornece instruções detalhadas sobre como trabalhar com o Visual Studio .NET 2003 para criar e
depurar um aplicativo do .NET Compact Framework em C#. Você criará um aplicativo baseado em formulários do C# que
será implantado em um emulador baseado no Windows CE. Este aplicativo manipulará os eventos de botão do mouse
pressionado e liberado, e mouse em movimentação, além de capturar os dados de rabisco do usuário. Os dados de
rabisco podem ser visualizados em um aplicativo .NET Framework para desktops, também escrito em C#. Este artigo
está dividido em três partes, cada uma contendo vários exercícios que você pode completar. É necessário ter cerca de 60
minutos para realizar todas as etapas. (18 páginas impressas)
Baixe o Windows CE 5.0 Embedded Development Labs.msi na Central de Download da Microsoft.
Nesta página
Introdução
Neste artigo, você escreverá um aplicativo para capturar o movimento do mouse em um dispositivo baseado no
Microsoft® Windows® CE, que é similar a capturar uma assinatura em um dispositivo portátil.
Para realizar os exercícios deste artigo, verifique se a sua estação de trabalho tem os seguintes softwares instalados:

• Microsoft Windows XP Professional

• Microsoft Visual Studio® .NET 2003


Para este artigo, você usará o emulador do Microsoft Windows CE .NET, que emula um dispositivo baseado em x86. As
etapas que você realizará neste artigo são idênticas às necessárias para criar um aplicativo para dispositivos baseados
no Windows CE.
Este exercício tem um download que inclui o aplicativo CodeClip, que você pode usar para ajudar a percorrer o exercício.
Para obter mais informações sobre os assuntos deste artigo, visite o Microsoft Windows Embedded Developer Center (em
inglês) ou o Microsoft Visual Studio Developer Center (em inglês).
Início da página

Parte 1: Criando o aplicativo inicial


Nesta parte do exercício, você realizará os seguintes procedimentos:

• Criar o aplicativo base

• Definir as opções do projeto


Alterar o texto exibido na barra de título do

formulário

• Renomear o comando de menu para sair do aplicativo

• Criar e testar o aplicativo


Você verá como um aplicativo pode ser criado rapidamente para manipular a entrada do usuário e estabelecer
comunicação em uma rede corporativa ou na Internet.
Para criar o aplicativo base
1. Abra o Visual Studio .NET 2003 usando o atalho da área de trabalho.
2. No menu File (Arquivo), clique em New Project (Novo Projeto).
3. Em Project Types (Tipos de Projeto), selecione Visual C# Projects (Projetos do Visual C#).
4. Em Templates (Modelos), selecione Smart Device Application (Aplicativo de Dispositivo
Inteligente).
5. Na caixa Name (Nome), digite Scribble.

1. Clique em OK.
Agora você verá as opções do projeto. Há vários tipos de projeto que você pode criar.
Também é possível visar dispositivos baseados no Pocket PC ou no Windows CE. Talvez você esteja se perguntando por
que há duas opções de assistente (e se você tiver o SDK do Smartphone, haverá três). No final, os aplicativos serão
gerados no formato MSIL (Microsoft Intermediate Language), que independe do processador e do sistema operacional. O
motivo é que os dispositivos baseados no Pocket PC e no Windows CE normalmente têm diferentes layouts de tela. Os
menus dos dispositivos para Pocket PC ficam na parte inferior da tela, enquanto os menus dos dispositivos baseados no
Windows CE normalmente ficam na parte superior da tela. O assistente criará um aplicativo de estrutura com o tamanho
de formulário e o layout de menu corretos.
Para definir as opções do projeto
1. No assistente do Smart Device Application (Aplicativo de Dispositivo Inteligente), selecione Windows CE como
plataforma e Windows Application como tipo de projeto, como mostra a ilustração a seguir.

2. Clique em OK.
A Figura 1 mostra a aparência da interface de usuário do Visual Studio .NET após a geração do aplicativo base. Observe
que o lado esquerdo da janela exibe vários controles que você pode adicionar ao formulário. O centro da janela exibe o
formulário do aplicativo e é também a área que você pode usar para editar ou adicionar código-fonte. O lado direito da
janela exibe o Solution Explorer (o espaço de trabalho) e uma área que pode ser usada para configurar partes do
aplicativo.
Figura 1. Aplicativo base no Visual Studio .NET
Neste ponto, você pode alterar o texto padrão exibido na barra de título do formulário.
Para alterar o texto exibido na barra de título
1. Selecione a propriedade Text (Texto) para Form1, como mostra a ilustração a seguir.

2. Altere o texto de Form1 para Scribble.


Observe que o título do formulário mudou. Agora você pode adicionar um menu ao aplicativo, o que facilitará a sua
saída do aplicativo.
3. Na caixa de ferramentas do Visual Studio, clique em e arraste MainMenu para o formulário.
Você observará que o menu é adicionado ao formulário, como mostra a ilustração a seguir.

Adicionar comandos de menu é muito simples.


4. No menu do formulário, clique em Type Here (Digite Aqui).
5. Digite File (Arquivo) e pressione ENTER.
6. Digite Send Scribble (Enviar Rabisco) e pressione ENTER.
7. Digite Exit (Sair) e pressione ENTER.
8. Selecione o comando de menu Exit (Sair).
Como mostra a ilustração a seguir, as propriedades mudam para refletir o comando de menu selecionado.

Por padrão, o comando de menu File (Arquivo) | Exit (Sair) chama-se menuItem2. Se você fosse adicionar código a esse
comando de menu, o manipulador do clique seria exibido como menuItem2_Click. Renomear esse comando de menu
refletirá melhor seu propósito.
Para renomear o comando de menu para sair do aplicativo
1. Use o painel Properties (Propriedades) para alterar menuItem2 para FileExit.
2. Clique duas vezes no comando de menu File (Arquivo) | Exit (Sair) do
formulário.
Esta etapa abre o painel de edição do código, como mostra a ilustração a seguir.

1. No manipulador FileExit_Click, digite this.Close( );, como mostra a ilustração a seguir.

Observe como o Microsoft IntelliSense® solicita que você mostre o que pode fazer com o item
"this".
Agora você está pronto para criar e testar o aplicativo. (É claro que, neste estágio, o aplicativo ainda não está finalizado.
Você ainda precisa adicionar alguma funcionalidade ao aplicativo para adicionar suporte para rabiscar na área do
cliente.)
Para criar e testar o aplicativo
1. Clique em Build (Criar) | Build Scribble (Criar Rabisco).
O aplicativo deve criar sem avisos ou erros. Agora você está pronto para implantar o aplicativo
2. Clique em Debug (Depurar) | Start without Debugging (Iniciar sem Depurar). (Você depurará posteriormente
neste artigo.)
3. Quando você receber uma lista de dispositivos que podem ser implantados, selecione Windows CE .NET Emulator
(Default) para os fins deste exercício, como mostra a ilustração a seguir.

4. Clique em Deploy (Implantar).


Esta etapa inicia o emulador do Windows CE .NET e implanta o Microsoft .NET Compact Framework. Após a
implantação do .NET Compact Framework, o aplicativo é iniciado como mostra a ilustração a seguir.
Agora que o aplicativo está sendo executado, você pode iniciar a adição de código para manipular os eventos de botão
do mouse pressionado, de movimento e de botão do mouse não pressionado.
1. No aplicativo de rabisco, clique em File (Arquivo) | Exit
(Sair).
Início da página

Parte 2: Manipulando eventos de botão do mouse pressionado, de movimento e


de botão do mouse não pressionado
O aplicativo de rabisco captura o movimento do mouse do usuário, mas somente quando o botão do mouse está
pressionado. Portanto, você precisa de um booleano para mostrar se o botão do mouse está pressionado (capturando)
ou não (não capturando). Você verificará o booleano no manipulador de movimento do mouse. Se o booleano estiver
definido, você adicionará o novo ponto X,Y a uma matriz de pontos (que você também precisa adicionar ao aplicativo).
Você acompanhará todos os pontos desenhados na área do cliente do aplicativo. Você armazenará essa lista de pontos
em uma ArrayList.
Nesta parte do exercício, você realizará os seguintes procedimentos:

• Criar uma classe para armazenar os pontos

• Adicionar código à classe

• Criar uma matriz

• Alternar para o modo de design

• Selecionar o evento MouseDown


Adicionar código ao manipulador de eventos

MouseDown

• Adicionar código ao manipulador de eventos MouseUp

• Criar o aplicativo
Para criar uma classe para armazenar os pontos
1. No menu Project (Projeto), clique em Add Class (Adicionar Classe).
2. Na caixa Name (Nome), digite csPoint.cs como mostra a ilustração a seguir.
3. Clique em Open (Abrir).
Esta etapa abre o código-fonte da nova classe. Observe que a classe é parte do mesmo espaço para nome do
aplicativo "Scribble".
4. Exclua a definição de classe do csPoint.
Esta etapa deixa o código a seguir.
using System;

namespace Scribble

Agora você precisa adicionar algum código à classe. Em vez de digitar o código, você pode colá-lo usando uma
ferramenta chamada CodeClip. O CodeClip é um aplicativo auxiliar que simplifica a cópia de fragmentos pré-selecionados
de código para a Área de Transferência. O CodeClip é exibido como uma faixa azul transparente na parte superior da
janela.
Para adicionar código à classe
1. Inicie a ferramenta CodeClip.
2. Abra o Compact Framework Lab.
3. Clique duas vezes no item csPoint.
Esta etapa copia o código csPoint para a área de
transferência.
Cole o código no arquivo de classe csPoint.

O arquivo de classe terá a aparência a seguir. Esta é uma classe muito simples que armazena as posições X e Y de um
evento de botão do mouse pressionado ou de movimento.
using System;

namespace Scribble

public class csPoint

public csPoint(int iX,int iY)

x=iX;

y=iY;

}
public int GetX( )

return x;

public int GetY( )

return y;

int x;

int y;

O aplicativo precisará armazenar algumas informações sobre se o botão do mouse está pressionado e a localização
anterior do mouse. Você também precisará de uma matriz para armazenar os pontos, que serão as variáveis globais no
aplicativo.
Para criar uma matriz
Clique na guia Form1.cs, como mostra a ilustração a seguir, para voltar ao código do aplicativo

principal.

• Localize a função FileExit.

• Use o CodeClip para copiar o código Globals para a área de transferência.

• Cole o código Globals no aplicativo antes do manipulador FileExit.


Você tem várias opções para escrever aplicativos do Windows CE: Microsoft Win32®, Microsoft Foundation Classes (MFC)
ou código gerenciado. Para adicionar manipuladores de eventos de botão do mouse pressionado, não pressionado e de
movimento a um aplicativo Win32, você precisaria saber quais mensagens foram enviadas para o aplicativo a partir do
Windows; essas são WM_LBUTTONDOWN, WM_MOUSEMOVE e WM_LBUTTONUP. O Microsoft eMbedded Visual C++® e
o MFC facilitam um pouco a adição de manipuladores por meio do assistente, que lista cada mensagem do Windows que
a classe atual pode manipular.
O procedimento a seguir mostra como é fácil adicionar manipuladores de eventos do mouse ao aplicativo do .NET
Compact Framework.
Nas guias do projeto, você observará o Form1.cs exibido em negrito. Este é o seu documento aberto atual. Você está
olhando para o modo de exibição de código do formulário e precisa alternar para o modo de design para adicionar
manipuladores de eventos.
Para alternar para o modo de design
No painel Project (Projeto), clique na guia Form1.cs [Design], como mostra a ilustração a seguir.

No painel Properties (Propriedades), na parte inferior direita do Visual Studio, você pode alterar vários atributos desse
formulário, como nome, cor e tamanho de fonte. A Figura 2 mostra o painel Properties (Propriedades).
Ao lado do botão Properties (Propriedades), você verá o botão Events (Eventos). Esse botão é usado para selecionar
eventos para o formulário ou controle selecionado atualmente.
Figura 2. Painel Properties (Propriedades)
Para selecionar o evento MouseDown
1. No painel Properties (Propriedades), clique no símbolo do raio.
2. Percorra o painel Properties (Propriedades) até localizar os eventos do mouse, como mostra a ilustração a
seguir.

3. Clique duas vezes no evento MouseDown.


Essa etapa adiciona um manipulador MouseDown ao aplicativo e também abre o painel de edição do código.
Agora você precisa adicionar algum código ao manipulador MouseDown. Novamente, o método mais eficiente é usar o
CodeClip.
Para adicionar código ao manipulador MouseDown
1. Inicie a ferramenta CodeClip.
2. Selecione o item MouseDown, como mostra a ilustração a seguir.

1. Clique no botão Copy (Copiar).


2. Alterne novamente para o Visual Studio .NET.
3. Clique no comando de menu Edit (Editar) | Paste (Colar) para colar o texto.
Este código define o booleano bMouseDown como verdadeiro, armazena as posições atuais de X e Y e adiciona um
ponto à matriz.
private void Form1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)

bMouseDown=true;

x=e.X;
y=e.Y;

arList.Add(new csPoint(x,y));

4. No painel Project (Projeto), clique na guia Form1.cs [Design].


5. Localize e clique duas vezes no evento MouseDown.
6. Clique na faixa do CodeClip para exibir a caixa de diálogo CodeClip.
7. Selecione o item MouseMove e clique no botão Copy (Copiar).
8. Alterne novamente para o Visual Studio .NET.
9. Clique no comando de menu Edit (Editar) | Paste (Colar) para colar o texto.
O código a seguir do evento MouseMove é um pouco mais complicado que o código do evento MouseDown. Verifique se o
botão do mouse está pressionado (observe que você ainda receberá uma notificação de MouseMove, mesmo se os
botões do mouse não estiverem pressionados). Se o botão do mouse estiver pressionado, crie um objeto gráfico (similar
a um DeviceContext na programação do Win32). Em seguida, desenhe uma linha do ponto anterior até o novo ponto.
Nesse caso, você está desenhando a linha em vermelho; essa cor é fácil de mudar, porque, no Win32, todas as cores são
referenciadas por seu valor RGB (vermelho, verde e azul).
private void Form1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)

if (bMouseDown)

Graphics gr=this.CreateGraphics();

if (x == -1 && y == -1)

x=e.X;

y=e.Y;

gr.DrawLine(new Pen(Color.Red),e.X,e.Y,x,y);

x=e.X;

y=e.Y;

arList.Add(new csPoint(x,y));

Para adicionar código ao manipulador MouseUp


1. No painel Project (Projeto), clique na guia Form1.cs [Design].
2. Localize e clique duas vezes no evento MouseDown.
3. Clique na faixa do CodeClip para exibir a caixa de diálogo CodeClip.
4. Selecione o item MouseMove e clique no botão Copy (Copiar).
5. Alterne novamente para o Visual Studio .NET.
6. Clique no comando de menu Edit (Editar) | Paste (Colar) para colar o
texto.
No manipulador MouseUp, basta definir o booleano bMouseDown como falso, para mostrar que o botão do mouse não
está pressionado no momento, e adicionar um ponto com valores -1, -1, para mostrar que você terminou um segmento
de linha, como mostra o código a seguir.
private void Form1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)

bMouseDown=false;
arList.Add(new csPoint(-1,-1));

Agora é o momento de criar o aplicativo.


Para criar o aplicativo
1. Clique em Build (Criar) | Build Scribble (Criar Rabisco).
O aplicativo deve criar sem avisos ou erros. Agora você está pronto para implantar o aplicativo.
2. Clique em Debug (Depurar) | Start without Debugging (Iniciar sem Depurar). (Você depurará posteriormente
neste artigo.)
3. Quando você receber uma lista de dispositivos que podem ser implantados, selecione Windows CE .NET Emulator
(Default) para os fins deste exercício, como mostra a ilustração a seguir.

4. Clique em Deploy (Implantar) para implantar e iniciar o aplicativo.


5. Mova o mouse e mostre que nenhuma tinta é exibida no aplicativo. O aplicativo exibirá tinta somente quando o
botão do mouse estiver pressionado.
6. Clique e mantenha pressionado o botão esquerdo do mouse, mova o mouse e solte o botão.
A linha desenhada no aplicativo deve se parecer com as linhas na ilustração a seguir.

Parabéns! Você acaba de escrever um aplicativo de rabisco em C#.


7. No aplicativo de rabisco, clique em File (Arquivo) | Exit (Sair).
Início da página

Parte 3: Depurando o aplicativo Scribble do C#


Até aqui, você criou e executou o aplicativo. Convém depurar aplicativos novos. No caso de escrever um aplicativo do
Windows CE (por meio do eMbedded Visual C++ ou do Visual Studio .NET 2003), o sistema operacional e o aplicativo
estão sendo executados em dispositivos remotos conectados ao computador de desenvolvimento. Portanto, você precisa
baixar o aplicativo para o dispositivo antes de depurar. Dependendo da experiência do usuário, a depuração de um
aplicativo do Windows CE é muito parecida com a de um aplicativo da área de trabalho.
Nessa parte do exercício, você definirá um ponto de interrupção no manipulador de botão do mouse pressionado e
traçará o fluxo do aplicativo, percorrendo-o.
Para definir um ponto de interrupção no manipulador de botão do mouse pressionado e percorrer o
aplicativo
1. Clique na guia Form1.cs, como mostra a ilustração a seguir, para voltar ao código do aplicativo principal.

2. Localize a função Form1_MouseDown.


3. Defina um ponto de interrupção na linha bMouseDown=true; clicando na linha e pressionando F9.
Você verá um símbolo vermelho de ponto de interrupção sendo exibido na margem ao lado da linha
bMouseDown=true;, como mostra a ilustração a seguir.

Agora você está pronto para executar o aplicativo.


4. Clique em Debug (Depurar) | Start (Iniciar) ou pressione F5.
Esta etapa cria o aplicativo (se necessário) e baixa o aplicativo para o emulador.
5. Depois que o aplicativo iniciar o emulador, clique na área de cliente do aplicativo.
O aplicativo será interrompido na linha bMouseDown=true;, como mostra a ilustração a seguir. Agora você pode
percorrer as linhas de código.

Há vários itens que podem ser examinados enquanto o aplicativo está em execução. O painel inferior esquerdo do
aplicativo do Visual Studio pode mostrar os resultados da pesquisa ou outro item. Você pode alterar esse painel para
mostrar as variáveis locais atuais.
6. Expanda o item this na lista de variáveis locais, como mostra a ilustração a seguir.

Você observará que o bMouseDown está definido atualmente como falso, e que os valores de X e Y estão definidos
atualmente como zero.
7. Percorra o aplicativo pressionando F11.
Você observará que os valores de X, Y e bMouseDown mudam conforme você percorre o código.
Ao atingir a linha de código que adiciona um novo item ArrayList, também precisará "renovar" uma instância da
classe csPoint. Enquanto você percorre o código, percorrerá a classe csPoint.
8. Pressione F5 para executar o aplicativo.
9. Clique em File (Arquivo) | Exit (Sair).
Início da página

Resumo
Segue um resumo dos exercícios fornecidos neste artigo:

• Você criou um aplicativo de dispositivo inteligente do C# para Windows CE.


Você adicionou suporte para eventos de movimento do mouse, de botão do mouse pressionado e de botão do mouse

não pressionado no formulário.

• Você adicionou suporte a desenho, incluindo a criação de uma caneta personalizada.

• Você depurou o aplicativo Scribble do C#.


© 2005 Microsoft Corporation. Todos os direitos reservados. Termos de uso.

Calculadora de Juros no Smartphone


Por José Luiz Galego Jr
José Luiz Galego Jr (galegojr@hotmail.com) MCP, desenvolve em .NET desde o ano de 2000, em dispositivos móveis
desde 2002 usando .NET. Esteve presente no MDC 2004 nos EUA evento 100% voltado para dispositivos móveis.

Introdução
Os Smartphones com Windows Mobile são pequenos, poderosos e práticos no dia a dia, além disso, criar programas para
essa plataforma é tão fácil quanto criar um aplicativo WinForm ou uma aplicação ASP.NET.
Uma calculadora de juros é útil quando temos que decidir a melhor forma de pagamento. Este artigo mostra, passo a
passo como é fácil criar uma solução para a Smartphone.
Início da página

Tecnologias Utilizadas
• .NET Compact Framework

• Windows Mobile 2003

• MS Visual Studio .NET 2003

• Microsoft Smartphone 2003 SDK


Início da página

Objetivo
Mostrar como fazer um aplicativo para o Smartphone, utilizando chamadas as APIs para facilitar a entrada de dados e
como utilizar VB.NET e C# no mesmo aplicativo.
Vamos criar 2 projetos:

• Class Library em C#

• Windows Form em VB.NET


Em C# nós vamos fazer as chamadas das APIs através de Interop, e em VB.NET usar as funções financeiras nativas para
cálculo de juros.
Início da página

Programação
1. Abra o Visual Studio .NET e crie uma "Blank Solution"
2. Escolha o diretório onde a solução ficará:

3. Adicione um projeto do tipo Smart Device Application


4. No Smart Device Application Wizard escolha a plataforma Smartphone, e um projeto do tipo Class Library

5. Apague o arquivo Class1.cs gerado automaticamente e adicione uma nova classe UI no arquivo UI.cs
6. Acrescente a referência ao Windows.Forms
Esta referência é necessária, pois esta classe utiliza controles dos WinForms.

7. Acrescente as linhas abaixo no início do arquivo:


using System.Windows.Forms;

using System.Runtime.InteropServices;

Essas declarações permitem a chamada aos serviços de API e suporte para controles de WinForms.
8. Coloque as seguintes declarações para as APIs
// Declaracoes Interop

[DllImport("coredll.dll", EntryPoint="GetCapture")]

private static extern IntPtr GetCapture();

[DllImport("coredll.dll", EntryPoint="GetWindow")]

private static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);

[DllImport("coredll.dll", EntryPoint="SendMessage")]

private static extern uint SendMessage(IntPtr hWnd, uint msg, uint wParam, uint lParam);

Estas chamadas permitem enviar mensagens para os controles de formulários, permitindo setar os textboxes para
aceitar somente números.
9. Coloque as declarações abaixo:
const uint EM_SETINPUTMODE = 0x00DE;

const uint EIM_SPELL = 0;

const uint EIM_AMBIG = 1;

const uint EIM_NUMBERS = 2;

const uint EIM_TEXT = 3;

const int GW_CHILD = 5;


const uint LB_SETCURSEL = 0x0186;

10. Crie o método para setar o tipo de entrada:


public static void SetInputMode(Control ctrl, InputMode mode)

ctrl.Capture = true;

IntPtr h = GetCapture();

ctrl.Capture = false;

IntPtr hEditbox = GetWindow(h, GW_CHILD);

SendMessage(hEditbox, EM_SETINPUTMODE, 0, (uint)mode);

Este método, permitirá escolher o modo de input de dados de determinado controle.


11. Por fim crie o seguinte enumerator:
public enum InputMode

Spell = 0,

T9 = 1,

Numbers = 2,

Text = 3

12. Compile o código e veja se está correto


13. Adicione um projeto Smart Device Application, só que escolha VB.NET

14. No Wizard escolha Smartphone e Windows Application:


15. Renomeie o arquivo form1.vb para CalcJuros.vb e a classe de Form1 para CalcJuros, não esqueça de ir
em propriedades do projeto através do Solution Explorer e alterar o StartUp Object conforme abaixo:

16. Adicione a referência a Class Library


17. Monte a sua Interface com o usuário de forma que ela fique como abaixo:

As caixas de texto devem ter os nomes:


txtPrecoAVista
txtParcelas
txtValorParcelas
Abaixo da caixa de texto txtValorParcelas deve ser criado um label lblJuros e o menu "Calcula" deve ser chamado de
menuCalcula
18. De um double click no form ( tome cuidado para não clicar em nenhum controle ) e no evento
CalcJuros_Load digite o código abaixo:
Private Sub CalcJuros_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles
MyBase.Load

SmartUtil.UI.SetInputMode(txtPrecoAVista, SmartUtil.InputMode.Numbers)

SmartUtil.UI.SetInputMode(txtParcelas, SmartUtil.InputMode.Numbers)

SmartUtil.UI.SetInputMode(txtValorParcelas, SmartUtil.InputMode.Numbers)

End Sub

19. De um double click no menu calcula e digite o código abaixo:


Private Sub menuCalcula_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles
menuCalcula.Click

Dim PrecoAVista As Double = CType(txtPrecoAVista.Text, Double)

Dim NumeroDeParcelas As Double = CType(txtParcelas.Text, Double)

Dim ValorDaParcela As Double = CType(txtValorParcelas.Text, Double)

Dim TaxaDeJuros As Double = Rate(NumeroDeParcelas, -ValorDaParcela, PrecoAVista)

lblJuros.Text = TaxaDeJuros.ToString("p")

End Sub
20. Escolha o deploy no emulador:

21. Coloque o projeto CalcJurosApp como "StartUp project"


22. Execute o aplicativo, após a inicialização do emulador, você verá a seguinte imagem, repare que as
caixas de texto já estão prontas para receber somente "input" numérico.

23. Coloque alguns números:


Valor à vista: 1200
Número de parcelas: 12
Valor de cada parcela: 120
E aperte Calcula ( você deverá obter 2,92% )

Início da página

Referências:
Site da MSDN sobre Windows Mobile
http://msdn.microsoft.com/mobility/default.aspx
Artigo sobre deployment de aplicativos Smartphone
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsmtphn2k3/html/sp_2003_app_deploy_demyst.asp
Coletânea de artigos para Smartphone
http://weblogs.asp.net/nleghari/articles/smartphone.aspx

O que há de novo para desenvolvedores do Windows Mobile 2003


– baseados em Pocket PC
Artigo original encontra-se em
http://msdn.microsoft.com/library/default.asp?url= /library/en-us/dnppc2k3/html/winmob03.asp
Introdução

O Microsoft® Windows Mobile® 2003 baseado em Pocket PC é um marco importante para os usuários e
desenvolvedores de Pocket PC. Pocket PC 2003 é baseado no novo sistema operacional, Microsoft® Windows® CE.NET
4.2, e é o primeiro dispositivo que tem o Microsoft Windows .NET Compact Framework integrado na ROM. Este
aperfeiçoamento e muito mais, forma um novo fundamento para o desenvolvimento de aplicações móveis. A proposta
deste artigo é discutir as mudanças e as novidades para desenvolvedores Pocket PC acostumados com o Software
Development Kit for Windows Móbile 2003 baseado em Pocket PCs e como migrar para dispositivos Pocket PC 2003 e o
Pocket PC 2003 SDK. Download do Pocket PC 2003 SDK from the Mobile Devices Developer. Downloads.
Visão Geral

Em resumo, as os principais pontos são:

• Microsoft Windows .NET Compact Framework – O .NET Compact Framework está na ROM de todos os novos
dispositivos Pocket PC 2003, e pode ser instalado na RAM dos dispositivos Pocket PC 2000 e Pocket PC 2002.

• Microsoft Visual Studio® .NET 2003 – O Pocket PC 2003 SDK está integrado com o Visual Studio .NET 2003 IDE e pode
ser usado tanto pelo Microsoft Visual Basic® .NET e Microsoft Visual C#™ .NET.

• eMbedded Visual Tools 3.0 – O objetivo do dispositivo Pocket PC 2003, este ambiente de desenvolvimento não será
mais suportado. Se você precisar suporte ao código nativo, você usará o eMbedded Visual C++® 4.0 (com SP2), e para
desenvolver códigos gerenciados (.NET) usará o Visual Studio .NET 2003.

• eMbedded Visual C++ - Grandes alterações estão relacionadas ao novo sistema operacional, o Windows CE .NET, como
debugging e ferramentas remotas.

• eMbedded Visual Basic – Grandes alterações incluem o fato que o Pocket PC 2003 SDK não suporta o desenvolvimento
em eMbedded Visual Basic. Aplicações existentes no eMbedded Visual Basic continuarão rodando no Pocket PC 2003 por
uma questão de compatibilidade. Desenvolvedores eMbedded Visual Basic que desejam migrar, podem usar o Visual
Basic .NET e usar dos benefícios da nova ferramenta.

Grandes novidades incluem:

• Novo Sistema Operacional - Dispositivos Poket PC 2003 rodam no Windows CE .NET 4.2. O novo sistema operacional
permite dispositivos serem mais robustos e rodarem mais rápidos. Microsoft Pocket Internet Explorer, o emulator de
tecnologias, serviços de rede, Microsoft Windows Media™ são algumas tecnologias que foram extensivamente
aperfeiçoadas.

• Melhoras no Emulator – O ambiente do emulador é muito mais robusto e suporta três modos: Pocket PC 2003, Pocket
PC Phone Edition 2003, no qual suporta rádios externos usando WaveCom WMOD2B externo GSM módulo de rádio, e
Pocket PC Phone Edition 2003 com Virtual Radio. Acrescentando, novas características no seu emulador inclui suporte
switch virtual, mapeamento de drive, e suporte GAPI.

• Configuração Remota – A habilidade para configurar Pocket PC (e Smartphone) é um forte caminho de customização
de dispositivos tanto localmente como remotamente. Esta habilidade está até disponível para cenários Over The Air
(OTA).

• Browser Atualizados - Pocket Internet Explorer agora suporta HTML 4.01, CSS, XHTML, WML 2.0 entre outros.

• Suporte para Phone Edition – suporte melhorado para coisas como chegada indesejada de mensagens Short Message
Service (SMS).

• Outras Características – Existem muitas mais novas características a serem exploradas e utilizadas.

Mudanças para o Desenvolvedor no Pocket PC 2003


Este capítulo mostrará as mudanças para os desenvolvedores Pocket PC quando migrar para o Pocket PC 2003 SDK.

eMbedded Visual Tools 3.0

Quando desenvolve aplicações para Pocket PC 2003, você não pode usar mais o eMbedded Visual Tools 3.0. Entretanto,
para o objetivo do Pocket PC 2000 e 2002, você ainda precisa usar este conjunto de ferramentas. Felizmente, você pode
ter para manutenção e suporte as aplicações existentes, as novas ferramentas são desenhadas para coexistirem com as
antigas. Então, você pode manter eMbedded Visual Tools 3.0 instalada quando instalar o eMbedded Visual C++ 4.0
(desenvolvimentos em eMbedded Visual Basic não serão suportados no Pocket PC 2003, então, separe seções conforme
acima) e o Visual Studio .NET (Visual Studio .NET 2003, .NET Compact Framework e outras).

Mudanças para Desenvolvedores eMbedded Visual C++

Se você precisar desenvolver aplicações não gerenciadas (código nativo) use C++ para Pocket PC, você precisa para
afixar com eMbedded Visual C++. Você não pode usar a nova ferramenta (Visual Studio .NET 2003) para criar aplicações
não gerenciadas. Se você está focando Pocket PC 2003 ou a próxima geração de Smartphone, você precisa usar o
eMbedded Visual C++ 4.0 com Service Pack 2 instalado. Você não pode usar eMbedded Visual C++ 3.0 para
desenvolver, instalar ou debugar no Pocket PC 2003 e a próxima geração de aplicações para Smartphone. No entanto,
ambas ferramentas podem coexistir, você pode ainda manter e suporte para aplicações Pocket PC 2000 e 2002 no
mesmo PC.

A primeira coisa que você notará quando migrar a aplicação do eMbedded Visual C++ 3.0 para eMbedded Visual C+ 4.0
é que não existe suporte para atualização automática do arquivo de projeto. O caminho mais fácil para resolver isto é
criar um projeto em branco eMbedded Visual C++ 4.0 e adicionar os arquivos manualmente.

A maioria das alterações no eMbedded Visual C++ 4.0 estão relacionadas com o novo sistema operacional – Windows CE
.NET – usado no Pocket PC 2003. Mas, existem também um número de características adicionais nesta ferramenta, que
estão relacionadas ao debugging e ferramentas remotas.

Quando debugar no eMbedded Visual C++ 4.0, você tem suporte para tratamento de Exception. Acrescentado para o
padrão Microsoft Win32® API estrutura de tratamento de Exception (SEH), você pode ter vantagem de tratar qualquer
tipo de Exception, o qual assegura que seu código está mais portável e mais flexível.

Uma característica interessante do debugging é a habilidade de se unir a um processo ativo. Isto significa que mesmo
que você tenha uma aplicação que trave sem iniciar o debug, você pode unir o processo pela opção de menu Build, Start
Debug, e anexar para o Windows CE Process. Faça sua seleção na lista de processos ativos, forneça o caminho para o
executável no PC, e o debug irá iniciar e anexar ao processo selecionado. Outra boa característica é o Just-In-Time (JIT)
debugging que força você diagnosticar Exceptions não tratadas em vez de terminar a aplicação; e, se você definir um
breakpoint, você pode parar todas as tarefas em uma aplicação multi-tarefa.

Com eMbedded Visual C++ 4.0, existe uma nova ferramenta chamada Remote Call Profiler. Com esta ferramenta você
pode seguir a pista das chamadas na execução da aplicação de inúmeras formas. É importante mencionar também o
Remote Performance Monitor que permite o desenvolvedor monitorar um número de métricas pré-definidas em tempo
real (como o Microsoft Windows XML Performance Monitor).

Outra característica inclui o suporte para intrínsecos (funções são compiladas a medida que o código na linha é
chamado), um assistente de projeto ATL fora de processo (para criar serviços COM fora de processo), e Standard
Template Library (STL, no qual suporta acesso para um subset dos mais amplos algoritmos usados e estrutura de
dados).

Mudanças para Desenvolvedores eMbedded Visual Basic

A Microsoft anunciou no final de 2001 que eMbedded Visual Basic não continuaria o direcionamento envolvendo a nova
plataforma .NET e ferramentas para dispositivos. Então, o Pocket PC 2003 SDK não suporta o desenvolvimento em
eMbedded Visual Basic e dispositivos Pocket PC 2003 não incluem o eMbedded Visual Basic ou runtime ADOCE na ROM.
Porém, o eMbedded Visual Basic e runtime ADOCE estão disponíveis para downloads na Internet: Pocket PC Downloads.
eMbedded Visual Basic podem ser instalados na RAM dos dispositivos Pocket PC 2003. Isto significa que existem
aplicações eMbedded Visual Basic que continuarão a rodar nos dispositivos Pocket PC 2003, assim os desenvolvedores
podem continuar a usar o eMbedded Visual Basic junto com o Pocket PC 2002 SDK para focar nos dispositivos Pocket PC
2003.

Desenvolvedores eMbedded Visual Basic que desejam avançar podem migrar para Visual Basic .NET e usar os benefícios
das novas ferramentas. A alteração mais significante, do ponto de vista da linguagem, é na realidade que o Visual
Basic .NET é orientado a objeto, moderna linguagem e que códigos são construídos usando classes comuns do .NET
Compact Framework como um System.Windows.Forms para Windows Forms e System.Data para gerenciar um banco de
dados com métodos comuns, propriedade e eventos. Suporte natural para tarefas comuns como fortemente tipados,
usando classes, chamando XML Web Services e implementando estrutura de tratamento de Exception.

Não existem assistentes ou processo automático que converte código eMbedded Visual Basic para Visual Basic .NET.
Código dever ser escrito manualmente e este esforço inclui enxergar as modificações:

• Linguagem Gramatical - eMbedded Visual Basic é uma linguagem VB Script, e então, as diferenças de gramática pode
ser significantes dependendo do tipo de código.

• Implementação Comum de Biblioteca de Códigos – Bibliotecas de códigos comuns existentes no eMbedded Visual Basic
precisam ser portadas. Desde que o uso de classes é suportado, a implementação e uso de bibliotecas de código é
provavelmente diferente. Desde que o .NET Compact Framework é um subset do .NET Framework, bibliotecas de classes
existentes no Visual Basic .NET podem parcialmente alavancar o desenvolvimento para Pocket PC.

• Navegação da Aplicação e Fluxo de Controle – O controle de formulários e a navegação da aplicação são manuseados
através de tipos do System.Windows.Forms e implementados diferentemente do eMbedded Visual Basic.

• Banco de dados – O acesso a banco de dados é controlado através do subset do ADO.NET. A Microsoft fornece um
gerenciador de dados para o Microsoft SQL Server 2000 Windows CE Edition 2.0 (SQL Server CE 2.0) . O .NET Compact
Framework não inclui tipos gerenciados para acessar dados local as vezes chamado CEDB ou Pocket Access, o qual tem
sido muito utilizado entre desenvolvedores eMbedded Visual Basic.

• XML – Gerenciamento e manipulação de XML é suportado nativamente.

• XML Web Services - eMbedded Visual Basic não fornece suporte para chamar componentes remotos. Suporte para XML
Web Services é um dos pontos centrais do .NET Compact Framework e considerado para ser o primeiro mecanismo de
integração completo do .NET Framework.

• Tratamento de Exception – Tratamento de erros no eMbedded Visual Basic consiste em quatro palavras: "On Error
Resume Next", e jamais acaba "If Err.Number <> 0 Then". A estrutura e o tratamento de Exception usa o bloco Try . . .
Catch . . . Finally e irá melhorar os códigos robustos e tolerância a falha.

Como exemplo, vamos ver o tratamento de erros no eMbedded Visual Basic verso Visual Basic .NET. O código de
exemplo abre um arquivo texto e lê linha a linha até o final, um conceito muito simples para programadores básicos.

Você tem que adicionar um If Err.Number <> 0 para pegar e tratar qualquer Exception no eMbedded Visual Basic, o qual
ilustra o seguinte código:
Open file
' Variables
Dim sLine As String
Dim file As FILECTL.file
Set file = CreateObject("FILECTL.file")

Screen.MousePointer = 11
' Error handling by "resuming next"
On Error Resume Next

' Open application settings


file.Open "\appdata.txt", fsModeInput

' Check if error occurred


If Err.Number <> 0 Then
MsgBox "File could not be opened!", vbCritical, "Error"
Exit Sub
End If

' Loop through file


Do While Not file.EOF
' Read line-by-line
sLine = file.LineInputString
' Check if error occurred
If Err.Number <> 0 Then
MsgBox "Data could not be retrieved!", vbCritical, "Error"
Exit Sub
End If
Loop

Screen.MousePointer = 1

' Close file


file.Close
O código inicia com o On Error Resume Next, que significa que a execução do código continua na próxima linha mesmo
se ocorrer um erro. O objeto Err contém informação sobre qualquer possível erro que ocorra. Para checar, após cada
linha de código, se a propriedade Err.Number do objeto Err está diferente do que 0 (zero), o desenvolvedor pode então
inserir um código para tratar o erro. Isto significa que o desenvolver tem que adivinhar qual linha, dentro do código, que
poderia causar um erro. Veja um código em Visual Basic .NET que faz a mesma coisa, com o tratamento de Exception:
' Open data file

' Variables
Dim sLine As String
Dim file As StreamReader

Cursor.Current = Cursors.WaitCursor

' Error handling by Try-Catch-Finally


Try
file = New System.IO.StreamReader("\appdata.txt")

While file.ReadLine <> Nothing


sLine = file.ReadLine
End While

file.Close()

Catch ex As Exception
Select Case ex.Message
Case "FileNotFoundException"
MsgBox("File could not be opened!", MsgBoxStyle.Critical, "Error")
Case Else
MsgBox("Data could not be retrieved!", MsgBoxStyle.Critical, "Error")
file.Close()
End Select
Finally
Cursor.Current = Cursors.Default
End Try
Todo o código essencial que lida com o gerenciamento de arquivo está localizado no bloco Try, o código que lida com
qualquer erro está localizado no bloco do Catch. O código é muito melhor organizado e todas as Exceptions que poderão
ocorrer estão tratadas aqui, resultando na melhor leitura do código e aplicação robusta.

Novidades para Desenvolvedores Pocket PC 2003

Esta seção discutirá alterações para os desenvolvedores Pocket PC quando migrar para Pocket PC 2003 SDK. A
documentação Pocket PC 2003 SDK, o qual está instalado em C:\Program Files\Windows CE Tools\wce420\POCKET PC
2003\Help por padrão, contém significantes informações a respeito dos novos desenvolvedores. Alguns destaques da
documentação incluem:

"O que há de novo no Microsoft Windows Powered Pocket PC 2003"

"Trabalhando com o Pocket PC Emulator"

"Programando o Pocket PC"

"Linhas gerais de interface de usuário no Pocket PC"

A documentação SDK também contém valiosos projetos de exemplo.

Novo Sistema Operacional

Pocket PC 2003 foi construído em cima do Windows CE 4.20 enquanto o Pocket PC 2000 e 2002 foram construídos em
cima do Windows CE 3.0. Windows CE é um sistema operacional que foi construído em módulos, que significa que o
centro do sistema operacional contém módulos e serviços que estão sendo usados quando estão elaborando novas
instâncias do sistema operacional. Dispositivos que foram construídos em cima do Windows CE 4.20 não contêm
automaticamente todas as características do Windows CE 4.20 e isto é também o caso do Pocket PC 2003. A seguinte
figura ilustra os módulos do Windows CE 4.20.
Figura 1: Windows CE 4.20 é um modulo do sistema operacional

As novas características do sistema operacional Windows CE 4.20 tem um impacto definitivo nos dispositivos atuais e o
Pocket PC 2003 SDK. Isto inclui:

• Aperfeiçoamento do emulator

• Melhoria e novidades no suporte a Bluetooth

• Melhoria no Kernel com aumento da performance e economia do tamanho da ROM

• Nova geração da camada de protocolos de rede, com suporte para TCP/IPv6. TCP/Ipv6 suporta um significante
aumento de endereços.

• Melhoria de drivers

• Um novo sistema de arquivo (Binary ROM Image File System) que permite dispositivos Pocket PC 2003 suportar NOR
ou NAND flash memory, e então, exige módulos para executar na RAM. Isto permite flexibilidade OEMs para negociar
escolhendo NAND x NOR x RAM entre os dispositivos.

Novas Ferramentas

A documentação do Pocket PC 2003 Software Development Kit (SDK) descreve todas as novidades da ferramenta SDK e
adicionais, como Message Queuing (MSMQ) no Windows para Pocket PC, HTTP Server para Pocket PC e SQL Server CE.
Estas ferramentas incluem:

• CabWiz.ddf – arquivo Template para criar arquivos CAB.

• CabWiz.exe – utilitário de linha de comando que pega um arquivo de instrução em relação a parâmetros e construtores
do arquivo .CAB que contém o arquivo de programa e instruções de configurações. Para informações adicionais, por
favor veja na documentação do SDK, o tópico "Using CABWiz".

• EmuASCfg.exe – esta ferramenta configura o ActiveSync e o emulador Pocket PC para permitir estabelecer a relação do
ActiveSync com o Ethernet Virtual Switch. Você deverá usar eMbedded Visual C++ para iniciar o emulador Pocket PC
usando a opção VirtualSwitch antes de executar esta ferramenta. Para informação de uso, digite "emuascfg.exe /?" na
linha de comando.

• RapiConfig.exe - RapiConfig fornece um caminho para configuração XML a ser executada no dispositivo ou o emulador
conectado via ActiveSync. Para informações de uso, digite "rapiconfig /?" na linha de comando.

Melhorias no Emulator

O Pocket PC 2003 SDK inclui um novo ambiente de emulação, no qual está implementado como um verdadeiro
computador duplicando o hardware que roda Windows CE. Porque o novo emulador é uma representação exata do
sistema operacional Windows CE e dos componentes do Pocket PC, um maior nível de fidelidade e similaridade entre um
atual dispositivo Pocket PC e o dispositivo do ambiente do emulador podem ser executados. O novo emulador não está
limitado para somente emular a linguagem inglesa no Pocket PC. Para uma perspectiva de desenvolvimento, a
estabilidade e robustes do emulador está significantemente comprovado.

O emulador do Pocket PC 2003 tem três modos de emulação: Pocket PC 2003, Pocket PC Phone Edition 2003 no qual
suporta radios externas usando WaveCom WMOD2B externo GSM radio, e Pocket PC Phone Edition 2003 com Virtual
Radio.

Figura 2: Emulador Pocket PC 2003

Outros tópicos:

• Emulador suporta drive mapping

Emuladores agora suportam armazenar cartões tendo em vista compartilhar as pastas. Quando uma pasta é
compartilhada no emulador, aparecerá como um cartão armazenado e o emulador responderá como se isto fosse um
dispositivo real. Emulador suporta Gaming APIs (GAPI) Emulador de imagens do Pocket PC 2003 suporta GAPI.

• Novo emulador não pode rodar lado a lado com versões anteriores

O novo emulador (versão 4.2) não suporta rodar lado a lado com versões anteriores do emulador. Entretanto, você pode
rodar múltiplas instâncias do novo emulador simultaneamente.• Emulator suporta sincronização sobre a Ethernet

Usando o VirtualSwitch driver com eMbedded Visual C++ 4.0, desenvolvedores podem sincronizar com ActiveSync sobre
a Ethernet sem precisar usar a porta serial. Configuração Remota

O Windows Powered Smartphone herdou múltiplas características do Pocket PC, mas uma coisa que foi introduzida
primeiramente para Smartphone 2002 foi a infra-estrutura do Configuration Manager. Pocket PC 2003 suporta a mesma
infra-estrutura do Configuration Manager que o Windows Powered Smartphone 2002.O principal componente que lida
com configuração remota é o Configuration Manager do dispositivo. Ele trata todas as requisições de configuração e
envia para os vários Configuration Service Providers (CSPs). Alguns importantes CSPs tratam as conexões de rede,
configurações de e-mail, política de segurança, opções de sincronização e instalação ou remoção da aplicação.Veja uma
destas CSPs, o primeiro tratamento dos favoritos do browser, e veja como usar isto para adicionar um link favorito no
Internet Explorer para Pocket PC. A CSP é controlada pelo fornecimento do arquivo XML. Veja um exemplo:
< wap-provisioningdoc>
< characteristic type="BrowserFavorite">
< characteristic type="Business Anyplace">
< parm name="URL" value="http://www.businessanyplace.net"/>
< /characteristic>
< /characteristic>
< /wap-provisioningdoc>
Este padrão de arquivo XML inclui duas importantes seções. A primeira é a seção de instalação que especifica um
número de opções de instalação. Um exemplo da opção “NoUninstall” que previne a configuração para aparecer na lista
de aplicativos instalados (Settings/System/Remove Programs). A segunda seção é a configuração do CSP atual, e aqui
nós adicionamos um link favorito para o site da comunidade de desenvolvedores. O nome do favorito é “Business
Anyplace” e a URL é ”http://www.businessanyplace.net”.

Para maiores informações, veja o tópico Device Management no SDK. Para maiores informações sobre os Configuration
Service Providers (CSPs) que estão disponíveis para Pocket PC 2003, veja a documentação do CSP no SDK.

Existem vários caminhos para acessar o Configuration Manager:

• Configuração Local

Acesso local para a configuração das funcionalidades de gerenciamento é permitido através da função
DMProcessConfigXML. Esta função permite submeter o dado Extensible Markup Language (XML) que configura as
alterações no dispositivo móvel.
• Configuração Remota

Pocket PC 2003 Phone Edition suporta configuração remota over-the-air (OTA) através do mesmo Wireless Application
Protocol (WAP) mecanismos de transmissão e métodos de segurança com o Smartphone 2002. O dispositivo móvel
processa o OTA XML data.
• CAB Provisioning Format (CPF)

Para fazer o dispositivo ler o arquivo XML, você precisa nomear como it_setup.xml dentro do pacote de instalação (.cab),
ou de preferência um arquivo CAB Provisioning Format (CPF), com a extensão .cpf. O comando para isso é:
Gradiente Partner
makecab.exe /D COMPRESS=OFF _setup.xml balink.cpf
Existem vários caminhos para pegar o arquivo CPF (balink.cpf) no dispositivo. Você pode simplesmente copiar o arquivo
para o Pocket PC e executar a instalação, ou você pode fornecer over-the-air (OTA). Existem várias opções OTA, e o
caminho é colocar o arquivo no Web Server e digitar a URL no Internet Explorer do Pocket PC. Você também tem a
opção para usar o WAP que, por exemplo, permite enviar a configuração usando uma mensagem Short Message Service
(SMS). Quando instalar a aplicação, você usa um arquivo CPF mas o caminho para criar este arquivo é totalmente
diferente. No lugar de usar o utilitário makecab, você usa o utilitário CABWiz (use instalação CABWiz para SmartPhone).
Pegue um arquivo de instalação padrão (.inf) similar para usar quando criar uma instalação básica para Pocket PC.

Maiores informações

Mais informações sobre como trabalhar com configuração remota é fornecido no arquivo de ajuda do Adaptation Kit for
Mobile Operators incluído no Pocket PC 2003 SDK.

Browser Atualizado
O Pocket Internet Explorer incluído com o Pocket PC 2003 é uma atualização considerável comparada com a versão
anterior. Suporte como HTML 4.01, XHTML e Cascading Style Sheets (CSS) torna um browser válido para muitos usos.
HTML 4.01 é o padrão atual para Web Browsers e suporte a CSS abrirá um mundo de sites existentes usando este
caminho eficiente de padronização de layout de página. O crescimento interessante para suportar conteúdo HTML no
format XML, XHTML, é também uma excelente soma. Especialmente por causa de XHTML é um padrão comum quando
suportar conteúdo Web para telefones móveis. Da mesma forma, a atualização para WML 2.0 é também muito bem
vinda. WML 2.0 é principalmente o padrão WML 1.x convertido para XHTML e também é um padrão comum em telefones
móveis.

O suporte a Microsoft JScript® (JScript 5.5) está no mesmo nível do Microsoft Internet Explorer 5.5, o que significa uma
combinação com mais um completo Documento Object Model (DOM), muitos scripts escritos para aplicações desktop
serão capazes de rodar no Pocket PC 2003. Para maiores informações sobre as diferentes versões do Jscript, veja em
Version Information. Documentação detalhada sobre o uso do JScript, incluindo uma referência da linguagem, tutoriais e
exemplos, estão disponíveis em Windows Script.

O suporte para "próxima geração de Internet", IPv6, oferece um futuro maravilhoso de exclusivos endereços para todos
os dispositivos móveis e verdadeira comunicação ponto-a-ponto, mesmo que sem fio. Se você ainda não tem, leia mais
em IPv6 page on MSDN.

No Pocket PC 2003, XML é apoiado do mesmo jeito que no Internet Explorer 5. No entanto, não existe compatibilidade
anterior com o XML DOM suportado no Internet Explorer 4 e o ligamento de dados não é suportado. Porém, existe
acessibilidade DOM para XML via Jscript.

Quando o dispositivo Pocket PC 2003 estiver detectando a conexão para um Web Site, você pode ver a string (no
Internet Information Server isto é identificado pela variável (Server Variable) HTTP_USER_AGENT):
Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320)
Para maiores detalhes de como fazer isto, veja em Make Your Web Applications Support Pocket PC .

O suporte a imagens também foi consideravelmente melhorado. Os formatos PNG, JPEG, GIF, WMBP, 2BP e BMP são
suportados nativamente. Além disso, mapas e GIFs animados podem ser utilizados. Através da biblioteca de imagem
extensível pode também adicionar suportes e outros formatos.

Maiores informações

Maiores informações sobre o Internet Explorer for Pocket PC está descrito na seção “Creating Online Content for Pocket
PC” do arquivo de ajuda do “Writing Application for Pocket PC” incluído no Pocket PC 2003 SDK.

Suporte para Phone Edition

O suporte para Pocket PC Phone Edition 2003 tem melhorias e um grande exemplo é a habilidade para criar aplicações
cliente de chegada de mensagens Short Message Service (SMS). Incluído com o Pocket PC 2003 SDK tem um exemplo
chamado MapiRule que exibe como pode fazer isto. O exemplo é um componente COM que implementa o IMailRuleClient
interface. Esta interface e os métodos permitem ao desenvolvedor executar regras de filtros customizados na chegada de
mensagens, e trata a medida que chegam na aplicação. Esta funcionalidade está disponível tanto para o Pocket PC 2003
como para a próxima geração da plataforma Smartphone.

Vamos ver como implementar um exemplo do método ProcessMessage (parâmetros são IMsgStore *pMsgStore, ULONG
cbMsg, LPENTRYID lpMsg, ULONG cbDestFolder, LPENTRYID lpDestFolder, ULONG *pulEventType, MRCHANDLED
*pHandled) da interface IMailRuleClient:
SizedSPropTagArray(1, sptaSubject) = { 1, PR_SUBJECT};
SizedSPropTagArray(1, sptaEmail) = { 1, PR_SENDER_EMAIL_ADDRESS};
ULONG cValues = 0;
SPropValue *pspvSubject = NULL;
SPropValue *pspvEmail = NULL;
IMessage *pMsg = NULL;
// Get the message from the entry ID
pMsgStore->OpenEntry(cbMsg, lpMsg, NULL, 0, NULL, (LPUNKNOWN *) &pMsg);

// For SMS, the subject is also the message body


pMsg->GetProps((SPropTagArray *) &sptaSubject, MAPI_UNICODE, &cValues, &pspvSubject);

// get the sender's address or phone number


pMsg->GetProps((SPropTagArray *) &sptaEmail, MAPI_UNICODE, &cValues,
Primeiro, a mensagem (pMsg) é encontrada, e o assunto da mensagem (pspvSubject, SMS corpo da mensagem), o
remetente (pspvEmail, endereço ou o número do telefone) é capturado. Se a mensagem tiver o texto "zzz", uma caixa
de mensagem é exibida com o texto da mensagem e o remetente no título. Sendo assim, a mensagem é também
removida do Inbox. O fragmento de código acima tem sido um pouco simples comparado como exemplo atual.

Outras Características

Nesta seção você encontrará algumas alterações adicionais e novas características. Alterações

• Objetos COM agora usa o modelo free-threaded – Todos os novos objetos COM, incluindo controles Active X, devem
ser free-threaded para otimizar a performance deles. Objetos COM desenvolvidos anteriormente continuarão rodar
corretamente, sem se preocupar com o modelo deles, porque o run-time do sistema operacional checa para assegurar o
modelo usado.

• Enumeração SIPSTATE movida para diferente cabeçalho de arquivo – A enumeração SIPSTATE alterou de aygshell.h
para shellapi.h. Códigos anteriores compilados sem shellapi.h agora pode precisar incluir o arquivo de cabeçalho.

• Algumas constantes longas de strings CEMAPI não são suportadas – A seguinte constante de string, antes definida no
cemapi.h, não são suportadas: kszCapAmountToFetch, kszCapAttachAmount, kszCapAgeFilter, kszCapSMTPAuthenticate,
kszCapMoveToTrash.

Novas Características

• Capacidade de extensão de rota de envio de WAP permite customizar o processamento de mensagens – Aplicações
podem interceptar diferentes tipos de envio de mensagens WAP e redirecionar para diferentes clientes para customizar o
tratamento via funções PushRouter_xxx declaradas no pushclient.h. O envio para o cliente é registrado para notificação
com o Push Router para receber tipos específicos do envio de mensagens do dispositivo para processamento. Esta
capacidade é interceptada pelo SMS. Esta funcionalidade está suportada somente para dispositivos com GSM, não em
dispositivos CMDA.

• Arquivos CAB podem ser usados para dispositivos móveis – Arquivos CAB podem ser usados como mecanismo de
entrega para abastecer dispositivos Pocket PC 2003.

• Suporte L2TP/IPSec VPN - Esta tecnologia permite dispositivos móveis usar conexões Layer Two Tunneling Protocol
(L2TP) com Internet Protocol Security (IPSec). A combinação do L2TP e IPSec, conhecida como L2TP/IPSec, é uma
tecnologia muito segura para criar acessos remotos em uma conexão Virtual Private Network (VPN) através de redes
públicas como a Internet.

• File Explorer suporta extensões de menu contextual – O file explorer agora suporta extensões de menu contextual para
tipos de arquivos registrados. Isto é feito pela implementação da interface IContextMenu, adiciona algumas entradas no
registro.

• Propriedade de contexto da janela de entrada é controlável – As funções SHSetInputContext e SHGetInputContext


permitem executar modificações e consultas da propriedade de contexto da janela de entrada. Estas propriedades auto-
correção e auto-preenchimento estão prontas para serem usadas para o campo. SHSetInputContext também suporta
uma classe de contexto pré-definido como "phone" ou "e-mail", no qual abrange um grupo específico de configurações
para várias características do contexto.

• Notificações podem forçar o dispositivo ligar – O #define, SHNF_DISPLAYON, é definido no aygshell.h e força ligar o
display quando dispara uma notificação.

• Reconhecimento automático de gestos é controlável – O código de notificação NM_RECOGNIZEGESTURE, definido em


commctrl.h, fornece habilidade para os desenvolvedores de aplicação Pocket PC 2003 decidir ou não se eles gostariam
de tirar vantagem do reconhecimento automático de gestos nos controles comuns.

• Maioria dos serviços de e-mail é configurável via Configuration Manager - O Email Configuration Service Provider (CSP)
permite configurar o serviço de e-mail Internet-Protocol (como IMAP4 e POP3) via o dispositivo de infraestrutura
Configuration Manager.

• Customizar leitura ou formulário Compose para Inbox pode ser registrado – As interfaces IMessageFormEx e
IFormProviderEx, declaradas no cemapi.h, permite aos desenvolvedores customizar uma configuração na leitura da caixa
de entrada (Inbox) ou formulário Compose que adapta o que as aplicações precisam. Do mesmo modo podem ser
usados para construir SEM ou clientes MMS.

• Suporte a IPv6 – Muitas APIs e componentes do Pocket PC 2003 APIs agora suportam endereçamento IPv6-style.
IDccManSink2 é a nova interface, no qual suporta IPv6, e é usada para notificar aplicações clientes sobre as conexões do
dispositivo.

• Funcionalidades Bluetooth está mais controlável – As funções BthGetMode e BthSetMode, declaradas no bthutil.h,
fornece habilidade para consultar o estado atual do painel de controle Bluetooth, tanto quanto modificar o estado
(Bluetooth Power Off, Connectable, Discoverable e etc.).

• WAP sobre SMS é suportado - O Pocket PC 2003 juntou o Smartphone para suportar WAP sobre SMS.

Conclusão

O novo Pocket PC 2003 SDK contém tudo o que você precisa para desenhar e desenvolver aplicações móveis para a nova
plataforma e dispositivos. Usando o Visual Studio .NET e Visual C# ou Visual Basic .NET, você pode alcançar todos os
benefícios da estrutura de programação .NET – e usando eMbedded Visual C++ 4.0, você pode continuar criar aplicações
nativas para dispositivos. O fato que a maioria das aplicações escritas para Pocket PC 2002 ainda rodarem nos
dispositivos com Pocket PC 2003 assegura uma continuidade e o retorno do investimento.

Comece a utilizar as novidades do Pocket PC 2003 para aumentar a experiência e a riqueza dos seus usuários de
aplicações. Faça o download do Pocket PC 2003 SDK .

No Stress, think .NET, think Wireless...

Renato Haddad
rehaddad@msn.com
Microsoft Most Valuable Professional
Autor de diversos livros e ministra palestras e treinamentos sobre a tecnologia .NET.
Autor de diversos treinamentos multimídia de ASP.NET e Visual Studio .NET 2003.

Referências:
http://www.smartdevelopers.microsoftdev.com
http://www.gotdotnet.com
http://www.microsoft.com/windowsmobile/products/pocketpc/default.mspx
http://www.microsoft.com/windowsmobile/information/businesssolutions/wifi/default.mspx
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/guide_ppc/htm/
_ppc_writing_applications_for_pocket_pc_uwxf.asp

Wi-Fi para Pocket PC


Alguns trechos deste artigo encontram-se em http://www.microsoft.com/windowsmobile/information/businesssolutions/wifi/
wifi101/default.mspx#1

O que é Wi-Fi?
Você já parou para pensar na quantidade de fios espalhados pelos escritórios, casas ou empresas? Imagine quando você precisar
adicionar ou retirar um fio para ligar uma rede. Isso sem falar na manutenção da mesma, tendo que puxar diversos pontos de rede,
etc. Em um típico escritório de reuniões onde existem diversas salas, provavelmente cada sala tem diversos cabos de redes
disponíveis para você conectar o computador ou o Pocket PC.
Que tal excluir todos os fios e montar uma rede sem-fio (wireless)? Esta é a proposta da tecnologia Wi-Fi, que permite
montar uma rede sem o emaranhado de fios por todo lado. Com isso, você pode acessar seu e-mail, a Web e Streaming
Media.

Velocidade

Wi-Fi usa banda larga para acessar Internet para usuários sem fio. É um caminho rápido e conveniente para um ponto
online na casa, no escritório e em viagens. Lugares onde você pode acessar redes Wi-Fi são conhecidos como hotspots.
Wi-Fi, ou 802.11b, opera em área com 2.4 Ghz e suporta velocidades acima de 11 Mbps. Existem dois outros protocolos
no 802.11, incluindo (a) e (g) que estão disponíveis para uso público, mas 802.11b é mais comum no mundo.

Um hotspot Wi-Fi é criado para estabelecer um ponto de acesso para uma conexão de Internet. O ponto de acesso
transmite um sinal sem fio numa pequena distância – cerca de 100 metros. Quando um Device permite Wi-Fi, como um
Pocket PC, encontrar um hotspot, o Device pode na mesma hora conectar na rede sem fio. Muitos hotspots estão
localizados em lugares que são confortavelmente acessível ao público, como aeroportos, cafés, hotéis e livrarias. Muitas
casas e escritórios também têm redes Wi-Fi. Enquanto alguns hotspots são gratuitos, a maioria das redes públicas são
suportadas por uma Internet Service Provider (ISPs) que cobram uma taxa dos usuários para conectar na Internet.

Como acessar um hotspot Wi-Fi?

Hotspots estão crescendo progressivamente no mundo. De fato, atualmente existem hotspots nas 2500 lojas Starbucks,
na Borders Books, em alguns aeroportos, Four Seasons e Wyndham hotéis, escritórios, casas e parques. Algumas lojas
McDonalds já iniciaram a instalação de hotspots Wi-Fi. No Brasil, foi feito um teste em uma cafeteria de São Paulo, mas
a questão era como cobrar pelo acesso. Todos Pocket PCs com suporte a Compact Flash, ou Wi-Fi embutido pode acessar
hotspots. Novas funcionalidades “configuração zero” nos Pocket PCs rodando Windows Mobile descobre um hotspot
rapidamente. Assim que você liga seu Pocket PC rodando o novo Windows Mobile ele pesquisa automaticamente as redes
Wi-Fi no qual ele pode conectar. Quando seu Pocket PC encontrar uma rede Wi-Fi, ele pergunta se você quer se
conectar. Então, abra o Internet Explorer e o Wi-Fi Internet Service Provider irá guiá-lo através do processo de login
para conectar na Internet. Uma vez ligado, selecione “Yes”, abra o Internet Explorer e conecte. Isto que é facilidade!
Agora, imaginem a quantidade de serviços que isso poderá agregar as aplicações desenvolvidas que consomem XML Web
Services? Imagine um vendedor de produtos farmacêuticos que visita uma carteira de clientes por semana, quando ele
chegar em uma farmácia para tirar o pedido, com o Pocket PC e com uma aplicação Smart Device Application, por
exemplo, após a montagem do pedido, o Pocket PC já identifica a rede Wi-Fi conectando-o na rede, e assim, o mesmo já
transmite os dados via XML Web Services para o escritório do laboratório para efetuar o pedido.

Veja alguns exemplos funcionais a serem mostrados com esta tecnologia:


• Restaurantes – imagine os garçons anotando o pedido com um Pocket PC ligados via Wi-Fi diretamente na rede do
restaurante, interligando a cozinha, o caixa, o estoque, etc;

• Bancos – quando você entrar no banco, o Pocket PC já identifica a rede e permite o acesso ao sistema do banco,
evitando filas e ganhando tempo;

• Hospitais – a estrutura física de um hospital normalmente é muito grande, mas é possível montar diversas antenas
com redes Wi-Fi com alcance de até 100 metros, dotar os médicos com equipamentos que suporte Wi-Fi, por exemplo,
Pocket PCs e Tablet PCs contendo um aplicativo para administrar os pacientes acessando o banco de dados dos pacientes
em tempo real;
Como eu conecto Wi-Fi com meu Pocket PC?

A funcionalidade “Configuração Zero Wi-Fi” do Windows Mobile 2003 oferece um caminho cômodo para conectar um rede
Wi-Fi. Este recurso é um software que automaticamente pesquisa uma rede Wi-Fi quando usada em um dispositivo que
permite Wi-Fi, criando facilmente um ponto de rede rapidamente.

Quanto custa usar o serviço de Wi-Fi Hotspot?

O custo do acesso a Internet Wi-Fi depende do provedor. Alguns provedores de serviços Wi-Fi oferecem uma
mensalidade ou um plano de acesso diário. Com certeza teremos uma estrutura definida em breve, pois isso está
crescendo rapidamente e o acesso aos dados em qualquer lugar e hora tornou-se inevitável.
Conclusão

Esta tecnologia deve agregar valores as aplicações desenvolvidas que utilizam recursos de rede, como XML Web
Services, permitindo uma comunicação rápida com servidores. É fundamental notar que disponibilizar o acesso as
informações está ficando cada vez mais fácil, integrando todos os meios de comunicação. Em um tempo curtíssimo,
teremos no mesmo dispositivo tecnologias como Wi-Fi, Bluetooth, GSM, GPRS, WAP, SOAP e Câmera digital. Tudo isso
no mundo Wireless.

No Stress, think .NET, think Wireless...


Renato Haddad

rehaddad@msn.com
Microsoft Most Valuable Professional
Autor de diversos livros e ministra palestras e treinamentos sobre a tecnologia .NET.
Autor de diversos treinamentos multimídia de ASP.NET e Visual Studio .NET 2003.

Referências:
• http://www.gotdotnet.com
• http://www.smartdevelopers.microsoftdev.com
• http://www.microsoft.com/windowsmobile/products/pocketpc/default.mspx
• http://www.microsoft.com/windowsmobile/information/businesssolutions/wifi/default.mspx
• http://msdn.microsoft.com/library/default.asp?url=
/library/en-us/guide_ppc/htm/_ppc_writing_applications_for_pocket_pc_uwxf.asp

Entendendo Aplicações Móveis no .NET


Por Renato Haddad, Microsoft Most Valuable Professional. Autor de diversos livros e ministra palestras e treinamentos
sobre a tecnologia .NET.
O que você entende por Mobilidade? Tudo o que você pode operar a distância ou sem fio é considerado Mobilidade.
Hoje em dia muitas aplicações são executadas isoladamente, e quando precisam agregar valores à mesma,
normalmente são utilizados recursos externos como telefone celular, e-mail, Short Message Service (SMS) ou algum
serviço de BIP. A pergunta é: Porquê não acrescentar todas estas funcionalidades a sua aplicação existente, seja
ASP.NET ou Windows Application?
Ferramentas de Desenvolvimento

Veja o que é preciso ter para você desenvolver aplicações móveis.


Visual Studio .NET 2002

Se você tem a versão Visual Studio .NET 2002, para desenvolver aplicações para telefones celulares, terá que instalar o
Microsoft Móbile Internet Toolkit (MMIT) que é um SDK. Selecione uma aplicação do tipo Móbile Web Application.

Para executar a aplicação em um emulador você pode instalar o Microsoft Móbile Emulator 3.0 (MME 3.0). Selecione o
menu View / Móbile Explorer Browser / Show Browser. Informe a URL e pressione ENTER.
Para desenvolver aplicações para PDA (Partner, iPaq) você terá que instalar o Smart Device Extension (SDE) que
também é um SDK.
Visual Studio .NET 2003

Caso tenha o VS.NET 2003 todas as ferramentas estão disponíveis dentro dela, ou seja, você não precisa instalar
nenhum SDK ou ferramenta auxiliar para desenvolver qualquer tipo de aplicação. No caso de telefones celulares você
deverá instalar um emulador que pode ser conseguido em qualquer site dos fabricantes Nokia, Openwave, etc. A
Microsoft não disponibilizou o MME em função da grande quantidade de Devices executarem uma aplicação ASP.NET
Móbile Application.Para celulares selecione uma aplicação do tipo ASP.NET Móbile Application.
Veja a execução de uma página no emulador da Openwave.

Para Pocket PCs selecione uma aplicação do tipo Smart Device Application.

Linguagens

Aqui é o ponto forte da plataforma .NET. Você pode utilizar o Visual Basic .NET ou o Visual C# para desenvolver qualquer
aplicação, assim todo o conhecimento que você já tem com aplicações ASP.NET ou Windows, é possível aproveitar a
estrutura da linguagem utilizada, claro que com as devidas classes existentes para tais aplicações. É possível ainda
consumir Web Services e Componentes de forma transparente para a aplicação, bastando ter apenas uma conexão com
a Internet para consumir Web Services.
Recursos utilizados

Quando você for desenvolver uma aplicação para telefone celular utilizando o Visual Studio .NET 2003, irá criar uma
aplicação do tipo ASP.NET Móbile Application. Os controles utilizados são parecidos com os de uma página ASP.NET, no
entanto, mais limitados. A produtividade e a facilidade de desenvolver aqui é que você não precisa se preocupar em que
Device (celular) irá executar a aplicação, se o micro-browser aceita ou não tal tipo de controle. O processo é semelhante
ao processamento de uma página ASP.NET, quando o Web Server recebe a solicitação do celular (via operadora, é
claro), o Framework identifica qual é o Device que fez tal solicitação e monta o WML (Wireless Markup Language) que ele
suporta. Isso é fantástico, porque além de você não precisar se preocupar com as limitações do Device, o código e os
controles fontes são únicos para toda a aplicação. Vale dizer que o .NET já suporta mais de 200 Devices de diferentes
fabricantes.

E como fica o papel da operadora de celular com o .NET? Isso você terá que checar juntamente com cada operadora,
mas via de regra, todo celular que suporte WAP (Wireless Application Protocol) tem um Setup que você configura com o
número IP, User ID e Password que a operadora fornece. Em seguida, é preciso informar qual página será executada,
sendo que isso é na verdade uma URL completa, por exemplo, http://Servidor/site/pagina.aspx.

Você deve estar perguntando: É possível abrir esta página em um browser? A resposta é, claro que sim, pois lembre-se
que o Framework é que gera o produto final, e no caso do browser ele irá gerar um CHTML. Por este motivo você não
deve desanimar quando for testar uma página que você criou e não tiver nenhum emulador.

No caso de PDA, leia-se Pocket PC você deverá criar uma aplicação do tipo Smart Device Application, que será uma
aplicação do tipo Windows. Os controles existentes são mais limitados que uma Windows Applications, mas contém o
suficiente para criar uma excelente aplicação para este dispositivo.
Gradiente Partner

Para delírio total das aplicações, imagine juntar todos os recursos de um telefone celular com os recursos de um Pocket
PC. Isso é o que oferece este aparelho da Gradiente que opera com GSM e GPRS. Você está 24 horas ligado na WEB e
basta a sua operadora oferecer comunicação via GSM. Todos os recursos deste aparelho você pode verificar no site da
Gradiente, mas aqui vou ilustrar algumas funcionalidades em nível de aplicação .NET.

É possível utilizar o Visual Studio .NET 2003 para desenvolver para o Partner? A resposta é sim, e além disso você tem
disponível todos os recursos de uma aplicação Windows ou ASP.NET. Imagine o seguinte cenário: Você precisa coletar
informações em tempo real para apresentar a um cliente para decidir um investimento. Você pode desenvolver de duas
formas:

1) Criar uma aplicação do tipo Smart Device Application, montar a estrutura, fazer o Deploy no Partner (com apenas um
click) e visitar o cliente para orientar no investimento, por exemplo um gerente de contas de um banco que visita
clientes potenciais. Você abre a aplicação, manipula os dados e quando necessitar de algum dado que esteja no servidor,
ele consome um Web Service que retorna os dados. Lembre-se que você está 24 horas ligado na Internet. Sendo assim,
isso é muito simples. Além disso, é possível armazenar todos os dados no SQL Sercer CE, fazer as devidas operações
bancárias Off-Line e quando desejar, replicar no banco de dados do servidor através de um Web Service.

2) Criar uma aplicação do tipo ASP.NET Móbile Application, instalar no servidor e acessar via browser ou telefone celular.
Neste caso, a aplicação será executada no servidor da empresa, podendo consumir qualquer tipo de recurso como
Componente ou Web Service. O banco de dados pode estar armazenado no SQL Server, e sendo assim dispensa o uso
de ter o banco de dados Off-line. Neste caso, você estará totalmente On-line fazendo as transações diretamente no
banco de dados da empresa. A questão é o preço que se paga por isso, atualmente é cobrado por pacote, não por
impulso telefônico. Em compensação você tem toda a equipe de vendas ligados na empresa.

Além de tudo isso, você pode sincronizar os dados do Outlook (agendas, contatos e e-mails), enviar e receber Short
Message Service (SMS).

Conclusão

O mundo móvel veio para agregar valores às aplicações, use o Visual Studio .NET 2003 para desenvolver e a linguagem
que você conhece.
Dicas do Renato Haddad

Aqui estão as minhas dicas para o mundo móvel:

Mergulhem de cabeça na plataforma .NET;


Defina e estude uma linguagem para esta plataforma;
Comece agregando pequenas funções móveis às aplicações
atuais, a fim de conquistar o espaço e confiando Wireless;
Podem ter certeza que será uma questão de tempo, em um
breve período muitas aplicações terão uma parte móvel;
As empresas devem se preparar para investir em
tecnologia móvel para gerar economia de tempo e dinheiro
na tomada de decisões.
No site MSDN Brasil você encontrará diversos tutoriais para desenvolver aplicações móveis. Vale dizer que estes tutoriais
são explicados passo a passo para guiá-lo no desenvolvimento.

Renato Haddad
rehaddad@msn.com
Microsoft Most Valuable Professional
Autor de diversos livros e ministra palestras e treinamentos sobre a tecnologia .NET.
Autor de diversos treinamentos multimídia de ASP.NET e Visual Studio .NET 2003.

Referências:
http://www.asp.net/mobile
http://www.gotdotnet.com
http://www.smartdevelopers.microsoftdev.com

Portando uma aplicação que usa GDI+ para o Pocket PC


Por Alexandre Santos Lobão
Na primeira versão que escrevi deste artigo, havia uma série de pontos interessantes a destacar sobre o que é necessário fazer
para portar uma aplicação GDI+ do desktop para o Pocket PC.
Então, chegou o Visual Studio 2003 (chamado anteriormente de Everett).
Se portar uma aplicação criada com a Compact Framework integrada ao Visual Studio 2001 já era simples, as mudanças sutis
ocorridas na Compact Framework – agora parte integrante do Visual Studio – tornou as coisas ainda mais simples, como
veremos.

No caso específico deste artigo, estaremos falando sobre como portar uma aplicação que usa recursos gráficos da GDI+ - mais
especificamente, um clone do jogo Tetristm, que batizamos de .Nettrix; e que é criada no primeiro capítulo do livro “.Net
Game Programming with DirectX 9.0”.

Criando aplicações para múltiplos dispositivos

A .Net framework abre novos horizontes para todos os programadores, e especialmente para os programadores de jogos, com
sua facilidade de criar um mesmo código que rodará em diversos dispositivos, sobre diferentes sistemas operacionais.
É claro que esta compatibilidade nunca será 100%, uma vez que cada dispositivo tem suas próprias características, com seus
pontos fracos e seus pontos fortes; mas é realmente interessante ver quão poucos ajustes são necessários para portar uma
aplicação de um PC convencional para um Pocket PC, especialmente se ela tiver sido escrita seguindo boas práticas de
programação orientada a objetos.

Em versões anteriores do Visual Studio (antes do .Net), caso desejássemos criar um programa para rodar em um dispositivo
móvel, precisaríamos usar uma versão específica do compilador para cada dispositivo, e, pior que isso, não havia nenhum
compromisso de que o sistema operacional oferecesse funções compatíveis. Na verdade, mesmo funções que poderiam ser
compatíveis várias vezes possuíam diferenças tão grandes que portar um programa era, muitas vezes, uma questão de apagar
tudo e reescrever completamente o programa.

Este problema de portabilidade era especialmente verdadeiro para programas que utilizavam funções gráficas. Mesmo os
programas mais simples precisavam de uma série de ajustes, nem sempre óbvios, para rodarem em dispositivos diferentes.

Criando uma aplicação para Smart Devices no Visual Studio 2003

Como já dissemos, o Visual Studio .Net 2003 já oferece suporte integrado para a criação de projetos para a Compact
Framework, a versão do .Net utilizada em dispositivos móveis. Existem dois novos templares de projeto, nomeados “Smart
Device Application” e “ASP.NET Mobile Application”, que nos permitem criar aplicações que irão rodam em dispositivos
baseados no Pocket PC ou no Windows CE. A Figura 1 apresenta a janela de “Nova Solução” (“New Solution”) do Visual
Studio, com destaque para a aplicação do tipo “Smart Device”. Para nosso exemplo, iremos portar o programa para o Pocket
PC.

Figura 1: “Smart Device Application”, um dos novos tipos de aplicação oferecidos pelo Visual Studio .Net 2003

Uma vez que o projeto para “smart devices” é criado, são oferecidas novas opções de menu específicas para este tipo de
projeto: No menu Tools, existe uma opção “Connect to Device” , e sob o menu Build também há uma nova opção, “Deploy”.

Depois de criar um programa, podemos pressionar diretamente o botão de “Start” na barra de ferramentas do Visual Studio,
da mesma forma que faríamos com projetos para PC convencional. O Visual Studio, depois de compilar o programa com as
bibliotecas apropriadas à plataforma que escolhemos para o projeto, irá abrir uma janela que nos permitirá escolher o
dispositivo de destino para a aplicação – emulador ou dispositivo real - conforme apresentado na Figura 2.
Figura 2: Escolhendo o dispositivo de destino de nossa aplicação

Se nós escolhermos realizar o deploy do programa para o emulador, o Visual Studio irá carregar o emulador e se conectar a
ele antes de iniciar o envio do programa. O emulador é uma cópia exata do Pocket PC, incluindo todos os programas básicos
deste sistema (sim, ele vem com “Solitaire”, também!), e ainda nos permite escolher uma imagem de apresentação (“skin”)
para o emulador, que é um bitmap com botões que realmente funcionam. O emulador nos permite testar nossa aplicação
exatamente da mesma forma que faríamos com o dispositivo real, sem a necessidade de ligar um dispositivo à sua máquina de
desenvolvimento.

Uma vez que o emulador foi carregado ou após checar se o dispositivo está realmente conectado, o Visual Studio .Net irá
realizar a instalação não apenas da aplicação que criamos mas também de todas as bibliotecas necessárias para que o
programa rode no dispositivo desejado. Como padrão, a aplicação será copiada para o diretório \Windows no Pocket PC, e o
Visual Studio irá executá-la automaticamente, iniciando o debug – que pode ser realizado a partir do PC, para a aplicação
rodando no Pocket!

Portando uma aplicação existente para o Pocket PC

É possível rodar qualquer aplicação simples de PCs desktop no Pocket PC, com pequenos ajustes, e existem programas simples
que efetivamente não precisarão de nenhum ajuste, exceto, é claro, a criação de novos controles de interface no template de
projeto “Smart Device Application”.

No que se refere às funções gráficos, é claro que nem todas as funções da GDI+ estão presentes nos dispositivos móveis, mas
muitas das funções estão lá e, mais importante que isso, mantém as mesmas interfaces. Com isso, portar aplicações que
usam recursos gráficos tornou-se muito mais simples que nas versões anteriores do Visual Studio.

Para ilustrar alguns dos problemas pelos quais passando quando portanto uma aplicação baseada em GDI+ para o Pocket PC,
nós iremos portar um clone do TetrisTm para este dispositivo móvel. Não é objetivo deste artigo mostrar todo o código da
aplicação, mas apenas discutir os problemas comuns encontrados em migrações deste tipo, destacando pequenos trechos de
código. O código completo da aplicação para PC e Pocket PC está disponível para download em:

.Nettrix versão desktop


.Nettrix versão Pocket PC

Primeiro passo: Criando a interface

O primeiro e tedioso passo para portar uma aplicação para um dispositivo móvel é recriar todos os elementos de interface em
um projeto específico para o dispositivo, uma vez que os controles de interface não são compatíveis e não podem ser copiados
entre projetos de diferentes plataformas, e não existe nenhum assistente que realize a conversão por nós. A Figura 3
apresenta telas similares para Pocket PC e desktop PC.
Figura 3: A interface de nosso jogo, no PC e Pocket PC

Segundo passo: Copiando de código

Depois de criar os elementos de interface, nós podemos copiar todo o código, das janelas e outros módulos, para o programa
do dispositivo móvel. No nosso exemplo, o código é organizado em quatro módulos: O módulo da janela principal e três
módulos de classe, que são apresentados no diagrama de classes simplificado da Figura 4.

Figura 4: O diagrama de classes do jogo .Nettrix

Sem entrar em muitos detalhes, no diagrama da Figura 4 a classe Square desenha e apaga um quadrado na tela, possuindo
propriedades como cor e posição; a classe Block desenha, apaga e move (operação combinada de apagar e desenhar) um
conjunto de quatro quadrados, que formam um bloco básico com diferentes formatos e cores para o .Netrix. Por fim, a classe
GameEngine possui algumas funções e propriedades de uso geral, como o array utilizado para calcular colisões entre objetos
no jogo, e as funções para realizar operações sobre este array.

Além destas classes, parte da lógica do jogo está implementada diretamente na janela, em código distribuído em alguns
eventos: A inicialização de variáveis está no evento de Load; o loop principal do jogo, que controla a queda dos blocos, está no
evento Tick de um objeto Timer; e a rotina de tratamento do input do usuário está no evento de KeyPress do form.

Último Passo: Concertando os erros

Embora não seja nosso objetivo aqui explicar todos os problemas possíveis que podem ocorrer quando migrando uma
aplicação baseada em GDI+ para um dispositivo móvel, nós estaremos apresentando pelo menos um bom exemplo para cada
um dos possíveis tipos de erros encontrados na migração:

 Erros de compilação que ocorrem devido a diferenças em funções, eventos gerados pelos objetos de interface ou outros
elementos na programação.
 Erros de compilação devido a funcionalidades que não existem na plataforma destino da migração.
 Erros em tempo de execução que ocorrem devido a comportamentos diferentes em funções compatíveis, ou na
inicialização de objetos.
 Mal funcionamento do programa, onde o Visual Studio não acusa nenhum erro, porém o programa não funciona como
esperado, devido a algum tipo de comportamento ligeiramente diferente entre as funções compatíveis nas duas
plataformas.
Modificações em funções, eventos e outros elementos de programação

Um exemplo deste tipo de erro é a modificações dos parâmetros esperados pelo método Show da MessageBox,
que são diferentes na versão para Pocket PC. Nesta plataforma, o parâmetro que define qual será o botão default
é mandatório, e nós também precisamos nos preocupar com o ícone a ser usado na janela de mensagem, uma vez
que nem todos os ícones estão presentes. Por exemplo, o ícone “Stop” (que, em versões anteriores, era chamado
de “Hand”) corresponde ao ícone “Hand” no Pocket PC.

Assim, uma linha de código no PC convencional como a vista a seguir:


MessageBox.Show("GAME OVER", ".NetTrix", MessageBoxButtons.OK, _
MessageBoxIcon.Stop)

Após a conversão será:


MessageBox.Show("GAME OVER", ".NetTrix", MessageBoxButtons.OK, _
MessageBoxIcon.Hand, MessageBoxDefaultButton.Button1)

Este erro ilustra perfeitamente o primeiro tipo de erros que podemos esperar encontrar quando portando jogos para
dispositivos móveis: Algumas funções recebem parâmetros ligeiramente diferentes, e alguns dos overrides (formas diferentes
de chamar a mesma função, com mudança nos parâmetros de chamada) não estão presentes no Pocket PC. Estes são os
problemas mais simples de se resolver, uma vez que tudo o que precisamos fazer são ajustes simples, como completar
parâmetros que faltam ou corrigir os valores dos parâmetros para atender ao padrão da plataforma compacta.

Um outro exemplo deste tipo de erros é a modificação em alguns nomes de eventos e seus argumentos. Por exemplo, o
evento “Activated” da janela não existe no Pocket PC, correspondendo a evento “GotFocus”.

Missing features

O segundo tipo de problemas que devemos corrigir quando migrando nossas aplicações gráficas é que algumas
funções, métodos e eventos não estão presentes na plataforma móvel, devido às suas limitações. Este tipo de erro
pode ser um pouco mais difícil de corrigir, pois uma vez que não existe uma correspondência direta entre
funções nas plataformas, precisaremos procurar algum método que substitua a funcionalidade perdida; sendo que,
por vezes, pode ser necessário escrever grandes partes do programa devido a isto.

No jogo usado como nosso exemplo, nós encontramos um erro deste tipo no método Show da classe Square: O
objeto Graphics, utilizado na GDI+ para operações diretas de desenho sobre um objeto, é muito mais simples no
Pocket PC do que em computadores de mesa, e não suporta o método DrawPath, utilizado para desenhar um
quadrado com um degradée de cores. Neste caso, vamos precisar reescrever por inteiro o método, fazendo com
que ele simplesmente desenhe um quadrado com uma borda sólida de cor diferente.

A versão para PCs convencionais do método Show, que desenha um quadrado e o preenche com um degradée das
bordas em direção ao centro é apresentada na próxima listagem de código. Nas duas listagens a seguir,
“BackColor”, “Forecolor”, “Location” e “Size” são propriedades da classe Square que definem a cor de fundo, a
cor de frente, a localização e o tamanho do quadrado a ser desenhado.
Public Sub Show(ByVal Graph As Graphics)
Dim graphPath As Drawing2D.GraphicsPath
Dim brushSquare As Drawing2D.PathGradientBrush
Dim surroundColor() As Color
Dim rectSquare As Rectangle

' Create a path consisting of one rectangle


graphPath = New Drawing2D.GraphicsPath()
rectSquare = New Rectangle(location.X, location.Y, _
size.Width, size.Height)
graphPath.AddRectangle(rectSquare)

' Creates the gradient brush which will draw the square
' Note: There’s one center color and an array of border colors
brushSquare = New Drawing2D.PathGradientBrush(graphPath)

brushSquare.CenterColor = forecolor
surroundColor = New Color() {backcolor}
brushSquare.SurroundColors = surroundColor

' Finally draws the square


Graph.FillPath(brushSquare, graphPath)
End Sub

A versão para Pocket PC do mesmo método ficou muito mais simples, como podemos ver na próxima listagem
de código.
Public Sub Show(ByVal Graph As Graphics)
' Draws the square
Graph.FillRectangle(New Drawing.SolidBrush(backcolor), _
location.X, location.Y, size.Width, size.Height)
' Draws the square border
Graph.DrawRectangle(New Pen(forecolor), _
location.X, location.Y, size.Width - 1, size.Height - 1)

End Sub

Um outro deste mesmo tipo se refere à criação do objeto Graphics: enquanto em PCs nós podemos criar este objeto através do
handle de janela de um controle na tela, na versão da GDI+ para o Pocket PC este objeto só pode ser criado a partir de uma
imagem.

De fato, não temos acesso aos handles dos controles de interface na Compact Framework; de forma que devemos evitar
quaisquer funções que recebam um handle como parâmetro se quisermos fazer programas mais facilmente portáveis.

Corrigindo erros de tempo de execução

Depois de corrigir os erros de compilação, nossos programas já estarão aptos a serem executados no Pocket PC,
mas, em alguns casos, eles irão abortar tão logo comecem a rodar.

Na primeira versão da Compact Framework nós encontramos alguns problemas como esse porque todos os
bitmaps dos controles do tipo PicturesBox precisavam ser explicitamente criados, ou qualquer comando realizado
sobre eles resultaria em erro.

Para corrigir este problema tudo o que precisávamos fazer é adicionar uma linha extra ao evento de Load da
janela principal:
PicBackground.Image = New Bitmap(PicBackground.Width, PicBackground.Height)

Este tipo de erro não acontece mais no Visual Studio 2003, mas ele ilustra bem a terceira variedade de erros que podemos
encontrar quando migrando programas para outras plataformas: não temos mais erros de compilação, mas o programa gera
um erro em tempo de execução porque alguma coisa (uma função, um método ou um evento) não ser comporta como o
esperado.

Esta classe de erros é um pouco mais difícil de corrigir que os erros da categoria anterior, pois o erro pode ocorrer em um
lugar diferente onde ele foi gerado. Por exemplo, se em nosso jogo não criássemos o bitmap como mostrado acima, o erro
ocorreria apenas quando a classe Square precisasse desenhar o primeiro quadrado na tela, quando o jogo fosse iniciado.
Dependendo de como criássemos nossas rotinas de captura de erro, poderíamos precisar marcar breakpoints e realizar um
debug passo a passo até descobrir a origem do erro, e, então, precisaríamos ainda descobrir porque a função de desenho do
quadrado – que está perfeitamente correta – estaria gerando um erro imprevisto.

Corrigindo mal funcionamento do programa

Uma vez que resolvemos todos os problemas de compilação e todos os erros em tempo de execução, nós estamos prontos
para encarar a última e mais difícil categoria de erros que nós encontramos quando portando programas para outras
plataformas: tudo funciona bem, mas alguma coisa não funciona conforme o esperado. Ou, em outras palavras, não há erros,
mas nosso programa não funciona conforme o planejado.

A Figura 5 apresenta um erro deste tipo, que ocorria quando realizávamos a migração para Pocket PC usando o Visual Studio
2001 com a Compact Framework: a tela não era atualizada adequadamente pelas rotinas de desenho, assim, nós só
conseguíamos ver parte dos blocos após fechar a janela de mensagem de “Game Over”, o que forçava o redesenho da
imagem apresentada na PictureBox abaixo.
Figura 5: No Visual Studio 2001, com uso da Compact Framework, a tela não era atualizada conforme o esperado

No Visual Studio 2003 este comportamento indesejado foi concertado, mas vamos continuar com este exemplo apenas como
forma de ilustrar o que podemos fazer em casos semelhantes.

Bom, nós temos um program que funciona sem problemas, mas ele não atualiza a tela. O código não tem nenhum erro
intrínsico (como, por exemplo, a rotina de desenho não estar sendo chamada), uma vez que o mesmo código executava sem
problemas e com os resultados esperados no PC de mesa. Olhando novamente para o programa, não conseguimos encontrar
nada errado, e até mesmo a documentação sobre desenvolvimento para dispositivos móveis não oferece uma pista concreta
sobre o que pode estar ocorrendo. O que podemos fazer?

Como sempre, não há truques de mágica: em situações como esta, tudo o que podemos fazer é buscar exemplos de
programas similares que funcionem, como os exemplos que vêm com o SDK da Compact Framework, que foram incorporados
ao próprio Visual Studio na versão de 2003.

Passando pelos exemplos que fazem parte da .Net Compact Framework, nós iremos encontrar um exemplo simples de um
programa gráfico chamado “Bubbles”. Este programa apresenta alguns círculos que se movem pela tela, criando a ilusão de
bolhas de sabão flutuando.

Comparando passo a passo os programas, percebemos que a única diferença na chamada às funções gráficas em ambos é a
forma como o objeto Graphics é criado: enquanto nosso programa cria este objeto a partir de um bitmap contido em uma
PictureBox, o exemplo da Compact Framework cria o objeto Graphics diretamente a partir do form, e os desenhos são
realizados no fundo da janela.

Assim, para fazer o .Nettrix rodar no Visual Studio .Net 2001, nós precisávamos modificar as rotinas de desenho para desenhar
diretamente no form, esquecendo o uso de PictureBoxes. Na versão de 2003, este problema foi resolvido, assim, as mesmas
rotinas funcionam em ambas as plataformas sem modificação – o que não quer dizer que, em outros programas, não
possamos ter problemas semelhantes a este ocorrendo.

Toques Finais

Um toque final que precisamos fazer é criar quaisquer elementos adicionais de interface que melhorem a usabilidade do
programa no dispositivo móvel. No exemplo de nosso jogo, nós criamos um novo conjunto de botões com setas, que serão
utilizados pelo usuário para mover os blocos clicando com a caneta na tela do Pocket PC. As rotinas de tratamento de teclado
foram mantidas, de forma a permitir que usuários que prefiram usar um teclado conectado ao Pocket PC possam usar também
esta interface com o jogo.

A Figura 6 apresenta o jogo, rodando em um Pocket PC.


Figura 6: Com pequenas alterações, aqui está .Nettrix, versão para Pocket PC

Um dos pontos mais interessantes especificamente a respeito da migração deste jogo é que, uma vez que ele foi migrado para
o Pocket PC, foi possível copiar o código de volta para a versão de desktop, e ele compilou e executou sem erros. Depois das
atualizações, o código passou a ser 100% compatível entre as duas plataformas, uma vez que a GDI+ do Pocket PC é
simplesmente um subconjunto da GDI+ para desktops, e que todas as outras funções utilizadas estão presentes em ambas as
plataformas.

Escreva para um dispositivo, use em vários, mais uma maravilha do mundo .Net!

Teste você mesmo o programa:

 .Nettrix versão desktop


 .Nettrix versão Pocket PC
Aplicações Móveis no .NET

Por Renato Haddad, Microsoft Most Valuable Professional. Autor de diversos livros e ministra palestras e treinamentos sobre a
tecnologia .NET.

As aplicações desenvolvidas para telefones celulares ganharam um espaço grande no mundo sem fio, como bancos, mercado
financeiro, notícias, índices, área médica, entre outras. No entanto, antes do Mobile .NET você desenvolvia basicamente
utilizando WML (Wireless Markup Language) e caso precisasse de páginas dinâmicas, incrementava o ASP (Active Server
Pages), entre outras tecnologias. Com o Visual Studio .NET é possível criar uma aplicação para executar em telefones celulares
ou diretamente em um navegador. Para isso, você precisa instalar o Mobile internet Toolkit 1.0 (veja em downloads
www.microsoft.com/mobile), que é um SDK para desenvolvimento.

Para simular uma aplicação dentro do Visual Studio .NET você pode instalar o Microsoft Mobile Emulator 3.0 ou qualquer outro
dispositivo que suporte o Framework. Outros simuladores podem ser conseguidos nos fabricantes como Nokia e Ericson, fato
que cresce a cada dia o número de dispositivos criados para essa finalidade.

Neste artigo vamos criar uma simples aplicação com alguns formulários e controles para consultas de Saldos, Extratos e
Contas diretamente no telefone celular. A facilidade que o ambiente RAD (Rapid Application Development) proporciona dentro
do Visual Studio .NET, permite um desenvolvimento sem traumas, usando todos os recursos das classes que o Framework
dispõe. Quando você instala o SDK irá perceber que a Toolbox de controles chamada Mobile Web Forms contém uma
quantidade de componentes que uma aplicação móvel suporta, e a IDéia é exibir os mais utilizados.

Uma aplicação móvel é considerada uma aplicação ASP.NET porque será armazenada em um servidor de aplicações na WEB
rodando sob o internet information Server. Crie um novo projeto chamado VSPJ do tipo Mobile Web Application. No Solution
Explorer, adicione um novo item chamado BancoVirtual.aspx do tipo Mobile Web Form. No formulário atual, altere o ID para
frmMain e o Title para Banco Virtual .NET e adicione os seguintes controles: três TextBox nomeados Agencia, Conta e Senha
respectivamente; para o controle Senha altere a propriedade Password para True; Adicione um Command (botão de comando)
com o Text = Entrar; Adicione um Label chamado ResultadoSenha. Esse formulário fará a autenticação do usuário da conta, se
for inválido o menu de opções não será exibido. Vale dizer que todos os controles derivam da classe
System.Web.Ui.MobileControls.MobilePage.
Figura 1 - Formulário de Autenticação.

Veja a estrutura das tabelas e o relacionamento existente.

Figura 2 - Banco de dados.

Como o banco de dados é o SQL Server, então vá até a janela de código (F7) e insira na primeira linha a
referência para a classe para acessar o ADO.NET.

Imports System.Data.SqlClient

Como a conexão será usada em diversas partes do código, insira a seguinte linha antes do evento Page_Load para
que fique disponível em qualquer parte do aplicativo.

Dim conn As New SqlConnection("initial Catalog=Bancos;Data Source=wind;User ID=sa")

Digite o código no botão de comando Entrar que identifica o correntista. isso pode ser feito dando um duplo
clique no respectivo botão na janela de Design.

Private Sub Command1_Click(ByVal sender As


System.Object, ByVal e As System.EventArgs)
Handles Command1.Click
Dim agencia As integer = Me.Agencia.Text
Dim conta As integer = Me.Conta.Text
Dim senha As String = Me.Senha.Text
Dim sql As String
sql = "SELECT IDConta, email FROM
Correntistas WHERE Agencia="
_& agencia & " AND conta=" & conta &
" AND senha='" & senha & "'"
Dim cmd As New SqlCommand(sql, conn)
Dim dr As SqlDataReader
Try
conn.Open()
dr = cmd.ExecuteReader()
if Not dr.Read() Then
ResultadoSenha.Text = "Dados
incorretos !!!"
dr.Close()
Else
Session("IDConta") =
dr("IDConta")
Session("email") = dr("email")
dr.Close()
ActiveForm = frmMenu
End if
Finally
conn.Close()
End Try
End Sub

Se o correntista for válido, é preciso exibir o formulário com as opções de menu. Para isso, adicione um objeto Form
(formulário) com o ID = frmMenu contendo um componente List. Altere as seguintes propriedades do List: Decoration =
Numbered; para adicionar os itens selecione a propriedade items, clique no construtor (...) e digite as opções (item Text) com
os respectivos valores (Value): Saldo Atual - 1; Extrato Conta - 2; Pagar Contas - 3. Desta forma você já tem as opções de
menu.

Figura 3 - Formulário com as opções do menu.

Para cada opção existirá um formulário específico para tal finalidade com controles distintos, a fim de explorar os
recursos da aplicação. insira mais três formulários com os seguintes controles e propriedades:

Formulário de Saldo Bancário


ID: frmSaldo
Label: ID = Saldo
Command: Text = voltar

Formulário de Extrato Conta Corrente


ID: frmExtrato
TextView: ID = extrato
Command: Text = Enviar e-mail
Command: Text = voltar
Figura 4 - Formulários de Saldo e Extrato.

Formulário de Contas
ID: frmContas
List: SelectType = CheckBox
Command: Text = Selecionar
TextView: ID = contas
Command: Text = Efetuar Pagto
Label: ID = ResultadoContas
Command: Text = voltar
Figura 5 - Formulários de Contas.

Salve o projeto, compile e execute no navegador ou no simulador. Caso você tenha instalado o MME 3.0, selecione o menu
View/Mobile Explorer Browser / Show Browser. O endereço da URL é http://localhost/VSPJ/BancoVirtual.aspx. Digite os dados
e clique no botão Entrar.

Figura 6 - Execução do aplicativo para um usuário válido.

Para ativar os formulários é preciso identificar qual opção o usuário selecionou e preencher os respectivos dados,
portanto no frmMenu dê um duplo clique no List e digite o seguinte código de acordo com a opção:

Private Sub List1_itemCommand


(ByVal sender As System.Object, ByVal
e As
System.Web.Ui.MobileControls.ListCommandEven
tArgs)
Handles List1.itemCommand
Select Case Cint(e.Listitem.Value)
Case 1
ActiveForm = frmSaldo
Saldo.Text = "R$ " &
FormatNumber(VerificaSaldo(), 2)

End Sub

Se a opção selecionada for Saldo, é invocada uma rotina que ativa o formulário frmSaldo e preenche o controle
Saldo com o conteúdo da função VerificaSaldo formatado com duas casas decimais. Digite a função que lê o
valor do saldo na tabela. Esta função pesquisa o campo Saldo na tabela Correntistas de acordo com a
Identificação da conta contida na Session("IDConta"), que é a variável de sessão criada quando da autenticação
no aplicativo. Em seguida são definidos o Command, o DataReader, a abertura da conexão e a execução do
DataReader.

Function VerificaSaldo() As Double


Dim sql As String = "SELECT Saldo FROM
Correntistas WHERE IDConta=" &
Session("IDConta")
Dim cmd As New SqlCommand(sql, conn)
Dim dr As SqlDataReader
Try
conn.Open()
dr = cmd.ExecuteReader()
if dr.Read Then
VerificaSaldo = dr("Saldo")
End if
Finally
dr.Close()
conn.Close()
End Try
End Function

Figura 7 - Pesquisa o saldo na conta.

Continuando o código do List1_itemCommand, veja como fica a segunda opção para consulta de extrato bancário
que exibe o formulário frmExtrato.

Case 2
ActiveForm = frmExtrato
VerificaExtrato()

Note que é preciso digitar a rotina VerificaExtrato que exibe a movimentação dos últimos 5 dias. Essa rotina pode ser digitada
após o End Sub do List1_itemCommand.

Sub VerificaExtrato()
Dim saldoAtual As Double =
VerificaSaldo()
Dim sql As String = "SELECT Data, Tipo,
Valor FROM Movimentacao WHERE IDConta="
_& Session("IDConta") & " AND Data >=
getDate()-5"
Dim cmd As New SqlCommand(sql, conn)
Dim dr As SqlDataReader
Try
conn.Open()
dr = cmd.ExecuteReader()
With extrato
.Text = "Saldo: " &
FormatNumber(saldoAtual, 2) & "<br>"
Do While dr.Read()
.Text += Day(dr("Data")) &
"/" & Month(dr("Data")) & " "
if dr("Tipo") = "c" Then
.Text += "+ "
saldoAtual +=
dr("Valor")
Else
.Text += "- "
saldoAtual -=
dr("Valor")
End if
.Text +=
FormatNumber(dr("Valor"), 2) & "<br>"
Loop
.Text += "<br>Saldo Atual: " &
FormatNumber(saldoAtual, 2)
End With
Finally
dr.Close()
conn.Close()
End Try
End Sub
Figura 8 - Extrato da conta no celular

Caso você queira enviar o extrato para a sua conta de e-mail cadastrada no banco, clique no botão e-mail. A
classe utilizada é a imports System.Web.Mail que deve ser digitada no início das linhas dos códigos. Veja o
código para enviar o e-mail no formato HTML cujo corpo do e-mail estará armazenado na variável msg.

Private Sub Command2_Click


(ByVal sender As System.Object, ByVal
e As System.EventArgs) Handles
Command2.Click
Dim email As New MailMessage()
Dim saldoAtual As Double =
VerificaSaldo()
Dim sql As String = "SELECT Data, Tipo,
Valor FROM Movimentacao WHERE IDConta="
_& Session("IDConta") & " AND Data >=
getDate()-2"
Dim cmd As New SqlCommand(sql, conn)
Dim dr As SqlDataReader
Dim msg As String
Try
conn.Open()
dr = cmd.ExecuteReader()
msg = "<HTML><BODY>Confira o
extrato<br>"
msg += "<font color=blue
size=7><b>Saldo: "
_& FormatNumber(saldoAtual,
2) & "</b></font><br>"
msg += "<font color=black size=4>"
Do While dr.Read()
msg += Day(dr("Data")) & "/" &
Month(dr("Data")) & " "
if dr("Tipo") = "c" Then
msg += "+ "
saldoAtual += dr("Valor")
Else
msg += "- "
saldoAtual -= dr("Valor")
End if
msg += FormatNumber(dr("Valor"),
2) & "<br>"
Loop
msg += "</font><br>Saldo Atual: " &
FormatNumber(saldoAtual, 2)
msg += "</BODY></HTML>"
Finally
dr.Close()
conn.Close()
End Try
With email
.From = "banco@banco.com.br"
.To = Session("email")
.Subject = "Extrato bancário"
.Body = msg
.BodyFormat = MailFormat.Html
End With
SmtpMail.Send(email)
End Sub

Para explorarmos os recursos de alguns controles, vamos inserir um conjunto de contas que você pode autorizar o débito
automático pelo telefone celular. No entanto, não iremos desenvolver a rotina de débito, e sim, mostrar como manipular o
controle. Como isso refere-se a terceira opção do menu, veja o restante do código referente ao List1_itemCommand .

Case 3
ActiveForm = frmContas
Dim arrayContas As New
ArrayList()
With arrayContas
.Add("Telefone")
.Add("Celular")
.Add("Mercado")
.Add("Aluguel")
.Add("Carro")
.Add("Luz")
.Add("Agua")
.Add("Academia")
End With
Me.lstContas.DataSource =
arrayContas
Me.lstContas.DataBind()
lstContas.Visible = True
Command8.Visible = False
Command9.Visible = True
contas.Text = ""
End Select
End Sub

Note que o formulário frmContas é ativado, é montado um array com as opções de contas e o controle lstContas é carregado
com o array. É interessante notar que a propriedade DataSource define a origem dos dados e o método DataBind preenche o
controle, dispensando assim, ter que montar um looping e adicionar cada linha do array.
Private Sub Command9_Click
(ByVal sender As System.Object, ByVal
e As System.EventArgs) Handles Command9.Click
Dim objitem As
MobileControls.MobileListitem
contas.Text = ""
lstContas.Visible = False
For Each objitem in Me.lstContas.items
if objitem.Selected Then
contas.Text &= objitem.Text & " -
"
End if
Next
Command9.Visible = False
Command8.Visible = True
End Sub

que poderia conter uma rotina para realmente


Para autorizar o pagamento crie o código para o botão Efetuar Pagto
efetivar o débito na conta corrente. Aqui só estamos exibindo um texto para verificação.

Private Sub Command8_Click


(ByVal sender As System.Object, ByVal
e As System.EventArgs) Handles Command8.Click
ResultadoContas.Text = "Suas contas serão
debitadas automaticamente na sua conta"
End Sub

Este artigo serviu para você explorar alguns controles disponíveis para celulares, acessar um banco de dados via ADO.NET, e
verificar o funcionamento da aplicação.

Desenvolver aplicativos para telefones celulares deixou de ser um mercado fechado a poucos desenvolvedores. O Mobile
internet Toolkit nos proporciona através do Visual Studio .NET integrar as aplicações existentes que rodam na internet ou
Windows Application com o telefone celular, aumentando a abrangência da sua aplicação.

Bem vindos ao mundo móvel. "No stress, think .NET".

Referências:

1. http://www.asp.net/mobile
2. http://www.gotdotnet.com
3. www.microsoft.com/mobile

4. www.mas.com.br/dicas
Aplicações móveis para telefones celulares com ASP.NET

O uso de aplicações móveis nos sistemas vem se tornando cada vez mais comum. A aplicabilidade se dá no tocante à facilidade
de comunicação em qualquer lugar e a qualquer momento, permitindo integrar diversos aplicativos e plataformas.

Quem desenvolve esse tipo de aplicação usando por exemplo, WML (Wireless Markup Language) com ASP (Active Server
Pages) lendo banco de dados, o Microsoft inovou completamente a forma de desenvolver e integrar os dados. O universo de
linguagens utilizadas nesse tipo de desenvolvimento se resume a apenas uma, ou seja, aquela que você domina e tem suporte
para a plataforma .NET. Estamos falando de VB.NET ou C#. A Microsoft dispõe de um SDK Mobile para ser utilizado dentro do
Visual Studio .NET onde a forma de apresentação e produtividade une todos os recursos que você já utiliza nas aplicações WEB
(Web Forms), por exemplo.

Com isso, basta você saber trabalhar com o ambiente do VS.NET onde as ferramentas disponíveis na ToolBox Mobile Forms
contém todos os componentes para desenvolver a aplicação.

Porquê criar uma aplicação móvel? Qual a sua utilidade?

Se você pensar em um universo de aplicações que precisam dispor de dados o tempo todo em qualquer lugar, isso já se
justifica. Alguns exemplos, coletar os pedidos de compra em campo e laboratórios farmacêuticos em geral; uso de aplicações
na área hospitalar onde o médico pode verificar e aprovar um determinado exame em tempo real.
Com a plataforma .NET os próprios servidores com o Mobile Information Server (MIS) e .NET Alerts estão integrados com os
demais servidores. Isso significa dizer que você pode ter aplicações desenvolvidas em ASP.NET conversando perfeitamente
com o MIS, como por exemplo, emitir um Short Message para um celular de um vendedor através da uma aplicação WEB.

Crie um projeto no VS.NET do tipo ASP.NET Mobile Application chamado PalestrasMovel.aspx.

Figura do VS.Net

Abra a toolbox e adicione 4 formulários no mesmo documento. Isso quer dizer que quando os dados forem para o celular que o
solicitou a página, todos os formulários também são carregados, motivo esse se dá pela velocidade de conexão e solicitações
feitas no servidor.

Para cada formulário adicione os respectivos controles conforme a seguinte tabela:

Nome Form FormMenu

Link1: Palestras NavigateURL: #Form1

Link2: Inscrever NavigateURL: #FormInscricao

Nome Form: Form1

1 Label

1 List

Nome Form: Form2

1 Label

1 List

Nome Form: FormInscricao

2 TextBox (Nome e e-mail)

1 Buttom

1 Label

Para navegar através dos formulários, clique nos respectivos links (palestras/inscrever) onde serão invocados os
formulários declarados na propriedade NavigateURL dos controles. Em Palestras é preciso inserir o seguinte
código no evento Page_Load da página que monta um Array contendo os nomes das palestras a serem exibidas.
Note que o objeto List1 necessita informar a fonte de dados, que nesse caso é o ArrayPalestras e o DataBind para
preencher.

Private Sub Page_Load(ByVal sender As


System.Object, ByVal e As System.EventArgs)
Handles MyBase.Load
If Not IsPostBack Then
Dim ArrayPalestras As New
ArrayList()
With ArrayPalestras
.Add("ASP.NET")
.Add("Web Services")
.Add("Componentes")
.Add("Visual Studio.NET")
.Add("ADO.NET")
End With
List1.DataSource = ArrayPalestras
List1.DataBind()
Label2.Text = "Palestras .NET"
End If
End Sub

Quando o usuário selecionar uma determinada palestras, será ativado (exibido) o formulário Form2, que contém
um Label um List. Sendo assim, para carregar o List com os nomes das palestras dê um duplo clique no List e
digite o seguinte código. Note que dependendo da palestra selecionada (Select Case e.ListItem.Text) é montado
um array chamado ArrayTopicos que será a fonte de dados do List2.

Private Sub List1_ItemCommand


(ByVal sender As System.Object, ByVal e As
System.Web.UI.MobileControls.ListCommandEven
tArgs)
Handles List1.ItemCommand
ActiveForm = Form2
Dim ArrayTopicos As New ArrayList()
Select Case e.ListItem.Text
Case "ASP.NET"
With ArrayTopicos
.Add("Novas
funcionalidades")
.Add("Linguagens
utilizadas")
.Add("Ferramentas de
Desenvolvimento")
.Add("Plataforma de
instalação")
End With
Case "Web Services"
With ArrayTopicos
.Add("O que é")
.Add("Como criar e
consumir")
.Add("Uso no mercado")
.Add("Vantagens e
Desvantagens")
End With
Case "Componentes"
With ArrayTopicos
.Add("Definição dos
componentes .NET")
.Add("Instalação dos
componentes")
.Add("Ambiente distribuído")
End With
Case "Visual Studio.NET"
With ArrayTopicos
.Add("Produtividade")
.Add("Facilidade de uso")
.Add("Universo de
linguagens")
.Add("Uso de Web Services")
End With
Case "ADO.NET"
With ArrayTopicos
.Add("Novas classes")
.Add("Suporte a XML")
.Add("Dados desconectados")
End With
End Select
Label1.Text = e.ListItem.Text
With Me.List2
.Items.Clear()
.DataSource = ArrayTopicos
.DataBind()
End With
End Sub

Para você fazer a inscrição no evento, clique no link Increver na página principal, preencha os dados e clique no
botão Enviar. Antes de mais nada, para você chegar até este formulário, foi preciso definir a propriedade
NavigateURL como FormInscricao. Dê um duplo clique no botão Enviar e digite o respectivo código. Cabe
ressaltar que como será enviado um e-mail é preciso referenciar a classe de e-mail na primeira linha da tela de
códigos. Nada o impede de desenvolver uma rotina para cadastrar o respectivo usuário no banco de dados.

Imports System.Web.Mail

Private Sub Command1_Click(ByVal sender As System.Object, ByVal e As


System.EventArgs) Handles Command1.Click
'você pode inserir rotina de Insert no Database
Dim msg As String
Dim email As New MailMessage()
msg = "Caro (a) " & txtNome.Text.Trim.ToUpper() & vbNewLine
msg += "Sua Inscrição no evento PDC Brasil 2002 foi efetuada com sucesso."
email.From = "pdc@microsoft.com.br"
email.To = txtEmail.Text.Trim
email.Subject = "Inscrição no PDC Brasil 2002"
email.Body = msg
SmtpMail.Send(email)
Label3.Text = "Inscrição efetuada, em breve você receberá um e-mail."
End Sub

Veja a explicação de parte do código:

Define a variável msg que contém a mensagem a ser enviada no corpo do e-mail.
Dim msg As String

Define o objeto email que contém a classe MailMessage.


Dim email As New MailMessage()

Monta o texto da variável msg.


msg = "Caro (a) " & txtNome.Text.Trim.ToUpper() & vbNewLine

Define as propriedades do e-mail.


email.From = "pdc@microsoft.com.br"
email.To = txtEmail.Text.Trim
email.Subject = "Inscrição no PDC Brasil 2002"
email.Body = msg

Envia o e-mail através do método Send.


SmtpMail.Send(email)

Exibe um texto para o usuário.


Label3.Text = "Inscrição efetuada, em breve você receberá um e-mail."

Salve o projeto, compile e execute-o no navegador. Para exibir o simulador do telefone celular selecione o menu View / Mobile
Explorer Browser / Show Browser. Acompanhe a seqüência dos acontecimentos quando você seleciona uma palestra.

Caso você selecione a opção Inscrever, será preciso informar o nome e o e-mail.

Pronto, a partir de agora você já sabe como montar um aplicativo móvel para interagir com a sua aplicação ou com o usuário.

Renato Haddad
rehaddad@msn.com
Microsoft Most Valuable Professional
Autor dos livros: "C# - Aplicações e Soluções", "VB.NET - Conceitos e Aplicações" e "ASP.NET para desenvolvedores ASP" -
Editora Érica.
Veja mais artigos em (www.mas.com.br/dicas)

Reviewer's Guide do Microsoft Mobile Internet Toolkit

Durante os últimos anos, o mundo vem assistindo a uma incrível explosão de novos dispositivos: telefones celulares,
pagers, PDAs (Personal Digital Assistants), entre tantos outros, permitem que seus usuários naveguem por sites da Web
a qualquer hora e em qualquer lugar. O desenvolvimento de aplicações para tais dispositivos é o grande desafio com o
qual se deparam os desenvolvedores da atualidade. Veja porque:
 São necessárias diferentes linguagens de markup, incluindo HTML para PDAs, linguagem markup para
dispositivos portáteis (WML - Wireless Markup Language) voltado a telefones celulares WAP (Wirelesss
Application Protocol). e HTML compacto (cHTML) para telefones I-Mode japoneses.
 Um dispositivo é diferente do outro em sua concepção. Por exemplo, os dispositivos têm uma quantidade
variada de linhas de tela, diferente orientação do monitor -- horizontal ou vertical -- e monitores coloridos ou em
branco-e-preto.
 Os dispositivos possuem diferentes formas de conectividade de rede, variando de conexões de 9.6 KB dos
celulares a 11 MB de LANs sem-fio.
 Os dispositivos possuem diferentes capacidades. Alguns dispositivos podem apresentar imagens, outros podem
efetuar uma chamada telefônica e outros ainda podem receber mensagens escritas.

O Microsoft Mobile Internet Toolkit atende a todos esses desafios colocados aos desenvolvedores, simplesmente
afastando-os de detalhes desnecessários existentes na tecnologia de equipamentos sem-fio. Desta forma, os
desenvolvedores poderão, com incrível rapidez e facilidade, construir aplicações móveis para a Web com markup
suficiente para uma grande gama de dispositivos portáteis.

O Mobile Internet Toolkit contém:


 Controles de Web Forms Móveis (Mobile Web Forms Controls) que geram linguagens markup para
diferentes dispositivos.
 O Mobile Internet Designer, que trabalha com o novo ambiente de design integrado (IDE) do Visual
Studio .NET, mantendo os desenvolvedores em um ambiente de desenvolvimento baseado no princípio de
arrastar-e-soltar.
 Capacidades de Browser suficientemente poderosas para ampliar o potencial dos dispositivos ASP .NET aos
dispositivos móveis.
 Tutorial QuickStart com amostras de código.
 Ampla Documentação para o desenvolvedor
 Amostras de Código para adaptação a dispositivos.

O Mobile Internet Toolkit permite a geração de markup para uma grande diversidade de dispositivos, incluindo telefones
celulares WAP, telefones celulares compactos dos modelos cHTML, Pocket PCs, dispositivos Palm e pagers do tipo RIM
Blackberry.

Controles de Web Forms Móveis

Os controles de Web Forms móveis são controles ASP .NET que se situam do lado do servidor, oferecendo aos seus
usuários os necessários elementos de interface como lista, comando, chamada, calendário etc. No momento da execução
de um comando, os controles móveis geram o registro adequado para o dispositivo, que, pode assim marcar ou registrar
o pedido. Como resultado, o desenvolvedor poderá escrever uma aplicação para um dispositivo móvel apenas uma vez,
aproveitando o código, posteriormente, em múltiplos dispositivos.

Pelo fato de estes controles móveis estarem fundamentados nos controles ASP .NET, o desenvolvedor poderá aproveitar
todo o conhecimento adquirido com o desenvolvimento de aplicativos para desktop na criação de aplicações móveis. Ele
poderá também aproveitar a mesma lógica de aplicativos de negócios e de acesso a dados empregada na aplicação de
desktop. Na prática, os Web Forms para dispositivos portáteis e para o desktop podem conviver dentro de um mesmo
projeto do Visual Studio .NET. Com isso, o desenvolvimento de aplicações torna-se mais rápido, reduzindo os custos de
manutenção.

Os exemplos abaixo são apenas uma pequena amostra do que representa programar com controles móveis. No exemplo
a seguir, o programa "Hello, World" cria uma página de Web Forms para dispositivos móveis com um único formulário:
este formulário contém um controle de Label com um string que diz: "Hello, Mobile World".
< %@ Page language="c#"
Inherits="System.Web.UI.MobileControls.MobilePage" %>

< %@ Register TagPrefix="Mobile"


Namespace="System.Web.UI.MobileControls"
Assembly="System.Web.Mobile"%>

< mobile:Form id=Form1 runat="server" >


< mobile:Label id=Test Runat="Server" >
Hello, Mobile World
< /mobile:Label >

< /mobile:Form >

Na ilustração abaixo, pode-se notar como este código se manifesta em diferentes dispositivos. O primeiro dispositivo é
um telefone celular rodando em um browser WAP que suporta WML. O segundo dispositivo é um Pocket PC rodando em
um browser HTML.
Figura 1. O programa "Hello, World" executado tanto em um PocketPC como em um telefone celular.

O Mobile Internet Toolkit também possibilita ao desenvolvedor personalizar o markup gerado por controles móveis em
um dispositivo específico. O desenvolvedor pode designar modelos e estilos para um dispositivo específico dentro da
página de dispositivos móveis.

Mobile Internet Designer

O Mobile Internet Designer amplia o potencial do Visual Studio .NET IDE na criação de aplicações para dispositivos
móveis. Após instalar o Mobile Internet Designer, o desenvolvedor poderá criar e desenvolver sua aplicação móvel da
mesma forma que ele desenvolveria uma aplicação em Windows Forms ou em Web Forms. O Mobile Internet Designer
aproveita o tradicional ambiente de design do Visual Studio permitindo que os desenvolvedores:
 Criem um projeto móvel para a Web.
 Adicionem uma página de Web Forms móvel ao seu projeto.
 Arrastem um controle de Web Forms para seu formulário.
 Dêem duplo clique em um controle para escrever sua lógica.
 Reconstruam uma aplicação.
 Rodem a aplicação.

O Mobile Internet Designer torna muito fácil e rápida a criação e manutenção de aplicações móveis para a Web. Além
disso, ele possibilita que os atuais desenvolvedores de aplicações para desktop possam aprender a criar aplicações
móveis com o uso do Visual Studio .NET, de forma muito rápida.

A ilustração abaixo mostra uma aplicação móvel desenvolvida em Visual Studio .NET a partir do Mobile Internet
Designer.

Mecanismo de Capacidade do Dispositivo

Para que a apresentação dos controles móveis seja bem sucedida, é essencial que se tenha informações precisas sobre a
capacidade da tela do dispositivo para o qual está sendo feita a aplicação. Como condição mínima, os controles móveis
devem utilizar as seguintes informações sobre os dispositivos:
 Tipo de linguagem markup (HTML, WML, cHTML)
 Tipo de Browser
 Número de linhas da tela ou do display
 Suporte a Cookies
 Tamanho da tela

O Mobile Internet Toolkit amplia o esquema do arquivo machine.config -- utilizado em aplicações de desktop ASP .NET
para rastrear informações sobre o dispositivo e o browser -- para incluir outras características do dispositivo móvel. Além
de ampliar o esquema, o Mobile Internet Toolkit contém adaptadores de dispositivo para uma grande variedade de
dispositivos móveis. A informação sobre as capacidades, contidas neste arquivo, permite que os adaptadores de
dispositivos otimizem o markup para determinados dispositivos. Os adaptadores de dispositivos possibilitam, ainda, que
o mecanismo de capacidade do dispositivo seja ampliado por terceiros.

Recursos Avançados: Expansibilidade

O Mobile Internet Toolkit possibilita ao desenvolvedor criar novos controles agregados, a partir de controles móveis
existentes; possibilitando também o suporte a adaptadores de dispositivos para outros equipamentos através do
mecanismo de capacidade do dispositivo.

Conclusão

O Mobile Internet Toolkit oferece toda a tecnologia e as ferramentas necessárias para a construção, implementação e
manutenção de sofisticadas aplicações móveis, de forma muito rápida. A estrita integração com o Visual Studio .NET
garante que os desenvolvedores aproveitem todo conhecimento adquirido com a criação de aplicações para o desktop na
produção de aplicações para dispositivos móveis. Com o uso dos recursos de expansibilidade do Mobile Internet Toolkit,
pode-se acrescentar suporte adicional a dispositivos móveis

Windows Mobile - O início de uma nova era


Jim Wilson
Junho de 2004
Aplica-se a:
# Software Microsoft® Windows Mobile™
# Microsoft .NET Compact Framework
# Microsoft Visual Studio® .NET 2003
# Microsoft SQL Server™ 2000
# Microsoft Windows® CE
# Microsoft ActiveSync®
# Microsoft eMbedded Visual C++®
# Microsoft Visual Basic® .NET
Resumo: Nesta primeira edição de "Você pode levá-lo com você", Jim Wilson analisa os novos recursos de mobilidade
do Whidbey e como eles afetam o futuro do Windows Mobile. (7 páginas impressas)
Nesta página

Introdução
Estou escrevendo esta primeira edição de "Você pode levá-lo com você" em uma manhã de domingo em minha casa em
Exeter, no estado de New Hampshire, nos Estados Unidos. Falta mais de uma semana para a Microsoft® Mobile DevCon
(MDC) 2004 e estou ansioso por ela, como apresentador e como participante. Apesar de ainda ter muito trabalho a fazer
antes da MDC, mal posso esperar pelo momento. A MDC deste ano marca o início de uma nova era; uma era em que o
desenvolvimento móvel dá o próximo grande passo para ser usado amplamente pelas empresas.
No momento em que a maioria de vocês ler isso, a MDC já terá passado e você terá tido a chance de ver as sessões ou
baixar as apresentações em Microsoft PowerPoint®. Você pode até já ter instalado o Whidbey Beta contendo a primeira
visão da próxima geração de desenvolvimento de software Microsoft Windows Mobile™. Se você teve a oportunidade de
fazer qualquer uma dessas coisas, acho que vai concordar que os desenvolvedores móveis nunca tiveram acesso ao tipo
de ferramentas oferecidas pelo Whidbey. A combinação dessas ferramentas com a experiência cada vez maior que os
desenvolvedores estão obtendo na criação de aplicativos móveis finalmente culminou na era dos aplicativos móveis
amplamente adotados em nível empresarial.
A chegada dessa era é exatamente o que me incentivou a começar esta coluna.
Início da página

Para onde vamos


Como desenvolvedores empresariais, lidamos com grandes volumes de dados, interações complexas com usuários,
personalidades difíceis e processos de negócios desafiadores. Temos o desafio de descobrir como navegar pelos
labirintos de classes disponíveis, ferramentas e técnicas e como aplicá-las para criar soluções móveis que tenham algum
significado e que possam efetivamente lidar com nossos dados, usuários e processos.
Meu objetivo com esta coluna é tratar de uma série de questões que os desenvolvedores empresariais enfrentam no
espaço móvel, do ponto de vista do que chamo de "programação pragmática". Para mim, a programação pragmática
significa entender as classes, as ferramentas e as técnicas de acordo com as soluções que elas podem fornecer. Ela
também significa que, com freqüência, várias classes, ferramentas e técnicas devem ser consideradas em relação umas
às outras e em como podem ser combinadas como parte de uma solução geral. Em resumo, a programação pragmática
refere-se a compreender a tecnologia como um meio para um fim, em vez de um fim por si mesma.
Durante os próximos meses, analisaremos uma ampla gama de questões que os desenvolvedores empresariais
enfrentam ao criar aplicativos de mobilidade. Em alguns meses veremos os detalhes da criação de uma determinada
solução, em outros analisaremos ferramentas úteis e em outros momentos nos aprofundaremos em uma classe ou uma
biblioteca particularmente úteis. Seja qual for o tópico, o objetivo é o mesmo: compartilhar informações significativas
que ajudarão desenvolvedores que estejam criando aplicativos empresariais para a plataforma Windows Mobile.
Início da página

Whidbey e Windows Mobile


Achei um tanto quanto desafiador decidir por onde começar para a primeira edição de uma nova coluna. Então ocorreu-
me que, já que os recursos de mobilidade do Whidbey me motivaram a criar esta coluna, poderíamos começar dando
uma olhada no que penso ser algumas das adições e modificações mais notáveis do Whidbey e que afetam o
desenvolvimento de aplicativos móveis.
Observação Escrito antes do real lançamento do Whidbey Beta com os avanços do Windows Mobile, este artigo se baseia
em informações pré-beta. Nenhum desses recursos está finalizado e eles podem mudar entre este momento, o
lançamento do beta e o lançamento do produto final. Certifique-se de verificar a documentação do produto antes de usar
qualquer um desses recursos.
Estes são alguns dos meus recursos de mobilidade favoritos no Whidbey, não necessariamente nesta ordem. Há muitos
deles para investigarmos com profundidade este mês, mas fique em contato, pois falaremos detalhadamente sobre esses
recursos nos próximos meses.
Início da página

GUI
Confessemos: a interface gráfica do usuário, ou GUI, é onde a maior parte da experiência do usuário se concentra.
Embora a versão atual do Microsoft .NET Compact Framework forneça uma primeira versão razoável da biblioteca
Microsoft Windows® Forms, ela é um tanto quanto limitada. Mas isso está mudando com a chegada do Whidbey.
Suporte para diversas orientações e resoluções de tela
Durante anos, todos os Pocket PCs baseados no Microsoft Windows Mobile tiveram um layout de tela comum com
orientação de retrato e resolução de 240x320. Essa resolução não é mais o caso. Agora a maioria dos dispositivos Pocket
PC da geração mais recente têm diversos recursos de tela, incluindo suporte para modos retrato e paisagem, e alguns
são até mesmo quadrados. Alguns dispositivos oferecem suporte até mesmo para resoluções de 480x640, que é a
mesma resolução do computador desktop original. O Whidbey foi atualizado para fornecer suporte a todos esses tipos de
tela, permitindo que você crie aplicativos capazes de aproveitar totalmente os recursos do dispositivo a que se destinam.
Os aplicativos podem até mesmo responder a alterações das configurações de tela enquanto estão sendo executados.
Aperfeiçoamento do comportamento do controle
A criação de aplicativos que devem funcionar com diferentes resoluções e orientações representa um desafio que não
existia antes. Por sorte, o Whidbey nos oferece uma grande ajuda ao incluir suporte para acoplagem e ancoragem de
controles. A acoplagem permite que um controle se conecte diretamente a uma das bordas do formulário. A ancoragem
permite que a posição de um controle seja definida em relação a uma ou mais bordas. Controles acoplados ou ancorados
mantêm automaticamente suas posições e tamanhos relativos, conforme o exigido pelas mudanças de tamanho do
formulário pai.
É claro, diversos recursos de controle adicionados aos Service Packs 1 e 2 do .NET Compact Framework também estão
incluídos no Whidbey, como suporte a tabulação, eventos de teclado e suporte a ForeColor/BackColor.
Designer de formulários WYSIWYG
Quem nunca trabalhou com aplicativos de dispositivos inteligentes anteriores ao Whidbey pode estar se perguntando
qual é a grande vantagem do WYSIWIG (what you see is what you get). Quem já trabalhou nessa situação conhece o
problema muito bem. Uma verdadeira frustração era que as fontes e os tamanhos dos controles eram diferentes no
Visual Studio® .NET 2003 do que quando exibidos no dispositivo real. Esse problema foi resolvido.
Essa única alteração seria suficiente, mas ainda há mais. Agora o Designer de Formulários oferece suporte aos diversos
modos de tela do Pocket PC, incluindo retrato, paisagem e quadrado. O modo pode ser modificado durante o design,
para que você possa garantir que o aplicativo pareça adequado em dispositivos que executem qualquer um dos modos.
E, como se não bastasse, agora o Designer de Formulários oferece suporte a capas. Com elas, você pode ver
exatamente como será a aparência do formulário em relação ao dispositivo real, como mostra a figura

Figura 1 Whidbey Smart Device Designer com capa.


Início da página

SQL Server CE
O Microsoft SQL Server™ 2000 Windows® CE Edition (SQL Server CE) é nosso armazenamento de dados local ao
criarmos aplicativos .NET Compact Framework. A próxima geração do SQL Server CE, com codinome Laguna, é um
grande passo adiante. Estes são alguns dos aperfeiçoamentos.
Cursor atualizável e rolável
Ao trabalhar com grandes volumes de dados, às vezes eu achava um pouco frustrante trabalhar com o SQL Server CE do
.NET Compact Framework. O problema era que as classes de bancos de dados do .NET Compact Framework permitiam
apenas uma capacidade limitada de um usuário aproveitar a natureza local do SQL Server CE. Lembre-se de que o SQL
Server CE é executado de maneira completamente local no dispositivo e tem suporte total a recursos de rolagem e
atualização, mas esses recursos tinham suporte limitado do .NET Compact Framework. Finalmente esse problema foi
resolvido por meio da adição da classe SqlCeResultSet. Com essa classe, os aplicativos .NET Compact Framework podem
rolar, atualizar e vincular diretamente a dados no SQL Server CE. Não é mais necessário pagar o alto custo da classe
DataSet ao precisar desses recursos. Acho que esse é um dos aperfeiçoamentos mais importantes do Whidbey e logo
falaremos sobre ele em outra edição desta coluna.
Acesso multiusuário
Como o termo diz, agora o SQL Server CE oferece suporte a acesso multiusuário. Há suporte total para que vários
aplicativos acessem e modifiquem bancos de dados do SQL Server CE. Agora o banco de dados oferece suporte a
bloqueio de linha e de página completo com escalação de bloqueio. Apesar de secundário, provavelmente um dos
impactos mais notáveis dessa alteração em vários desenvolvedores é que eles não precisam mais fechar seus bancos de
dados no SQL Server CE Query Analyzer antes de iniciarem o aplicativo que usa o banco de dados.
Acesso e administração de desktop
A Microsoft adicionou suporte de computador desktop ao SQL Server CE. Você pode exibir, administrar e interagir com
bancos de dados SQL Server CE a partir da próxima geração do SQL Server Enterprise Manager.
Acredite ou não, você também pode escrever aplicativos de computadores desktop que possam ler e criar bancos de
dados do SQL Server CE. Agora, não fique preocupado que a Microsoft tenha algum plano bizarro de tornar o SQL Server
CE um banco de dados desktop. Nada poderia ser menos verdade. Esse recurso é fornecido simplesmente para testes e
produção de bancos de dados. Há várias situações em que algum tipo de aplicativo de computador desktop ou de
servidor precisam criar um banco de dados SQL Server CE para que um aplicativo de dispositivo inteligente possa
acessar os dados do aplicativo. Essas interfaces de programação de aplicativos (APIs) estão aqui para resolverem esse
caso específico.
Início da página

Experiência do desenvolvedor
O Visual Studio .NET 2003 e o .NET Compact Framework melhoraram tremendamente a experiência do desenvolvedor de
dispositivos. Eles são muito mais estáveis e certamente muito mais fáceis de serem usados do que muitas outras
ferramentas de desenvolvedores de dispositivos. O desenvolvimento de dispositivos ainda está longe de ser tão fácil
como a criação de aplicativos desktop, mas o Whidbey dá um grande salto nessa direção.
Depuração aperfeiçoada
A depuração de dispositivos sempre foi lenta. Parte dessa lentidão deve-se à velocidade de conexão relativamente baixa
(serial ou USB na maioria dos casos) entre o dispositivo móvel e o computador desktop, mas esse não é o único motivo.
Outro grande fator na lentidão é que a maior parte da funcionalidade de depuração ocorre no dispositivo, que tem
apenas uma fração da memória e da CPU disponível no computador desktop. Então o que pode ser feito?
Que tal mover mais da funcionalidade de depuração para o computador desktop? O depurador de dispositivos do
Whidbey foi completamente redesenhado. Ele foi reprojetado para que o trabalho que precise absolutamente ser
executado no dispositivo seja executado nele. Agora, a maior parte do comportamento de depuração ocorre no
computador desktop, resultando em um depurador nitidamente mais rápido e mais dedicado.
Melhor emulador
Há tantos aperfeiçoamentos no emulador que realmente não posso listar todos aqui. Vou me concentrar no que penso
serem os melhores aperfeiçoamentos.
Agora o emulador aceita ser conectado como convidado do Microsoft ActiveSync®, o que o torna mais parecido como um
dispositivo real.
O emulador oferece suporte ao armazenamento de várias imagens de emulador. Esse armazenamento permite que você
defina várias configurações de emulador (configurações, programas instalados, etc.) e as restaure conforme o
necessário, simplificando o teste e a recuperação de erros. Ao usarmos o emulador atual do Visual Studio .NET 2003,
podemos ter somente uma imagem salva e se o emulador precisar ser redefinido por algum motivo, essa imagem
precisará ser reconfigurada totalmente.
O emulador permite que uma pasta do computador desktop seja configurada como cartão de armazenamento. Isso
facilita o compartilhamento de dados entre o computador desktop e o emulador. Além disso, é mais fácil ter dados
comuns entre diversas imagens do emulador.
O novo emulador também permite alternar entre as diversas resoluções e orientações de tela (retrato e paisagem).
Projetos C++
Às vezes a criação de aplicativos destinados a dispositivos móveis exige que usemos C++ além do .NET Compact
Framework. Até o momento, esse requisito significava que precisávamos usar dois ambientes de desenvolvimento
integrados (IDEs) separados: o Visual Studio .NET 2003 para o código .NET Compact Framework e o eMbedded Visual
C++® IDE para o código C++. O Whidbey resolve esse problema ao adicionar suporte para projetos C++ para
dispositivos móveis.
Agora como Whidbey podemos criar, editar e depurar projetos C++ destinados a dispositivos móveis dentro do mesmo
ambiente Visual Studio que usamos para nossos projetos .NET Compact Framework. Não precisamos mais de um
eMbedded Visual C++ IDE separado e agora podemos fazer todo o nosso desenvolvimento para dispositivo móvel, seja a
parte gerenciada (.NET Compact Framework) ou a parte não gerenciada (C++), em um único IDE.
Interoperabilidade Win32 e COM aperfeiçoada
O .NET Compact Framework aperfeiçoou substancialmente a interoperabilidade Microsoft Win32® e COM. Diversos
problemas comuns de empacotamento, como passar seqüências de caracteres incorporadas ou classes com tipos de
referências incorporados, foram resolvidos. Além disso, agora há suporte para interoperabilidade com o COM. O .NET
Compact Framework pode chamar objetos COM e, com algum trabalho, os objetos COM podem chamar objetos .NET
Compact Framework. O runtime .NET Compact Framework agora pode ser hospedado de forma muito parecida com
o .NET Framework completo. Há vários detalhes nessa questão que abordaremos em edições futuras desta coluna.
IntelliSense aperfeiçoado
Devido à necessidade de manter um modelo de objetos consistente com o computador desktop e de levar em conta
certas realidades dos recursos da plataforma, há alguns casos em que as classes .NET Compact Framework expõem
propriedades e métodos herdados cuja chamada é ilegal. Esses casos são freqüentemente chamados de "herdados mas
não suportados". Infelizmente, no Visual Studio .NET 2003, esses métodos ilegais ainda são exibidos no recurso de
tecnologia Microsoft IntelliSense® e freqüentemente resultam em aplicativos que recebem erros de tempo de execução.
O Whidbey corrige esses erros removendo esses métodos e propriedades herdados mas não suportados da
funcionalidade IntelliSense. Os métodos e propriedades herdados mas não suportados estão em assemblys .NET
Compact Framework, portanto ainda é possível chamá-los (e receber erros), mas pelo menos o IntelliSense não os
mostrará.
Início da página

Adições importantes
Já vimos vários recursos excelentes, mas ainda há muito mais. Com o tempo, avaliaremos diversos recursos novos, mas
aqui há algumas de minhas adições preferidas.
Suporte a Smartphone
O Smartphone é considerado um membro da primeira classe do Whidbey, como suporte completo para a criação de
aplicativos Smartphone baseados no Microsoft Windows Mobile (atualmente limitados a recursos do .NET Compact
Framework versão 1). O Designer de Formulários e o IntelliSense se adaptam automaticamente para funcionarem
adequadamente para o Smartphone, expondo somente os controles, as classes e os métodos que têm suporte.
Suporte para novos recursos de idiomas do Whidbey
O .NET Compact Framework não está apenas fornecendo seus próprios aperfeiçoamentos, mas também inclui suporte
aos recursos avançados de idiomas que estão sendo lançados com o Whidbey. Para desenvolvedores do Microsoft Visual
Basic® .NET, há suporte para o novo espaço para nomes "My.*". Os desenvolvedores C# podem esperar suporte para
genéricos, métodos anônimos e iteradores.
Notification Broker
Um avanço muito notável do Windows Mobile é o Notification Broker, que não é um recurso do Whidbey, mas um recurso
da própria futura plataforma Windows Mobile. O Notification Broker serve como mecanismo centralizado de publicação e
assinatura para praticamente todos os eventos que podem ocorrer no dispositivo. Ele permite que os aplicativos
registrem interesse em eventos significativos, inclusive uma alteração de status da rede, alteração de status do
ActiveSync, chegada de uma chamada telefônica e chegada de uma mensagem SMS (Short Message Service). O
aplicativo é notificado automaticamente quando o evento ocorre. A beleza dessa arquitetura é que ela é completamente
extensível e centralizada, fornecendo um modelo de programação comum para todos esses eventos. Esse é outro
recurso que veremos em mais detalhes em uma futura edição desta coluna.
Início da página

Conclusão
Isso conclui nossa primeira edição. Espero que a visão geral desses novos recursos para dispositivos do Whidbey tenha
deixado você tão impressionado com o futuro do desenvolvimento do Windows Mobile quanto eu estou. Estamos na
aurora de um novíssimo dia.
Nos próximos meses, entraremos em muito mais detalhes sobre os diversos tópicos apresentados aqui. Também
veremos alguns tópicos de desenvolvimento mais gerais que não estão atados ao Whidbey. Espero que você fique
conosco.
Agora, preciso voltar a trabalhar em minhas apresentações para a MDC. Espero ver muitos de vocês por lá; se não, nos
vemos no mês que vem.
(© 2004 Microsoft Corporation. Todos os direitos reservados. Termos de uso.)

Acessando o SQL Server direto do Pocket PC


Por Renato Haddad, Microsoft Most Valuable Professional .NET Compact Framework. Autor de diversos livros e ministra
palestras e treinamentos sobre a tecnologia .NET.
Tecnologias Utilizadas:

• .NET Compact Framework


O Pocket PC têm ganho um mercado rapidamente em diversas empresas onde o uso de mobilidade se torna
imprescindível. Esta tendência tem se demonstrado uma grande oportunidade para agregar valor as soluções desktop
existentes e vender a aplicação com um diferencial de mercado, onde as informações estarão na palma da mão a
qualquer hora.
O propósito deste artigo é mostrar como acessar um banco de dados SQL Server diretamente do Pocket PC. Para isto, é
necessário que haja uma conexão com a rede, seja Wi-Fi ou através do ActiveSync. Como as redes Wi-Fi estão
crescendo em todo lugar, acredito que daqui há alguns anos teremos acesso a qualquer lugar e hora em todo lugar do
planeta terra. Com isso, é possível vc desenvolver uma aplicação para força de vendas onde os vendedores estarão
apenas com um Pocket PC conectado via Wi-Fi diretamente no servidor da empresa.
O bando de dados que usarei neste artigo é o Northwind do SQL Server. Abra o Visual Studio .NET 2003 e crie um
projeto com as seguintes características:

• Project Types: Visual Basic Projects

• Templates: Smart Device Application

• Name: PocketPC

• Location: C:\MSDN\PocketPC
Clique no botão OK e note que é exibida a janela para você selecionar o tipo de aplicação:

Clique no botão OK. No Solution Explorer, adicione um novo Windows Form chamado AccessSQLServer.
Crie uma UI com dois Buttons (btnProdutos e btnClientes), um DataGrid (gridDados) um Label (text: IP) e um TextBox
(IP). Altere a propriedade do formulário MinimizeBox = true.

Como iremos acessar o SQL Server, adicione uma referência à classe System.Data.SqlClient. Para se trabalhar com
DataAdapter é preciso referenciar a classe System.Data.Common.
Pressione F7 para digitar o código. Na primeira linha digite a referência a classe:
Imports System.Data.SqlClient

Veja os códigos dos respectivos botões. Note que é invocada a rotina GetData passando como argumento a instrução
SQL definida.
Private Sub btnProdutos_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnProdutos.Click

Try

Dim sql As String = "Select ProductName, UnitsInStock, UnitPrice FROM Products"

GetData(sql)

Catch ex As Exception

MessageBox.Show(ex.Message)

End Try

End Sub

Private Sub btnClientes_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnClientes.Click

Try

Dim sql As String = "Select * FROM Customers"

GetData(sql)

Catch ex As Exception

MessageBox.Show(ex.Message)

End Try

End Sub
Digite a rotine GetData, que recebe a instrução sql como argumento.
Private Sub GetData(ByVal sql As String)

Dim conexao As String = "Database=Northwind;user id=sa;Data Source=" + IP.Text

Dim conn As New SqlConnection(conexao)

Dim adapter As New SqlDataAdapter(sql, conn)

Dim ds As New DataSet

conn.Open()

adapter.Fill(ds, "tabela")

Try

gridDados.DataSource = Nothing

gridDados.DataSource = ds.Tables(0)

MessageBox.Show("Existem: " + ds.Tables(0).Rows.Count.ToString + " registros")

Catch ex As SqlException

Throw

Catch ex As Exception

Throw

Finally

conn.Close()

End Try

End Sub

Veja a explicação de cada linha:


Define a rotina

Private Sub GetData(ByVal sql As String)

Define a variável conexao do tipo string o qual contém toda a string de conexão com o banco de dados

Northwind. O Data Source é o número IP da máquina a ser acessada, que neste caso é o número capturado

da UI (TextBox = IP).

Dim conexao As String = "Database=Northwind;user id=sa;Data Source=" + IP.Text

Define a string de conexão

Dim conn As New SqlConnection(conexao)

Define o DataAdapter das respectivas variáveis sql e conn.

Dim adapter As New SqlDataAdapter(sql, conn)

Define o DataSet que conterá a tabela a ser montada na memória com todos os dados oriundos

da instrução SQL.

Dim ds As New DataSet

Abre a conexão e executa o DataAdapter. Aqui o DataSet recebe a DataTable “tabela”


na memória.

conn.Open()

adapter.Fill(ds, "tabela")

Inicia a rotina de tratamento de erro

Try

Configura o DataGrid gridDados que irá exibir o conteúdo da DataTable (índice=0)

contida no DataSet.

gridDados.DataSource = Nothing

gridDados.DataSource = ds.Tables(0)

Envia uma mensagem ao usuário informando o número de registros da DataTable/DataGrid.

MessageBox.Show("Existem: " + ds.Tables(0).Rows.Count.ToString + " registros")

Caso ocorra algum erro, é tratado aqui. Neste caso, como é um sub-rotina, é preciso

inserir o THROW para avisar as rotinas a chamou que ocorreu um erro aqui.

Catch ex As SqlException

Throw

Catch ex As Exception

Throw

Ocorrendo ou não um erro, a conexão com o banco de dados é fechada.

Finally

conn.Close()

End Try

End Sub

Para executar, clique no botão Execute ou pressione F5. A janela aberta solicita que você informe onde irá executar. Use
o emulador do Pocket PC e, note que nesta janela são exibidos alguns emuladores que vocês não tem, mas é só fazer
download do link (http://www.microsoft.com/downloads/details.aspx?familyid=4953d34d-692f-4c87-ac69-
cb235dbdad1d&displaylang=en). Clique no botão Deploy e faça os testes.
Informe o número IP correto e clique no botão Produtos:

Clique no botão Clientes para visualizar todos os clientes:

Início da página

Conclusão
O uso de um Pocket PC para acessar informações remotamente, juntamente com a tecnologia de Wi-Fi (Wireless Fidelity)
facilitou o acesso a qualquer banco de dados. As aplicações que necessitam da informação a qualquer hora e lugar pode
usufruir desta técnica mostrada neste artigo. Se formos pensar em um futuro “próximo” onde teremos redes Wi-Fi
espalhadas em qualquer lugar, dizer que o acesso as informações é difícil, com certeza seu cliente não entenderá desta
forma.
Bons estudos e lembre-se: No Stress, think .NET!!!
Renato Haddad (rehaddad@msn.com) é MVP, editor da revista MSDN Magazine Brasil, ministra treinamentos e
palestras sobre .NETe autor de diversos livros e treinamentos em CD multimídia de ASP.NET, Visual Studio .NET 2003 e
Aplicações Móveis para celulares e Pocket PC, tanto no Brasil como em outros países da América Latina.
Referências:
www.microsoft.com/windowsmobile

Exibindo Imagens no Pocket PC


Por Renato Haddad, Microsoft Most Valuable Professional .NET Compact Framework.
Editor da revista MSDN Magazine Brasil, autor de diversos livros e ministra palestras e
treinamentos sobre a tecnologia .NET.
Tecnologias Utilizadas

• .NET Compact Framework


O objetivo deste artigo é mostrar o uso controle ImageList no Pocket PC. Assim você poderá criar um catálogo de
produtos e instalar no Pocket do vendedor da sua empresa para o mesmo visitar os clientes e efetuar os pedidos.
Abra o Visual Studio .NET 2003 e crie um projeto com as seguintes características:

• Project Types: Visual Basic Projects

• Templates: Smart Device Application

• Name: PocketPC

• Location: C:\MSDN\PocketPC

Clique no botão OK e note que é exibida a janela para você selecionar o tipo de aplicação:
Clique no botão OK. No Solution Explorer, adicione um novo Windows Form chamado Catalogo.

Monte uma UI conforme a seguinte figura, contendo 2 Buttons (btnProx e btnAnt), 1 PictureBox (SizeMode =
StrechImage) e 1 ImageList. Cabe ressaltar que para o PictureBox você tem a propriedade SizeMode que deverá
adequar de acordo com o tipo de exibição que você tiver, assim como o tamanho da foto.
Configure a propriedade do formulário MinimizeBox = False.
Agora é preciso inserir as imagens no controle ImageList, portanto, selecione-o, exiba as propriedades (F4) e note que a
proriedade Imagens contém uma Collection onde iremos informar todas as fotos.

Clique no botão Add para adicionar todas as imagens e serem exibidas no Pocket.
O próximo passo é inserir o código para os botões. Basicamente defino uma variável índice do tipo Integer = -1 para
poder controlar o rolamento das imagens. Já que todas estão armazenadas no controle ImageList1 e o índice sempre
começa em zero, então nos botões verifico qual é a posição atual na collection e desloco para a próxima ou anterior.
Dim indice As Integer = -1

Private Sub btnProx_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnProx.Click

If indice <= 0 Then

indice = ImageList1.Images.Count - 1

Else

indice -= 1

End If

MostraImagem(indice)

End Sub

Private Sub btnAnt_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnAnt.Click

If indice >= ImageList1.Images.Count - 1 Then

indice = 0

Else

indice += 1

End If

MostraImagem(indice)
End Sub

Private Sub MostraImagem(ByVal ind As Integer)

PictureBox1.Image = ImageList1.Images(ind)

Text = "Imagem " + (ind + 1).ToString() + " de " + ImageList1.Images.Count.ToString()

End Sub

Na primeira vez que o formulário for aberto é preciso exibir a primeira figura da lista.

Localize a sessão Initialize e insira o seguinte código que chamará a rotina MostraImagem

passando o índice 0 (zero) como parâmetro.

Public Sub New()

MyBase.New()

'This call is required by the Windows Form Designer.

InitializeComponent()

'Add any initialization after the InitializeComponent() call

MostraImagem(0)

End Sub

Para executar o projeto, abra o Solution Explorer, pressione o botão direito no nome da solução, selecione propriedades
e escolha no Startup object o formulário a ser executado (Catalogo).
Pressione F5 ou o botão de executar. Selecione o emulador e clique no botão Deploy.

Clique nos botões Próxima e Anterior para navegar nas imagens.

Conclusão
Com este artigo você poderá contruir um catálogo de produtos para fazer o deploy diretamente no Pocket PC. O esforço
é mínimo para trabalhar com estas imagens e o impacto visual nos clientes é grande. Note que com pouco código de
programação criei uma forma diferente de apresentar as imagens. Caso queira, é possível carregar uma imagem
diretamente de um arquivo no Pocket usando os comandos de imagem. Um desafio maior que você pode implementar é
transferir uma imagem através de um Web Service.
Bons estudos e lembre-se: No Stress, think .NET!!!
Renato Haddad (rehaddad@msn.com) é MVP, editor da revista MSDN Magazine Brasil, ministra treinamentos e palestras
sobre .NETe autor de diversos livros e treinamentos em CD multimídia de ASP.NET, Visual Studio .NET 2003 e Aplicações
Móveis para celulares e Pocket PC, tanto no Brasil como em outros países da América Latina.
Início da página

Referências:
www.microsoft.com/windowsmobile

Menus e Input Panel no Pocket PC


Por Renato Haddad, Microsoft Most Valuable Professional .NET Compact Framework.
Autor de diversos livros e ministra palestras e treinamentos sobre a tecnologia .NET.
Tecnologias Utilizadas

• .NET Compact Framework


Quando desenvolvemos aplicações para dispositivos móveis (seja Pocket PC, Smartphone ou qualquer outro device) é
importante pensar na interface com o usuário. O objetivo deste artigo é mostrar dois controles muito utilizados no
Pocket PC: o Menu e o InputPanel. Irei mostrar ainda alguns truques da linguagem VB.NET para novos programadores.
O tamanho da tela e a forma que o usuário irá interagir pode tornar uma aplicação viável do ponto de vista de
produtividade. O uso de uma caneta touch-screen para o Pocket é a forma usual de interagir com a aplicação, pelo
menos enquanto não utilizamos a tecnologia de Speach para poder dar os comandos por voz. Sendo assim, alguns tipos
de controles como TextBox, onde o usuário precisa digitar algo, é imprescindível que o teclado seja exibido ou não no
momento correto.
Vamos criar um projeto no Visual Studio .NET 2003 (que na minha opinião é a melhor ferramenta para Pocket PC)
conforme a seguinte opções:

• Project Types: Visual Basic Projects

• Templates: Smart Device Application

• Name: PocketPC

• Location: C:\MSDN\PocketPC

Clique no botão OK e note que é exibida a janela para você selecionar o tipo de aplicação:

Clique no botão OK. No Solution Explorer, adicione um novo Windows Form chamado Menu.
Monte uma UI de acordo com a seguinte figura: dois Labels, dois TextBox (txtNome e txtEmail), dois Buttons (btnMsg1,
e btnMsg2), dois checkBox, um MainMenu e um InputPanel. Configure as seguintes propriedades do formulário:
BackColor=Wheat e MinimizeBox=False. A propriedade MinimizeBox permite minimizar o formulário, mas se você
configurar para False, automaticamente o formulário é fechado e retirado da memória.

Para criar um menu, basta selecioná-lo e digitar as opções conforme o aplicativo. Caso queira inserir um separador de
opção, clique com o botão direito na posição a ser inserido e selecione Insert Separator. Insira as seguinte opções:
Os nomes default dos menus são MenuItem1, 2, 3 e assim sucessivamente. Para alterá-los, a opção mais rápida é
clicando com o botão direito sobre qualquer opção e selecione Edit Names. Neste artigo vou manter os nomes default.

Códigos

Menu
Dê um duplo clique na opção “Apagar conteúdo” e digite o seguinte código que irá apagar o conteúdo digitado nos
respectivos TextBoxes. Note que usei o String.Empty e não o “”. O efeito é o mesmo, mas já que estamos aprendendo a
programar em .NET, usemos da forma correta.
Private Sub MenuItem3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles MenuItem3.Click

txtNome.Text = String.Empty

txtEmail.Text = String.Empty

End Sub

Volte a UI e dê um duplo clique na opção “Capturar dados”, o qual captura o conteúdo digitado em cada TextBox e exibe
em uma janela. Note que existe uma verificação se existem dados digitados nos TextBoxes. Caso esteja faltando algum,
é exibida uma mensagem de Atenção. Caso contrário, é definida uma variável chamada “dados” do tipo StringBuilder, o
qual monta na memória uma string com tudo o que é necessário exibir ao usuário e usa o Messagebox.Show para enviar
a respectiva mensagem. É claro que você poderia concatenar toda a mensagem direto em Messagebox.Show, no
entanto, como isto é um artigo, aproveitei para mostrar o uso do StringBuilder, que é sem nenhuma dúvida o meio mais
rápido de se manipular strings.
Private Sub MenuItem2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles MenuItem2.Click

If txtNome.Text.Trim = String.Empty Or _

txtEmail.Text.Trim = String.Empty Then

MessageBox.Show("Por favor digite todos os campos", _

"Atenção")

Else

Dim dados As New System.Text.StringBuilder

dados.Append("Caro(a): ")

dados.Append(txtNome.Text.Trim)

dados.Append("-bem vindo ao mundo Pocket PC")

dados.Append(vbNewLine)

dados.Append("Seu e-mail é: ")

dados.Append(txtEmail.Text.Trim)

MessageBox.Show(dados.ToString, "POCKET PC")

End If

End Sub

Digite o código do menu Sair


Private Sub MenuItem7_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles MenuItem7.Click

Me.Close()

End Sub

Selecione o menu Project / PocketPC Properties e define na opção Startup Object o Menu, que é o formulário a ser
executado quando você fizer o Deploy da aplicação.
Pressione F5 ou selecione o menu Debug / Start para executar os testes. Será exibida uma janela contendo todas as
opções de emuladores. Você pode selecionar o emulador que tiver instalado na máquina, e caso tenha um pocket
conectado via ActiveSync, selecione a opção Pocket PC Device. Cabe ressaltar que os emuladores do Pocket PC 2003
podem ser conseguidos em “Emulator Images for Windows Mobile 2003 Second Edition-based Pocket PC Development”
(http://www.microsoft.com/downloads/details.aspx?familyid=4953d34d-692f-4c87-ac69-
cb235dbdad1d&displaylang=en).

Clique no botão Deploy e teste a aplicação.


InputPanel
Como temos dois TextBoxes no formulário, é preciso uma forma de exibir automaticamente o InputPanel assim que o
foco entrar nos TextBoxes. Sendo assim, selecione o objeto txtNome em Class Name e o evento GotFocus em Method
Name.

Digite o seguinte código para exibir o InputPanel:


Private Sub txtNome_GotFocus(ByVal sender As Object, ByVal e As System.EventArgs)

Handles txtNome.GotFocus

InputPanel1.Enabled = True

End Sub

Você concorda que o mesmo código serve para o txtEmail, então, o que fazer? Você tem 3 alternativas:
1) Criar o mesmo código para o txtEmail (esta não é a melhor opção), ficando:
Private Sub txtEmail_GotFocus(ByVal sender As Object, ByVal e As System.EventArgs)

Handles txtEmail.GotFocus

InputPanel1.Enabled = True

End Sub

2) Criar uma rotina que recebe o valor True ou False para exibir ou não o InputPanel.
Private Sub TrataInputPanel(ByVal exibe As Boolean)

InputPanel1.Enabled = exibe
End Sub

E na chamada dos códigos dos botões, você invocaria esta rotina passando o argumento True:
Private Sub txtNome_GotFocus(ByVal sender As Object, ByVal e As System.EventArgs)

Handles txtNome.GotFocus

TrataInputPanel(True)

End Sub

Private Sub txtEmail_GotFocus(ByVal sender As Object, ByVal e As System.EventArgs)

Handles txtEmail.GotFocus

TrataInputPanel(True)

End Sub

3) Esta é a melhor alternativa que o .NET proporciona aos programadores. Veja a rotina original:
Private Sub txtNome_GotFocus(ByVal sender As Object, ByVal e As System.EventArgs)

Handles txtNome.GotFocus

InputPanel1.Enabled = True

End Sub

Repare no Handles. Note que é invocado o objeto + evento, portanto, é só incluir o outro objeto + evento no final da
linha, sendo:
Private Sub txtNome_GotFocus(ByVal sender As Object, ByVal e As System.EventArgs)

Handles txtNome.GotFocus, txtEmail.GotFocus

InputPanel1.Enabled = True

End Sub

Só isso, mais nenhum outro código é preciso. Cabe ressaltar que isto vale para qualquer objeto (button, dropdown,
listbox, etc). Sendo assim, use e abuse do Handles adicionando objetos e eventos ao mesmo toda vez que precisar
executar uma rotina. Faça o mesmo código para o evento LostFocus para ocultar o InputPanel quando os TextBoxes
perderem o foco.
Private Sub txtNome_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs)

Handles txtNome.LostFocus, txtEmail.LostFocus

InputPanel1.Enabled = False

End Sub
Truques
Agora vamos aos truques de programação de objetos. Vale lembrar que isto serve para qualquer aplicação em .NET.
Você tem dois Buttons que devem disparar uma mensagem qualquer ao usuário ou executar uma rotina. Normalmente
você faria o seguinte procedimento:

• Dê um duplo clique no primeiro botão;

• É criado o seguinte código:


Private Sub btnMsg1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnMsg1.Click

End Sub

Note que é criado o Sub btnMsg1_Click com os argumentos sender (que é o objeto) e o “e” (que são os argumentos do
objeto). No final é criado o:
Handles btnMsg1.Click

Com certeza você colocaria o código da rotina aqui dentro, ficando:


Private Sub btnMsg1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnMsg1.Click

MessageBox.Show("Hello Pocket")

End Sub

E para o botão btnMsg2 você criaria o código:


Private Sub btnMsg2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnMsg2.Click

MessageBox.Show("Hello Pocket")

End Sub

Mas, como você acabou de aprender que existe o Handles, então este código servirá para os dois botões, sendo:
Private Sub btnMsg1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnMsg1.Click, btnMsg2.Click

MessageBox.Show("Hello Pocket")

End Sub
O que você acha que significa o btnMsg1_Click? Este é apenas um nome default para a rotina. Altere toda esta rotina
para o seguinte código:
Private Sub MinhaRotina(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnMsg1.Click, btnMsg2.Click

MessageBox.Show("Hello Pocket. Você clicou no objeto: " & _

CType(sender, Button).Text)

End Sub

Temos assim uma rotina genérica que será disparada pelos objetos descritos no Handles. Observe ainda que para saber
qual é o objeto ativo, usei o CType(sender, Button). Com isso, este código serve para qualquer botão existente no
formulário.

Outra dica de programação ocorre em situações em que você precisa ocultar ou exibir um determinado controle.
Usaremos o checkBox para isto, portanto, o código usual para ocultar o txtNome é:
Private Sub CheckBox1_CheckStateChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles CheckBox1.CheckStateChanged

If CheckBox1.Checked Then

txtNome.Visible = True

Else

txtNome.Visible = False

End If

End Sub

Na minha opinião este não é um código “bem escrito”. Existe uma forma mais elegante de escrever o mesmo código,
sendo:
Private Sub CheckBox1_CheckStateChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles CheckBox1.CheckStateChanged

txtNome.Visible = CheckBox1.Checked

End Sub

Aproveite e insira no Handles o segundo CheckBox.


Private Sub CheckBox1_CheckStateChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles CheckBox1.CheckStateChanged, CheckBox2.CheckStateChanged

txtNome.Visible = CheckBox1.Checked
End Sub

No entanto, você deve estar pensando que o código valera para os dois objetos no Handles, mas note que o True ou
False depende apenas do CheckBox1. Sendo assim, a melhor alternativa é o seguinte código, onde existe uma rotina
com os respectivos Handles anteriores que atribui o True ou False em relação ao objeto atual passado como parâmetro.
Private Sub TrataVisible(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles CheckBox1.CheckStateChanged, CheckBox2.CheckStateChanged

txtNome.Visible = CType(sender, CheckBox).Checked

End Sub

Aproveitando o artigo, já que criei a opção Visible no menu Ferramentas, então dê um duplo clique nesta opção e digite
o seguinte código que oculta ou não o txtMail e atribui o True ou False à respectiva opção do menu. Note que a variável
blnStatus é do tipo Boolean e contém a negativa do Checked do MenuItem 5 (Ferramentas/Visible).
Private Sub MenuItem5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles MenuItem5.Click

Dim blnStatus As Boolean = Not MenuItem5.Checked

MenuItem5.Checked = blnStatus

txtEmail.Visible = blnStatus

End Sub
O Pocket PC permite desabilitar um determinado controle para que o usuário não tenha acesso. Crie o código para o
menu Ferramentas/Enable.
Private Sub MenuItem6_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles MenuItem6.Click

Dim blnStatus As Boolean = Not MenuItem6.Checked

MenuItem6.Checked = blnStatus

txtEmail.Enabled = blnStatus

End Sub

Conclusão
Antes de criar uma interface com o usuário, pense em facilitar ao máximo o manuseio do dispositivo, analise junto ao
seu usuário como que ele irá manipular os dados e crie a UI de acordo com a necessidade dele. Aplique as dicas e
truques da linguagem deixando o seu código mais limpo, produtivo e de fácil manutenção.
Bons estudos e lembre-se: No Stress, think .NET!!!
Renato Haddad (rehaddad@msn.com) é MVP, editor da revista MSDN Magazine Brasil, autor de 9 livros de tecnologia
Microsoft, ministra treinamentos e palestras sobre .NETe autor de diversos livros e treinamentos em CD multimídia de
ASP.NET, Visual Studio .NET 2003 e Aplicações Móveis para celulares e Pocket PC, tanto no Brasil como em outros países
da América Latina.
BLOG: http://br.thespoke.net/MyBlog/RenatoHaddad/MyBlog.aspx

Controle de Estoque no Pocket PC: do eVB ao VB .NET


por Leandro Machado
Para ler todas as matérias da MSDN Magazine, assine a revista no endereço www.neoficio.com.br/msdn
Criar projetos para dispositivos móveis é desafiador. Por um lado você tem todo o poder de um Pocket PC nas mãos, e
por outro os dados que estão no servidor. Então, como se comunicar com o banco de dados no servidor e usar as
funcionalidades do Pocket PC para desenvolver um projeto de coletores de dados para controlar o estoque? O objetivo
deste artigo é ensinar como fazer isso usando a linguagem VB.NET e o bando de dados SQL Server. Em alguns
momentos, farei uma comparação entre o eVB (Embebbed Visual Basic) e o VB.NET (Visual Basic .NET).
Inicialmente, eu precisava escolher uma linguagem de desenvolvimento para a aplicação. A Microsoft oferecia duas
opções de linguagens para o desenvolvimento Embedded: o C e o modelo visual , o eVB. A opção pela linguagem
baseada em visual foi natural, na medida em que toda a equipe de desenvolvimento já a utilizava para o
desenvolvimento de aplicações Desktop e uma vez que o custo de aprendizado poderia ser minimizado com essa
escolha.
Primeiramente, desenvolvi uma aplicação Off-line sincronizando os dados pelo ActiveSync. Os motivos: o alto custo em
equipamentos para conectar os dispositivos à rede e, principalmente, a complexidade de codificar a aplicação para
acessar diretamente o servidor SQL Server.
Com o lançamento do Visual Studio.Net 2003 e com a popularização da tecnologia Wi-Fi, optei novamente por trocar a
plataforma de desenvolvimento. Essa decisão foi a mais fácil de minha carreira profissional, haja vista as incomparáveis
facilidades proporcionadas pelo Visual Studio.Net.
A seguir, demonstrarei como fiz a primeira migração de um aplicativo desenvolvido em eVB para a plataforma .Net. Essa
migração demandou apenas um dia de trabalho. Vale ressaltar que levei esse tempo porque separei algumas horas para
ler o Help do programa.

Conferência de Estoque
O aplicativo em questão é simples e fácil de entender. Uma rotina do departamento de Expedição da empresa é a
conferência física do estoque. No passado, o relatório de estoque era impresso em intermináveis páginas, as quais eram
divididas entre os conferentes, que saíam em campo conferindo o que estava na lista. Quando encontravam algum item
com uma quantidade diferente da especificada na lista, anotavam essa diferença na própria lista. O procedimento, além
de muito demorado, gerava muitas dúvidas, e nunca se tinha certeza de que tudo havia sido conferido corretamente. O
sistema de conferência pelo Pocket proporcionou maior velocidade e confiabilidade ao procedimento de conferência de
estoque.
Início da página

Tornando a aplicação On-Line


A grande mudança proporcionada pela troca da plataforma para o VS.Net consistiu em possibilitar o acesso a dados On-
Line pela aplicação. A tecnologia utilizada foi a Web Services. Foi criado e publicado na Intranet da empresa um Web
Service, o qual será consumido pela aplicação que roda no Pocket PC através da rede local, usando Wi-Fi.
No Web Service, foi criada uma função que retorna um DataSet que poderá ser usado em diferentes pontos do projeto.
Como a aplicação trabalha desconectada, o Web Service é invocado no servidor, conecta a base de dados, monta um
DataSet, retorna os dados à aplicação e se desconecta. Veja na Listagem 1 o código do Web Service com a função
Pesquisa, o qual recebe como parâmetro uma variável do tipo String com a instrução SQL ou com a Stored Procedure
desejada e, em seguida, processa e retorna um DataSet à aplicação.
Listagem 1 Web Service com a função Pesquisa
<WebMethod(Description:="Retorno de DataSet")> _

Public Function Pesquisa(ByVal xSql As String) As DataSet

‘Coloque a string de conexão com seu banco de dados

Dim conexao As String =”String de conexão”

Dim conn As SqlConnection

Dim da As SqlDataAdapter

Dim ds As DataSet

conn = New SqlConnection(conexao)


Try

conn.Open()

da = New SqlDataAdapter(xSql, conn)

ds = New DataSet

da.Fill(ds, "Tabela")

Return ds

Catch ex As Exception

Throw ex

Finally

conn.Close()

End Try

End Function

No Web Service, também foi criado uma Sub para operações de Insert, Update e Delete, que recebe como parâmetro
uma variável do tipo String com a instrução SQL ou com a Stored Procedure desejada e processa a instrução. Caso
ocorra algum erro durante o processamento da instrução, a Sub retornará o erro por meio da linha Throw ex (veja a
Listagem 2).
Listagem 2 Sub para operações de Insert, Update e Delete
<WebMethod(Description:="RS sem Retorno")> _

Public Sub FazRSsr(ByVal xSql As String)

‘Coloque a string de conexão com seu banco de dados

Dim conexao As String = “String de Conexão”

Dim myConnection As New SqlConnection(conexao)

myConnection.Open()

Dim sql As String = xSql

Dim myCommand As New SqlCommand(sql, myConnection)

Dim myTrans As SqlTransaction

myTrans = myConnection.BeginTransaction()

myCommand.Connection = myConnection

myCommand.Transaction = myTrans

Try

myCommand.ExecuteNonQuery()

myTrans.Commit()

Catch ex As Exception

myTrans.Rollback()

Throw ex

Finally

myConnection.Close()

End Try

End Sub

Com o Web Service criado, o próximo passo é publicar no servidor com o IIS (Internet Information Server) da Intranet,
já que o consumo desse Web Service seria feito apenas internamente, pela rede local.
No eVb o acesso a dados era feito por ODBC. Criava-se uma fonte de dados ODBC e usava-se o Microsoft ActiveSync
para importar essa fonte de dados para o Pocket PC, gerando assim uma base de dados local no Pocket. A cada conexão
do dispositivo à base, o ActiveSync cuidava de replicar os dados do Pocket com a base SQL Server. Essa sincronização
eventualmente gerava conflitos, e nossos analistas eram obrigados a resolvê-los manualmente.
Início da página

Criando uma nova aplicação


Abra o VS.Net 2003, clique em New Project ou pressione CTRL + SHIFT + N. Em Visual Basic Projects, selecione
SmartDeviceApplication, escolha o nome do seu projeto e o local onde irá gravá-lo. Pressione OK e, na janela aberta,
selecione em “What Platform do you want to target?” a opção “Pocket PC”. Por fim, clique no botão OK.
Início da página

A Interface
A Toolbox do .Net é muito ampla e a quantidade de objetos e classes disponíveis é infinitamente maior do que no eVB,
mas uma de minhas preocupações durante a migração foi manter o mesmo layout da interface, pois os usuários do
sistema já estavam habituados com o layout desenvolvido em eVB. Mesmo optando por manter o layout, os ganhos de
produtividade no desenvolvimento foram muitos e vocês verão agora o porquê.
Na Toolbox, selecione o objeto InputPanel, que será usado para mostrar um teclado na tela do Pocket PC de modo a
permitir que o usuário digite os dados. Use o código da Listagem 3 para habilitar ou desabilitar a visualização do teclado.
Listagem 3 Habilita ou desabilita a visualização do teclado
Private Sub txt_NumeroOrdem_GotFocus( _

ByVal sender As Object, _

ByVal e As System.EventArgs) _

Handles txt_NumeroOrdem.GotFocus

InputPanel1.Enabled = True

End Sub

Private Sub txt_NumeroOrdem_LostFocus( _

ByVal sender As Object, _

ByVal e As System.EventArgs) _

Handles txt_NumeroOrdem.LostFocus

InputPanel1.Enabled = False

End Sub

Em uma aplicação para Pocket PC, não é aconselhável o uso de muitos Forms para racionalizar o uso de memória. Uma
boa saída para isso é o uso de TabControls, onde o programador pode inserir um desses objetos na tela, ajustá-lo ao
tamanho da tela e usar quantos tabs forem necessários para a aplicação, economizando assim o uso de Forms. Veja na
Figura 1 o formulário com os tabs criados.
Figura 1 Telas da aplicação
De cara notei uma grande mudança em relação ao eVB. Embora o recurso do objeto TabControl existisse no eVB, o
gerenciamento da visualização dos Tabs precisava ser feito manualmente. Para cada Tab, era necessário o uso de um
frame, o que reduzia ainda o espaço disponível na tela do Pocket. Dentro de cada Frame eram colocados os objetos.
Tanto em ambientes de desenvolvimento como em execução, o evento click nas Tabs não executa nada, ou seja, o
desenvolvedor é quem deve disparar uma rotina do tipo Frame1.Viseble=true e Frame2.Viseble=False.
No .Net esse gerenciamento é feito pelo próprio controle, e toda a codificação necessária no eVB torna-se desnecessária.
Com o objeto inserido e dimensionado na tela, na barra Properties, clique na propriedade TabPages para exibir a janela
de configuração TabPage Collection Editor, onde iremos criar e configurar cada Tab da página. No exemplo, utilizei
Quatro Tabs (veja a Figura 1).
Na Tabpage1, serão exibidas informações das conferências geradas na Aplicação Desktop, que trabalha em conjunto com
a aplicação do Pocket PC. Foram usados um DataGrid e um Label. Na Tabpage2, serão exibidos os dados da pesquisa de
cada item de estoque a ser pesquisado e conferido. Foram usados nove TextBox, um Label para cada TextBox, quatro
Buttons e um VScrollBar. Na Tabpage3, serão exibidos em um Grid os itens já conferidos. Foram usados dois Buttons,
um DataGrid e um Label. Na Tabpage4, serão exibidos os itens conferidos, porém com dados físicos diferentes dos dados
que constam na base SQL Server, denominados “Dúvidas”. Foram usados dois Buttons, um DataGrid e um Label.
Início da página

Códigos
Antes de tudo precisamos fazer referência ao WebService que iremos consumir. Abra a janela do Solution Explorer,
clique com o botão direito no nome do projeto e selecione Add Web Reference. Digite a URL completa do local
(http://servidor/projeto/pagina.asmx) onde está publicado seu Web Service e use o botão GO para confirmar a
existência deste. Em seguida, é só clicar em Add Reference.
Pressione F7 para abrir a janela de código e defina as seguintes variáveis globais:
Option Explicit

'código da conferência selecionada

Dim cellValue As Integer

'Código do Num de ordem conferida selecionado

Dim cellConf As Integer = 0

O Tabpage1 é o padrão e é nele que o usuário deve selecionar a conferência de estoque com a qual pretende trabalhar.
Essas conferências são geradas na aplicação Desktop pelos responsáveis por cada estoque da empresa. Como a empresa
possui diversos estoques diferentes, o DataGrid colocado nesse Tabpage serve para listar os códigos de todas as
conferências geradas no sistema Desktop. No evento Load do Form, digite o código da Listagem 4.
Listagem 4 Código do Load do formulário
Private Sub Conferencia_Frm_Load(ByVal sender As Object, ByVal e As System.EventArgs)

Handles MyBase.Load

Cursor.Current = Cursors.WaitCursor

CarregaGridConferencias()

Cursor.Current = Cursors.Default

End Sub

Private Sub CarregaGridConferencias()

'Configura o estilo de formatação do grid

'A propriedade MappingName = "Tabela" é devido ao

'retorno .Tables("Tabela") do WebService

Dim style As New DataGridTableStyle

style.MappingName = "Tabela"

'Para cada coluna do grid, que deseja formatar

'deve-se criar um DataGridTextBoxColumn

Dim tcol As New DataGridTextBoxColumn

With tcol

.HeaderText = "Conferência"

.MappingName = "ConfCodigo"

.Width = 100

End With

Dim tcol2 As New DataGridTextBoxColumn

With tcol2

.HeaderText = "Data"

.MappingName = "ConfData"

.Width = 100
End With

'Adicione os DataGridTextBoxColumn ao

'DataGridTableStyle

style.GridColumnStyles.Add(tcol)

style.GridColumnStyles.Add(tcol2)

'Adicione os DataGridTableStyle ao DataGrid

dtg_Conferencia.TableStyles.Add(style)

'Instancia o WebService e carrega o grid

Try

Dim WS As New WebReference.Service1

dtg_Conferencia.DataSource = WS.Pesquisa("sp_PDA_Conferencia").Tables("Tabela").DefaultView

Catch ex As Exception

Msgbox("Erro : " & ex.Message, MsgBoxStyle.Critical, "ERRO!")

End Try

End Sub

Note que na rotina CarregaGridConferencias() a maior parte da codificação trata apenas da formatação do DataGrid,
tanto das linhas quanto das colunas. Usei a classe DataGridTableStyle porque ela facilita a formatação e exibição dos
dados no Grid.
Os dados do banco de dados SQL Server são carregados no DataGrid através do consumo do Web Service criado no
início do artigo. No consumo do Web Service, usei a estrutura Try/Catch para tratar o erro e, se não fosse esse
tratamento, a codificação estaria resumida a duas linhas.
Dim WS As New WebReference.Service1

dtg_Conferencia.DataSource = WS.Pesquisa("sp_PDA_Conferencia").Tables("Tabela").DefaultView

O parâmetro do Web Service é o nome de Stored Procedure (veja a Listagem 5), mas seria possível passar qualquer
código SQL.
Listagem 5 Stored Procedure
CREATE Procedure sp_PDA_Conferencia

as

Select ConfCodigo, ConfData

From ConfereEstoque

GO

No eVb não existe o DataGrid, mas sim algo parecido com o MSFlexGrid. Esse recurso era outro grande gerador de
código, pois a formatação e a população do Grid eram feitas manualmente, linha por linha, coluna por coluna, célula por
célula. Tudo manualmente. É necessário dizer que esse método manual de popular o Grid torna o carregamento dos
dados extremamente lento, devido ao fato de o código processar individualmente todas as linhas do Recordset gerado
para a tarefa.
Como os dados estão exibidos no GRID, o usuário precisa selecionar a conferência desejada usando a caneta no Grid que
contém a conferência em que irá trabalhar (veja a Listagem 6).
Listagem 6 Seleciona um item no DataGrid
Private Sub dtg_Conferencia_Click( _

ByVal sender As System.Object, _


ByVal e As System.EventArgs) _

Handles dtg_Conferencia.Click

Cursor.Current = Cursors.WaitCursor

Try

LimpaCampos(TabPage2.Controls)

' Valor que guarda a Linha selecionada

Dim vIntRow As Integer

vIntRow = dtg_Conferencia.CurrentRowIndex

'Seta a seleção para a Coluna 0, que é a coluna

'do código. A linha da seleção é mantida na

'variável vIntRow

dtg_Conferencia.CurrentCell = _

New DataGridCell(vIntRow, 0)

Dim selectedCell As DataGridCell

selectedCell = dtg_Conferencia.CurrentCell

Dim selectedItem As Object

selectedItem = _

dtg_Conferencia.Item(selectedCell.RowNumber, _

selectedCell.ColumnNumber)

cellValue = CInt(selectedItem)

'Preenche o Label com o Código da Conferencia

'selecionado no grid

lbl_Codigo.Text = "Código Selecionado:" & _

cellValue.ToString

Me.Text = "Conferência:" & cellValue.ToString

Catch ex As Exception

Msgbox("Erro : " & ex.Message, MsgBoxStyle.Critical, "ERRO!")

Finally

Cursor.Current = Cursors.Default

End Try

End Sub

Selecionado o Código da Conferência, nossa Tab de trabalho passa a ser a Tabpage2. Nessa Tab, o usuário digita o
número de ordem que identifica o item a ser conferido. Quando ele selecionar o Botão “Pesquisa”, será feito um novo
consumo ao Web Service para carregar as características do item na tela Pocket PC (veja a Listagem 7).
Listagem 7 Código do botão Pesquisa
Private Sub btn_Pesquisa_Click( _

ByVal sender As System.Object, _

ByVal e As System.EventArgs) _

Handles btn_Pesquisa.Click
Cursor.Current = Cursors.WaitCursor

If cellValue = 0 Then

Msgbox("Você não selecionou nenhum Código de Conferência!",

MsgBoxStyle.Information, "Atenção!")

Cursor.Current = Cursors.Default

Exit Sub

ElseIf IsNumeric(txt_NumeroOrdem.Text.Trim) = False Then

Msgbox("Você não digitou números válidos!", MsgBoxStyle.Information, "Atenção!")

Cursor.Current = Cursors.Default

Exit Sub

Else

MoveCampos()

Cursor.Current = Cursors.Default

End If

End Sub

Sub MoveCampos()

Try

Dim WS As New WebReference.Service1

Dim dts As New DataSet

dts = WS.Pesquisa("sp_PDA_PesquisaSubOrdem " & cellValue & "," & CInt(txt_NumeroOrdem.Text))

If dts.Tables("Tabela").Rows.Count = 0 Then

Dim vStrMsg As String = _

Msgbox("Produto não encontrado!Deseja Marcar como Dúvida?", MsgBoxStyle.YesNo, "Atenção!")

If vStrMsg = MsgBoxResult.No Then

Exit Sub

Else

HabilitaText(TabPage2.Controls)

txt_Ordem.Text = txt_NumeroOrdem.Text.Trim

txt_Status.Enabled = False

btn_Confirma.Enabled = False

btn_SalvaDuvida.Text = "Salvar Dúvida!"

End If

Else

LimpaCampos(TabPage2.Controls)

txt_NumeroOrdem.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubOrdem"))

txt_Descricao.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubDescricao"))

txt_Diametro.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubDiametro"))

txt_Formato.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubFormato"))


txt_Gm2.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubGramatura"))

txt_Largura.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubLargura"))

txt_Ordem.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubOrdem"))

txt_Quantidade.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubQuantidade"))

txt_Status.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "ConfSimNaoDuvida"))

If txt_Status.Text = "C" Then

txt_Status.Text = "CONFERIDO"

ElseIf txt_Status.Text = "D" Then

txt_Status.Text = "DÚVIDA"

ElseIf txt_Status.Text = "N" Then

txt_Status.Text = "NÃO CONFERIDO"

End If

End If

Catch ex As Exception

Msgbox("Erro : " & ex.Message, MsgBoxStyle.Critical, "ERRO!")

Finally

Cursor.Current = Cursors.Default

End Try

End Sub

O mais importante do código da Listagem 7 é novamente o consumo do Web Service. Se o Web Service não retornar
nenhum valor no DataSet, sugerimos ao usuário marcar o item como “Dúvida”, caso contrário colocamos os dados de
retorno do Web Service que estão no Data Set nos TextBox correspondentes.
Veja na Listagem 8 um exemplo para gravar Item como conferido no banco de dados.
Listagem 8 Gravar dados no banco de dados
Private Sub btn_Confirma_Click( _

ByVal sender As System.Object, _

ByVal e As System.EventArgs) _

Handles btn_Confirma.Click

'Verifica se há alguma coisa preenchida na tela

'antes de fazer o update

Cursor.Current = Cursors.WaitCursor

If txt_Ordem.Text = "" Then

Msgbox("Não há nada em Tela para ser Marcado como Conferido!",

MsgBoxStyle.Exclamation, "Atenção!")

Cursor.Current = Cursors.Default

Exit Sub

End If

'Rotina para ver se número não foi conferido

If txt_Status.Text <> "NÃO CONFERIDO" Then

Msgbox("Esse número consta como conferido. Você não pode salvar duas vezes!",
MsgBoxStyle.Exclamation, "Atenção!")

Cursor.Current = Cursors.Default

Exit Sub

End If

Dim WS As New WebReference.Service1

Dim xSql As String = "sp_PDA_UpdateConferido " & cellValue & "," & txt_Ordem.Text

Try

WS.FazRSsr(xSql)

Msgbox("Conferido!", MsgBoxStyle.Information, "OK!")

LimpaCampos(TabPage2.Controls)

dtg_Conferidos.DataSource = Nothing

dtg_Duvida.DataSource = Nothing

Catch ex As Exception

Msgbox("Erro Confirmar: " & ex.Message, MsgBoxStyle.Critical, "ERRO!")

Finally

Cursor.Current = Cursors.Default

End Try

End Sub

O consumo do Web Service no código da Listagem 8 destina-se a gravar dados no banco de dados. Passei uma Stored
Procedure com o código do item conferido para o Web Service, que cuidou de gravar os dados na base SQL Server. Caso
o Web Service tenha algum problema para gravar os dados, o tratamento do Catch mostrará um MsgBox com o erro.
Os Tabpages3 e 4 possuem DataGrids para ajudar o usuário a verificar o que foi gravado como conferido e o que foi
gravado como dúvida. O código é basicamente consumir o mesmo Web Service para carregar o DataGrid e a formatação
usa a mesma Classe de Formatação de Grid utilizada acima.
Quando o item pesquisado não for encontrado na Base de Dados, o usuário deverá digitar os dados do item e gravá-lo
na base como “Dúvida”. Para executar esse procedimento, consumi a mesma Sub do Web Service usado para gravar o
Item como “Conferido”, passando para isso o nome da Stored Procedure adequada ao Web Service.
Início da página

Conclusão
Com este projeto, o tempo de execução de conferências de estoque foi reduzido à metade. E, com o complemento do
programa na aplicação Desktop, o tempo para solução das dúvidas também foi reduzido à metade, pois com a aplicação
migrada e funcionando On-Line os encarregados puderam acompanhar de seus Desktops o andamento da conferência
em tempo real.
A partir deste projeto, a equipe ganhou em produtividade e desenvolveu outros aplicativos com sucesso em muito menos
tempo do que era feito no eVB. Além disso, não foram mais necessárias correções manuais resultantes de falhas na
replicação e sincronização dos dados.
O projeto em .Net economizou muito na codificação, se comparado ao eVB, mas o que mais tornou interessante a
migração foi o fato de ela nos permitir utilizar a mesma plataforma de desenvolvimento que a aplicação Desktop. Como
as duas aplicações se completam, isso torna o desenvolvimento muito mais produtivo.
Leandro Machado (leandrobcm@kmpapel.com.br) é pós-graduado em Análise de Sistemas e atualmente gerencia o
departamento de TI da KM Indústria e Comércio de Papel.
OLHO: Fiz a migração em apenas um dia de trabalho. Vale ressaltar que levei esse tempo porque separei algumas horas
para ler o Help do programa.

Perguntas freqüentes para desenvolvedores sobre a migração de


plataformas Windows Mobile
Microsoft
Setembro de 2004
Aplica-se a:

• Software Windows Mobile™

• Pocket PCs baseados no Windows Mobile


Smartphones baseados no Windows

Mobile

• Microsoft eMbedded Visual C++®

• Microsoft eMbedded Visual Basic®


Resumo: Este artigo aborda a maioria dos problemas comuns e das perguntas freqüentes dos desenvolvedores
relacionadas à migração de uma versão da plataforma Microsoft Windows Mobile para outra. Este artigo também contém
links para páginas em inglês. (10 páginas impressas)
Nesta página
Introdução
O software Windows Mobile™ foi criado para ser facilmente adaptado a uma grande variedade de dispositivos de
hardware de diversos fabricantes, operadoras móveis e integradores. Para criar uma plataforma com um conjunto
consistente de recursos para uso pelos desenvolvedores, a plataforma Windows Mobile fornece duas plataformas
definidas: o Pocket PC e o Smartphone baseados no Windows Mobile. Nessas categorias, a plataforma Windows Mobile
permite a personalização por OEMs e operadoras, os quais criam dispositivos exclusivos e atraentes para o mercado.
Nossa meta na Microsoft é garantir uma base de conhecimento consistente para os desenvolvedores de todos esses
dispositivos, para que os aplicativos construídos em um dispositivo e uma plataforma (Pocket PC e Smartphone)
funcionem em outro dispositivo com pouca ou nenhuma modificação. Embora enfatizemos essa meta, existem variações
ocasionais nos produtos e nas plataformas que podem fazer com que os aplicativos tenham comportamentos diferentes
entre alguns dispositivos.
Para garantir que estamos fornecendo o máximo de informações à nossa comunidade de desenvolvedores, este artigo
aborda alguns dos problemas e das perguntas freqüentes mais comuns sobre a migração entre dispositivos e
plataformas. Também fornecemos um email no final deste artigo que você pode usar para relatar problemas comuns,
para que possamos adicioná-los a estas perguntas freqüentes.
Estas perguntas freqüentes têm como público-alvo os desenvolvedores que estão migrando de uma versão anterior do
software Windows Mobile ou trabalhando para estabelecer uma base de código única para todos os dispositivos baseados
no Windows Mobile.
Início da página

Interface do usuário
Por que tenho aceleradores numéricos duplicados nos menus do meu Smartphone?
Quando você cria um menu no Smartphone baseado no Windows Mobile 2003, o sistema operacional adiciona
automaticamente aceleradores numéricos ao menu. Como o sistema operacional faz isso automaticamente para você, o
Designed for Windows Mobile Software Application Handbook for Smartphone (em inglês) especifica que não se deve
adicionar números aos menus para indicar o acelerador do teclado numérico. Essa alteração foi feita na mudança do
Smartphone 2002 para o Smartphone 2003, portanto, os aplicativos que adicionam aceleradores manualmente obtêm
duplicatas na plataforma 2003.
Por que os aceleradores do teclado numérico não funcionam com as caixas de listagem no Smartphone
2003?
Quando a interface do usuário do Smartphone apresenta uma lista ou um menu, geralmente, você pode usar o teclado
numérico para acelerar o acesso ao item desejado pressionando o item equivalente do teclado numérico. Por exemplo,
exibir a lista de sons ou tons de chamada e pressionar a tecla "3" acessa o primeiro item que começa com a letra "d".
Esse recurso funciona bem nos aplicativos de ROM do telefone, mas talvez você encontre aplicativos desenvolvidos com
o Microsoft® eMbedded Visual C++® ou o Microsoft Visual Studio® que não aceitem essas combinações de teclado
numérico com as caixas de listagem. (Essas combinações serão corrigidas em uma versão futura do Smartphone.) Você
também pode contornar esse problema fazendo a subclassificação da janela de destino e filtrando
WM_IME_COMPOSITION para selecionar manualmente o item da lista.
Por que os aceleradores do teclado numérico não funcionam com as caixas de listagem no Smartphone
2003?
Alguns dispositivos baseados no Windows Mobile foram lançados com as cores do sistema de COLOR_BTNFACE e
COLOR_HIGHLIGHT definidas na mesma cor. Geralmente, esses valores são diferentes. Portanto, quando
COLOR_BTNFACE e COLOR_HIGHLIGHT são definidos na mesma cor, alguns elementos da interface do usuário podem
não ser exibidos de formas diferentes, como esperado. É preciso comparar os valores de BTNFACE e HIGHLIGHT e
ajustar os valores de cor de COLOR_BTNFACE e COLOR_HIGHLIGHT, caso valores iguais estejam sendo usados.
Meu Pocket PC costumava indicar determinados eventos pela maneira como o LED piscava. Parece que isso mudou da
versão 2002 para a 2003. O que mudou?
O Pocket PC 2002 fornece opções em "Sons e Notificações" para definir valores para as opções "Exibir mensagem na
tela" e "Piscar para". Essas opções não estão disponíveis no Windows Mobile 2003 para Pocket PC. Para obter mais
informações, consulte Differences in LED flashing functionality between Pocket PC 2002 and Pocket PC 2003 (em inglês).
Início da página

Arquivos e armazenamento
Como posso identificar locais de armazenamento em dispositivos de forma consistente?
O Smartphone tem um armazenamento persistente e um volátil. O armazenamento volátil é reiniciado cada vez que
você desliga o telefone. Quaisquer dados que precisem ser mantidos quando o telefone for desligado são salvos no
armazenamento persistente. O armazenamento persistente é montado como um diretório fora da raiz do software
Windows Mobile 2003 para Smartphone e do software Windows Mobile 2003 Second Edition para Smartphone.
A maioria dos dispositivos Smartphone 2002 usava uma pasta IPSM para representar seus locais de armazenamento
persistente. No Smartphone 2003, o local padrão é geralmente "Armazenamento". Para que o seu aplicativo possa
sempre detectar o caminho do armazenamento correto, você deve utilizar a API SHGetSpecialFolderPath para determinar
locais comuns de armazenamento, como Meus Documentos.
O nome dos locais da placa de armazenamento nos dispositivos Smartphone e Pocket PC pode variar entre os
fabricantes. Como um dispositivo pode ter várias placas de armazenamento com vários nomes, seu aplicativo não pode
pressupor a nomeação ou o caminho de uma placa específica. Use as funções FindFirstFlashCard e FindNextFlashCard
para permitir a enumeração por programação das placas de armazenamento.

Por que meus aplicativos falham após uma suspensão/um reinício?


Por que meus identificadores de arquivo são inválidos em uma placa de armazenamento depois de um
Pocket PC ser desligado e ligado novamente?
Na plataforma 2003, o retardo padrão que pode afetar o carregamento e o descarregamento de alguns drivers em
resposta a um evento de suspensão/reinício foi reduzido. Esse retardo pode afetar os aplicativos que esperam a
preservação de identificadores de arquivo associados a placas SD durante um evento breve de suspensão/reinício. Se, ao
migrar para o Windows Mobile 2003, você tiver problemas com aplicativos que esperam que os arquivos permaneçam
abertos em placas SD entre eventos de suspensão/reinício, tente aumentar para 4096 (caso o valor atual seja inferior) o
seguinte valor do Registro:
HKEY_LOCAL_MACHINE\System\StorageManager
PNPUnloadDelay : 4096
Por que não consigo instalar software em alguns dispositivos Smartphone?
Os Smartphones baseados no Windows Mobile possuem um modelo de segurança de aplicativo que pode ser configurado
pelo fabricante do dispositivo ou pela operadora móvel. Esse modelo de segurança é discutido em detalhes em A
Practical Guide to the Smartphone Application Security and Code Signing Model for Developers (em inglês).
Além das configurações de segurança padrão recomendadas pela Microsoft, o modelo de segurança do Smartphone
permite que uma operadora móvel ou um fabricante de dispositivos proteja seus bens personalizando o dispositivo. Na
maioria dos casos, é necessário apenas verificar as configurações de segurança padrão de cada operadora para
compreender o nível de assinatura necessário para instalar e executar aplicativos em seu dispositivo. Não se esqueça de
que as tentativas de modificar áreas protegidas do Registro ou dados do dispositivo durante a instalação ou execução de
aplicativos poderão ser negadas. Por causa da natureza transacional da instalação do Smartphone, uma tentativa de
modificar uma chave do Registro protegida pode causar a falha da instalação. Por exemplo, alguns telefones Samsung
i600 inicialmente protegiam a chave do Registro HKCR, o que causava algumas falhas de instalação dos aplicativos que
tentavam registrar uma associação de arquivo sob a chave HKCR. A Samsung liberou um patch para expor essa chave.
Para obter mais informações, consulte os seguintes recursos (todos em inglês):

• Mobile2Market Frequently Asked Questions

• Build Applications for Win/dows Mobile-based Smartphones

• Samsung SCH-i600 Smartphone Portal


Estou observando um comportamento imprevisível de meu aplicativo em determinados dispositivos. Qual é a
causa provável?
Meu aplicativo é iniciado, mas é encerrado pelo sistema. O que está acontecendo?
A plataforma Windows Mobile foi desenvolvida para ser executada em dispositivos com pouca memória. Em geral, é
preciso garantir a alocação bem-sucedida da memória antes de usá-la, durante sua solicitação, seja através de malloc()
ou do uso do operador new. Se isso não for feito, poderá ocorrer uma tentativa de uso de memória que não está alocada
para você, o que causará uma violação de acesso.
Os dispositivos são fornecidos com configurações diferentes de memória interna e disponível no primeiro uso. Quando o
espaço de armazenamento torna-se criticamente baixo, os aplicativos que gravam dados no armazenamento e as novas
instalações podem começar a falhar. Se o seu aplicativo for afetado por uma dessas condições, não se esqueça de
verificar a memória e o armazenamento disponíveis no dispositivo de teste. Se o seu aplicativo tiver requisitos de
memória mais rigorosos, talvez seja conveniente verificar a quantidade de memória disponível durante a instalação e
aconselhar os usuários a desinstalar os softwares não utilizados.
Para verificar a memória e o armazenamento em dispositivos Pocket PC, toque em Iniciar, Configurações e Memória.
Para verificar a memória e o armazenamento em dispositivos Smartphone, toque em Programas, Configurações e
Sobre.
Outro fator que pode influenciar a memória disponível para os aplicativos é o espaço de memória "endereçável". O
espaço de memória endereçável é o mapeamento lógico de memória ao qual processos, componentes executáveis e a
memória alocada devem se ajustar para serem executados. O espaço de memória endereçável pode colaborar para
vários problemas de memória, pois pode haver uma grande quantidade de memória disponível no sistema e, mesmo
assim, a área de memória endereçável pode estar esgotada.
O sistema operacional Windows CE, no qual é baseado o software Windows Mobile, também compartilha áreas de
memória endereçável quando você carrega DLLs para preservar memória entre os vários processos. É importante
compreender primeiro a forma como o sistema operacional lida com a memória. Esse assunto é discutido no artigo
Windows CE .NET Advanced Memory Management do MSDN (em inglês).
Por que o arquivo COREDLL.DLL encontra-se no Slot 0 nos dispositivos baseados no Windows Mobile 2003?
A partir do software Windows Mobile 2003, o arquivo coredll.dll foi movido do Slot 1 para o Slot 0 a fim de fornecer um
melhor gerenciamento de memória com o Compact Framework. Embora o Slot 0 seja geralmente uma área de
leitura/gravação da memória, o arquivo coredll.dll ainda é uma DLL XIP (Execute In Place) e, por isso, não precisa ser
copiado para a RAM para ser executado.
Às vezes, o arquivo Autorun.exe não é iniciado como previsto nos dispositivos que contêm mais de uma
placa de armazenamento. Por quê?
O Windows Mobile fornece um mecanismo para iniciar automaticamente um executável chamado "autorun.exe" quando
colocado em uma placa de armazenamento em um diretório especial nomeado de acordo com o tipo do processador (ou
seja, – \placa de armazenamento\2577\autorun.exe). Alguns dispositivos oferecem suporte a várias placas de
armazenamento ou usam placas de armazenamento internas e não removíveis para autoconfigurar o dispositivo. O
arquivo autorun.exe poderá não ser iniciado conforme previsto em dispositivos que contenham mais de uma placa de
armazenamento com um arquivo autorun.exe. Isso será corrigido em versões futuras do Windows Mobile.
Por que meus bancos de dados CEDB não funcionam mais após a migração para o Windows Mobile 2003?
O formato interno dos bancos de dados foi alterado no Pocket PC e no Smartphone baseados no Windows Mobile 2003.
Essa alteração faz com que alguns aplicativos de terceiros parem de funcionar ou exibam uma mensagem de erro ao
tentar acessar dados. São afetados os aplicativos que instalam bancos de dados a partir de arquivos, juntamente com o
próprio programa. Se você possui ou usa um aplicativo que utiliza o CEDB e que funcionava no Pocket PC ou Smartphone
2002 e não funciona corretamente quando esses dispositivos são executados no Windows Mobile 2003, há um assistente
para auxiliar na migração dos bancos de dados. Essa ferramenta também é útil para usuários que inseriram dados em
aplicativos em uma versão anterior do Pocket PC ou do Smartphone, estão fazendo a atualização para a versão 2003 e
desejam migrar seus dados.
Database Conversion Wizard Power Toy
Início da página

Segurança
Por que não consigo usar a RAPI com meu Smartphone?
O uso de APIs RAPI proporciona uma maneira conveniente de acessar recursos de dispositivos a partir de um
computador desktop. O modelo de segurança do Smartphone pode ser configurado para proibir e restringir o uso da
RAPI. O Smartphone usa uma diretiva de segurança de RAPI (4097) que pode ser configurada para permitir ou proibir a
RAPI completamente ou para permiti-la sob uma função restrita (SECROLE_USER_AUTH). Se a RAPI for configurada para
execução sob uma função restrita, você não terá acesso a alguns tipos de chamadas proibidas: CeMountDBVol e
CeRapiInvoke. Os dispositivos Smartphone podem ser configurados para permitir todos os tipos de chamada de RAPI,
mas cabe à operadora móvel ou ao fabricante do dispositivo definir essa diretiva ao construir o dispositivo.
Preciso assinar meus arquivos MUI?
No Smartphone 2002, os arquivos MUI não exigiam assinatura. No Smartphone 2003 e nas versões subseqüentes, os
arquivos MUI precisam ser assinados, bem como outros binários necessários (EXEs, DLLs e CABs).
Como parte de nossas iniciativas de segurança, estamos começando a assinar todos os nossos executáveis.
Entretanto, após assinarmos um aplicativo para Pocket PC, ele não funciona mais. Por quê?
Ao contrário do Smartphone, as plataformas de Pocket PC até a 2003 Second Edition não oferecem suporte à assinatura
de executáveis. Se você assinar um aplicativo para Pocket PC em alguma dessas plataformas, ela relatará que o
aplicativo está corrompido. Esse suporte será oferecido nas futuras implementações da plataforma.
Início da página

Hardware
Que recursos de pad direcional devo visar com meu aplicativo?
Atualmente, a Microsoft exige dos fabricantes a implementação de um pad direcional de cinco direções em dispositivos
Windows Mobile. Alguns fabricantes superaram as exigências básicas fornecendo pads de controle avançados. Os pads
direcionais de oito direções não são incomuns. É preciso lembrar que, embora os dispositivos avançados ofereçam mais
recursos aos aplicativos, talvez nem todos os dispositivos disponíveis no mercado ofereçam suporte a esses recursos. Os
aplicativos devem ser desenvolvidos para funcionar bem em relação às especificações de hardware básicas, de forma a
fornecer a melhor experiência ao usuário e oferecer amplo suporte ao dispositivo.
Início da página

Navegação e conectividade com a Internet


Por que as postagens HTTP superiores a 16 K falham no Windows Mobile 2003?
Quando você muda da plataforma Windows Mobile 2002 para a Windows Mobile 2003, as postagens HTTP superiores a
16 KB podem falhar. Após a postagem de 16 KB de dados, as transações podem atingir o tempo limite e relatar um erro.
Para contornar esse problema, divida os dados em partes pequenas, inferiores a 16 KB.
Por que as páginas da Web têm aparência e comportamento diferentes em dispositivos 2003?
Nos dispositivos baseados no Windows Mobile 2003, o Microsoft Pocket Internet Explorer redimensiona automaticamente
alguns tipos de conteúdo da Web para melhorar a exibição dentro da área do navegador. Esse recurso de
redimensionamento é uma alteração feita em relação à plataforma 2002. Se você otimizou seu conteúdo para
dispositivos móveis e deseja desativar o redimensionamento automático, use a seguinte marca META:
<meta name="MobileOptimized" content="240">

Meu código que usa o controle HTML não exibe mais as imagens corretamente. Por quê?
Se você usa o controle HTML e processa imagens manualmente, esse comportamento foi alterado na plataforma 2003.
No Pocket PC 2002, o arquivo apontado pela marca IMG SRC será transferido em seu estado atual para o manipulador

de notificação NM_INLINE_IMAGE. (Por exemplo, "arquivo://\\Meus Documentos\\logo.bmp" será transferido em seu
estado atual, sem qualquer modificação, para o manipulador WM_NOTIFY.) No Pocket PC 2003, o controle altera a
URL para "arquivo:///Meus Documentos/logo.bmp". Se você estiver acostumado a lidar com a imagem em seu
aplicativo, a seqüência de caracteres NM_HTMLVIEW::szTarget deverá ser convertida corretamente para carregar a
imagem.
Se você estiver usando MFC e quiser personalizar a manipulação das imagens, no Pocket PC 2002, teria que retornar

um valor diferente de zero no manipulador NM_INLINE_IMAGE. A plataforma 2002 também ignora o valor no terceiro
parâmetro de OnNotify. Na plataforma 2003, defina o parâmetro LRESULT de OnNotify como um valor diferente de
zero para informar ao controle que você mesmo já manipulou o evento. Observe que, em ambas as plataformas, é
preciso enviar DTM_SETIMAGE e também retornar um valor diferente de zero do manipulador de notificação.
O que preciso fazer para que meu Pocket PC resolva o "host local" corretamente?
Se você tiver problemas ao usar o servidor HTTP do Pocket PC ou ao conectar-se ao "host local" com um dispositivo
baseado no Windows Mobile 2003, talvez seja preciso configurar o dispositivo da seguinte forma:
1. Crie um arquivo chamado _setup.xml com o seguinte conteúdo:
<wap-provisioningdoc>

<characteristic type="CM_Mappings">

<characteristic type="536870911">

<parm name="Pattern" value="*://localhost/*" />

<parm name="Network" value="{e8e89f5a-d3bb-4c58-9b4e-08279d31044e}" />

</characteristic>
</characteristic>

<characteristic type="Registry">

<characteristic

type="HKLM\Software\Microsoft\ConnMgr\Providers\{EF097F4C-DC4B-4c98-8FF6-AEF

805DC0E8E}\localhost-null">

<parm name="DestId"

value="{e8e89f5a-d3bb-4c58-9b4e-08279d31044e}" datatype="string" />

<parm name="Type" value="0" datatype="integer" />

<parm name="Enable" value="1" datatype="integer" />

</characteristic>

</characteristic>

<characteristic type="CM_Mappings">

<characteristic type="536870911">

<parm name="Pattern" value="*://127.0.0.1/*" />

<parm name="Network" value="{e8e89f5a-d3bb-4c58-9b4e-08279d31044e}" />

</characteristic>

</characteristic>

<characteristic type="Registry">

<characteristic type="HKLM\Software\Microsoft\ConnMgr\Providers

\{EF097F4C-DC4B-4c98-8FF6-AEF805DC0E8E}\localhost-null">

<parm name="DestId" value="{e8e89f5a-d3bb-4c58-9b4e-08279d31044e}"

datatype="string" />

<parm name="Type" value="0" datatype="integer" />

<parm name="Enable" value="1" datatype="integer" />

</characteristic>

</characteristic>

</wap-provisioningdoc>

2. Crie um arquivo de formato de configuração CAB da maneira descrita no MSDN (artigo em inglês).
3. Coloque o arquivo CPF em seu dispositivo e toque nele no Gerenciador de Arquivos. O instalador CAB o processará e
excluirá o arquivo. Não há retorno visual, mas, nesse ponto, você deve conseguir usar http://host local para acessar
o servidor Web local sem uma conexão com a rede.
Por que recebo erros como 12029, 12031 e 12007 quando estabeleço minha conexão usando o InternetOpen
com INTERNET_OPEN_TYPE_PRECONFIG?
Quando você usa INTERNET_OPEN_TYPE_PRECONFIG, o InternetOpen tenta utilizar qualquer informação de proxy
configurada no dispositivo. Muitos Smartphones estão configurados para fazer o roteamento através de proxies
específicos de operadora, o que pode afetar determinados tipos de comunicação com a rede. O
INTERNET_OPEN_TYPE_PRECONFIG recupera suas informações de proxy do Registro, o que pode ser alterado pela
configuração do Pocket Internet Explorer. Uma maneira rápida de verificar se você está lidando com um problema de
proxy é testar o INTERNET_OPEN_TYPE_DIRECT, usar o INTERNET_OPEN_TYPE_PROXY e transferir as informações sobre
o proxy diretamente para o InternetOpen.
Meu código Javascript não funciona mais. O que mudou?
Ao fazer referência a objetos em Javascript com o Windows Mobile 2003, talvez seja necessário usar uma referência de
objeto totalmente qualificada. Por exemplo, se você tivesse um botão dentro de uma marca HTML <FORM>, poderia
acessar o botão <OBJECT>, no Pocket PC 2002, pelo atributo ID, em qualquer ponto do script. No Pocket PC 2003, a
informação deve ser totalmente qualificada, com
window.nome_do_formulário.identificação_do_objeto.ButtonGo.Disabled = 1, por exemplo:
Window.form1.ButtonGo.Disabled = 1
Não consigo carregar um documento XML em Javscript no Windows Mobile 2003.
Usar o Microsoft.XMLDOM para carregar um arquivo XML local em Javascript resultará em um erro de acesso negado no
Windows Mobile 2003. Mas esse método funciona na plataforma 2002. Esse novo erro é causado pelas alterações de
segurança feitas na plataforma 2003. Para contornar esse problema, você pode carregar os dados através de um objeto
ActiveX®, que pode ser criado com êxito em Javascript.
Por que minhas URLs não estão sendo resolvidas corretamente nos dispositivos 2003?
O Pocket Internet Explorer pode codificar incorretamente os dados POST/GET que contenham algumas das entidades de
caracteres HTML. Por exemplo, qualquer URL que contenha &or, &lang ou & seria convertida incorretamente. Uma lista
das entidades afetadas pode ser encontrada em http://www.w3.org/TR/html401/sgml/entities.html (em inglês). Esse
problema afeta apenas os dispositivos baseados na plataforma 2003, e será corrigido nas futuras atualizações da
plataforma.
Início da página

Exibição
Como lidar com dispositivos com suporte a alta resolução, rotação e paisagem?
O software Windows Mobile 2003 Second Edition introduz novos recursos de exibição à plataforma Windows Mobile. Se
você fez suposições em relação à resolução e à orientação de tela, talvez tenha que tomar algumas providências para
garantir que os aplicativos funcionem em todos os dispositivos baseados no Windows Mobile 2003 Second Edition. A
primeira etapa é testar os aplicativos para verificar se eles funcionam conforme previsto em dispositivos de alta
resolução e nos modos paisagem ou quadrado.
Você também encontrará um novo aviso ao instalar aplicativos antigos em dispositivos baseados no Windows Mobile
2003 Second Edition. Informações sobre os novos recursos e instruções sobre como empacotar o aplicativo para suporte
nessa plataforma podem ser encontradas no pacote de recursos para desenvolvedores para o software Windows Mobile
2003 Second Edition.
Por que as imagens da barra de ferramentas parecem desaparecer do meu aplicativo ao executar o Windows
Mobile 2003 Second Edition?
As imagens da barra de ferramentas parecem desaparecer por causa de uma alteração de comportamento feita no
Windows Mobile 2003 Second Edition, que faz com que as imagens sejam excluídas quando a barra de ferramentas é
removida do formulário. O desaparecimento das imagens da barra de ferramentas também pode ocorrer devido a uma
alteração das propriedades MinimizeBox, WindowState ou FormBorderStyle do formulário. Essa alteração pode provocar
a recriação do formulário no nível nativo, o que resulta na remoção de sua barra de ferramentas do formulário.
Para contornar isso, defina a propriedade ImageList da barra de ferramentas após executar as operações mencionadas
anteriormente.
Na execução em modo paisagem do Windows Mobile 2003 Second Edition, todas as minhas caixas de diálogo
têm barras de rolagem, embora não tenham controles posicionados abaixo da parte inferior da tela. Por
quê?
As barras de rolagem são exibidas mesmo que as caixas de diálogo não tenham controles posicionados abaixo da parte
inferior da tela porque uma alteração de comportamento do Windows Mobile 2003 Second Edition faz com que barras de
rolagem desnecessárias sejam exibidas. Como o recurso de barra de rolagem automática não ocorre quando há um
controle guia na caixa de diálogo, uma solução alternativa seria criar um controle guia (classe SysTabControl32), colocá-
lo fora da área da tela (usando coordenadas negativas) e desativar sua propriedade WS_VISIBLE. Isso garante que
nenhuma barra de rolagem vertical seja introduzida automaticamente quando o dispositivo for alternado para o modo
paisagem.
Devo usar GETRAWFRAMEBUFFER, GAPI ou GDI? E o DirectX?
Freqüentemente, os desenvolvedores perguntam qual abordagem devem usar para desenvolver elementos gráficos e
jogos em plataformas Windows Mobile para oferecer suporte a um número maior de recursos de exibição e usufruir das
mais eficientes interfaces disponíveis. Os recursos podem variar de um dispositivo para o outro. O Microsoft DirectX®
estará disponível para as versões futuras da plataforma Windows Mobile para fornecer uma interface familiar aos
desenvolvedores de jogos que visam os dispositivos baseados no Windows Mobile. Uma solução eficiente para os
aplicativos que precisam de acesso rápido ao vídeo começa com a solicitação de ExtEscape a GETRAWFRAMEBUFFER, o
retorno a GAPI e, por fim, a GDI, respectivamente.
Geralmente, GETRAWFRAMEBUFFER é implementado em dispositivos com telas de alta resolução e buffers de quadro
endereçáveis linearmente. Se você obtiver um erro ao solicitar esse ExtEscape, seu dispositivo não possui uma tela de
alta resolução endereçável linearmente. Geralmente, a GAPI oferece aos desenvolvedores de jogos um suporte
abrangente a dispositivos, incluindo compatibilidade com versões anteriores para os aplicativos existentes. A GDI é o
núcleo do sistema operacional e impede que o usuário veja os detalhes do hardware. Normalmente, ela oferece um
suporte surpreendentemente bom aos aplicativos que usam muitos elementos gráficos.
Início da página

Áudio
Por que meu fluxo de áudio está entrecortado?
Talvez você note uma saída de rotina de serviço de som entrecortada em alguns dispositivos Pocket PC e Smartphone.
Normalmente, isso ocorre quando o uso da CPU aproxima-se de 100% e uma alta taxa de bits é usada. Esse problema
ocorre geralmente por causa de pequenos retardos na solicitação de retorno de chamada do sistema operacional ou do
driver de som introduzido em determinados dispositivos baseados no Windows Mobile 2003.
No desenvolvimento de jogos, é comum criar um segmento separado para atender à solicitação de dados adicionais de
som do driver de som. No entanto, esse método pode colaborar para o retardo que causa esse problema. Para contornar
esse problema, você pode integrar suas rotinas de serviço de som ao segmento principal (aquele que alcança cerca de
100% de uso da CPU). Além disso, pode incluir um Sleep(0) no loop principal, o que pode melhorar a latência das
chamadas de retorno.
Se os problemas persistirem quando o jogo estiver carregando elementos gráficos ou executando qualquer tarefa que
leve mais tempo do que a execução dos buffers de som da fila, considere a possibilidade de mover o carregamento de
elementos gráficos para um ponto anterior da execução do programa a fim de evitar que isso seja feito durante a
execução do som.
Por que minha gravação está em branco?
A gravação do som em alguns dispositivos Smartphone 2002 poderá ser atrasada ou suspensa durante períodos de
atividade da rede UDP. Esse problema poderá surgir se você começar a gravar usando um aplicativo que utilize um fluxo
do tráfego de UDP e, em seguida, parar a gravação. O período durante o qual a atividade da rede UDP ocorreu poderá
provocar um fluxo de gravação vazio. Em alguns dispositivos, esse fluxo de gravação vazio é causado por um problema
do driver.
Início da página

Ambientes de compilação e de desenvolvimento


Por que o meu código se comporta de maneira diferente após a compilação com o eMbedded Visual C++ 4.0?
O Microsoft eMbedded Visual C++® 4.0 mudou a forma como algumas otimizações de código era realizadas. Se você
encontrar diferenças de comportamento em áreas altamente otimizadas de seu código, considere a possibilidade de
desativar temporariamente essas otimizações para ajudá-lo a determinar se as alterações de compilador podem ser um
fator. Para desativar as otimizações, basta abrir Project (Projeto), escolher Settings (Configurações) e desativar as
otimizações na guia C++. Se você estiver migrando projetos do eMbedded Visual C++ 3.0 para o eMbedded Visual C++
4.0, não se esqueça de consultar o artigo Migrating to the eVC 4.0 Environment do MSDN (em inglês).
O que mudou nos controles ActiveX?
A partir do software Windows Mobile 2003, os controles ActiveX recém-criados exigem que o modelo de segmentação do
componente seja declarado como Free ou Both quando registrados. Nas versões anteriores do Windows Mobile, o
sistema ignorava essa configuração de registro, mas agora ela é necessária.
Por que os aplicativos que usam o eMbedded Visual Basic ou o ADOCE falham nos dispositivos baseados no
Windows Mobile 2003?
Nos dispositivos baseados no Windows Mobile 2003, o ADOCE e o tempo de execução do Microsoft eMbedded Visual
Basic® não é mais incluído na ROM. Eles podem ser instalados, baixados e instalados manualmente. Lembre-se, no
entanto, de que não há suporte ao desenvolvimento de código usando o eMbedded Visual Basic na versão 2003. O
objetivo é apenas permitir que os clientes executem aplicativos Pocket PC 2002 não modificados na versão 2003.
Baixe o tempo de execução do eMbedded Visual Basic para Pocket PC 2003.
O que aconteceu com a imgdecmp.dll? Como posso carregar formatos de imagem comuns diretamente
através de programação?
Tradicionalmente, os dispositivos Windows Mobile incluíam uma biblioteca ROM chamada imgdecmp.dll para uso pelos
aplicativos da Microsoft. Essa biblioteca exportava uma API que não era documentada no SDK, mas que era usada pelos
desenvolvedores para carregar arquivos de imagem. A partir do Windows Mobile 2003 Second Edition, essa biblioteca foi
removida e as novas APIs do shell, SHLoadImageFile e SHLoadImageResource, foram adicionadas aos cabeçalhos de
aygshell.h. Essas APIs podem ser usadas no lugar do suporte a imagens anteriormente fornecido pela imgdecmp.dll.
Observação: A Imgdecmp.dll será removida de uma versão futura da plataforma Windows Mobile.
Por que não consigo localizar (enumerar) dispositivos Bluetooth usando
WSALookupService/WSALookupServiceNext?
Os fabricantes de dispositivos podem carregar dispositivos Windows Mobile com a opção de pilhas Bluetooth. A partir do
Windows Mobile 2003, a Microsoft fornece uma pilha Bluetooth compatível com as nossas APIs de Bluetooth e de
soquete. Se um fabricante de dispositivos optar por usar uma pilha Bluetooth alternativa, talvez seja necessário usar o
SDK ou o conjunto de APIs fornecido juntamente com esse outro fornecedor. Por exemplo, muitos produtos da iPaq
usam uma pilha Bluetooth Widcomm. A Widcomm fornece seu próprio SDK aos fornecedores para que eles desenvolvam
aplicativos Bluetooth com base nesses dispositivos.
Início da página

Fale conosco
Ajude-nos a tornar mais fácil a migração entre diferentes plataformas e dispositivos Windows Mobile.
Se você enfrentar problemas inconsistência de desenvolvimento entre plataformas Windows Mobile ou precisar criar
código personalizado para contornar problemas específicos de dispositivo, informe-nos pelo email
wmdevmig@microsoft.com.
© 2004 Microsoft Corporation. Todos os direitos reservados. Termos de uso.

Uma comparação técnica entre os recursos de replicação e acesso remoto


a dados no SQL Server 2005 Mobile Edition 3.0
Debra Dove
Março de 2005
Resumo: compare os conjuntos de recursos das duas soluções de conectividade do Microsoft SQL Server 2005 Mobile
Edition 3.0 (SQL Server Mobile), replicação de mesclagem e RDA (acesso remoto a dados), e saiba qual delas pode ser
mais vantajosa para o seu projeto. Este artigo também contém links para páginas em inglês (7 páginas impressas).

Introdução
O Microsoft SQL Server 2005 Mobile Edition 3.0 (SQL Server Mobile) dá suporte a dois métodos de troca de dados com
um banco de dados do SQL Server:
Replicação de mesclagem, uma solução robusta e cheia de recursos que permite a um aplicativo móvel fazer

alterações autônomas em dados replicados e, posteriormente, mesclar essas alterações com um banco de dados do
Microsoft SQL Server e resolver conflitos quando necessário.
O RDA (acesso remoto a dados) permite que um aplicativo móvel acesse (receber) e envie dados para/de uma tabela

de banco de dados remoto do Microsoft SQL Server e uma tabela de banco de dados local do SQL Server Mobile. O
RDA também pode ser usado para emitir comandos SQL em um servidor que esteja executando o SQL Server.
Início da página

Conectando o SQL Server Mobile ao SQL Server


Quando você usa o RDA ou a replicação, o SQL Server Mobile se conecta ao SQL Server por meio de um servidor IIS
(Serviços de informações da Internet) da Microsoft. Como a conectividade com o SQL Server é administrada pelo IIS, o
dispositivo só precisa se conectar ao servidor IIS da Web usando o protocolo HTTP ou HTTPS. Isso permite que você
execute operações de RDA e replicação em qualquer tipo de conexão de rede que ofereça suporte a HTTP: LANs (redes
locais), WANs (redes de longa distância) e conexões com o Microsoft ActiveSync®.
Tanto o RDA quanto a replicação são bastante adequados para o transporte sem fio. A compactação é usada para
diminuir o tamanho dos dados transmitidos. A criptografia pode ser usada para proteger dados confidenciais do usuário
durante a transmissão.
O RDA e a replicação oferecem suporte à Autenticação integrada do Windows e à autenticação do SQL Server durante a
conexão com o SQL Server.
Início da página

Introdução à replicação de mesclagem


A replicação do SQL Server Mobile é baseada na replicação de mesclagem do Microsoft SQL Server. A replicação de
mesclagem pode ser implementada por meio de um banco de dados do Microsoft SQL Server 2000 ou do Microsoft SQL
Server 2005.
Ela é ideal para aplicativos móveis porque permite que os dados sejam atualizados de modo autônomo e independente
no dispositivo portátil e no servidor. Os dados no dispositivo e no servidor são posteriormente sincronizados para que
seja possível enviar as alterações do cliente ao servidor e receber as novas alterações do servidor.
Embora a replicação de mesclagem exija mais configuração e manutenção no servidor do que o RDA, ela oferece
diversas vantagens, entre as quais se destacam as seguintes:

• A replicação oferece recursos internos e personalizados de resolução de conflitos.

• A replicação possibilita sincronizar dados de várias tabelas de uma só vez.


A replicação oferece opções avançadas de replicação de dados, incluindo:

Seleção de tipos de artigo e filtragem para melhorar o

desempenho

• Gerenciamento do intervalo de identidade


O Microsoft SQL Server inclui muitas ferramentas para:

Criar e sincronizar assinaturas do SQL Server

Mobile

• Monitorar assinantes de cada publicação


Para obter informações gerais sobre como funciona a replicação de mesclagem, consulte "How Merge Replication Works"
no SQL Server Books Online (em inglês). Para obter informações específicas de replicação relacionadas a assinantes do
SQL Server Mobile, consulte "How Replication Works" e "Replication Architecture" no SQL Server Mobile Books Online
(em inglês).
Início da página

Introdução ao RDA (acesso remoto a dados)


O RDA permite que aplicativos móveis acessem dados de uma tabela de banco de dados remoto do SQL Server e os
armazene em uma tabela de banco de dados local do SQL Server Mobile. O aplicativo poderá, então, ler e atualizar a
tabela do banco de dados local do SQL Server Mobile. Também existe a opção de o SQL Server Mobile controlar todas as
alterações que são feitas na tabela local. Posteriormente, o aplicativo poderá atualizar os registros alterados da tabela
local de volta na tabela do SQL Server.
No SQL Server Mobile, a propagação de dados da tabela do SQL Server para uma tabela local do SQL Server Mobile é
chamada de recepção de dados. A propagação das alterações feitas na tabela local do SQL Server Mobile de volta para a
tabela do SQL Server é chamada de envio de dados.
O RDA é apropriado quando a funcionalidade completa da replicação de mesclagem do SQL Server Mobile, incluindo a
resolução de conflitos, não é necessária.
A decisão entre usar o RDA e a replicação no aplicativo SQL Mobile depende da finalidade, da função, da escala e dos
requisitos do aplicativo móvel. Cada solução de conectividade de dados apresenta suas próprias vantagens e
desvantagens. Essas vantagens e desvantagens são analisadas nas próximas seções.
Início da página

Comparação de recursos
Esta seção faz uma pequena análise das diferenças de suporte de recursos comparáveis entre a replicação e o RDA. O
objetivo desta seção não é descrever cada recurso disponível para replicação ou RDA, mas apenas mostrar as diferenças
dos recursos que podem ser comparados. Por exemplo, existem vários recursos adicionais de replicação para os quais o
RDA não tem nenhuma funcionalidade comparável, por isso eles não são examinados neste artigo.
Início da página

Capacidade de invasão do servidor


Os vários recursos de replicação de mesclagem do SQL Server Mobile são derivados dos recursos de replicação de
mesclagem do SQL Server. O SQL Server Mobile se beneficia desses recursos mediante a assinatura de publicações do
SQL Server. Criar uma publicação no servidor é uma operação que adiciona várias tabelas de sistema ao banco de dados
que será publicado e uma coluna de sistema de Identificador Exclusivo a cada tabela de usuário publicada. Esses objetos
de sistema são adicionados ao banco de dados do servidor para o gerenciamento da replicação. O RDA não requer que
nenhuma alteração seja feita no banco de dados do servidor. Como o RDA não é invasivo em relação ao esquema no
servidor, sua funcionalidade fica limitada e não oferece todos os recursos da replicação, mas permite transferir dados
entre o SQL Server e o SQL Server Mobile sem nenhum trabalho de configuração no servidor. Em algumas situações,
talvez você não tenha permissão para alterar o esquema no sistema back-end. Nesse caso, o RDA é uma possível opção
de conectividade para transferir alterações de dados entre o SQL Server e o SQL Server Mobile. Outra opção a ser
considerada, se você desejar obter a funcionalidade robusta de replicação e se o esquema do banco de dados back-end
não puder ser alterado, é usar um SQL Server de camada intermediária como servidor de replicação e, depois, transferir
os dados para o sistema back-end utilizando outra tecnologia não-invasiva, como DTS (Data Transformation Services) ou
serviços da Web.
Em resumo, a replicação requer pequenas alterações no esquema do banco de dados do servidor, mas essas alterações
permitem usar um conjunto de recursos bastante robusto; já o RDA não exige alterações no banco de dados do servidor,
porém não é uma solução de conectividade completa.
Início da página

Definição do esquema e dos dados


Conforme já mencionamos neste documento, como preparativo para a replicação, você deve criar uma publicação no
servidor. Os dados e o esquema a serem replicados para o Assinante são especificados no servidor quando a publicação
é criada. O esquema é definido automaticamente no cliente quando a assinatura é criada pela primeira vez. A publicação
define as tabelas (artigos) a serem replicadas para o Assinante, incluindo filtragem de linha (limitando o fluxo de dados
através de uma cláusula WHERE) e filtragem de coluna (limitando as colunas da tabela a ser replicada). Uma publicação
pode conter uma ou várias tabelas. A definição de publicação inteira, incluindo os dados e o esquema das tabelas que
estão sendo replicadas, é criada no banco de dados do SQL Server Mobile por meio de um método de sincronização.
Quando o RDA é usado, o cliente controla quais dados são transferidos do servidor, inclusive a definição dos dados (e a
filtragem de linha) e a definição do esquema (e a filtragem de coluna). Ao contrário da replicação, em que várias tabelas
podem fazer parte de uma única sincronização, o RDA limita-se a uma tabela por processo de recepção de dados.
Em resumo, os dados e o esquema a serem criados inicialmente no banco de dados do SQL Server Mobile são definidos
no servidor com a replicação e no cliente com o RDA. Uma publicação de replicação pode conter várias tabelas, ao passo
que o RDA está limitado a uma tabela por download (processo de recepção de dados do RDA).
Início da página

Alterações no esquema
Quando a replicação é usada, as alterações feitas no esquema (como adicionar ou eliminar colunas e restrições, ou ainda
modificar uma definição de coluna) podem ser replicadas para o Assinante depois que a assinatura é criada. Quando uma
alteração pendente no esquema precisa ser replicada para um Assinante, primeiro ela é replicada e, depois, as
alterações delta resultantes são trocadas entre o Editor e o Assinante. As alterações feitas no esquema do servidor não
necessariamente fazem com que o aplicativo seja modificado e recompilado; por exemplo, se uma coluna tiver sido
adicionada ou removida por não estar sendo utilizada no aplicativo. Pequenas alterações feitas no esquema do banco de
dados de assinaturas são permitidas. Para obter mais informações, consulte "Replication Limitations" no SQL Server
Mobile Books Online (em inglês).
Quando o RDA é usado, não há suporte para as alterações feitas no esquema do servidor. Dependendo da alteração no
esquema, um envio de dados do cliente para o servidor poderá não ser bem-sucedido. Se o esquema do servidor for
alterado, o cliente deverá eliminar a tabela e receber todos os dados do servidor novamente. Alterações no esquema do
servidor podem fazer com que você tenha de modificar e recompilar seu aplicativo. Pequenas alterações no esquema do
cliente são permitidas. Para obter mais informações, consulte "Remote Data Access (RDA) Limitations" no SQL Server
Mobile Books Online (em inglês).
Em resumo, quando a replicação é usada, as alterações que são feitas no esquema do servidor são permitidas, não
havendo perda de dados do Assinante. Entretanto, não há suporte para as alterações no esquema do servidor quando o
RDA é utilizado, e tais alterações podem resultar em perda de dados no cliente. Pequenas alterações no esquema são
permitidas nas tabelas de conectividade do SQL Server Mobile tanto para a replicação quanto para o RDA.
Colunas de identidade
Em muitos aplicativos, convém usar um número incremental para gerenciar registros inseridos em um banco de dados
cliente. Por exemplo, se o usuário estiver inserindo novos pedidos em uma tabela, convém atribuir um número
incremental automaticamente para cada pedido. Quando um sistema como esse é utilizado, não deve haver conflitos de
números entre os clientes. A replicação oferece suporte ao gerenciamento automático do intervalo de identidade para as
colunas integer e bigint de publicações e assinaturas. O uso do gerenciamento automático do intervalo de identidade
assegura que não haverá conflitos de linhas, independentemente do número de clientes que você tiver. Para obter mais
informações sobre como utilizar esse recurso de replicação, consulte "Replicating Identity Columns" no SQL Server Books
Online (em inglês).
Quando o RDA é utilizado, não há suporte para o gerenciamento automático de colunas de identidade. Para usar essa
propriedade de tipo de dados, você deve gerenciar os valores do sistema inteiro manualmente.
Restrições e índices
Como a replicação oferece suporte a várias tabelas na definição da publicação, os índices e as restrições de integridade
referencial são automaticamente replicados do servidor para o banco de dados de assinaturas.
Pelo fato de o RDA oferecer suporte apenas ao recebimento de uma única tabela por vez, as restrições de integridade
referencial não são transferidas, mas os índices podem ser transferidos. Outras definições de esquema devem ser feitas
no cliente.
Troca de alterações de dados controladas
Se você usar a replicação, objetos de sistema serão criados no banco de dados de publicação quando a publicação for
criada e no banco de dados do SQL Mobile quando a assinatura for criada. Esses objetos possibilitam um modelo de troca
de dados bastante robusto entre o SQL Server e o SQL Server Mobile. A replicação tem como objetivo controlar as
alterações nos bancos de dados de publicação e de assinaturas. Existem dois níveis de controle do fluxo de dados entre o
Editor e o Assinante:
O controle no nível de linha em uma tabela implica que a linha inteira seja transferida durante a sincronização. Esse

nível de controle pode ser mais dispendioso para a transferência de dados, dependendo da velocidade da conexão,
mas requer que menos informações de controle sejam armazenadas no Editor e no Assinante.
O controle no nível de coluna diminui o volume de dados transferidos porque controla as alterações no nível de coluna

e de linha e permite que somente os dados de colunas alterados sejam transferidos do Assinante para o Editor. O
Editor sempre enviará toda a linha alterada para o Assinante porque não tem certeza de que a linha realmente já
existe no Assinante.
Para obter mais informações sobre níveis de controle, consulte "Using Row-Level and Column-Level Tracking" no SQL
Server Mobile Books Online (em inglês) e "Row-Level Tracking and Column-Level Tracking" no SQL Server Books Online
(em inglês).
O RDA usa objetos de sistema no banco de dados do SQL Server Mobile para controlar as alterações feitas nos dados.
Com essas informações, o RDA só envia as alterações, especificamente as linhas alteradas, do SQL Server Mobile para o
SQL Server. Para que o SQL Server Mobile recupere as alterações de dados do SQL Server, é necessária uma atualização
completa dos dados do cliente.
Em resumo, a replicação oferece suporte à troca de dados bidirecional das alterações delta, pois as alterações nos dados
são controladas no Editor e no Assinante. O RDA oferece suporte apenas à troca de dados de alterações delta do SQL
Server Mobile para o SQL Server e exige uma atualização completa dos dados para que sejam recebidas as alterações do
SQL Server para o SQL Server Mobile, pois as alterações nos dados são controladas apenas no banco de dados do SQL
Mobile.
Início da página

Tipos de tabelas que podem ser propagadas


Ao adicionar uma tabela a uma publicação de replicação, você pode definir propriedades na tabela para controlar o fluxo
de dados. Além de usar as propriedades da tabela para controlar o fluxo de dados entre o Editor e o Assinante, você
pode usá-las para melhorar o desempenho da sincronização. Para obter mais informações, consulte "Parameterized Row
Filters" e "Optimizing Merge Replication Synchronization Performance with Download-Only Articles" no SQL Server Books
Online (em inglês).
O RDA não oferece suporte a nenhuma propriedade de tabela usada para controlar o fluxo de dados ou melhorar o
desempenho. Os dados a serem enviados para o cliente são controlados somente pelo código de aplicativo no cliente.
Início da página

Conflitos
Quando a replicação é usada como solução de conectividade, os conflitos em potencial incluem dados alterados por
usuários diferentes e linhas que não foram aplicadas devido a um erro. A replicação oferece suporte integral à resolução
e ao gerenciamento de conflitos no servidor, incluindo resolvedores de conflitos internos e personalizados. Para obter
mais informações, consulte "Replication Conflict Detection and Resolution" no SQL Server Mobile Books Online (em
inglês) e "Merge Replication Conflict Detection and Resolution" no SQL Server Books Online (em inglês).
Quando o RDA é usado, os conflitos só incluem linhas que não foram aplicadas devido a um erro. O RDA não detecta se
um usuário diferente alterou os dados. Por isso, ele sempre tem o comportamento "a última gravação prevalece" quando
envia dados ao servidor. Os conflitos não são gerenciados, mas podem ser reportados em uma tabela de erros no
cliente. Não há suporte para resolvedores de conflitos, uma vez que os conflitos não são resolvidos.
Início da página

Ferramentas (interface do usuário)


O SQL Server Management Studio inclui um abrangente conjunto de ferramentas que podem ser usadas para criar e
gerenciar assinaturas, inclusive ferramentas de monitoramento utilizadas para gerenciar várias assinaturas e
supervisionar os tempos de sincronização e desempenho.
O RDA só pode ser usado por meio de código; não há ferramentas disponíveis no SQL Server ou no SQL Server Mobile
para enviar e receber alterações ou para monitorar clientes.
Início da página

Conclusão
Neste artigo, você conheceu as principais diferenças entre os recursos mais importantes da replicação de mesclagem e
do RDA. Com essas informações, você pode fazer a escolha certa quanto à solução de conectividade mais adequada para
as suas necessidades de aplicativo do SQL Server Mobile. Por exemplo, seu aplicativo pode se beneficiar da simplicidade
do RDA ou da funcionalidade robusta da replicação de mesclagem. Em alguns casos, você pode combinar os recursos do
RDA e da replicação de mesclagem em uma única solução para dados não conflitantes no servidor.
Para obter mais informações
Página do produto SQL Server
© 2005 Microsoft Corporation. Todos os direitos reservados. Termos de uso.

Globalização de aplicações móveis utilizando Resource Files


Por Max Mosimann Netto, fundador e líder do grupo de usuário Codificando.net, autor de
vários artigos sobre mobilidade, ministra treinamentos sobre desenvolvimento e
integração de aplicações para dispositivos móveis.
Tecnologias Utilizadas

• .NET Compact Framework


Com a globalização em alta, agora não são exigidos somente softwares de qualidade e empresas certificadas. Criou-se a
necessidade de softwares disponíveis em vários idiomas.
Para se manter a frente dos concorrentes, o software deve ser capaz de resolver problemas com rapidez e efeciência, e
além de tudo, deve ser de fácil entendimento por todos que o usam.
A plataforma .NET disponibiliza um recurso chamado Resource Files - Arquivos que contém informações (dados de
qualquer tipo) e que podem ser usados como um mini banco de dados. Os arquivos Resource Files são basicamente
estruturas XML que possuem uma divisão simples e ordenada.
Neste artigo, apresento uma forma simples de usar Resource Files para internacionalizar uma aplicação desenvolvida
para Pocket PC.
A aplicação é basicamente um exemplo de formulário de cadastro, que com apenas um click mudar de idioma.
Adicionei apenas as opções Português e Inglês, mas não há um limite determinado para o número de idiomas que uma
aplicação pode possuir. Porém, devemos levar em consideração os recusos de disco e memória que os dispositivos
possuem.
Iremos criar um novo projeto do tipo Smart Device Application, e escolher a plataforma Pocket PC, como pode ser visto
na Figura 1 e na Figura 2.

Figura 1
Figura 2
Iremos adicionar os controles e nomeá-los, como pode ser visto na Figura 3 e na tabela abaixo:

Name Text

lblTitulo Cadastro

lblNome Nome:

lblEndereco End.:

lblCidade Cidade:

lblTelefone Telefone:

lblEmail E-mail:

btnCadastrar Cadastrar >>

Figura 3
Iremos adicionar agora, dois arquivos Resource Files que irão conter os textos nos dois idiomas (Português e Inglês).
Acessamos o menu Project > Add New Item. Selecionamos o arquivo do tipo Assembly Resource File e iremos nomeá-
lo como strings.en-US.resx , como pode ser visto na Figura 4. Esse nome é apenas um padrão criado por mim, o nome
do arquivo pode ser gerado de acordo com sua criatividade e necessidade. Escolhi este formato, pois indica que o
arquivo contém strings no idioma en-US (Inglês EUA).

Figura 4
Assim que adicionado, devemos preencher o conteúdo do arquivo com os respectivos valores. Para cada controle que
desejamos internacionalizar, devemos adicionar no arquivo .resx (Resource File) o valor para o novo idioma, como pode
ser observado na Figura 5 e Figura 6.

Figura 5
Figura 6
A lógica que iremos adicionar irá ler os valores do arquivo de idiomas que acabamos de fazer, e substituirá o valor
(propriedade Text) atual pelo value encontrado para seu respectivo nome no arquivo resx.
Iremos adicionar dois métodos (procedure e function) em nosso código. O primeiro: GlobalizaForm() é uma procedure
que será responsável por varrer todos os controles da aplicação, e passar seu respectivo nome para a função
GetControlValue() que por sua vez, será responsável por acessar o arquivo de idiomas (.RESX) e verificar se o controle
informado possui um value alternativo naquele idioma. Caso o controle não possua um value no idioma escolhido, o
sistema ignora-o e passa para o próximo controle.
Private Sub GlobalizaForm()

Dim sValue As String

For Each ctrl As Control In Me.Controls

Try

sValue = objRes.GetString(GetControlValue(ctrl), currentCulture)

If sValue <> "" Then

ctrl.Text = sValue

End If

Catch

End Try

Next

End Sub

Public Function GetControlValue(ByVal ctrl As Control) As String

Try

Dim fi As FieldInfo() = ctrl.Parent.GetType().GetFields(BindingFlags.NonPublic _

Or BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.IgnoreCase)

For Each f As FieldInfo In fi

If f.GetValue(ctrl.Parent).Equals(ctrl) Then

Return f.Name.Substring(1)

End If

Next

Return ""

Catch

End Try

End Function
Devemos também, adicionar a referência a dois namespaces:
Imports System.Globalization

Imports System.Reflection

Devemos também declarar as variaveis dos objetos responsáveis por acessar arquivos .RESX e manipular a cultura da
aplicação:
Dim currentCulture As CultureInfo

Dim objRes As System.Resources.ResourceManager

Essas duas últimas, devem ser declaras de forma global, ou seja: na raiz da classe do projeto.
No evento Load do formulário, devemos criar a instância do objeto Resources, indicando qual padrão de arquivos que ele
deve assumir como arquivos presentes na aplicação.
Private Sub frmMultLanguage_Load(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles MyBase.Load

objRes = New Resources.ResourceManager("Pocket_MultLanguage.strings", _

System.Reflection.Assembly.GetExecutingAssembly)

End Sub

O primeiro parâmetro do método ResourceManager é o nome da Solution que criamos para nosso projeto e o nome do
arquivo .resx base, em nosso caso strings.
O segundo parâmetro é o path completo incluíndo o nome do executável de nossa aplicação. O método
GetExecutingAssembly retorna o caminho completo do executável da aplicação quando executado no Pocket.
No evento Click de cada submenu que criamos, iremos adicionar o código para que a aplicação seja convertida para o
novo idioma (no caso do exemplo: Português > Inglês e Inglês > Português).
Private Sub mnuEnglish_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles mnuEnglish.Click

mnuEnglish.Checked = True

mnuPortugues.Checked = False

currentCulture = New CultureInfo("en-US")

GlobalizaForm()

End Sub

Private Sub mnuPortugues_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles mnuPortugues.Click

mnuEnglish.Checked = False

mnuPortugues.Checked = True

currentCulture = New CultureInfo("pt-BR")

GlobalizaForm()

End Sub

Quando executada, nossa aplicação será como vista na Figura 7. O idioma da aplicação poderá ser alterado bastando
clicar no menu de idiomas e escolher um dentre a lista, como pode ser visto na Figura 8.
Figura 7
Figura 8
Ao selecionarmos o idioma English por exemplo, o sistema percorrerá todos os objetos disponíveis na aplicação, e
tentará encontrar um texto correspondente no arquivo de idiomas. Caso localize um valor para o controle corrente, o
mesmo terá seu texto alterado de acordo com o texto informado no arquivo de idiomas. Como pode ser visto na Figura
9.
O resultado final será uma aplicação multi-idiomas, que possui todos os arquivos necessários para ser traduzida em
tempo real para qualquer idioma (desde que preparados seus respectivos arquivos .RESX).

Figura 9
Espero que este artigo tenha esclarecido como é possível de forma simples e rápida, transformar sua aplicação em uma
aplicação globalizada. O código fonte completo do exemplo utilizado no artigo, pode ser baixado clicando aqui.
Abraços,
Max Mosimann Netto

Windows Mobile 5.0 - Acesso a Dados com Pocket PC (code name


Magneto)
Por Renato Haddad
A Microsoft lançou recentemente o sistema operacional para o mundo móvel, chamado Windows Mobile 5.0, o qual
contém diversas novas funcionalidades para quem desenvolve tanto para mobilidade quanto sistemas embarcados
(embedded).
O foco deste artigo é mostrar o acesso a dados no Pocket PC Magneto, assim como as facilidades de criar um banco de
dados no SQL Server Mobile Edition. Só vale um alerta: você irá se apaixonar com tanta facilidade!

Para usar o Magneto é preciso ter o Visual Studio .NET 2005 instalado, e em seguida, instalar o SDK do Windown Mobile
5.0. Uma vez instalado, no VS.NET 2005, selecione o menu File/New/Project, selecione Smart Device / Pocket PC
Magneto e os devidos nomes e pastas onde o projeto será criado.

Clique no botão OK para criar o projeto. Será aberta uma janela contendo um skin (desenho) de um novo Pocket PC com
as funcionalidades do Windows Mobile 5.0.
Para montar o acesso a dados, no VS.NET 2005 temos um novo conceito o qual permite definir toda a parte de acesso a
dados (database e tabelas) em um nova janela chamada Data Sources. O melhor de tudo é que você poderá usar
qualquer dado do Data Sources em qualquer formulário. Selecione o menu Data / Show Data Souces (Shift + Alt + D)
para exibir a respectiva janela. Existe um link chamado Add New Data Source para definir a fonte de dados, portanto
clique neste link.

O próximo passo é selecionar a origem, podendo ser um Database, um Web Service ou um Object (no caso de
desenvolvimento em camadas). Selecione Database e clique no botão Next.
Nesta janela você deverá selecionar o botão New Connection para definir uma nova conexão.

Note que o Data Source sugerido é o Microsoft SQL Server. Como não quero usá-lo, e sim o SQL Server Mobile Edition,
clique em Change para alterar o Data Source.
São exibidos todos os Data Sources provenientes de conexão, portanto, selecione o Microsoft SQL Server Mobile Edition
e clique em OK. Cabe ressaltar que o Data Provider será o .NET Framework Data Provider for SQL Server Mobile Edition.

Uma vez seleciona o Data Source, é preciso informar qual será o database a ser usado. Neste caso, clique no botão
Browse e localize o northwind.sdf.
Clique em OK e veja como ficou a string de conexão com o database.
Clique em Next. Note que será exibida uma mensagem informando que a conexão selecionada usa um arquivo de dados
local. Clique em Yes para aceitar.

É exibida uma janela contendo todos os objetos, Tables, Views, Stored Procedures e Functions. Selecione a Table
Employees e clique em Finish. Observe ainda o nome do DataSet, neste caso é Northwind DataSet. Caso queira alterar,

basta difitar o novo nome.


Assim, a janela de Data Sources irá exibir todos os campos da tabela Employees. A novidade é que você pode escolher
como que os dados serão exibidos, seja em DataGrid ou Details. Deixe como DataGrid.
Já para os campos, você pode escolher o tipo de controle que será exibido, podendo ser conforme a seguinte figura.

O proximo passo é o melhor de todos, ou seja, montar o formulário. Tudo o que você tem a fazer é apenas selecionar a
tabela ou os campos desejados e arrastá-los para o corpo do formulário. Note que é automaticamente inserido um
DataGrid no formulário.
O Magneto permite rotacionar o formulário de forma que o mesmo exiba mais dados. Clique com o botão direito no
nome do formulário e selecione Rotate Left. Ajuste o tamanho do DataGrid de forma a ocupar toda a tela.

O meio mais fácil é configurar a propriedade Dock = Fill. Isto é uma excelente novidade para Pocket PC, o ajuste de uma
determinado controle ao tipo de borda.
Quando a tabela foi arrastada para o formulário, foi criado automaticamente três componentes, sendo:
NorthwindDataSet, que é o DataSet em si; EmployeesBindingSource, que é o componente de DataBinding dos controles
(novidade no VS.NET 2005); EmployeesTableAdapter, que é a DataTable com a tabela Employees.

Defina a propriedade do formulário MinimizeBox = false, para que quando você fechar o formulário no Pocket, o mesmo
não fique na memória.
Outra propriedade interessante que existe no Magneto é a possibilidade de definir/trocar a plataforma em que rodara,
sendo as opções conforme a seguinte figura.

Salve o projeto e pressione F5 para executar o formulário. Selecione o emulador Windows Mobile 5.0 Pocket PC Emulator
e clique em Deploy.
Toda a aplicação será salva e iniciará o processo de compilação, geração dos arquivos CAB e o deploy.

Com isso conseguiremos montar diversos tipos de aplicações com muita facilidade, e principalmente, de acordo com a
necessidade do usuário. Com os novos recursos do Magneto, agregaremos mais funcionalidades as aplicações.
No stress, think Windows Mobile 5.0!
Renato Haddad (rehaddad@msn.com) é MVP, editor da revista MSDN Magazine Brasil, ministra treinamentos e
palestras sobre .NETe autor de diversos livros e treinamentos em CD multimídia de ASP.NET, SQL Reporting Services,
Visual Studio .NET 2003 e Aplicações Móveis para celulares e Pocket PC, tanto no Brasil como em outros países da
América Latina.
Título: Escrevendo Imagens através de Stream
Descrição: Utiliza Stream para ler uma determinada Imagem.
Categoria: Programação
Aplicação: Web
Linguagem: VB.NET

Autor: Israel Aéce


E-Mail: israel@projetando.net
Algumas vezes temos um Stream que contém dados para imprimirmos
na tela para o usuário. Um cenário muito usado é quando por exemplo
precisamos exibir ao usuário um avatar, ou mesmo uma imagem que
está armazenada em uma DB qualquer e para não interferir o
processamento da página corrente, criamos uma segunda página, esta
responsável por recuperar o arquivo e carregar em um objeto do tipo
FileStream, e assim, percorrer este objeto e imprimi-lo no browser.

A classe FileStream está contida dentro do Namespace System.IO, qual


temos que importá-lo para a utilização desta classe sem a necessidade
de digitarmos o "path" completo da mesma. Apenas como exemplo,
utilizaremos um arquivo que está no disco para exibição.

Em primeiro lugar, importamos o Namespace System.IO:

1 Imports System.IO

Abaixo o código no evento Page_Load do WebForm, que carrega a imagem para o Stream e en seguida, exibimos a
imagem no browser:

1 Private Sub Page_Load(ByVal sender As Object, ByVal e As _


2 System.EventArgs) Handles MyBase.Load
3
4 Response.Clear()
5 Dim fs As FileStream
6 Dim b(1024) As Byte
7 Try
8 fs = File.Open(Server.MapPath("Images/Win2003.jpg"), _
9 FileMode.Open, FileShare.Read, FileShare.None)
10
11 While (fs.Read(b, 0, b.Length) > 0)
12 Response.BinaryWrite(b)
13 End While
14 Finally
15 If Not (fs Is Nothing) Then
16 fs.Close()
17 End If
18 End Try
19 Response.End()
20 End Sub

Analisando o código acima, através do método Open da Classe File (que também está contida no Namespace
System.IO), que nos retorna um objeto do tipo FileStream, atribuímos o seu retorno à um objeto FileStream. Depois
disso, percorremos o nosso Stream enquanto o seu conteúdo seja maior que 0, pois através do método Read lemos
um bloco de bytes do Stream e escrevemos esses dados em um determinado buffer. E por fim, chamamos o método
Response.End() para encerrar a execução, já que a página foi criada apenas para esta finalidade.
Título: Inserindo um Item no DropDownList
Descrição: Aqui será mostrado como incluir um novo Item no DropDownList, pois muitas vezes após o DataBind() precisamos incluir um
novo item como por exemplo "Selecione...".
Categoria: DropDownList
Aplicação: Web
Linguagem: VB.NET

Autor: Israel Aéce


E-Mail: israel@projetando.net

Código:
Conhecendo o Namespace System.IO
por:André Ramos

Desde que comecei a estudar assuntos englobados por TI vi um grande vínculo entre aplicações e arquivos
responsáveis por armazenar dados. Se não me engano, um dos primeiros programas que escrevi, ainda no
colégio, na linguagem Basic, capturava algo digitado no teclado e guardava em um texto, para recuperar em outro
momento. Bem, depois disso, e já atuando como profissional em TI, tive algumas experiências parecidas
utilizando COBOL, Visual Basic, Visual C++, ASP e, finalmente, a plataforma .NET!

Lógico que, pela própria evolução das linguagens de programação, as que utilizam a plataforma .NET
estão, na minha opinião, no topo da lista quanto à facilidade para o manuseio de arquivos. Utilizando o
Namespace System.IO podemos trabalhar com leitura e escrita de arquivos e data streams de modo
síncrono e assíncrono.

O Namespace System.IO
Como parte da Base Class Library (BCL) da plataforma .NET, temos o namespace System.IO, dotado de classes
responsáveis pelo manuseio de arquivos e diretórios. Para conhecer melhor algumas das facilidades que este
namespace provém torna-se conveniente conhecer as seguintes classes:

Classe Descrição
Directory Expõe métodos estáticos para criação,
movimentação e enumeração dentre
diretório e subdiretórios.
File Provê métodos estáticos para a criação,
cópia, exclusão, movimentação, e abertura
de arquivos, e auxilia na criação de objetos
do tipo FileStream
FileStream Expõe um Stream sobre um arquivo,
suportando operações de leitura e escrita
síncronas e assíncronas.
Stream Provê uma visão genérica de uma
seqüência de bytes.
StreamReader Implementa um TextReader que lê
caracteres de um stream de bytes em um
encoding particular.
StreamWriter Implementa um TextReader para escrever
caracteres para um stream em um
encoding particular.
TextReader Representa um leitor que pode ler séries
seqüenciais de caracteres.
TextWriter Representa um escritor que pode escrever
séries seqüenciais de caracteres. Esta
classe é abstrata.
Tabela parcialmente extraída da MSDN Library

Trabalhando a princípio com diretórios, pode-se executar a verificação da existência de determinado


diretório, bem como sua exclusão, ou a criação de um novo, conforme código abaixo:

using System;
using System.IO;
namespace ManipArqDir
{
/// <summary>
/// Classe para a manipulação de diretórios
/// </summary>
public class ManipDir
{
//Método construtor
public ManipDir()
{
}
//Método utilizado para verificar a existência de um
diretório
public Boolean existe(string caminhoDiretorio)
{
return Directory.Exists(@caminhoDiretorio);
}
//Método utilizado para criar um diretório
public void criar(string caminhoDiretorio)
{
try
{
Directory.CreateDirectory(@caminhoDiretorio);
}
catch(IOException ioEx)

{
throw new Exception(ioEx.Message);
}
}
//Método utilizado para excluir um diretório
public void excluir(string caminhoDiretorio)
{
try
{
Directory.Delete(@caminhoDiretorio);
}
catch(DirectoryNotFoundException dnfEx)
{
throw new Exception("Diretório não
encontrado.\nDirectoryNotFoundException: " +
dnfEx.Message.ToString());
}
catch(IOException ioEx)
{ throw new
Exception(ioEx.Message);
}
}
}
}

Já a manipulação de arquivos, como criação, escrita, leitura e exclusão, pode ser verificada com o
seguinte exemplo:

using System;
using System.IO;
namespace ManipArqDir
{
/// <summary>
/// Classe para a manipulação de arquivos
/// </summary>
public class ManipArq
{
//Método construtor
public ManipArq()
{
}
//Método utilizado para verificar a existência de um
//arquivo
public Boolean existe(string caminhoArquivo)
{
return File.Exists(@caminhoArquivo);
}
//Método utilizado para criar um arquivo
public void criar(string caminhoArquivo)
{
try
{
File.Create(@caminhoArquivo);
}
catch(IOException ioEx)
{
throw new Exception(ioEx.Message);
}
}
//Método utlizado para escrever um conteúdo em
//determinado arquivo
public void escrever(string conteudo, string
caminhoArquivo)
{
try
{
//Abro o arquivo
StreamWriter arquivo = new
StreamWriter(@caminhoArquivo);
//Escreve o conteúdo desejado
arquivo.WriteLine("** INÍCIO DO ARQUIVO
**");
arquivo.Write("\n\n" + conteudo + \n\n");
arquivo.WriteLine("** FIM DO ARQUIVO *");
//Fecho o arquivo
arquivo.Close();
}
catch(IOException ioEx)
{
throw new Exception(ioEx.Message);
}
}
//Método utilizado para ler o conteúdo de um arquivo
//public string ler(string caminhoArquivo)
{
try
{
//Abro o arquivo
//StreamReader arquivo = new
//StreamReader(@caminhoArquivo);
//Leio todo o conteúdo
string conteudo = arquivo.ReadToEnd();
//Fecho o arquivo
arquivo.Close();
return conteudo;
}
catch(IOException ioEx)
{
throw new Exception(ioEx.Message);
}
}
//Método utilizado para excluir um arquivo
public void excluir(string caminhoArquivo)
{
try
{
File.Delete(@caminhoArquivo);
} catch(IOException ioEx)
{
throw new Exception(ioEx.Message);
}
}
}
}
Conforme indicado acima, não deve-se ignorar o tratamento de excessões com try-catch-finally. Para
tanto, fazem parte do namespace System.IO as classes DirectoryNotFoundException,
EndOfStreamException, FileLoadException, FileNotFoundException, InternalBufferOverflowException,
IOException e PathTooLongException.

Conclusão
Tomando este artigo como base, pode-se ter um primeiro contato com o namespace System.IO,
possuindo assim um conhecimento básico quanto à utilização das classes presentes no BCL para a
manipulação de arquivos e diretórios.

Referência sobre o namespace System.IO na MSDN Library: http://msdn.microsoft.com/library/default.asp?


url=/library/en-us/cpref/html/frlrfsystemio.asp

Criptografando Conteudo de Documentos Textos


O algoritmo Rijndael (Joan Daemen e Vicente Rijmen) foi eleito o vencedor do concurso em 2000 para publicação como AES
entre os 15 algoritmos (CAST-256,CRYPTON , DEAL, DFC, E2, FROG, HPC, LOKI97, MAGENTA, MARS, RC6, RIJNDAEL, SAFER+,
SERPENT e TWOFISH).
O algoritmo Rijndael (Joan Daemen e Vicente Rijmen) foi eleito o vencedor do concurso em 2000 para publicação como AES entre
os 15 algoritmos (CAST-256,CRYPTON , DEAL, DFC, E2, FROG, HPC, LOKI97, MAGENTA, MARS, RC6, RIJNDAEL, SAFER+,
SERPENT e TWOFISH). Tem como característica a operação em quatro camadas: substituição de bytes (processamento não linear
através de caixas S-Box1), deslocamento de linhas (transposição dos blocos resultantes das caixas S-Box), mistura de colunas
(através de formulas tem a fnc de unir colunas dispares ordenadamente) e adição da chave.
Imports System
Imports System.IO
Imports System.Text
Imports System.Security.Cryptography

Public Class cript_content


Public Sub Criptografa(ByVal n_arq1 As String, ByVal n_arq2 As String)
Dim rjmd As New RijndaelManaged()
Dim chave As Byte() = {121, 241, 10, 1, 132, 74, 11, 39, _
255, 91, 45, 78, 14, 211, 22, 62}
Dim IV As Byte() = {121, 241, 10, 1, 132, 74, 11, 39, 255, _
91, 45, 78, 14, 211, 22, 62}
Dim trans As ICryptoTransform = rjmd.CreateEncryptor(chave, IV)
Dim arq As New FileStream(n_arq2, FileMode.Create)
Dim mode As CryptoStreamMode
mode = CryptoStreamMode.Write
Dim enc As New CryptoStream(arq, trans, mode)
Dim writer As New StreamWriter(enc)
Dim objStreamReader As StreamReader
objStreamReader = File.OpenText(n_arq1)
Dim conteudo = objStreamReader.ReadToEnd()
writer.WriteLine(conteudo)
writer.Close()
enc.Close()
arq.Close()
End Sub

Public Sub Descriptografa(ByVal n_arq As String)


Dim rjmd As New RijndaelManaged()
Dim chave As Byte() = {121, 241, 10, 1, 132, 74, 11, 39, _
255, 91, 45, 78, 14, 211, 22, 62}
Dim IV As Byte() = {121, 241, 10, 1, 132, 74, 11, 39, 255, _
91, 45, 78, 14, 211, 22, 62}
Dim trans As ICryptoTransform = rjmd.CreateDecryptor(chave, IV)
Dim arq As New FileStream(n_arq, FileMode.Open)
Dim mode As CryptoStreamMode
mode = CryptoStreamMode.Read
Dim enc As New CryptoStream(arq, trans, mode)
Dim objStreamReader As New StreamReader(enc)
Dim conteudo As String = objStreamReader.ReadToEnd()
MessageBox.Show(conteudo)
objStreamReader.Close()
enc.Close()
arq.Close()
End Sub
End Class

ADOCE for ADO Programmers


Are you an ADO developer that needs to move your knowledge and even code from PC to Pocket PC data-enabled applications? I
show you the similarities and differences between ADO and ADOCE as well as some code samples.
Applies to:
Microsoft Windows Powered Pocket PC 2000
Microsoft eMbedded Visual Tools
Microsoft SQL Server 2000 Developer Edition
SQL Server 2000 Windows CE Edition
Microsoft ADOCE (ActiveX Data Objects CE) version 3.1 included with SQL Server 2000 Windows CE Edition
Microsoft Visual Studio
ADO 2.6
Download 619-CF.exe.
Gotchas
As there is a memory leak in the CreateObject statement, you are encouraged to create the ADOCE objects only once in your
application. In the sample code I have put the CreateObject calls in each event procedure, but it was just to make it comparable
with the ADO code.
Languages Supported
English
Data Access
Most enterprise applications are data-centric, which means that what you need to do is related to stored information. My
preference for storing data, when it comes to the Pocket PC is of course to use a real database—the SQL Server 2000 Windows
CE Edition. Therefore, my first suggestion is to go and get it. You will need a license for SQL Server 2000 Developer Edition.
Once you have installed SQL Server 2000 Windows CE Edition, you will also get ADOCE 3.1, the latest version. This is the
version that I will compare to ADO 2.6, the latest version on the PC.
Main Differences
Okay, so let's start with a quick overview of the most important differences. Here's a list of things in ADO that are missing in
ADOCE:

 Command and Parameter objects

 Collection objects

 Property object

 Recordset persistence (Recordset.Save)

 Asynchronous queries

 Disconnected Recordsets

 Dynamic creation of Recordsets

 Multiple queries (Recordset.NextRecordset)

For me, the missing Recordset persistence means that I cannot store my Recordsets as Extensible Markup Language (XML) and
use them in other applications on the Pocket PC. And maybe even more importantly, I cannot load XML data into my Recordsets.
These are issues that relate to the fact that I need to transfer data from my server components to the Pocket PC and back—for
that I also need some transport. My suggestion is to look at what Odyssey Software has to offer. Both their CEfusion and ViaXML
products can be useful here. If you are looking for more future (read Microsoft .NET) ways to transfer data, you should have a
look at a cool piece of software called pocketSOAP.
However, the most important functionality is implemented into ADOCE. And we will now look at some differences in common
data access situations.
Data Retrieval
Let us start with the most common scenario—when you need to retrieve some information from a data store. On the PC, the
data retrieval code for populating a ListView control would look like this:
Dim litm As ListItem
Dim laco As Connection
Dim lars As Recordset
Dim lsSQL As String

' Add column headers


lvwArticles.ColumnHeaders.Add , , "Description", 2500
lvwArticles.ColumnHeaders.Add , , "Price", 600, lvwColumnRight
lvwArticles.ColumnHeaders.Add , , "Stock", 600, lvwColumnRight

' Create objects


Set laco = CreateObject("ADODB.Connection")
Set lars = CreateObject("ADODB.Recordset")

' Open Connection


laco.Open "Provider=SQLOLEDB;Data Source=SERVERNAME;" & _
"Initial Catalog=DBNAME;Trusted_Connection=Yes"

' Open Recordset


lsSQL = "SELECT * FROM Article"
lars.Open lsSQL, laco, adOpenForwardOnly, adLockReadOnly

' Clear list and get item rows


Do While Not lars.EOF
Set litm = lvwArticles.ListItems.Add(, , lars("Description").Value)
litm.SubItems(1) = lars("Price").Value
litm.SubItems(2) = lars("Stock").Value
lars.MoveNext
Loop

' Close Recordset and Connection


lars.Close
laco.Close
And here is how you would do it on the Pocket PC:
Dim litm As ListItem
Dim laco As Connection
Dim lars As Recordset
Dim lsSQL As String

' Add column headers


lvwArticles.ColumnHeaders.Add , , "Description", 2500
lvwArticles.ColumnHeaders.Add , , "Price", 600, lvwColumnRight
lvwArticles.ColumnHeaders.Add , , "Stock", 600, lvwColumnRight

' Create objects


Set laco = CreateObject("ADOCE.Connection.3.1")
Set lars = CreateObject("ADOCE.Recordset.3.1")

' Open Connection


laco.Open "Provider=Microsoft.SQLSERVER.OLEDB.CE.1.0;" & _
"Data Source=\DBNAME.sdf"

' Open Recordset


lsSQL = "SELECT * FROM Article"
lars.Open lsSQL, laco, adOpenForwardOnly, adLockReadOnly

' Clear list and get item rows


Do While Not lars.EOF
Set litm = lvwArticles.ListItems.Add(, , lars("Description").Value)
litm.SubItems(1) = lars("Price").Value
litm.SubItems(2) = lars("Stock").Value
lars.MoveNext
Loop

' Close Recordset and Connection


lars.Close
laco.Close
And as you can see, there are minor differences between the code samples. The most obvious being the connection string
argument for the Open method on the Connection object. If you omit the Provider parameter, ADOCE will assume that you want
to access a Pocket Access database file. And the SQL Server CE database is always referred to as a file name. In this case the
file is placed in the root of the device, but might as well have been put in your application directory (e.g., Data Source=\
Program Files\AppName\AppName.sdf).
So far everything is great. Considering that (a) all the code that you have that retrieves data can be reused almost with no
modifications at all and (b) most of your PC application data access code retrieves data, you realize that moving your data-
aware PC applications to the Pocket PC is not a huge effort.
Updating Data
Now, let us look at another scenario—when you need to add information to the data store. I have left out the code for opening
and closing the Connection and Recordset as it is identical to the data retrieval code above. On the PC, you would write code
similar to:
' Open Recordset
lsSQL = "SELECT * FROM Article"
lars.Open lsSQL, laco, adOpenDynamic, adLockOptimistic

' Add new Article


lars.AddNew
lars("Description").Value = "Test"
lars("Price").Value = 50
lars("Stock").Value = 100
lars.Update
And on the Pocket PC it would look something like this:
' Open Recordset
lsTable = "Article"
lars.Open lsTable, laco, adOpenDynamic, adLockOptimistic, _
adCmdTableDirect

' Add new Article


lars.AddNew
lars("Description").Value = "Test"
lars("Price").Value = 50
lars("Stock").Value = 100
lars.Update
As you can see, there is a difference in the way you open the Recordset. To enable updates, the Recordset has to be opened as
a table rather than with a SQL statement (SELECT). You could have done this on the PC (in ADO) as well, but the point is that
this is probably something you will have to change when porting code from PC to Pocket PC.
Now, let us look at an update scenario. Here is the code on the PC:
' Open Recordset
lsSQL = "SELECT * FROM Article WHERE Description='Test'"
lars.Open lsSQL, laco, adOpenDynamic, adLockOptimistic

' Update Article


If Not lars.EOF Then
lars("Price").Value = 200
lars.Update
End If
And on the Pocket PC:
' Open Recordset
lsTable = "Article"
lars.Open lsTable, laco, adOpenDynamic, adLockOptimistic, _
adCmdTableDirect
lars.Find "Description='Test'"

' Update Article


If Not lars.EOF Then
lars("Price").Value = 200
lars.Update
End If
As it is not possible to create an updateable Recordset with an SQL statement (SELECT) as we are used to on the PC, you can
use the Find method to get the wanted position in the Recordset.
If you want to add some performance, you could do the same thing with a "raw" SQL UPDATE statement, like this:
laco.Execute "UPDATE Article SET Price=200 WHERE Description='Test'"
And to complete the scenarios, we look at how to remove data. On the PC, the code would look something like this:
' Open Recordset
lsSQL = "SELECT * FROM Article WHERE Description='Test'"
lars.Open lsSQL, laco, adOpenDynamic, adLockOptimistic

' Delete Article


If Not lars.EOF Then lars.Delete
The corresponding code on the Pocket PC:
' Open Recordset
lsTable = "Article"
lars.Open lsTable, laco, adOpenDynamic, adLockOptimistic, _
adCmdTableDirect
lars.Find "Description='Test'"

' Delete Article


If Not lars.EOF Then lars.Delete
The logic is very much like the code for updating. Please note that the result of the Find method is a Recordset containing only
the records that match the search criteria. This means that you have the same content in the Recordset both on the PC and the
Pocket PC. I say this just to prevent you from thinking that you would be able to do more things with the Recordset on the
Pocket PC because you have opened the complete table.
And again, to add performance, you could do almost the same thing with a SQL DELETE statement, like this:
laco.Execute "DELETE Article WHERE Description='Test'"
I say "almost," because this last Execute will actually remove all rows that match the search criteria whereas the latter would
only remove the first occurrence. In a real-world scenario, the search criteria would probably be a unique key producing exactly
the same result.
For a complete example, please see the sample code.
Conclusion
Much of what you already know from ADO on the PC can be used on your Pocket PC even if some things have to be done in a
slightly different way. Also, most of the data access code that you have written can be reused with small modifications when
writing Pocket PC applications.
Why don't you start moving one of your existing PC enterprise applications to the Pocket PC today? Just start and then show
someone—this could be your next project.

Você também pode gostar