C#

SharePoint – Desenvolvimento em Camadas

Neste artigo vou demonstrar como desenvolver um aplicativo usando o ambiente SharePoint 2010.

Usarei um exemplo bem simples para ilustrar a questão da arquitetura e como desenvolver de forma nativa, sem necessidade de utilização de banco de dados externo a nossa aplicação.

Vou assumir como premissa que você ja tenha o ambiente SharePoint 2010 assim como o Visual Studio 2010 ou o Visual Studio 2012 instalado e configurado.

Utilizarei uma abordagem simples em 3 camadas para a arquitetura da aplicação.

A aplicação que será desenvolvida, será um “web diário“. A aplicação em si não é relevante (crie a sua própria), o que é importante neste artigo, é entender como desenvolver nativamente em SharePoint.

Então vamos por a mão na massa.

Para baixar o projeto Login to view.

 

A estrutura da nossa aplicação deverá ficar conforme a imagem abaixo:

 

image

Vamos criar nosso projeto. Com o Visual Studio aberto, clicamos em New Project (conforme imagem abaixo).

image

Em seguida será aprestada a tela para criação do projeto. Vamos preencher conforme a imagem a seguir:

image

Depois de preenchido o nome do projeto e clicado em “OK”, o Visual Studio vai iniciar o Wizard de criação de um projeto de Visual Webpart novo.

Neste Wizard, dizemos em qual o servidor do Sharepoint vamos utilizar para desenvolvimento, selecionamos “Deploy as a farm solution” e clicamos em Finish.

image

 

Assim que o Visual Studio terminar de gerar o projeto, teremos a seguinte configuração no nosso projeto:

image

Vamos renomear a Webparte que tem o nome de “VisualWebPart1” para “WPDiario” e dentro da pasta Features vamos renomear a “Feature1” para “FTDiario”. Não vou entrar em detalhes sobre a questão das Features neste artigo.

Agora, vamos criar a estrutura de pastas para a nossa aplicação.

Clique com  o botão direito sobre o projeto “WebDiario” e clique em Add -> New Folder e coloque o nome de “DAL” na pasta criada. Repita este procedimento para criar as pastas “BUS”, “ENT”, “USR”.

Após a criação de todas as pastas, arraste a webpart “WPDiario” para dentro da pasta “USR” conforme a imagem abaixo:

image

Vamos agora criar uma classe Diário para cada uma das camadas envolvidas na nossa aplicação:

Daremos os seguintes nomes para as classes:

Na pasta BUS: DiarioBus.cs

Na pasta DAL: DiarioDal.cs

Na pasta ENT: DiarioEnt.cs

Ficamos com a nossa aplicação da seguinte forma:

image

 

Agora que já temos a estrutura do projeto pronta para codificar, vamos criar a lista no SharePoint, que servirá como uma tabela de banco de dados para a nossa aplicação.

Vamos abrir o site onde estamos trabalhando nossa aplicação SharePoint. No meu caso, estou usando um site criado zerado e com o padrão de layout do sharepoint conforme imagem abaixo.

image

 

Vou partir da premissa que você sabe criar uma lista no sharepoint e já vou apresentar a lista pronta. Só para exclarecer, estou usando um “Custom List” com o nome de “LST_Diario”.

image

Uma vez que já temos nossa lista criada, podemo voltar para o Visual Studio e começar a codificar nosso projeto.Data

Vamos começar a codificar pela classe de entidade, pois precisamos mapear os atributos entre a lista do sharepoint “LST_Diario” e a classe de entidade “DiarioEnt.cs”.

Abrimos então o arquivo DiarioEnt.cs e adicionamos as linhas de código abaixo:

image

 

Após criarmos nossa classe de entidade que mapeia a lista do sharepoint, vamos codificar a classe “DiarioDal” da camada de dados “DAL”.

Implementarei inicialmente um método listar simples sem filtros. Não é uma boa prática, pois trará todos os dados da lista, mas para fins de exemplo está bom.

image

 

Uma vez a camada de dados pronta, podemos então implementar a camada de negócios “BUS”.

No nosso caso abaixo, não farei nenhum tratamento com regra de negócio, mas como manda a regra, eu tenho que sempre passar pela camada de negócio para chegar a camada de usuário, por isso , nosso método de negócio será somente uma chamada a classe de dados e retorno da lista conforme abaixo.

A modelagem mais adequada para a questão das listas seria ter uma classe DiárioEnt e uma classe ItemDiarioEnt, mas para simplificação do exemplo estou adotando a própria classe de DiarioEnt para listar seus itens.

Classe de negócio “DiarioBus” fica da seguinte forma:

image

 

Vamos agora codificar nossa Webpart. Vamos clicar em WPDiario e entrar no modo de design. Vou adicionar um objeto “botão” e um “GridView”.

O Botão tem como objetivo chamar o método da camada de negócio e o GridView exibirá os dados da lista.

O exemplo deverá ficar da seguinte forma:

image

Vamos dar um duplo clique no botão e iniciar a codificação da chamada ao método de negócio.BUS

 

image

Assim que tivermos codificado nosso exemplo, podemos fazer o Deploy para fazer o teste com nossa webpart. Lembre-se de adicionar algums itens na lista do sharepoint, caso contrário, não haverá retorno devido ao fato de não haver registro na lista do sharepoint.

Adicionei 3 linhas de registro para testar a aplicação.

SNAGHTML14c0b24b

Com a nossa webpart publicada, podemos acessar a página do sharepoint para testar nossa aplicação.

A página deverá ficar com a seguinte cara:

SNAGHTML14e0989d

Depois que clicamos no botão “Carregar Grid” nossa aplicação vai na lista do SharePoint buscar as informações da lista conforme a imagem abaixo.

SNAGHTML14e20387

 

Espero que tenha gostado do artigo.

Qualquer consideração que deseje fazer, entre em contato.

Márcio Pulcinelli.


Criando Virus de Computador

imageAcredito que este seja o artigo mais controverso que estou escrevendo, mas vou tentar dar uma visão de como funciona um vírus de computador, e ainda apresentar um código fonte na prática. De qualquer forma para entender como funciona, vou me utilizar de conceitos de biologia para definição de vírus.

Sei que a Wikipedia não é um lugar muito bom como fonte de referência, mas a definição que vou citar abaixo está bem clara do que é um vírus.

Um vírus é um pequeno agente infeccioso que pode se replicar somente no interior das células de organismos vivos. Vírus infectar todos os tipos de organismos, de animais e plantas para bactérias e archaea.

No caso de um computador, um vírus pode ser qualquer autômato (produto de software), que atue internamente fazendo com que o sistema operacional tenha comportamentos indesejados ou fora do padrão de execução. Ou seja, efetuando atividades que não são desejadas pelo usuário, como por exemplo a captura das teclas do teclado que foram digitadas pelo usuário, efetuar a leitura dos e-mails do usuário e envia-los para um outro servidor e muitas outas atividades que possam ser prejudicial ao usuário de uma forma geral.

Um vírus é sempre um parasita, ou seja, está sempre “escondido” ou “hospedado” em algum outro agente, que no caso do ser humano, são as células do corpo. No caso de um computador, um vírus pode se hospedar em algum outro software que tenha uma finalidade legítima que o usuário esteja esperando.

O que seria se hospedar em algum outro software? Quando digo “se hospedar”, na verdade quero dizer que o código do vírus deverá ser inserido (ou injetado) no código binário original do programa, desta forma infectando o programa hospedeiro. Uma outra abordagem seria o próprio programador do código original injetar, de forma maliciosa, o código que libertará o vírus no computador.

A abordagem que utilizaremos neste artigo será a de injetar de forma maliciosa o código que irá liberar o vírus dentro do computador. Trocando em miúdos, o nosso sistema vai gerar um novo executável que será o nosso vírus.

Como o intuito deste artigo é demonstrar como o vírus atuará, criarei uma aplicação hospedeira bastante trivial (uma calculadora) onde o usuário vai clicar e executar uma soma (bastante simples por sinal), mas que na verdade internamente vai infectar o sistema com um vírus. O nosso trabalho sobre o vírus será um pouco mais complexo, pois é onde queremos nos focar e dedicar mais atenção.

De qualquer maneira, vamos começar pelo começo!

O Hospedeiro

Criaremos agora nossa aplicação hospedeira (que estará infectada com o vírus). Para isso utilizaremos a linguagem C# por nos fornecer algumas vantagens, de qualquer forma, você poderá implementar a ideia em qualquer linguagem de programação que desejar.

Vamos abrir o Visual Studio para começar a criar nosso aplicativo hospedeiro. Nele vamos criar um formulário com dois campos para efetuar cálculos de soma e subtração.

SNAGHTMLef6ad

Assim que clicarmos em OK, nosso projeto será criado pelo Visual Studio. Existe um motivo para a utilização do .NET framework 2.0, que é fazer com que nossa aplicação rode no maior número de sistemas operacionais possível.

Se estiver tudo correto, você deverá estar vendo esta tela abaixo:

SNAGHTML1373d8

Desenhe o seu sistema hospedeiro como desejar, no meu caso montei uma calculadora (A mais simples que consegui) conforme a imagem abaixo.

SNAGHTML1b56b7

E o código da calculadora deverá ficar como apresentado abaixo:

SNAGHTML27b145

Neste momento já temos nossa calculadora funcionando, não é a mais bonita, mas funciona. Vamos compilar e colocar para funcionar, veja na imagem abaixo o resultado.

SNAGHTML2a95aa

Esta singela calculadora hospedará internamente o vírus que iremos criar agora.

A princípio pensei em criar o vírus usando Visual C++, pois me dá um conjunto de funcionalidades enorme para programação Windows, mas para fins educacionais, continuarei usando o próprio C#. Acredito que facilitará o entendimento do nosso exemplo.

O Vírus

Bom… Agora que já temos o nosso hospedeiro, vamos criar o autômato que se hospedará nele (nosso vírus). A questão das rotinas de execução do vírus dependem muito das heurísticas deste determinado vírus. Pensei em algumas opções e cheguei a conclusão de que, para este exemplo, vamos fazer o vírus capturar teclas do teclado para obter conversas e senhas do usuário e enviar esses dados por e-mail para uma conta qualquer.

Então! Mão a obra!!!

Vamos adicionar agora um outro projeto dentro da solução do Visual Studio na qual criamos nosso primeiro projeto (a calculadora).

SNAGHTML8b2c50

É importante que o vírus tenha um nome bastante parecido com os nomes dos arquivos do sistema operacional, pois isso fará com que fique mais complicado saber se o arquivo é um vírus ou um arquivo do sistema operacional.

Vamos criar uma aplicação do tipo “Windows Forms Application”.

Após criado o projeto, sua solução deverá ficar como a imagem abaixo:

SNAGHTML935f22

Vamos excluir o “Form1.cs”, pois ele não fará parte do nosso vírus.

SNAGHTML94c821

Depois que excluir o arquivo, crie uma classe com o nome de Win32.cs. Essa classe será responsável pelas funções da API do Windows que utilizaremos na construção do vírus.

Vou usar neste exemplo o conceito de Hook que é um mecanismo pelo qual um aplicativo pode interceptar eventos, tais como mensagens, ações do mouse e teclas digitadas. Uma função que intercepta um determinado tipo de evento é conhecido como um procedimento de Hook. Um procedimento de Hook pode agir em cada evento que recebe, em seguida, modificar ou rejeitar o evento.

Sei que a utilização de HOOK não é um conceito muito difundido, pois vai bem a fundo no sistema operacional, mas acredito que seja um dos principais conceitos para entender o desenvolvimento para Windows mais a fundo.

Vamos codificar nossa classe Win32.cs, o código segue abaixo (O código é pequeno, mas bastante útil em certas situações):

image

Essa é a nossa pequena classe que fará quase todo o trabalho de captura das teclas do teclado da máquina infectada.

Com a classe Win32.cs pronta, podemos agora partir para o desenvolvimento principal. Utilizarei a própria classe Program.cs, que foi gerada pelo Visual Studio, mas farei algumas modificações para que ela funcione como desejado.

Vamos abrir o código da nossa classe Program.cs e codificar conforme abaixo:

image

image

image

image

É importante observar que este código é somente para ilustrar como fazer um vírus, ele precisa ser bastante otimizado e melhorado. A título de exemplo, acho que está de bom tamanho, pois o intuito deste artigo é demonstrar como um vírus pode ser criado e não criar um vírus de verdade.

Assim que o código acima estiver criado, podemos compila-lo e testa-lo. Como essa aplicação não tem interface, para verificar se tudo está funcionando de forma correta, é necessário abrir o arquivo log.txt que é gerado através da rotina abaixo no nosso código.

image

Esta rotina é responsável por armazenar as teclas digitadas. Então, coloque a aplicação para rodar (ela vai rodar em background) e digite algum texto em qualquer lugar (no Word, Notepad, Excel, onde você quiser) e o conteúdo será armazenado.

Já temos o nosso vírus criado. Agora precisamos infiltrar o vírus no hospedeiro (nossa calculadora) e fazer com que o hospedeiro libere o vírus no sistema para que ele comece a capturar as teclas.

Infiltrando o Vírus no Hospedeiro

Esta é a parte final do artigo. Para começarmos, vamos voltar no projeto da calculadora, clicar com o botão direito sobre o projeto e em seguida em Properties.

SNAGHTMLceaed8

A seguinte tela se abrirá:

SNAGHTMLd27707

Após clicar no item Resources, clique em Add Existing File… Conforme a imagem abaixo:

SNAGHTMLd5431b

Será aberta uma caixa de seleção de arquivos conforme a imagem abaixo:

SNAGHTMLd8c333

Selecione o executável do vírus e clique em “Open”. Você deverá ver a janela da seguinte maneira:

SNAGHTMLdd1a76

Esta é a forma mais fácil de hospedar um vírus dentro de um hospedeiro. Existem outras formas mais baixo nível, talvez eu aborde em algum outro artigo, pois vai demandar um conhecimento mais aprofundado de Assembly ou IL que deixaria muito extenso este artigo.

Vamos agora finalizar codificando as rotinas de liberação do vírus através do hospedeiro.

Quando o usuário executar a calculadora, o vírus deverá ser liberado no sistema e deverá iniciar suas atividades.

Então, vamos iniciar a codificação. Como o Visual Studio já fez uma parte do trabalho, que é transformar o nosso vírus em array de bytes, precisamos agora extrair o vírus  de dentro do hospedeiro.

Vamos criar duas funções dentro do frmCalculadora.cs para fazer a extração do vírus e em seguida executar o vírus na máquina.

Segue abaixo o código das funções:

SNAGHTML4d9878

A primeira função (ou método), extrai o vírus para alguma pasta/nome arquivo passado por parâmetro, a segunda função executa o vírus na máquina.

Até aqui já temos uma boa ideia de como um vírus é hospedado em um outro aplicativo. Agora falta chamar os dois métodos que foram criados para liberar o vírus na máquina e coloca-lo em execução.

No construtor da classe frmCalculadora.cs vamos adicionar as chamadas aos métodos que criamos. Fazendo com que o vírus seja liberado e inicie seu trabalho conforme o código abaixo:

SNAGHTML5827ad

O código acima é bastante primário, o ideal seria desenhar um método que busque locais remotos dentro dos diretórios de sistema para que se torne mais complicado a descoberta do vírus. O nome do vírus também deveria ser um nome muito parecido com os nomes dos arquivos da pasta system, para manter a dificuldade. Seria também importante ajustar o atributo de visibilidade do arquivo para hidden, colocar uma chamada ao vírus no registro do Windows ou algum outro meio que faça com que o arquivo seja executado sempre que o sistema operacional for carregado.

De qualquer forma, como foi dito no início deste artigo, o intuito não era criar um vírus de verdade, mas sim apresentar como poderia ser feito.

Vamos agora compilar e testar o resultado do projeto. Rodamos a calculadora e automaticamente o vírus será lançado no sistema operacional e será executado passando a agir sozinho.

Se você fez tudo corretamente, e rodou a calculadora, já deve estar no diretório o seu vírus e residente em memória.

SNAGHTML7c827a

SNAGHTML7e60c7

Assim que houver alguma tecla pressionada, será gerado um arquivo log.txt no mesmo diretório em que o vírus está residente e as teclas estarão sendo capturadas e armazenadas neste arquivo. Se fosse um vírus de verdade, este arquivo deveria ser enviado para algum servidor na internet.

SNAGHTML842cbd

Se você abrir seu arquivo, verá as teclas que foram capturadas diretamente através do sistema de Hook do Windows.

Baixe o código fonte completo aqui –> Login to view.

Espero que tenham gostado do artigo. Como eu já disse algumas vezes, o intuito deste artigo não era desenvolver um vírus de verdade, mas sim conceber a ideia por traz da criação do vírus.

Este artigo é para que você intenda o funcionamento e possa se defender deste tipo de ameaça sabendo com o que está lhe dando. Nunca criar algo deste tipo para utilizar de forma antiética.


Até o próximo artigo. Deixe seu comentário!

Márcio Pulcinelli @ OminaVincit!


Utilizando Connection Factory em C#

 image Este artigo tem como objetivo apresentar uma abordagem bastante factível para a utilização de Connection Factory utilizando a linguagem C#.

Vou abordar neste artigo como criar as classes que farão acesso a dados de forma transparente facilitando a manutenção do código posteriormente.

 

Você poderá encontrar mais informações em: http://ultradbfactoring.codeplex.com/

Primeira Etapa

Para começar, criaremos um projeto do tipo Class Library que armazenará nossas classes criadas e assim poderemos adicionar posteriormente nossas classes a qualquer projeto que desejarmos utilizar. Crie seu componente conforme a imagem abaixo:

 image

Vamos então excluir a classe "Class1.cs" que o Visual Studio criou. Em seguida vamos começar a criar nossas classes.

SNAGHTML4072cf

Uma vez deletada a classe, vamos criar nossa primeira classe. Vamos clicar em Add New Item em seguida escolher a opção Classe conforme imagem abaixo:

SNAGHTML431362

Chamaremos esta classe de ConnectionFactory.cs, pois será a classe que nos gerará os objetos de acesso a dados de forma dinâmica.

Assim que clicarmos em "Add", o Visual Studio irá gerar a classe para nós conforme a próxima imagem:

SNAGHTML45d8f1

Veja que nossa classe está criada. Iremos adicionar dois métodos nesta classe: um método para criar objetos de banco de dados e outro método para recuperar strings de conexão pelo nome.

O primeiro método que será criado é o:

image

Este método é responsável por recuperar a string de conexão através do nome da conexão dado no arquivo app.config.

Vamos agora implementar nosso método:

SNAGHTML4f190d

Já com o nosso primeiro método (GetConnectionStringByName) implementado, podemos seguir para a criação do segundo método de nossa classe. Este método tem como objetivo criar objetos de banco de dados.

image

Vamos criar o conteúdo do método para que possamos partir para a próxima etapa do nosso projeto.

SNAGHTML546181

Nossa classe completa e finalizada deverá ser implementada conforme abaixo:

image

image

image

Compilamos nosso código para verificar se até aqui está tudo conforme o planejado ou se tem erros no código e em seguida partiremos para a segunda etapa da criação do nosso componente.

Segunda Etapa

Na segunda etapa do nosso projeto, criaremos uma classe chamada de DataAccessHelper, que tratará as informações de acesso a dados, tais como Insert, Update, Delete e Select no banco de dados.

Para isso, vamos criar nossa classe conforme imagem abaixo:

SNAGHTML689d97

Assim que clicarmos em "Add", o Visual Studio criará a classe dentro do nosso projeto. Veja a imagem abaixo com a classe gerada:

SNAGHTML6a512d

A primeira ação que devemos tomar é implementar a interface IDisposable na nossa classe uma vez que queremos gerenciar o código em memória.

Esta classe conta com a seguinte estrutura:

image

Abaixo segue nossa classe completa e implementada:

image

image

image

image

image

image

Já temos nossa classe de acesso a dados e já temos a classe de DbFactory. Neste ponto já podemos compilar e verificar se nosso código está compilando ou se gerou algum erro. Se você tiver feito tudo certo, seu código estará compilando sem erros.

Terceira Etapa

Nesta etapa iremos testar nosso projeto. Para isso iremos criar um novo projeto dentro da Solution "DBFactory". A questão é: qual o tipo de solução criar? Poderíamos criar uma console app, entretanto como eu quero testar nosso projeto, vou criar um projeto de teste do Visual Studio para nosso projeto. Então mãos a obra!!

Vamos criar nosso projeto de teste conforme a imagem abaixo:

SNAGHTML84d7da

Assim que clicarmos em "OK" o Visual Studio irá gerar o projeto de teste dentro da solução DBFactory conforme apresentado abaixo:

SNAGHTML871a65

Com podemos visualizar na imagem acima, o Visual Studio criou uma classe chamada de UnitTest1.cs. Vamos excluir este arquivo para começar a codificar nosso teste.

Antes de iniciarmos nossos testes, temos que adicionar o arquivo App.Config ao projeto de teste.

SNAGHTML8ba2b7

Uma vez adicionado, vamos clicar com o botão direito sobre o projeto de teste "DBFActoryTeste" e clicar em "Properties" e em seguida em Settings conforme imagem abaixo:

SNAGHTML90605a

Clique então no link a direita "This project does not contain…" para criar um novo arquivo. Em seguida no campo "name", digite o nome que desejar (no meu caso criei o nome "strConexao"). No campo Type procure por (Connection string), o campo Scope deverá ficar como "Application", no campo value você deverá clicar no botão com os "…" para criar uma nova string de conexão para o seu banco de dados conforme imagem abaixo:

image

Para este exemplo estou usando uma base do Ms Access. Mas como já foi dito, este modelo de acesso a dados pode usar qualquer base de dados (desde que se tenha o provider para o banco de dados).

image

Vamos agora abrir nosso App.Config e visualizar o conteúdo:

image

Podemos ver que o Visual Studio Criou uma tag dentro de <connectionStrings> e deu o nome de "DBFactoryTeste.Properties.Settings.strConexao".

Para que nosso componente possa acessar esta string de conexão gerada, temos que adicionar a seguinte tag dentro do App.Config:

image

Esta tag AMBIENTE_ATIVO faz parte do nosso projeto e ela tem a função de apontar para a conexão que desejamos utilizar no nosso projeto.

Uma vez tendo configurado nosso App.Config, vamos escrever um método de teste para abrir a conexão com o banco de dados configurado. Para fazer isso, vamos clicar no nosso projeto de teste "DBFactoryTeste" com o botão direito do mouse e em seguida "Add" –> "Unit Test" para adicionar um novo Teste Unitário conforme imagem abaixo:

SNAGHTMLab71e8

Para testar se nossa conexão está aberta, vamos selecionar a propriedade "ConexaoAberta" e clicar em "OK". Assim que o botão de "OK" for selecionada, o Visual Studio vai gerar uma classe chamada "DataAccessHelperTest.cs" no nosso projeto de teste. Vamos ajustar o conteúdo desta classe conforme a imagem abaixo:

image

Para executarmos nosso teste acima, abrimos a janela "Test View" e nosso teste será exibido na lista conforme imagem abaixo:

SNAGHTMLb32a7c

image Clique neste botão para executar o teste.

Se todo seu código estiver correto, você deverá entrar em na janela "Test Result" e ver o seguinte resultado:

SNAGHTMLb95949

Então é isso pessoal, espero que tenham gostado do artigo.


Qualquer dúvida me escreva.

Márcio Pulcinelli @ OminaVincit


Acessando WebCam com C#

image

Neste artigo vou demonstrar como desenvolver um aplicativo para utilizar sua webcam e a partir desse exemplo, você poderá fazer seus filmes e gravar fotos em disco.

Este projeto está baseado em: http://camonweb.codeplex.com/

Para acessar o projeto basta entrar no link acima.

 

Usaremos para desenvolver este aplicativo o Visual Studio 2010, entretanto você pode usar versões anteriores do IDE sem maiores problemas. Usaremos algumas chamas as APIs do Windows para o correto funcionamento.

São elas:

  • SendMessage (user32)
  • capCreateCaptureWindowA (avicap32)
  • OpenClipboard (user32)
  • EmptyClipboard (user32)
  • CloseClipboard (user32)
  • GetClipboardData (user32)

Usaremos algumas constantes do avicap32.dll de suma importância para a manipulação das imagens que são capturadas através da câmera.

Como sempre gosto de trabalhar orientado a objetos e componentizando tudo o que acredito importante manter o reuso, vamos criar dois projetos dentro da mesma solução: um projeto do tipo User Control e outro do tipo Windows Forms. No User Control vamos codificar o componente de acesso a WebCam, já no Windows Forms vamos utilizar nosso User Control criado.

Botando a Mão na Massa

Para dar início ao desenvolvimento da nossa aplicação, vamos abrir o Microsoft Visual Studio.

SNAGHTMLe6b21

SNAGHTML1497cc

Com nosso Microsoft Visual Studio aberto, vamos criar um projeto vazio (Blank Solution) conforme a imagem abaixo:

SNAGHTML24b73e

Esta solução vazia servirá para para adicionar dois projetos: Um User Control e o outro uma aplicação Windows Form que utilizará o User Control criado. Sendo assim, vamos finalizar a criação deste Blank Solution para darmos início a codificação do User Control. Clique em “OK” para finalizar.

Assim que clicar no botão “Ok”, o Visual Studio abrirá a sua solução onde não deverá conter nada além do próprio nome da solução conforme imagem abaixo:

SNAGHTML2b424d

Criando Nosso User Control

Vamos dar início a criação do nosso User Control. Nosso user control nada mais vai ser do que um controle visual que terá a capacidade de exibir nossa webcam.

Mãos a obra então!

Vamos clicar com o botão direito do Mouse sobre a nossa solução que acabamos de criar e vamos adicionar um User Control.

image

Assim que clicarmos em New Project, a seguinte tela será apresentada:

SNAGHTML3f6bbe

Selecione o item Windows dentro de Visual C#, em seguida Windows Forms Control Library. Adicione um nome, no meu caso escolhi “MinhaWebCamCtrl“. Para finalizar o assistente, clique em “OK”.

Assim que clicarmos em “OK”, teremos a seguinte tela:

SNAGHTML423801

Agora vamos iniciar a fase de ajustes nas propriedades do nosso componente. A primeira alteração que faremos é referente ao nome do controle. O Visual Studio cria o componente sempre com a nomenclatura “UserControl1.cs”. Vamos fazer uma alteração neste nome, vamos trocar para “MinhaWebCamComp.cs” ou qualquer outro nome que você deseje.

O Visual Studio vai perguntar se você deseja alterar todas as referências do nome antigo para o novo na seguinte tela:

SNAGHTML4872df

Clique em “Yes” e deixe o VS finalizar as modificações na estrutura do código. Assim que for finalizado, seu projeto deverá conter a seguinte configuração:

image

Vamos começar agora a adicionar os objetos no nosso controle. Para projetarmos uma webcam simples, só precisamos mesmo de um controle que é o que será usado nesse artigo. O controle é o System.Windows.Forms.PictureBox.

Vamos adicioná-lo agora ao nosso formulário do User Control, pois será esse objeto o responsável pela exibição da WebCam.

SNAGHTML5b71b7

Assim que arrastarmos o controle para o formulário, teremos a seguinte tela:

SNAGHTML5e6e7c

Quando seu controle estiver sobre o formulário, renomeie o controle de “pictureBox1” para “ImgWebCam” ou qualquer outro que ache mais pertinente (é importante lembrar que esses nomes que estou utilizando, serão usados no código). Quando tiver terminado de renomear o controle, precisaremos alterar mais uma propriedade do controle, que será o “Dock”. Alteraremos para “Fill” conforme a imagem abaixo:

image

Quando clicarmos em “Fill”, nosso componente deverá ficar com a seguinte aparência:

SNAGHTML6508a9

Para finalizarmos com a interface, vamos adicionar um controle do tipo “Timer” conforme imagem abaixo.

SNAGHTML6d61ef

Renomeie o Timer para “tmrRefrashFrame” e temos então, nossa interface do controle finalizada. Falta agora a codificação do controle.

Codificando Nosso Controle

A codificação desse controle, demanda um pouco de conhecimento das APIs do Sistema Operacional Windows. Por isso, separar em partes o código para facilitar o entendimento.

Vamos então alterar nossa visão para “Code View”, onde a seguinte imagem será apresentada:

SNAGHTML6fedba

Como podemos ver não há nada codificado no nosso controle.

Vamos começar pelas variáveis da classe.

namespace MinhaWebCamCtrl
{
    public partial class MinhaWebCamComp : UserControl
    {
        // Altura e largura da imagem gerada pela WebCam
        private int m_Width = 500;
        private int m_Height = 500;
        
        //Handle da janela de controle da webcam.
        private int mCapHwnd;

        //Flag para verificar se webcam foi parada.
        private bool bStopped = true;


        public MinhaWebCamComp()
        {
            InitializeComponent();
        }
    }
}

 

Como vemos acima, inicialmente temos as variáveis que serão utilizadas por toda a classe. Em seguida, devemos adicionar as chamadas as APIs do sistema operacional conforme abaixo:

namespace MinhaWebCamCtrl
{
    public partial class MinhaWebCamComp : UserControl
    {
        // Altura e largura da imagem gerada pela WebCam
        private int m_Width = 500;
        private int m_Height = 500;

        
        //Handle da janela de controle da webcam.
        private int mCapHwnd;

        //Flag para verificar se webcam foi parada.
        private bool bStopped = true;


        //Abaixo temos todas as chamadas das APIs do Sistema Operacional Windows
        //Essas chamadas fazem a interface do nosso controle com a WebCam e e com o SO.
        #region API Declarations

        //Esta chamada é uma das mais importantes e é vital para o funcionamento do SO.
        [DllImport("user32", EntryPoint = "SendMessage")]
        public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);

        //Esta API cria a instancia da webcam para que possamos acessa-la.
        [DllImport("avicap32.dll", EntryPoint = "capCreateCaptureWindowA")]
        public static extern int capCreateCaptureWindowA(string lpszWindowName, int dwStyle, int X, int Y, int nWidth, int nHeight, int hwndParent, int nID);

        //Esta API abre a área de transferência para que possamos buscar os dados da webcam.
        [DllImport("user32", EntryPoint = "OpenClipboard")]
        public static extern int OpenClipboard(int hWnd);

        //Esta API limpa a área de transferência.
        [DllImport("user32", EntryPoint = "EmptyClipboard")]
        public static extern int EmptyClipboard();

        //Esta API fecha a área de transferência após utilização.
        [DllImport("user32", EntryPoint = "CloseClipboard")]
        public static extern int CloseClipboard();

        //Esta API recupera os dados da área de transferência para utilização.
        [DllImport("user32.dll")]
        extern static IntPtr GetClipboardData(uint uFormat);

        #endregion



        public MinhaWebCamComp()
        {
            InitializeComponent();
        }
    }
}

 

Assim que definirmos as chamadas as APIs, vamos adicionar algumas constantes necessárias a nossa aplicação.

São elas:

namespace MinhaWebCamCtrl
{
    public partial class MinhaWebCamComp : UserControl
    {
        // Altura e largura da imagem gerada pela WebCam
        private int m_Width = 500;
        private int m_Height = 500;
        
        //Handle da janela de controle da webcam.
        private int mCapHwnd;

        //Flag para verificar se webcam foi parada.
        private bool bStopped = true;


        //Abaixo temos todas as chamadas das APIs do Sistema Operacional Windows
        //Essas chamadas fazem a interface do nosso controle com a WebCam e e com o SO.
        #region API Declarations

        //Esta chamada é uma das mais importantes e é vital para o funcionamento do SO.
        [DllImport("user32", EntryPoint = "SendMessage")]
        public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);

        //Esta API cria a instancia da webcam para que possamos acessa-la.
        [DllImport("avicap32.dll", EntryPoint = "capCreateCaptureWindowA")]
        public static extern int capCreateCaptureWindowA(string lpszWindowName, int dwStyle, int X, int Y, int nWidth, int nHeight, int hwndParent, int nID);

        //Esta API abre a área de transferência para que possamos buscar os dados da webcam.
        [DllImport("user32", EntryPoint = "OpenClipboard")]
        public static extern int OpenClipboard(int hWnd);

        //Esta API limpa a área de transferência.
        [DllImport("user32", EntryPoint = "EmptyClipboard")]
        public static extern int EmptyClipboard();

        //Esta API fecha a área de transferência após utilização.
        [DllImport("user32", EntryPoint = "CloseClipboard")]
        public static extern int CloseClipboard();

        //Esta API recupera os dados da área de transferência para utilização.
        [DllImport("user32.dll")]
        extern static IntPtr GetClipboardData(uint uFormat);

        #endregion

        #region API Constants

        public const int WM_USER = 1024;

        public const int WM_CAP_CONNECT = 1034;
        public const int WM_CAP_DISCONNECT = 1035;
        public const int WM_CAP_GET_FRAME = 1084;
        public const int WM_CAP_COPY = 1054;

        public const int WM_CAP_START = WM_USER;

        public const int WM_CAP_DLG_VIDEOFORMAT = WM_CAP_START + 41;
        public const int WM_CAP_DLG_VIDEOSOURCE = WM_CAP_START + 42;
        public const int WM_CAP_DLG_VIDEODISPLAY = WM_CAP_START + 43;
        public const int WM_CAP_GET_VIDEOFORMAT = WM_CAP_START + 44;
        public const int WM_CAP_SET_VIDEOFORMAT = WM_CAP_START + 45;
        public const int WM_CAP_DLG_VIDEOCOMPRESSION = WM_CAP_START + 46;
        public const int WM_CAP_SET_PREVIEW = WM_CAP_START + 50;

        #endregion



        public MinhaWebCamComp()
        {
            InitializeComponent();
        }

    }
}

Estas constantes são predefinidas pelo SO. Vamos agora definir dois métodos: um para Iniciar e outro para  parar a webcam. Utilizarei os nomes Start e Stop somente por conveniência. Junto com os métodos de Start e Stop ou vou adicionar um outro método para que simplesmente ajusta o tamanho da imagem da WebCam ao tamanho da imagem do nosso controle gráfico.

Abaixo temos os métodos de Start e Stop configurados:

namespace MinhaWebCamCtrl
{
    public partial class MinhaWebCamComp : UserControl
    {
        // Altura e largura da imagem gerada pela WebCam
        private int m_Width = 500;
        private int m_Height = 500;
        
        //Handle da janela de controle da webcam.
        private int mCapHwnd;

        //Flag para verificar se webcam foi parada.
        private bool bStopped = true;


        //Abaixo temos todas as chamadas das APIs do Sistema Operacional Windows
        //Essas chamadas fazem a interface do nosso controle com a WebCam e e com o SO.
        #region API Declarations

        //Esta chamada é uma das mais importantes e é vital para o funcionamento do SO.
        [DllImport("user32", EntryPoint = "SendMessage")]
        public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);

        //Esta API cria a instancia da webcam para que possamos acessa-la.
        [DllImport("avicap32.dll", EntryPoint = "capCreateCaptureWindowA")]
        public static extern int capCreateCaptureWindowA(string lpszWindowName, int dwStyle, int X, int Y, int nWidth, int nHeight, int hwndParent, int nID);

        //Esta API abre a área de transferência para que possamos buscar os dados da webcam.
        [DllImport("user32", EntryPoint = "OpenClipboard")]
        public static extern int OpenClipboard(int hWnd);

        //Esta API limpa a área de transferência.
        [DllImport("user32", EntryPoint = "EmptyClipboard")]
        public static extern int EmptyClipboard();

        //Esta API fecha a área de transferência após utilização.
        [DllImport("user32", EntryPoint = "CloseClipboard")]
        public static extern int CloseClipboard();

        //Esta API recupera os dados da área de transferência para utilização.
        [DllImport("user32.dll")]
        extern static IntPtr GetClipboardData(uint uFormat);

        #endregion

        #region API Constants

        public const int WM_USER = 1024;

        public const int WM_CAP_CONNECT = 1034;
        public const int WM_CAP_DISCONNECT = 1035;
        public const int WM_CAP_GET_FRAME = 1084;
        public const int WM_CAP_COPY = 1054;

        public const int WM_CAP_START = WM_USER;

        public const int WM_CAP_DLG_VIDEOFORMAT = WM_CAP_START + 41;
        public const int WM_CAP_DLG_VIDEOSOURCE = WM_CAP_START + 42;
        public const int WM_CAP_DLG_VIDEODISPLAY = WM_CAP_START + 43;
        public const int WM_CAP_GET_VIDEOFORMAT = WM_CAP_START + 44;
        public const int WM_CAP_SET_VIDEOFORMAT = WM_CAP_START + 45;
        public const int WM_CAP_DLG_VIDEOCOMPRESSION = WM_CAP_START + 46;
        public const int WM_CAP_SET_PREVIEW = WM_CAP_START + 50;

        #endregion



        public MinhaWebCamComp()
        {
            InitializeComponent();
        }

        #region Start and Stop Capture Functions

        /// <summary>
        /// Ajusta o tamanho da imagem da WebCam com o tamanho da tela.
        /// </summary>
        private void ImageSize()
        {
            m_Width = ImgWebCam.Size.Width;
            m_Height = ImgWebCam.Size.Height;
        }

        /// <summary>
        /// Iniciar a captura de telas da Webcam.
        /// </summary>
        public void Start()
        {
            try
            {
                //Ajusta o tamanho da imagem.
                ImageSize();

                // Por segurança, chamamos o método stop so para garantirmos que não estamos rodando o código.
                this.Stop();

                // Criamos a janela de captura usando a API "capCreateCaptureWindowA"
                mCapHwnd = capCreateCaptureWindowA("WebCap", 0, 0, 0, m_Width, m_Height, this.Handle.ToInt32(), 0);

                // Liberamos recurso ao sistema operacional.
                Application.DoEvents();

                //Enviamos a mensagem através do SO para conectar com o driver da WebCam.
                SendMessage(mCapHwnd, WM_CAP_CONNECT, 0, 0);

                // Ajustamos o intervalo de captura da webcam.
                // Podemos aqui criar uma propriedade do componente para 
                // alterarmos o tempo. Lembrando que quanto maior o tempo 
                // maior o atraso entre o capturado e o exibido.
                this.tmrRefrashFrame.Interval = 1;
                bStopped = false;
                this.tmrRefrashFrame.Start();
            }
            catch (Exception excep)
            {
                MessageBox.Show("Ocorreu um erro na exibição da WebCam. Verifique se está tudo conectado.\r\n\n" + excep.Message);
                this.Stop();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Stop()
        {
            try
            {
                // stop the timer
                bStopped = true;
                this.tmrRefrashFrame.Stop();
                
                // Liberamos recurso ao sistema operacional.
                Application.DoEvents();

                //Envia mensagem ao SO para desconectar a Webcam.
                SendMessage(mCapHwnd, WM_CAP_DISCONNECT, 0, 0);

                //Fecha a área de transferência.
                CloseClipboard();
            }

            catch (Exception excep)
            { // Não dispara nenhum erro.
            }

        }

        #endregion
    }
}

Como já temos os métodos de start e stop configurados, só falta agora iniciarmos a captura dos frames que farão com que nosso objeto de imagem (System.Windows.Forms.PictureBox) seja atualizado gerando a exibição do movimento na tela.

Vamos a última parte da criação do nosso componente que é captura dos frames a cada instante de tempo.

Para isso vamos codificar o final do código conforme abaixo:

namespace MinhaWebCamCtrl
{
    public partial class MinhaWebCamComp : UserControl
    {
        // Altura e largura da imagem gerada pela WebCam
        private int m_Width = 500;
        private int m_Height = 500;
        
        //Handle da janela de controle da webcam.
        private int mCapHwnd;

        //Flag para verificar se webcam foi parada.
        private bool bStopped = true;


        //Abaixo temos todas as chamadas das APIs do Sistema Operacional Windows
        //Essas chamadas fazem a interface do nosso controle com a WebCam e e com o SO.
        #region API Declarations

        //Esta chamada é uma das mais importantes e é vital para o funcionamento do SO.
        [DllImport("user32", EntryPoint = "SendMessage")]
        public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);

        //Esta API cria a instancia da webcam para que possamos acessa-la.
        [DllImport("avicap32.dll", EntryPoint = "capCreateCaptureWindowA")]
        public static extern int capCreateCaptureWindowA(string lpszWindowName, int dwStyle, int X, int Y, int nWidth, int nHeight, int hwndParent, int nID);

        //Esta API abre a área de transferência para que possamos buscar os dados da webcam.
        [DllImport("user32", EntryPoint = "OpenClipboard")]
        public static extern int OpenClipboard(int hWnd);

        //Esta API limpa a área de transferência.
        [DllImport("user32", EntryPoint = "EmptyClipboard")]
        public static extern int EmptyClipboard();

        //Esta API fecha a área de transferência após utilização.
        [DllImport("user32", EntryPoint = "CloseClipboard")]
        public static extern int CloseClipboard();

        //Esta API recupera os dados da área de transferência para utilização.
        [DllImport("user32.dll")]
        extern static IntPtr GetClipboardData(uint uFormat);

        #endregion

        #region API Constants

        public const int WM_USER = 1024;

        public const int WM_CAP_CONNECT = 1034;
        public const int WM_CAP_DISCONNECT = 1035;
        public const int WM_CAP_GET_FRAME = 1084;
        public const int WM_CAP_COPY = 1054;

        public const int WM_CAP_START = WM_USER;

        public const int WM_CAP_DLG_VIDEOFORMAT = WM_CAP_START + 41;
        public const int WM_CAP_DLG_VIDEOSOURCE = WM_CAP_START + 42;
        public const int WM_CAP_DLG_VIDEODISPLAY = WM_CAP_START + 43;
        public const int WM_CAP_GET_VIDEOFORMAT = WM_CAP_START + 44;
        public const int WM_CAP_SET_VIDEOFORMAT = WM_CAP_START + 45;
        public const int WM_CAP_DLG_VIDEOCOMPRESSION = WM_CAP_START + 46;
        public const int WM_CAP_SET_PREVIEW = WM_CAP_START + 50;

        #endregion



        public MinhaWebCamComp()
        {
            InitializeComponent();
        }

        #region Start and Stop Capture Functions

        /// <summary>
        /// Ajusta o tamanho da imagem da WebCam com o tamanho da tela.
        /// </summary>
        private void ImageSize()
        {
            m_Width = ImgWebCam.Size.Width;
            m_Height = ImgWebCam.Size.Height;
        }

        /// <summary>
        /// Iniciar a captura de telas da Webcam.
        /// </summary>
        public void Start()
        {
            try
            {
                //Ajusta o tamanho da imagem.
                ImageSize();

                // Por segurança, chamamos o método stop so para garantirmos que não estamos rodando o código.
                this.Stop();

                // Criamos a janela de captura usando a API "capCreateCaptureWindowA"
                mCapHwnd = capCreateCaptureWindowA("WebCap", 0, 0, 0, m_Width, m_Height, this.Handle.ToInt32(), 0);

                // Liberamos recurso ao sistema operacional.
                Application.DoEvents();

                //Enviamos a mensagem através do SO para conectar com o driver da WebCam.
                SendMessage(mCapHwnd, WM_CAP_CONNECT, 0, 0);

                // Ajustamos o intervalo de captura da webcam.
                // Podemos aqui criar uma propriedade do componente para 
                // alterarmos o tempo. Lembrando que quanto maior o tempo 
                // maior o atraso entre o capturado e o exibido.
                this.tmrRefrashFrame.Interval = 1;
                bStopped = false;
                this.tmrRefrashFrame.Start();
            }
            catch (Exception excep)
            {
                MessageBox.Show("Ocorreu um erro na exibição da WebCam. Verifique se está tudo conectado.\r\n\n" + excep.Message);
                this.Stop();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Stop()
        {
            try
            {
                // stop the timer
                bStopped = true;
                this.tmrRefrashFrame.Stop();
                
                // Liberamos recurso ao sistema operacional.
                Application.DoEvents();

                //Envia mensagem ao SO para desconectar a Webcam.
                SendMessage(mCapHwnd, WM_CAP_DISCONNECT, 0, 0);

                //Fecha a área de transferência.
                CloseClipboard();
            }

            catch (Exception excep)
            { // Não dispara nenhum erro.
            }

        }


        #endregion


        #region Codigo de Captura de video.

        /// <summary>
        /// Captura os frames
        /// </summary>
        private void tmrRefrashFrame_Tick(object sender, System.EventArgs e)
        {
            try
            {
                // Pausa o temporizador
                this.tmrRefrashFrame.Stop();

                //Ajusta o tamanho da imagem.
                ImageSize();

                // Envia ao SO a mensagem para capturar o próximo frame.
                SendMessage(mCapHwnd, WM_CAP_GET_FRAME, 0, 0);

                // copia o frame capturado para a área de transferência.
                SendMessage(mCapHwnd, WM_CAP_COPY, 0, 0);

                //Abre a área de transferência.
                OpenClipboard(mCapHwnd);

                //Busca os dados da área de transferÊncia, colocando os dados em 
                //uma estrutura de ponteiro.
                IntPtr img = GetClipboardData(2);

                //Fecha a área de transferÊncia.
                CloseClipboard();

                //Criamos aqui um objeto do tipo Bitmap.
                System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(m_Width, m_Height);

                //Criamos um objeto gráfico para manipular nossa imagem.
                System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp);

                //Ajustamos algumas propriedades do nosso objeto gráfico.
                //No caso abaixo, estou tentanto otimizar ao máximo a velocidade.
                //Mas também a possível ajustar para a qualidade da imagem.
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low;
                g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
                g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;

                //Pegamos a imagem que está na estrutura do ponteiro que buscamos da 
                //área de transferÊncia e carregamos dentro do nosso bitmap.
                g.DrawImage(Image.FromHbitmap(img), 0, 0, m_Width, m_Height);

                //Exibimos o frame da Webcam no controle que adicionamos no formulário
                //o frame foi salvo na variável do tipo Bitmap
                //e será exibido no controle System.Windows.Forms.PictureBox abaixo.
                ImgWebCam.Image = bmp;

                //fazemos um refresh na imagem.
                ImgWebCam.Refresh();

                // Liberamos recurso ao sistema operacional.
                Application.DoEvents();

                if (!bStopped)
                    this.tmrRefrashFrame.Start();
            }
            catch (Exception excep)
            {
                MessageBox.Show("Ocorreu um erro na exibição da WebCam. Verifique se está tudo conectado.\r\n\n" + excep.Message);
                this.Stop(); // stop the process
            }
        }
        #endregion

    }
    
}

Neste ponto nosso código já está funcionando, entretanto é importante que adicionemos um destrutor para essa classe. A codificação do destrutor é a mais simples possível conforme abaixo:

        //Para garantir que ao sair a webcam será finalizada.
        ~MinhaWebCamComp()
        {
            this.Stop();
        }

Segue abaixo o código do nosso controle finalizado:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace MinhaWebCamCtrl
{
    public partial class MinhaWebCamComp : UserControl, IDisposable
    {
        // Altura e largura da imagem gerada pela WebCam
        private int m_Width = 500;
        private int m_Height = 500;
        
        //Handle da janela de controle da webcam.
        private int mCapHwnd;

        //Flag para verificar se webcam foi parada.
        private bool bStopped = true;


        //Abaixo temos todas as chamadas das APIs do Sistema Operacional Windows
        //Essas chamadas fazem a interface do nosso controle com a WebCam e e com o SO.
        #region API Declarations

        //Esta chamada é uma das mais importantes e é vital para o funcionamento do SO.
        [DllImport("user32", EntryPoint = "SendMessage")]
        public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);

        //Esta API cria a instancia da webcam para que possamos acessa-la.
        [DllImport("avicap32.dll", EntryPoint = "capCreateCaptureWindowA")]
        public static extern int capCreateCaptureWindowA(string lpszWindowName, int dwStyle, int X, int Y, int nWidth, int nHeight, int hwndParent, int nID);

        //Esta API abre a área de transferência para que possamos buscar os dados da webcam.
        [DllImport("user32", EntryPoint = "OpenClipboard")]
        public static extern int OpenClipboard(int hWnd);

        //Esta API limpa a área de transferência.
        [DllImport("user32", EntryPoint = "EmptyClipboard")]
        public static extern int EmptyClipboard();

        //Esta API fecha a área de transferência após utilização.
        [DllImport("user32", EntryPoint = "CloseClipboard")]
        public static extern int CloseClipboard();

        //Esta API recupera os dados da área de transferência para utilização.
        [DllImport("user32.dll")]
        extern static IntPtr GetClipboardData(uint uFormat);

        #endregion

        #region API Constants

        public const int WM_USER = 1024;

        public const int WM_CAP_CONNECT = 1034;
        public const int WM_CAP_DISCONNECT = 1035;
        public const int WM_CAP_GET_FRAME = 1084;
        public const int WM_CAP_COPY = 1054;

        public const int WM_CAP_START = WM_USER;

        public const int WM_CAP_DLG_VIDEOFORMAT = WM_CAP_START + 41;
        public const int WM_CAP_DLG_VIDEOSOURCE = WM_CAP_START + 42;
        public const int WM_CAP_DLG_VIDEODISPLAY = WM_CAP_START + 43;
        public const int WM_CAP_GET_VIDEOFORMAT = WM_CAP_START + 44;
        public const int WM_CAP_SET_VIDEOFORMAT = WM_CAP_START + 45;
        public const int WM_CAP_DLG_VIDEOCOMPRESSION = WM_CAP_START + 46;
        public const int WM_CAP_SET_PREVIEW = WM_CAP_START + 50;

        #endregion



        public MinhaWebCamComp()
        {
            InitializeComponent();
        }

        //Para garantir que ao sair a webcam será finalizada.
        ~MinhaWebCamComp()
        {
            this.Stop();
        }

        #region Start and Stop Capture Functions

        /// <summary>
        /// Ajusta o tamanho da imagem da WebCam com o tamanho da tela.
        /// </summary>
        private void ImageSize()
        {
            m_Width = ImgWebCam.Size.Width;
            m_Height = ImgWebCam.Size.Height;
        }

        /// <summary>
        /// Iniciar a captura de telas da Webcam.
        /// </summary>
        public void Start()
        {
            try
            {
                //Ajusta o tamanho da imagem.
                ImageSize();

                // Por segurança, chamamos o método stop so para garantirmos que não estamos rodando o código.
                this.Stop();

                // Criamos a janela de captura usando a API "capCreateCaptureWindowA"
                mCapHwnd = capCreateCaptureWindowA("WebCap", 0, 0, 0, m_Width, m_Height, this.Handle.ToInt32(), 0);

                // Liberamos recurso ao sistema operacional.
                Application.DoEvents();

                //Enviamos a mensagem através do SO para conectar com o driver da WebCam.
                SendMessage(mCapHwnd, WM_CAP_CONNECT, 0, 0);

                // Ajustamos o intervalo de captura da webcam.
                // Podemos aqui criar uma propriedade do componente para 
                // alterarmos o tempo. Lembrando que quanto maior o tempo 
                // maior o atraso entre o capturado e o exibido.
                this.tmrRefrashFrame.Interval = 1;
                bStopped = false;
                this.tmrRefrashFrame.Start();
            }
            catch (Exception excep)
            {
                MessageBox.Show("Ocorreu um erro na exibição da WebCam. Verifique se está tudo conectado.\r\n\n" + excep.Message);
                this.Stop();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Stop()
        {
            try
            {
                // stop the timer
                bStopped = true;
                this.tmrRefrashFrame.Stop();
                
                // Liberamos recurso ao sistema operacional.
                Application.DoEvents();

                //Envia mensagem ao SO para desconectar a Webcam.
                SendMessage(mCapHwnd, WM_CAP_DISCONNECT, 0, 0);

                //Fecha a área de transferência.
                CloseClipboard();
            }

            catch (Exception excep)
            { // Não dispara nenhum erro.
            }

        }


        #endregion


        #region Codigo de Captura de video.

        /// <summary>
        /// Captura os frames
        /// </summary>
        private void tmrRefrashFrame_Tick(object sender, System.EventArgs e)
        {
            try
            {
                // Pausa o temporizador
                this.tmrRefrashFrame.Stop();

                //Ajusta o tamanho da imagem.
                ImageSize();

                // Envia ao SO a mensagem para capturar o próximo frame.
                SendMessage(mCapHwnd, WM_CAP_GET_FRAME, 0, 0);

                // copia o frame capturado para a área de transferência.
                SendMessage(mCapHwnd, WM_CAP_COPY, 0, 0);

                //Abre a área de transferência.
                OpenClipboard(mCapHwnd);

                //Busca os dados da área de transferÊncia, colocando os dados em 
                //uma estrutura de ponteiro.
                IntPtr img = GetClipboardData(2);

                //Fecha a área de transferÊncia.
                CloseClipboard();

                //Criamos aqui um objeto do tipo Bitmap.
                System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(m_Width, m_Height);

                //Criamos um objeto gráfico para manipular nossa imagem.
                using(System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp))
                {
                //Ajustamos algumas propriedades do nosso objeto gráfico.
                //No caso abaixo, estou tentanto otimizar ao máximo a velocidade.
                //Mas também a possível ajustar para a qualidade da imagem.
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low;
                g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
                g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;

                //Pegamos a imagem que está na estrutura do ponteiro que buscamos da 
                //área de transferÊncia e carregamos dentro do nosso bitmap.
                g.DrawImage(Image.FromHbitmap(img), 0, 0, m_Width, m_Height);
                }
                //Exibimos o frame da Webcam no controle que adicionamos no formulário
                //o frame foi salvo na variável do tipo Bitmap
                //e será exibido no controle System.Windows.Forms.PictureBox abaixo.
                ImgWebCam.Image = bmp;

                //fazemos um refresh na imagem.
                ImgWebCam.Refresh();

                // Liberamos recurso ao sistema operacional.
                Application.DoEvents();

                if (!bStopped)
                    this.tmrRefrashFrame.Start();
            }
            catch (Exception excep)
            {
                MessageBox.Show("Ocorreu um erro na exibição da WebCam. Verifique se está tudo conectado.\r\n\n" + excep.Message);
                this.Stop(); // stop the process
            }
        }
        #endregion
        void IDisposable.Dispose(){this.Dispose();}
    }
    
}

Vamos compilar o código para ver se houve algum erro e caso esteja tudo correto, vamos passar para a próxima etapa que deverá ser a mais simples.

SNAGHTMLa0cfa0

Se temos o código com Build Succeeded, então temos nosso controle configurado de forma correta.

Vamos para a segunda parte do nosso artigo:

Criando Nosso Aplicativo Executável

Vamos agora dar sequência a criação do executável.

No VS em “Solution Explorer” vamos adicionar um novo projeto só que dessa vez será um “Windows Forms Application” conforme imagem abaixo:

SNAGHTMLa59733

Para este aplicativo, dei o nome de MinhaAppWebCam, sinta-se a vontade para alterá-lo conforme sua vontade.

Após clicar em “OK”, teremos a segunte tela:

SNAGHTMLa78dd0

Vamos fazer algumas alterações no nosso form. A primeira é alterar o nome de “Form1.cs” para “frmExibeWebCam.cs”. A segunda é modificar o título do nosso formulário, vou colocar no meu “Márcio Pulcinelli WebCam 1.0”, mas você pode alterá-lo como desejar. A imagem deverá ficar como a abaixo:

SNAGHTMLaad98f

Como você deve ter notado, a nossa barra de ferramentas a esquerda, apresenta um novo controle que é exatamente o controle que criamos. Para adicioná-lo, faremos exatamente como fizemos nos outros controles do VS. Basta arrastá-lo e definir sua propriedade “BorderStyle” para “FixedSingle” e adicionar dois botões na tela conforme imagem abaixo:

SNAGHTMLb14108

Com a interface finalizada, vamos codificar os botões para iniciar a e finalizar a exibição da WebCam.

O código do nosso form deverá ficar como o seguinte:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace MinhaAppWebCam
{
    public partial class frmExibeWebCam : Form
    {
        public frmExibeWebCam()
        {
            InitializeComponent();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            minhaWebCamComp1.Start();
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            minhaWebCamComp1.Stop();
        }

    }
}

 

Pronto.

Agora basta compilar e se não houver nenhum erro, executamos nossa aplicação para acessar nossa webcam.

Se a imagem abaixo estiver apresentada, é só executar seu aplicativo.

SNAGHTMLb4fca9

Após executar o nosso aplicativo, teremos nosso programa rodando como a imagem abaixo.

SNAGHTMLb6e948

Clicamos então em iniciar WebCam para finalmente verificarmos o funcionamento da aplicação.

A seguinte tela aparecerá para selecionar qual o dispositivo de captura você deseja utilizar:

SNAGHTMLb8b2ce

Selecione o dispositivo e clique em “OK”.

Assim que o botão Ok for pressionado, sua Webcam será acessada através do aplicativo que acabamos de criar conforme imagem abaixo.

image

Olha eu aí na Webcam.

Baixe o código completo aqui –> Login to view.






Espero que tenham gostado do artigo.

Qualquer dúvida entre em contato.

Márcio Pulcinelli @ OminaVincit


Threads em C#

Vou falar um pouco neste artigo sobre como funciona o conceito de threads usando a linguagem C#.

O primeiro passo é saber o que é e como funciona as threads. Para isso vou recorrer às definições formais do conceito.

Linha de execução (em inglês: Thread), é uma forma de um processo dividir a si mesmo em duas ou mais tarefas que podem ser executadas concorrentemente. O suporte à thread é fornecido pelo próprio sistema operacional (SO), no caso da linha de execução ao nível do núcleo (em inglês: Kernel-Level Thread (KLT)), ou implementada através de uma biblioteca de uma determinada linguagem, no caso de uma User-Level Thread (ULT). Uma linha de execução permite que o usuário de programa, por exemplo, utilize uma funcionalidade do ambiente enquanto outras linhas de execução realizam outros cálculos e operações.

Uma Thread é Um fluxo de controle sequencial isolado dentro de um programa. Como um programa sequencial qualquer, um thread tem um começo, um fim, e uma seqüência de comandos. Entretanto, um thread não é um programa, não roda sozinho, roda dentro de um programa. Threads permitem que um programa simples possa executar várias tarefas diferentes ao mesmo tempo, independentemente umas das outras. Programas multi-threaded são programas que contém vários threads, executando tarefas distintas, ao mesmo tempo.

Não importa o nome que se dê (Threads, sequências, linhas de execução, encadeamento), Todas as aplicações são executadas em uma thread principal.

Uma aplicação pode ter mais de uma thread ao mesmo tempo, ou seja, podemos estar fazendo várias coisas ao mesmo tempo, um exemplo disso seria o próprio sistema operacional Windows. Quando você visualiza a barra de tarefas, percebe que os processos estão sendo executados ao mesmo tempo.

Mas não se engane, embora você esteja vendo a barra de tarefas exibir diversos aplicativos sendo executados, nenhum deles esta sendo executado ao mesmo tempo. Não existe computador que possa realizar tal proeza com uma única CPU. O que o Windows, e qualquer outro sistema que suporte a multitarefa, faz é estar alternando rapidamente entre diferentes threads de forma que cada thread pensa que esta executando independentemente, mas, só executa por algum tempo, interrompe e depois volta, e assim por diante. Agora, quando a máquina em que o sistema está rodando possui dois ou mais processadores, existe a possibilidade de haver um processamento paralelo real.

A linguagem C# (assim como o C++), utilizada neste artigo, suporta a execução paralela de código através do multithreading, onde uma Thread é uma caminho de execução independente que esta apto para rodar simultaneamente com outras threads.

Um aplicativo C# de qualquer tipo, Console, WPF ou Windows Forms inicia em uma única thread criada de forma automática pela CLR (Common Languagem Runtime) e pelo Sistema Operacional ( a thread principal – main ) e torna-se multithread pela criação de threads adicionais programadas dentro do código.

Vou iniciar utilizando o Visual Studio 2010 para criar uma thread simples como exemplo.

Primeiro exemplo:

Vamos abrir o Visual Studio e criar um Projeto do tipo console (aquele com janela com a cara do antigo DOS).

image

1- Selecionamos Console Application (conforme selecionado em amarelo).

2- Em seguida colocamos o nome (Thread_Console) no campo name.

3- Selecionamos a localização do projeto no campo Location.

4- E por último adicionamos o nome da Solução, no campo Solution Name.

5- Clique em OK para que o Visual Studio gere o projeto para você.

Apos o click em OK, o Visual Studio cria seu projeto com a seguinte estrutura:

image 

Para o nosso primeiro exemplo vamos adicionar o seguinte bloco de código:

Exemplo 1

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace Thread_Console { class Program { static void Main(string[] args) { // Instancia e inicia uma nova thread. Thread t = new Thread(NovaThread); t.Start(); // Ao mesmo tempo que roda a “NovaThread”,

// executa uma tarefa na thread principal, ou seja, a linha abaixo. for (int i = 0; i < 10000; i++) Console.Write("1"); } static void NovaThread() { for (int i = 0; i < 10000; i++) Console.Write("2"); } } }

 

Neste pequenino programa a thread principal instancia uma nova thread t que chama um método que imprime na tela o numero 2 (segunda thread). E ao mesmo tempo a thread principal imprime o número 1 (thread principal).

Resultado do programa executado!

image

Observe que a thread principal inicia e já cria a segunda thread que começa a imprimir o número 2, após isso a execução é intercalada e ocorre simultaneamente exibindo o resultado da figura mostrada acima.

O método Start() inicia a execução da segunda thread e após o término da execução ambas as threads são encerradas.

Sempre que a thread for iniciada pelo método Start() a propriedade IsAlive da thread retorna true até que a thread seja encerrada. Esta propriedade é importante para saber se a thread ainda está em execução.

É importante observar que uma thread termina quando o delegate passado para o construtor da thread encerra a execução e uma vez terminada uma thread não pode ser iniciada novamente.

A CLR atribui a cada thread sua própria alocação de memória na pilha (Stack) de forma que variáveis locais são mantidas separadas.

Vamos agora para nosso exemplo 2.

Exemplo 2

Vejamos agora um exemplo onde definimos um método com uma variável local e então chamamos o método simultaneamente na thread principal e na nova thread criada.

Vamos criar um novo projeto do tipo console com o nome Thread_Console_2 e vamos adicionar o seguinte código.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Thread_Console_2
{
    class Program
    {
        static void Main(string[] args)
        {
            // Instancia e chama NovaThread em uma nova Thread
            new Thread(NovaThread).Start();

            // Chama NovaThread na thread principal
            NovaThread();

            Console.ReadKey();
        }

        static void NovaThread()
        {
            for (int contador = 0; contador < 5; contador++)
                Console.Write('2');
        }
    }
}

 

Neste código estamos criando uma nova thread e chamando a rotina NovaThread. Em seguida chamamos novamente a rotina NovaThread na thread principal. A rotina NovaThread declara e usa uma variável local chamada contador. Uma cópia separada da variável contador é criada e cada thread aloca a área de memória Stack para a variável;

Dessa forma o resultado da execução é o seguinte:

image

As threads podem compartilhar dados se os mesmos tiverem uma referência comum para a mesma instância. Veja o exemplo a seguir:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Thread_Console
{
    class Program
    {
        bool flg;

        static void Main(string[] args)
        {
            // Cria uma instância comum da própria classe Program
            Program myPRog = new Program();

            //inicia a execução da rotina na nova instância criada.
            new Thread(myPRog.NovaThread).Start();
            
            //executa a rotina na mesma instãncia
            myPRog.NovaThread();
            
            Console.ReadKey();
        }

        // Note que NovaThread é agora um método de instância.
        void NovaThread()
        {
            if (!flg)
            {
                flg = true;
                Console.WriteLine("Olá Mundo!!!");
            }
        }
    }
}

 

O resultado desse código é exatamente a imagem abaixo:

image

Como todo bom exemplo de código, eu tinha que colocar nosso bom e velho olá mundo!

Ainda existe uma outra forma de compartilhar dados entre as threads é definir campos estáticos. Veja um exemplo no código a seguir:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Thread_Console
{
    class Program
    {
        static bool ok;

        static void Main(string[] args)
        {
            new Thread(NovaT).Start();

            NovaT();
            
            Console.ReadKey();
        }

        static void NovaT()
        {
            if (!ok)
            {
                ok = true;
                Console.WriteLine("Olá Mundo!!!");
            }
        }
    }
}

Ambos os código, do último e do penúltimo exemplo, tem o mesmo efeito e resultado, porém devemos tomar cuidado, pois podemos ter casos onde a execução deste código ocorra de forma inconsistente levando a erros de diversos tipo.

O problema é que uma thread pode estar verificando a instrução if enquanto a outra thread pode estar executando a instrução Writeline. O que quer dizer que até agora trabalhamos sem o conceito de Thread- Safety.

Mas o que seria isso??? Thread-Safety?

Bom, é um conceito existente no contexto de programas multi-threads onde um trecho de código é thread-safe se ele funcionar corretamente durante execução simultânea por vários threads. Meio confuso isso ainda. Vou explicar melhor.

Se o código não for thread-safe e estiver rodando em um ambiente multi-threads os resultados obtidos podem ser inesperados ou incorretos pois uma thread pode desfazer o que a outra thread já realizou. Esse é o problema clássico da atualização do contador em uma aplicação multi-thread.

Para resolver tal problema foi criado o conceito de Lock. Usando a instrução lock() vai bloquear os recursos até que a thread que o esta usando acabe de processá-lo.

O lock funciona da seguinte forma: Quando duas threads simultâneas contêm um lock() ou bloqueio, uma thread espera, até que o bloqueio seja liberado. Neste caso, somente uma thread terá acesso ao recurso por vez e dessa forma torná-lo thread-safe. Em contra-partida, há uma perda na performance da aplicação (óbvio, porem necessário em determinados caso).

No exemplo anterior podemos podemos fazer algumas modificações para usar o bloqueio ou lock da seguinte forma:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Thread_Console
{
    class Program
    {
        static bool ok;

        //Objeto criado para travar a execução da thread.
        static readonly object bloq = new object();

        static void Main(string[] args)
        {
            new Thread(NovaT).Start();
            
            NovaT();
            
            Console.ReadKey();
        }

        static void NovaT()
        {
            lock (bloq)
            {
                if (!ok)
                {
                    Console.WriteLine("Olá Mundo!!!");
                    ok = true;
                }
            }
        }
    }
}

Importante observar que enquanto a thread estiver bloqueada ela não consome recursos do sistema.

Também podemos esperar que uma outra thread termine a sua execução usando o método Join(). Veja o exemplo abaixo:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Thread_Console
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread myT = new Thread(ExecRotina);
            
            //inicia a thread
            myT.Start();
            
            //Enquanto a thread não finaliza sua execução, o método fica aguardando.
            myT.Join();
            
            Console.WriteLine("\r\n\r\nFim da Execução da Thread!");
            
            Console.ReadKey();
        }
        static void ExecRotina()
        {
            for (int i = 0; i < 10000; i++)
                Console.Write("Olá Mundo!!!!");
        }
    }
}

Abaixo podemos ver o resultado da execução do código criado acima. Como podemos ver a execução imprime na tela 10.000 vezes a frase “Olá Mundo”. Em seguida imprime “Fim da execução da Thread!”

image

Você pode usar o método Join() com parâmetros de tempo em milissegundos caso deseje determinar o tempo máximo que a thread deve permanecer rodando.

Você pode incluir um tempo de espera quando faz a chamada do Join em milissegundos ou como um TimeSpan que retornará true se a thread já terminou ou false se o tempo expirou.

Para incluir um tempo de espera, pausar uma thread, usamos o método Sleep(); O mesmo é um método estático que recebe como parâmetro um valor que define o período de espera:

Thread.Sleep (TimeSpan.FromHours (1));  // pausa por 1 hora
Thread.Sleep (500);                     // pausa por 500 milliseconds

 

Um ponto importante é que enquanto estiver esperando, quer seja através do método Sleep ou Join , a thread fica bloqueada e não consome recursos de CPU.

Se for definido o valor zero => Thread.Sleep(0) , a thread será suspensa para permitir que outras threads que estão aguardando o processamento sejam executadas.

Caso seja necessário bloquear uma thread indefinidamente utilize o valor Infinite => Thread.Sleep(infinite).

A partir da versão 4.0 do .NET Framework existe o método Yield => Thread.Yield(), que faz a mesma coisa, exceto pelo fato de que são liberadas somente threads que estão rodando no mesmo processo. O sistema operacional seleciona a thread que será executada.

Resumindo o que comentamos até o momento:

Método Descrição
Start Faz com que uma thread seja agendado para execução.
Lock Bloqueia os recursos até que a thread que o esta usando acabe de processá-lo.
Join Bloqueia o segmento de chamada até que um thread seja encerrado.
Sleep Bloqueia o corrente thread pelo número especificado de milissegundos

 

Mas precisamos ainda matar uma Thread para que ela possa ser abortada no meio do caminho, caso alguma coisa dê errado. Para fazer isso podemos matar uma thread diretamente chamando o método Abort para encerrar thread.

A chamada do método Abort faz com que a thread atual encerre a execução lançando a exceção: ThreadAbortException.

Para verificar se uma thread esta viva (rodando) consultamos sua propriedade IsAlive() a qual devolve true apenas se a thread foi iniciada e ainda não esta finalizada (morta).

Uma thread pode estar em um dos seguintes estados:

Unstarted

A thread foi criada mas não foi iniciada.

Running

A thread foi iniciada.

WaitSleepJoin

A thread espera por um.

Suspended

A thread foi suspensa.

Stopped

A thread parou ou foi abortada.






Então é isso. Este é o nosso artigo sobre Thread no C#, espero que tirem bastante proveito do artigo.

Qualquer dúvida é só entrar em contato!

Márcio Pulcinelli @ OminaVincit!