<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:thr='http://purl.org/syndication/thread/1.0' version='2.0'><channel><atom:id>tag:blogger.com,1999:blog-32803974</atom:id><lastBuildDate>Thu, 29 Apr 2010 00:26:37 +0000</lastBuildDate><title>DriverEntry - Did you think it'd be easy?</title><description>Blog destinado aos desenvolvedores de drivers e softwares de baixo nível para Windows.</description><link>http://www.driverentry.com.br/blog/</link><managingEditor>fernando@driverentry.com.br (Fernando Roberto da Silva)</managingEditor><generator>Blogger</generator><openSearch:totalResults>69</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-4010019450307371183</guid><pubDate>Wed, 28 Apr 2010 23:47:00 +0000</pubDate><atom:updated>2010-04-28T21:26:37.248-03:00</atom:updated><title>Último post pelo Blogger</title><description>&lt;img style="FLOAT: right; MARGIN: 0px 10px;" src="http://www.driverentry.com.br/images/BloggerToWP.png" border="0" /&gt;&lt;p&gt;Olá cambada! Puts, eu já estava com saudade de postar alguma coisa. Pena este ser mais um daqueles chatos posts Off-Topic. O que posso fazer? É o que temos para o momento.&lt;/p&gt;&lt;p&gt;Hoje pela manhã recebi um mais um e-mail de aviso do &lt;a href="http://www.blogger.com"&gt;Blogger&lt;/a&gt; dizendo que o suporte à publicação via FTP estão com os dias contatos. Como este recurso é utilizado por menos de 5% dos seus usuários, resolveram dar fim à ele. Mas essa não foi a principal razão pela qual eu decidi deixar de postar pelo Blogger para usar o &lt;a href="http://www.wordpress.org"&gt;WordPress&lt;/a&gt;. Eu já vinha ensaiando essa transição há pelo menos dois anos. A faculdade e outras desculpinhas esfarrapadas não me deixavam pôr essa idéia em prática. Alguns leitores já vinham reclamando da falta de um índice geral e da separação dos posts em categorias, coisas que o WordPress faz com uma mão nas costas.&lt;/p&gt;&lt;p&gt;Como um web designer, eu sou um excelente desenvolvedor de drivers, e eu já tinha ouvido falar que o WordPress usava PHP e MySQL, assuntos os quais eu não tenho a menor intimidade. Inicialmente contei com a ajuda do meu irmão que já possuia um &lt;a href="http://www.kabloc.com.br"&gt;blog&lt;/a&gt; pelo WordPress, mas assim como eu, ele também tinha faculdade e outras prioridades. Por um tempo eu contratei os serviços de um web designer que colocava em prática as minhas ídéias com relação ao novo layout, mas por fim ele se envolveu com algo maior e fiquei na mão novamente.&lt;/p&gt;&lt;p&gt;A idéia de migrar o blog persistia, mas a preguiça, a falta de tempo e de domínio de HTML e CSS me fizeram ir empurrando esse problema com a barriga. Cheguei até a ler o guia o oficial da famosa série &lt;span style="font-style:italic;"&gt;"Preciso aprender isso de qualquer jeito"&lt;/span&gt;, o livro &lt;a href="http://www.amazon.com/WordPress-Dummies-Computer-Tech/dp/0470149469"&gt;"WordPress for Dummies"&lt;/a&gt;. Pra quem está acostumado a ler livros de assuntos um tanto mais cabeludos, as mais de 380 páginas do livro foram como um passeio no parque. O mais surpreendente pra mim foi descobrir que o WordPress é uma ferramenta que não requer prática nem tampouco habilidade, e isso incluia as habilidades de PHP e MySQL que eu não tenho. Uma ferramenta poderosa, flexível e simples. A parte desagradável dessa história é que tudo que o livro falou sobre HTML e CSS é que ele não falava a respeito.&lt;/p&gt;&lt;a href="http://www.amazon.com/WordPress-Dummies-Computer-Tech/dp/0470149469"&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px;" src="http://www.driverentry.com.br/images/wordpress-for-dummies.gif" border="0" /&gt;&lt;/a&gt;&lt;p&gt;Até aqui excelente. Os 67 posts poderiam ser migrados mas o layout ainda foi o meu carma. Outras tentativas de encontrar alguém disposto a fazer o design pra mim me mostrou que estou no emprego errado. Esse negócio de desenvolver drivers não está com nada. Pra ganhar dinheiro mesmo o negócio é fazer Web Design.&lt;/p&gt;&lt;p&gt;Há uns três meses, quando recebi o primeiro aviso do Blogger sobre o fim do suporte ao FTP foi o momento &lt;span style="font-style:italic;"&gt;"Agora ou nunca"&lt;/span&gt;. Minha faculdade não me servia mais de desculpa e aos poucos fui colocando a mão na massa. Depois de muito trabalho manual de migrar cada post e cada comentário, veio o layout. Graças ao site &lt;a href="http://www.w3schools.com/"&gt;W3Schools&lt;/a&gt;, às aulas particulares de Photohop do meu irmão e à ajuda de alguns amigos, essa semana consegui publicar o novo blog. Ainda faltam muitas coisas. A página &lt;span style="font-style:italic;"&gt;"About Me"&lt;/span&gt; ainda não tem nada &lt;span style="font-style:italic;"&gt;about me&lt;/span&gt;. A página de treinamentos, que vai falar sobre os cursos que ofereço ainda fala exatamente isso: &lt;span style="font-style:italic;"&gt;"Essa página vai falar sobre os cursos que ofereço"&lt;/span&gt;. Ainda estou instalando alguns plug-ins que vão ajudar em uma coisa aqui e outra ali. Mas o fato é que há dois dias do prazo final do suporte ao FTP, o novo blog já está no ar.&lt;/p&gt;&lt;p&gt;Esse post foi o último a ser contruído no Blogger com o objetivo principal de avisar àqueles que seguem o RSS original de que o endereço agora mudou. O novo RSS será provido em um novo endereço.&lt;/p&gt;&lt;p&gt;Eu não poderia terminar esse blog sem agraceder às pessoas que tiveram a paciência de me aturar com testes e dúvidas sobre HTML, CSS e sei-lá mais o que. Um muito obrigado aos meus amigos &lt;a href="http://www.caloni.com.br"&gt;Lesma&lt;/a&gt;, Thiago &lt;a href="http://twitter.com/thiago_morais"&gt;Oliveira&lt;/a&gt;, &lt;a href="http://twitter.com/tgbrito"&gt;Thiago Brito&lt;/a&gt;, meu irmão &lt;a href="http://www.kabloc.com.br"&gt;Kabloc&lt;/a&gt;, &lt;a href="http://william.net.br/yap/"&gt;Willam&lt;/a&gt;, &lt;a href="http://www.oblita.com/"&gt;Francisco&lt;/a&gt;, à minha esposa Magda que migrou todos os comentários e a todos os outros que não me lembro agora (eu perturbei muita gente).&lt;/p&gt;&lt;p&gt;Se você não conhecia o blog antigo, &lt;a href="http://www.driverentry.com.br/blogger"&gt;aqui&lt;/a&gt; estão as páginas do velho aposentado.&lt;br/&gt;Valeu!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-4010019450307371183?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2010/04/ultimo-post-pelo-blogger.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-8637318931798652424</guid><pubDate>Fri, 26 Feb 2010 21:42:00 +0000</pubDate><atom:updated>2010-03-10T11:55:35.496-03:00</atom:updated><title>Sexto Encontro de Programadores C/C++</title><description>&lt;img style="FLOAT: right; MARGIN: 0px 10px;" src="http://www.driverentry.com.br/images/logo_com_c++_160x115.jpg" border="0" /&gt;&lt;p&gt;Começo de ano é sempre a mesma correria. Depois que a gente começa a se acostumar com a idéia de que o feriadão de Natal e Ano Novo acabaram, logo vem o carnaval e desanda tudo. Aproveitei este início de ano para tirar merecidas férias, já que finalmente meu curso de Engenharia da Computação terminou. Como um belo &lt;span style="font-style:italic;"&gt;start&lt;/span&gt; para minha reintegração à sociedade, nada melhor que uma viajem ao nordeste brasileiro. Semanas antes desta viajem fiquei sabendo do novo encontro da comunidade de programadores C/C++. Por causa da viagem eu perderia o evento.&lt;/p&gt;&lt;p&gt;Um mês se passou e semana passada vi que o evento foi adiado para o próximo dia &lt;span style="font-weight:bold;"&gt;seis de março&lt;/span&gt;. Muito boa notícia já que além de poder participar do evento também vou falar um pouquinho. &lt;a href="http://www.driverentry.com.br/blog/2008/03/driverentrycombr-d-as-caras.html"&gt;Já participei de outro encontro&lt;/a&gt; de programadores e fiquei muito feliz de poder falar para tantas pessoas sobre esse assunto tão misterioso para muitos, que é o desenvolvimento de drivers para Windows.&lt;/p&gt;&lt;p&gt;Sob meu ponto de vista os resultados foram muito bons. A palestra deu uma introdução ao assunto de desenvolvimento de drivers e obviamente os 60 minutos de palestra não foram suficientes para explicar o assunto com detalhes, mas foi interessante mostrar a ponta do &lt;span style="font-style:italic;"&gt;iceberg&lt;/span&gt; e poder responder a algumas perguntas dos presentes. Eu escrevi um &lt;a href="http://www.driverentry.com.br/blog/2008/06/tirando-o-atraso-p.html"&gt;post&lt;/a&gt; que comenta sobre o encontro, mas recomendo o &lt;a href="http://www.caloni.com.br/blog/archives/epa-ccpp-4-nossa-comunidade-ganhando-forma"&gt;post do meu amigo Lesma&lt;/a&gt; que ficou muito bom.&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Lidando com Memória Virtual em Drivers&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Bem, neste novo encontro não vou dar novamente uma introdução ao assunto, mas vou comentar sobre algumas carasterísticas e curiosidades sobre Memória Virtual no desenvolvimento de drivers. Entenda que não vou fazer um resumo do capítulo 9 do Windows Internals, "Memory Management" que fala sobre Page Table Entries e Working Sets, mas sim demonstrar que, diferentes de uma aplicação User-Mode, drivers precisam estar cientes dos conceitos fundamentais de memória virtual e paginação, controlando paginação de objetos e atendendo à requisítos de paginação de memória e espaço de endereçamento.&lt;/p&gt;&lt;p&gt;Os tópicos a serem discutidos na palestra serão os seguintes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Overview de Memória Virtual e Paginação.&lt;/li&gt;&lt;li&gt;Operações de I/O e manipulação de Buffers.&lt;/li&gt;&lt;li&gt;Prioridade de thread e acesso à memória.&lt;/li&gt;&lt;li&gt;Pools de alocação, Tags e Quotas.&lt;/li&gt;&lt;li&gt;Evitando Fragmentação.&lt;/li&gt;&lt;li&gt;Drivers no caminho da paginação.&lt;/li&gt;&lt;li&gt;Controlando paginação de funções.&lt;/li&gt;&lt;li&gt;Obtendo endereços reais de memória.&lt;/li&gt;&lt;li&gt;Dispositivos de acesso direto à memória (DMA).&lt;/li&gt;&lt;li&gt;Recursos do Driver Verifier.&lt;/li&gt;&lt;li&gt;Hands on: Operações de memórias por drivers no WinDbg.&lt;/li&gt;&lt;li&gt;Dúvidas.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Esta é a &lt;a href="http://ccppbrasil.org/wiki/Grupo:Encontro_VI"&gt;página oficial&lt;/a&gt; do evento e as &lt;a href="http://www.temporealeventos.com.br/inscricoes/inscricoes.php?area=95&amp;form=367"&gt;inscrições&lt;/a&gt; estão abertas.
Até lá!&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.driverentry.com.br/downloads/Palestra-060310.zip"&gt;Download dos slides&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-8637318931798652424?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2010/02/sexto-encontro-de-programadores-cc.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>3</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-4208475676533334470</guid><pubDate>Wed, 20 Jan 2010 20:48:00 +0000</pubDate><atom:updated>2010-02-22T16:38:45.699-03:00</atom:updated><title>Escrevendo Filtros</title><description>&lt;p&gt;Vamos brincar de algo mais interessante agora. Obviamente ainda vamos dar passos pequenos para não nos perder com tantos detalhes. Hoje falarei sobre filtros de drivers. O &lt;span style="font-style:italic;"&gt;IoManager&lt;/span&gt; do Windows nos permite adicionar funcionalidade a determinados drivers sem que tenhamos que substituí-lo. Um exemplo clássico seria um driver de criptografia de arquivos. Você não precisa escrever um novo driver de &lt;span style="font-style:italic;"&gt;file system&lt;/span&gt; para que se tenha tal funcionalidade. Você pode simplesmente escrever um filtro que ficaria entre o driver de &lt;span style="font-style:italic;"&gt;file system&lt;/span&gt; e o restante do sistema.&lt;/p&gt;&lt;p&gt;Na figura abaixo podemos observar o fluxo de &lt;a href="http://www.driverentry.com.br/blog/2007/02/legal-mas-o-que-uma-irp.html"&gt;IRPs&lt;/a&gt; que vão do &lt;span style="font-style:italic;"&gt;IoManager&lt;/span&gt; para um certo driver, um filtro é instalado sobre o driver existente e passa a receber as IRPs no lugar do driver original. Com isso o filtro tem a oportunidade de alterar os parâmetros das IRPs recebidas, ou logar a atividade do driver original, ou duplicar solicitações do sistema para esse driver, ou mesmo negar serviço do driver original.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/NewFilterDev.png" /&gt;&lt;/div&gt;&lt;p&gt;Numa operação de escrita, um filtro poderia criptografar os dados antes de enviá-los ao driver original, e de maneira similar, numa operação de leitura o filtro poderia decriptografar dados antes de entregá-los ao sistema.&lt;/p&gt;&lt;p&gt;Ainda não vai ser desta vez que construiremos um filtro de criptografia de &lt;span style="font-style:italic;"&gt;file system&lt;/span&gt;. Filtros de criptografia de arquivos em tempo real estão entre os drivers mais complexos a serem escritos. Vamos escolher um driver mais simples, pra não dizer um bem besta, para aplicar os conceitos básicos demonstrados aqui.&lt;/p&gt;&lt;p&gt;Por falar em driver besta, vamos utilizar o driver &lt;a href="http://www.driverentry.com.br/blog/2009/07/strings-no-kernel.html"&gt;deste post&lt;/a&gt; que já foi visto aqui. Este driver simplesmente armazena uma lista de strings enviadas por uma aplicação em operações de escrita. Tais strings são retornadas à aplicação em operações de leituras.&lt;/p&gt;&lt;p&gt;
&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Escrevendo a DriverEntry&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Como vimos neste &lt;a href="http://www.driverentry.com.br/blog/2007/06/nos-queremos-exemplos.html"&gt;outro post&lt;/a&gt;, uma das das coisas que a função &lt;span style="font-style:italic;"&gt;DriverEntry()&lt;/span&gt; faz num driver comum é setar as &lt;span style="font-style:italic;"&gt;Dispatch Routines&lt;/span&gt; que o driver vai atender para certo dispositivo. Para isso deve-se preencher o array de &lt;span style="font-style:italic;"&gt;Major Functions&lt;/span&gt; que fica na estrutura &lt;a href="http://msdn.microsoft.com/en-us/library/dd852039.aspx"&gt;DRIVER_OBJECT&lt;/a&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Seta as dispatch routines do driver.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pDriverObj-&amp;gt;MajorFunction[IRP_MJ_CREATE] = OnCreate;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pDriverObj-&amp;gt;MajorFunction[IRP_MJ_CLEANUP] = OnCleanup;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pDriverObj-&amp;gt;MajorFunction[IRP_MJ_CLOSE] = OnClose;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pDriverObj-&amp;gt;MajorFunction[IRP_MJ_READ] = OnRead;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pDriverObj-&amp;gt;MajorFunction[IRP_MJ_WRITE] = OnWrite;&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;No caso do nosso filtro de exemplo, queremos apenas monitorar a atividade do driver ao qual estamos atachados, dessa forma teremos sempre que encaminhar quaisquer IRPs recebidas ao driver de baixo. Uma forma simples e comum de fazer isso é setar todas dispath routines para uma única função. Essa função se encarrega de simplesmente logar a solicitação recebida e passá-la a diante.&lt;/p&gt;&lt;p&gt;Se dermos uma olhada na definicão de IRP_MJ_CREATE e seus amigos, veremos o seguinte trecho do arquivo wdm.h.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;// Define the major function codes for IRPs.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_CREATE&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x00&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_CREATE_NAMED_PIPE&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x01&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_CLOSE&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x02&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_READ&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x03&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_WRITE&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x04&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_QUERY_INFORMATION&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x05&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_SET_INFORMATION&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &lt;span style="color: purple;"&gt;0x06&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_QUERY_EA&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x07&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_SET_EA&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x08&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_FLUSH_BUFFERS&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x09&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_QUERY_VOLUME_INFORMATION &lt;span style="color: purple;"&gt;0x0a&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_SET_VOLUME_INFORMATION&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x0b&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_DIRECTORY_CONTROL&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x0c&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_FILE_SYSTEM_CONTROL&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &lt;span style="color: purple;"&gt;0x0d&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_DEVICE_CONTROL&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x0e&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_INTERNAL_DEVICE_CONTROL&amp;nbsp; &lt;span style="color: purple;"&gt;0x0f&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_SHUTDOWN&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x10&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_LOCK_CONTROL&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x11&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_CLEANUP&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &lt;span style="color: purple;"&gt;0x12&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_CREATE_MAILSLOT&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &lt;span style="color: purple;"&gt;0x13&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_QUERY_SECURITY&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x14&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_SET_SECURITY&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x15&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_POWER&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x16&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_SYSTEM_CONTROL&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x17&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_DEVICE_CHANGE&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x18&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_QUERY_QUOTA&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &lt;span style="color: purple;"&gt;0x19&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_SET_QUOTA&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x1a&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_PNP&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &lt;span style="color: purple;"&gt;0x1b&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_PNP_POWER&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; IRP_MJ_PNP&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &lt;span style="color: green;"&gt;// Obsolete....&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IRP_MJ_MAXIMUM_FUNCTION&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x1b&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Note que existe uma definição especial, a &lt;span style="font-weight:bold;"&gt;IRP_MJ_MAXIMUM_FUNCTION&lt;/span&gt;, que indica o índice máximo da tabela de &lt;span style="font-style:italic;"&gt;dispatch rotines&lt;/span&gt;. Utilizaremos um loop simples para fazer com que todas rotinas em nossa tabela aponte para uma única rotina que daremos o nome de &lt;span style="font-style:italic;"&gt;OnForwardDispatch&lt;/span&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Seta todas as dispatch routines do driver para&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; uma que encaminhe a IRP para o driver original.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;for&lt;/span&gt; (i = &lt;span style="color: purple;"&gt;0&lt;/span&gt;; i &amp;lt;= IRP_MJ_MAXIMUM_FUNCTION; i++)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; pDriverObj-&amp;gt;MajorFunction[i] = OnForwardDispatch;&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Veremos a implementação dessa rotina mais tarde. O próximo passo que daremos aqui é localizar o device ao qual vamos nos atachar. Para fazer isso, usaremos a rotina &lt;a href="http://msdn.microsoft.com/en-us/library/ms801459.aspx"&gt;IoGetDeviceObjectPointer()&lt;/a&gt;. Ela basicamente recebe o nome do device e nos retorna uma referência para ele.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;NTSTATUS &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; IoGetDeviceObjectPointer(&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN PUNICODE_STRING&amp;nbsp; ObjectName,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN ACCESS_MASK&amp;nbsp; DesiredAccess,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; OUT PFILE_OBJECT&amp;nbsp; *FileObject,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; OUT PDEVICE_OBJECT&amp;nbsp; *DeviceObject&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Observe que são dois os parâmetros de saída dessa rotina. Além do ponteiro para o &lt;span style="font-style:italic;"&gt;device object&lt;/span&gt; ainda recebemos um ponteiro para um &lt;span style="font-style:italic;"&gt;file object&lt;/span&gt;. Já ví algumas pessoas fazerem confusão com estes dois parâmetros, então vou dar alguma ênfase nisso.&lt;/p&gt;&lt;p&gt;O ponteiro para o &lt;span style="font-style:italic;"&gt;file object&lt;/span&gt; representa uma conexão criada entre seu driver e o device que você abriu. Como vimos &lt;a href="http://www.driverentry.com.br/blog/2007/08/usando-fileobject-e-fscontext.html"&gt;neste post&lt;/a&gt;, um &lt;span style="font-style:italic;"&gt;file object&lt;/span&gt; é criado para respresentar conexões entre aplicações user mode e seu driver. Aplicações usam esse &lt;span style="font-style:italic;"&gt;file object&lt;/span&gt; através do &lt;span style="font-style:italic;"&gt;handle&lt;/span&gt; obtido na chamada à rotina &lt;a href="http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx"&gt;CreateFile()&lt;/a&gt;. Aqui temos algo similar, mas apenas o Kernel foi envolvido. Isso significa que se você quizesse, você poderia lançar IRPs para o device solicitando operações como uma aplicação faria, mas não veremos isso hoje, ainda temos um filtro para terminar.&lt;/p&gt;&lt;p&gt;A grande confusão referente a estes dois parâmetros é com relação às referências entre os objetos. &lt;a href="http://msdn.microsoft.com/en-us/library/ms801459.aspx"&gt;Na documentação&lt;/a&gt; vemos que o chamador desta rotina deverá liberar a referência que ganhou quando o device não for mais utilizado. Fazemos isso simplesmente usando a rotina &lt;a href="http://msdn.microsoft.com/en-us/library/ms802945.aspx"&gt;ObDereferenceObject()&lt;/a&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;VOID &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; ObDereferenceObject(&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN PVOID&amp;nbsp; Object&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"Já sei! Como estamos obtendo uma referência para um device object, então devo passar o ponteiro do device object. Certo?"&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Er... Na verdade não. O &lt;span style="font-style:italic;"&gt;file object&lt;/span&gt; é uma referência indireta ao &lt;span style="font-style:italic;"&gt;device object&lt;/span&gt;. Conforme a figura abaixo, se imaginarmos que os contadores de referência apenas dizem respeito às nossas referências, quando chamarmos &lt;span style="font-style:italic;"&gt;ObDereferenceObject()&lt;/span&gt; para o &lt;span style="font-style:italic;"&gt;file object&lt;/span&gt;, seu contador de referência cairia para zero e uma nova chamada para &lt;span style="font-style:italic;"&gt;ObDeferenceObject()&lt;/span&gt; seria feita indiretamente para o &lt;span style="font-style:italic;"&gt;device object&lt;/span&gt;, fazendo com que seu contador de referência também caisse para zero destruindo o objeto.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/DeviceReferences.png" /&gt;&lt;/div&gt;&lt;p&gt;Depois de obter o ponteiro para o device destino, teremos que criar nosso próprio device, o qual receberá as IRPs no lugar do device original. Para isso ainda usaremos a rotina &lt;a href="http://msdn.microsoft.com/en-us/library/aa490468.aspx"&gt;IoCreateDevice()&lt;/a&gt; como faziamos com drivers, mas com algumas diferenças.&lt;/p&gt;&lt;p&gt;A primeira diferença é que seu device normalmente não tem nome. É possível criar filtros com nomes, mas isso pode gerar uma falha de segurança. Isso acontece pois quando um nome é consultado no &lt;span style="font-style:italic;"&gt;Object Manager&lt;/span&gt;, suas regras de segurança são avaliadas. Quando criamos um filtro com nome, criamos a possibilidade de o mesmo objeto ser obtido por um nome diferente e que pode ter regras menos restritivas de segurança. Mas esse é outro assunto.&lt;/p&gt;&lt;p&gt;Quando criamos um device, precisamos informar o tamanho do &lt;span style="font-style:italic;"&gt;device extension&lt;/span&gt;.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"O que vem a ser um device extension?"&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;span style="font-style:italic;"&gt;Device extension&lt;/span&gt; é simplesmente um espaço de memória que está associada ao &lt;span style="font-style:italic;"&gt;device object&lt;/span&gt;. Tal espaço normalmente mantém informações que dizem respeito ao device. O endereço do device ao qual estamos atachados normalmente fica no &lt;span style="font-style:italic;"&gt;device extension&lt;/span&gt;. Desta forma, podemos definir que nosso &lt;span style="font-style:italic;"&gt;device extension&lt;/span&gt; deve conter a seguinte estrutura.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//-f--&amp;gt; Nosso device externsion conter&amp;#225; apenas o endere&amp;#231;o&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; do device ao qual estamos atachados.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;typedef&lt;/span&gt; &lt;span style="color: blue;"&gt;struct&lt;/span&gt; _DEVICE_EXTENSION&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PDEVICE_OBJECT&amp;nbsp; pNextDeviceObj;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;} DEVICE_EXTENSION, *PDEVICE_EXTENSION;&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Depois de criar nosso &lt;span style="font-style:italic;"&gt;device object&lt;/span&gt;, configuramos o método de I/O copiando os bits &lt;span style="font-weight:bold;"&gt;DO_BUFFERED_IO&lt;/span&gt; e &lt;span style="font-weight:bold;"&gt;DO_DIRECT_IO&lt;/span&gt;. Se você não se lembra destes bits, dê uma olhada &lt;a href="http://www.driverentry.com.br/blog/2008/10/buffered-direct-ou-neither.html"&gt;neste post&lt;/a&gt;. O filtro deve propagar a escolha do driver original e o driver tem o compromisso de não mudar método durante seu tempo de vida.&lt;/p&gt;&lt;p&gt;Agora estamos prontos para nos atachar ao device escolhido, e faremos isso utilizando a rotina &lt;a href="http://msdn.microsoft.com/en-us/library/ms795450.aspx"&gt;IoAttachDeviceToDeviceStackSafe()&lt;/a&gt; para fazer nosso device entrar na pilha de dispositivos.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;NTSTATUS&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; IoAttachDeviceToDeviceStackSafe(&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN PDEVICE_OBJECT&amp;nbsp; SourceDevice,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN PDEVICE_OBJECT&amp;nbsp; TargetDevice,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN OUT PDEVICE_OBJECT&amp;nbsp; *AttachedToDeviceObject &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Com esta chamada obteremos o ponteiro do device ao qual estaremos atachados, esse device será o próximo device que receberá a IRP depois que você a passar a diante.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"Mas Fernando, nós já não temos o ponteiro do device de destino?"&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Muito bem, quando você obtém o ponteiro para um device, você teoricamente não sabe se existem filtros já atachados sobre ele. O ponteiro que você recebe nesta rotina pode não ser o ponteiro para o device de destino. Na figura abaixo podemos entender como essa relação acontece.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/NextDevice.png" /&gt;&lt;/div&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"Fernando, existe uma versão unsafe desta rotina?"&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Na verdade existe, a &lt;a href="http://msdn.microsoft.com/en-us/library/aa490466.aspx"&gt;IoAttachDeviceToDeviceStack()&lt;/a&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;PDEVICE_OBJECT &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; IoAttachDeviceToDeviceStack(&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN PDEVICE_OBJECT&amp;nbsp; SourceDevice,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN PDEVICE_OBJECT&amp;nbsp; TargetDevice&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Ela é considerada unsafe por causa de uma pequena janela de tempo que pode causar um race condition. Repare que a diferença entre estas rotinas é a forma de obter o endereço do próximo device. Na versão original, este endereço é obtido pelo retorno na função. Se colocarmos essa função para rodar em câmera lenta veremos os seguintes passos.&lt;/p&gt;&lt;p&gt;&lt;ol&gt;&lt;li&gt;Seu device é atachado à pilha de dispositivos.&lt;/li&gt;&lt;li&gt;O endereço do próximo device é retornado pela função.&lt;/li&gt;&lt;li&gt;Seu driver recebe este endereço no retorno da função e atualiza o device extension.&lt;/li&gt;&lt;li&gt;IRPs começam a chegar e seu driver às encaminham para o próximo device da lista.&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;&lt;p&gt;Tudo parece lindo e até dá a impressão de que tudo vai funcionar muito bem em qualquer situação, mas desenvolvedor de driver é um bixo treinado para buscar race conditions. Dê mais uma olhada na sequência, mas agora em câmera super lenta. Com essa câmera super lenta agora podemos observar os passos que podem ocorrer entre os passos 2 e 3.&lt;/p&gt;&lt;p&gt;&lt;ol&gt;&lt;li&gt;Seu device é atachado à lista de dispositivos.&lt;/li&gt;&lt;li&gt;O endereço do próximo device é retornado pela função.
&lt;ol&gt;&lt;li&gt;Sua thread é interrompida e uma IRP lançada por uma aplicação que roda em paralelo começa sua viagem por essa pilha de dispositivos.&lt;/li&gt;&lt;li&gt;Seu device, que já está atachado, recebe a IRP e tenta encaminhá-la ao device de baixo.&lt;/li&gt;&lt;li&gt;Oops! Nosso &lt;span style="font-style:italic;"&gt;device extension&lt;/span&gt; ainda não foi atualizado com tal endereço.&lt;/li&gt;&lt;li&gt;Seu driver se lembra de quando era uma criança e de tudo o que vivera até ali.&lt;/li&gt;&lt;li&gt;Ele decide entrar de vez naquela dança e enviar a IRP para um device cujo ponteiro ainda é NULL causando um BSOD.&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;"Jeremias, eu sou homem. Coisa que você não é e não atiro pelas costas não..."&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;&lt;p&gt;Enfim, entenda que mesmo que você utilize o retorno da rotina &lt;span style="font-style:italic;"&gt;IoAttachDeviceToDeviceStack(&lt;/span&gt;) diretamente para atualizar seu device extension, ainda assim existe uma janela de tempo em que seu device estará atachado, mas que o valor ainda não foi atualizado no device extension. Isso porque o valor de retorno de uma rotina vem por um registrador. Pegar o valor desse registrador e atualizar uma variável ainda dá chances para o azar.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; ----==== N&amp;#195;O COPIE ISSO ====----&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; Aqui ainda temos uma janela de tempo entre o device ser&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; atachado e o valor de pNextDeviceObj ser atualizado.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pDeviceExt-&amp;gt;pNextDeviceObj = IoAttachDeviceToDeviceStack(pMyDeviceObj,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pTargetDeviceObj);&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;A rotina &lt;span style="font-style:italic;"&gt;IoAttachDeviceToDeviceStackSafe(&lt;/span&gt;) faz o sistema interromper o fluxo de IRPs nesta pilha até que a variável apontada pelo ponteiro de saída seja atualizado. Por essa razão, o endereço passado nesta rotina deve ser o endereço final da variável onde este valor será armazenado, que em nosso caso é &lt;span style="font-style:italic;"&gt;pDeviceExt-&gt;pNextDeviceObj&lt;/span&gt; sem passar por variáveis intermediárias.&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px;" src="http://www.driverentry.com.br/images/homer_doh.png" border="0" /&gt;&lt;p&gt;Esses detalhes são importantes e farão você entender que usar a versão safe desta rotina não é garantia de que tudo dará certo. Imagine que usando a versão safe você receba o endereço do próximo device em uma variável local e depois atualize seu device extension. Esse é um daqueles típicos casos que é necessário substituir aquele componente que fica entre o teclado e a cadeira.&lt;/p&gt;&lt;p&gt;Acha preciosismo? Tente ler o capítulo 5 do livro &lt;a href="http://www.amazon.com/Programming-Microsoft-Windows-Driver-Model/dp/0735618038/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1264044523&amp;sr=1-1"&gt;"Programming the Microsoft Windows Driver Model"&lt;/a&gt; onde Walter Oney fala sobre como lidar com cancelamento de IRPs.&lt;/p&gt;&lt;p&gt;Depois de atacharmos nosso device já podemos liberar a referência obtida por &lt;span style="font-style:italic;"&gt;IoGetDeviceObjectPointer()&lt;/span&gt; utilizando a rotina &lt;span style="font-style:italic;"&gt;ObDereferenceObject()&lt;/span&gt;, já que a rotina &lt;span style="font-style:italic;"&gt;IoAttachDeviceToDeviceStackSafe()&lt;/span&gt; já garantiu a referência até que essa ligação seja desfeita.&lt;/p&gt;&lt;p&gt;Muito bem. Pra quem não conseguiu entender quase nada do que eu disse, segue o código fonte da implementação da nossa &lt;span style="font-style:italic;"&gt;DriverEntry()&lt;/span&gt; de exemplo. Sabe como é cabeça de programador, as vezes um &lt;span style="font-style:italic;"&gt;if&lt;/span&gt; vale mais que mil páginas de explicação.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;/****&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;***&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DriverEntry&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;**&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;**&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; Ponto de entrada do driver.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;**&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; Bem vindo ao inferno.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;extern&lt;/span&gt; &lt;span style="color: #a31515;"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;NTSTATUS&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;DriverEntry(IN PDRIVER_OBJECT&amp;nbsp; pDriverObj,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; IN PUNICODE_STRING pusRegistryPath)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; NTSTATUS&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; nts;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PDEVICE_OBJECT&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; pMyDeviceObj;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;int&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; i;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; UNICODE_STRING&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; usDeviceName;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PDEVICE_OBJECT&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; pTargetDeviceObj;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PFILE_OBJECT&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; pFileObj;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PDEVICE_EXTENSION&amp;nbsp;&amp;nbsp; pDeviceExt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Seta a rotina de descarga do driver.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pDriverObj-&amp;gt;DriverUnload = OnDriverUnload;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Seta todas as dispatch routines do driver para&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; uma que encaminhe a IRP para o driver original.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;for&lt;/span&gt; (i = &lt;span style="color: purple;"&gt;0&lt;/span&gt;; i &amp;lt;= IRP_MJ_MAXIMUM_FUNCTION; i++)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; pDriverObj-&amp;gt;MajorFunction[i] = OnForwardDispatch;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Inicializamos a string com o nome do device ao&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; qual queremos nos atachar.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; RtlInitUnicodeString(&amp;amp;usDeviceName,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; L&lt;span style="color: #a31515;"&gt;&amp;quot;\\Device\\StringList&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Obtemos o ponteito do device de destino&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; nts = IoGetDeviceObjectPointer(&amp;amp;usDeviceName,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; FILE_READ_DATA,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;amp;pFileObj,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;amp;pTargetDeviceObj);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; nts;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Criamos nosso device object&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; nts = IoCreateDevice(pDriverObj,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;sizeof&lt;/span&gt;(DEVICE_EXTENSION),&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; NULL,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pTargetDeviceObj-&amp;gt;DeviceType,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pTargetDeviceObj-&amp;gt;Characteristics,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; FALSE,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;amp;pMyDeviceObj);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Oops!&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; ObDereferenceObject(pFileObj);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; nts;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Obtem nosso DEVICE_EXTENSION&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pDeviceExt = (PDEVICE_EXTENSION)pMyDeviceObj-&amp;gt;DeviceExtension;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Utiliza o mesmo m&amp;#233;todo de IO do driver original&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pMyDeviceObj-&amp;gt;Flags |= pTargetDeviceObj-&amp;gt;Flags &amp;amp; (DO_BUFFERED_IO | DO_DIRECT_IO);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui nosso driver entra na pilha de dispositivos&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; nts = IoAttachDeviceToDeviceStackSafe(pMyDeviceObj,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; pTargetDeviceObj,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;amp;pDeviceExt-&amp;gt;pNextDeviceObj);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Oops!&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; IoDeleteDevice(pMyDeviceObj);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui n&amp;#227;o est&amp;#225; faltando um return.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Independente de estarmos atachados ou n&amp;#227;o, devemos liberar&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; a refer&amp;#234;ncia obtida do device de destino.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ObDereferenceObject(pFileObj);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; nts;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;
&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Escrevendo a OnDriverUnload&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Aqui é onde a festa acaba, antes de o nosso driver ser descarregado pelo sistema, teremos que remover nosso device da pilha de dispositivos e destruí-lo. (Risadas maléficas)&lt;/p&gt;&lt;p&gt;O código aqui é simples e não requer explicações se você for capaz de ler os comentários contidos nele.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;/****&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;***&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; OnDriverUnload&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;**&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;**&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; Rotina de descarga do driver.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;VOID&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;OnDriverUnload(IN PDRIVER_OBJECT&amp;nbsp; pDriverObj)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PDEVICE_OBJECT&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; pMyDeviceObj;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PDEVICE_EXTENSION&amp;nbsp;&amp;nbsp; pDeviceExt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Nosso device est&amp;#225; na lista de devices criados por este driver&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; ent&amp;#227;o vamos obt&amp;#234;-lo simples assim:&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pMyDeviceObj = pDriverObj-&amp;gt;DeviceObject;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui obt&amp;#234;mos o device extension.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pDeviceExt = (PDEVICE_EXTENSION)pMyDeviceObj-&amp;gt;DeviceExtension;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui removemos nosso device da pilha passando o endere&amp;#231;o do&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; pr&amp;#243;ximo device para a rotina abaixo.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IoDetachDevice(pDeviceExt-&amp;gt;pNextDeviceObj);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Agora podemos destruir nosso device (risadas mal&amp;#233;ficas)&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IoDeleteDevice(pMyDeviceObj);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"Fernando, nosso driver é forçado a se descarregar quando o driver original é descarregado?"&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Essa é a filosofia do &lt;span style="font-weight:bold;"&gt;WDM&lt;/span&gt;, drivers são carregados automáticamente quandos seus dispositivos são detectados e descarregados quando seus dispositivos são desativados ou removidos. Os filtros seguem as mesmas regras e são carregados/descarregados basendo-se nestes eventos.&lt;/p&gt;&lt;p&gt;Mas não é isso o que acontece aqui. Os drivers de exemplo que uso neste blog são do modelo &lt;span style="font-weight:bold;"&gt;Legacy&lt;/span&gt;, e não WDM. No modelo Legacy, drivers são iniciados seguindo sua ordem de carga no registry, e não tem nada a ver com a detecção do seu dispositivo. Os filtros precisam iniciar depois dos drivers originais, e isso também é controlado pela sua ordem de carga. &lt;a href="http://www.driverentry.com.br/blog/2009/12/drivers-de-boot-no-windows.html"&gt;Este post&lt;/a&gt; fala sobre a ordem de carga dos legacy drivers.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"Tá! Falou, falou e não respondeu minha pergunta. O que acontece se eu solicitar a descarga do driver original enquanto houver um filtro atachado sobre ele?"&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;A descarga dos drivers que formam uma pilha deve ocorrer de forma inversa à sua carga. Neste caso o filtro deve ser descarregado antes do driver original, desempilhando os devices de cima para baixo. Caso o driver original receba uma solicitação de descarga enquanto ainda hoverem referências a seus devices, seja por um filtro ou por uma aplicação, a descarga é adiada até que suas referências sejam desfeitas. Até lá, a tentativa de obter novas referências para um device que recebeu a solicitação de descarga serão negadas.&lt;/p&gt;&lt;p&gt;
&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Escrevendo Dispatch Routines&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;As &lt;span style="font-style:italic;"&gt;dispatch routines&lt;/span&gt; de um filtro também são diferentes das &lt;span style="font-style:italic;"&gt;dispatch routine&lt;/span&gt;s de um driver. Apensar de elas poderem completar uma IRP chamando &lt;a href="http://msdn.microsoft.com/en-us/library/aa490590.aspx"&gt;IoCompleteRequest()&lt;/a&gt;, elas normalmente repassam a socilitação adiante utilizando a rotina &lt;a href="http://msdn.microsoft.com/en-us/library/aa490633.aspx"&gt;IoCallDriver()&lt;/a&gt;. Vou falar mais sobre o comportamento das &lt;span style="font-style:italic;"&gt;dispatch routines&lt;/span&gt; de um filtro em posts futuros. Neste filtro de exemplo vamos apenas logar a atividade e repassar a solicitação ao próximo driver.&lt;/p&gt;&lt;p&gt;Quando falamos em repassar uma solicitação estamos na verdade falando de repassar IRPs. A leitura deste &lt;a href="http://www.driverentry.com.br/blog/2007/02/legal-mas-o-que-uma-irp.html"&gt;outro post&lt;/a&gt; é essencial para o que vamos fazer na implementação de nossas &lt;span style="font-style:italic;"&gt;dispatch routines&lt;/span&gt;.&lt;/p&gt;&lt;p&gt;Para acabar esse post ainda nessa vida, segue código da implementação de nossa &lt;span style="font-style:italic;"&gt;dispatch routine&lt;/span&gt;. Depois de ler o post sobre IRPs que acabei de recomendar, ler os comentários deste código devem ser suficientes para entender tudo o que foi feito aqui, ou não.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;/****&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;***&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; OnForwardDispatch&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;**&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;**&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; Nossa dispatch routine simplesmente loga a IRP&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;**&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; recebida e repassa a solicita&amp;#231;&amp;#227;o adiante.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;**&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;NTSTATUS&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;OnForwardDispatch(IN PDEVICE_OBJECT&amp;nbsp;&amp;nbsp;&amp;nbsp; pDeviceObj,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; IN PIRP&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; pIrp)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PDEVICE_EXTENSION&amp;nbsp;&amp;nbsp; pDeviceExt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PIO_STACK_LOCATION&amp;nbsp; pStack;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Obt&amp;#233;m ponteiro para o device extesion&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pDeviceExt = (PDEVICE_EXTENSION)pDeviceObj-&amp;gt;DeviceExtension;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Obt&amp;#233;m stack location corrente&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pStack = IoGetCurrentIrpStackLocation(pIrp);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Manda o nome na major routine da IRP&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ASSERT(pStack-&amp;gt;MajorFunction &amp;lt;= IRP_MJ_MAXIMUM_FUNCTION);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; DbgPrint(&lt;span style="color: #a31515;"&gt;&amp;quot;[StringFilter] : %s\n&amp;quot;&lt;/span&gt;, g_szMajorNames[pStack-&amp;gt;MajorFunction]);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Como n&amp;#227;o estamos modificando nada na stack location,&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; vamos deix&amp;#225;-la para que o pr&amp;#243;ximo device a use.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IoSkipCurrentIrpStackLocation(pIrp);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Encaminha a IRP para o pr&amp;#243;ximo device.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; IoCallDriver(pDeviceExt-&amp;gt;pNextDeviceObj,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; pIrp);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Caso não tenham entendido nada, não esqueçam de me mandar um e-mail expondo suas dúvidas (sem ofenças pessoais). Isso me ajudará a entender suas dificuldades e a melhorar minhas explicações.&lt;/p&gt;&lt;p&gt;
&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Testando o Filtro&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Essa é a parte fácil do post. Para testar o filtro teremos primeiro que compilar, instalar e iniciar o driver &lt;a href="http://www.driverentry.com.br/blog/2009/07/strings-no-kernel.html"&gt;deste post&lt;/a&gt;. Se você ainda não sabe como fazer isso, este &lt;a href="http://www.driverentry.com.br/blog/2006/09/getting-started.html"&gt;outro post&lt;/a&gt; pode te dar um ponto de partida. Depois disso, compile, instale e inicie o filtro.&lt;/p&gt;&lt;p&gt;Depois de instalados, podemos utilizar a aplicação de teste para exercitar o driver. Poderemos acompanhar a atividade do filtro observando suas mensagens de debug que podem ser vistas no depurador de Kernel ou simplemente usando &lt;a href="http://www.osronline.com/article.cfm?article=99"&gt;esta aplicação&lt;/a&gt;, que dispensa o uso de um depurador para ver as mensagens de debug de um driver.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/Filtering.png" /&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"Fernando, fiz um teste aqui e ví que ao iniciar o filtro ele loga um evento de &lt;span style="font-style:italic;"&gt;IRP_MJ_CLOSE&lt;/span&gt; mesmo antes de iniciar a aplicação de teste. O que eu fiz de errado?"&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/LogClose.png" /&gt;&lt;/div&gt;
&lt;p&gt;Não há nada de errado. Isso acontece por causa da sequência de passos seguidos na rotina &lt;span style="font-style:italic;"&gt;DriverEntry()&lt;/span&gt; do filtro. Inicialmente o driver chama a rotina &lt;span style="font-style:italic;"&gt;IoGetDeviceObjectPointer()&lt;/span&gt;, isso faz com que o &lt;span style="font-style:italic;"&gt;IoManager&lt;/span&gt; envie uma solicitação de &lt;span style="font-style:italic;"&gt;IRP_MJ_CREATE&lt;/span&gt; para o driver original. Depois disso nos atachamos à pilha de dispositivos e por fim chamamos a rotina &lt;span style="font-style:italic;"&gt;ObDereferenceObject()&lt;/span&gt; que vai finalizar a única referência do &lt;span style="font-style:italic;"&gt;file object&lt;/span&gt; que recebemos, enviando uma solicitação de &lt;span style="font-style:italic;"&gt;IRP_MJ_CLOSE&lt;/span&gt; para o driver de baixo. Como já estamos atachados a ele, então somos capazes de ver nossa própria atividade sobre o driver original. Isso pode ser observado pela pilha de chamadas que teremos se houver um &lt;span style="font-style:italic;"&gt;breakpoint&lt;/span&gt; em nossa &lt;span style="font-style:italic;"&gt;dispatch routine&lt;/span&gt; quando liberarmos a referência do &lt;span style="font-style:italic;"&gt;file object&lt;/span&gt; ao final da &lt;span style="font-style:italic;"&gt;DriverEntry()&lt;/span&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;kd&amp;gt; k&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;ChildEBP RetAddr&amp;nbsp; &lt;/pre&gt;&lt;pre style="margin: 0px; color: red;"&gt;f8af9bcc 804ee129 StringFilter!OnForwardDispatch&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8af9bdc 80578f6a nt!IopfCallDriver+0x31&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8af9c14 805b0b18 nt!IopDeleteFile+0x132&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8af9c30 80522bd1 nt!ObpRemoveObjectRoutine+0xe0&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8af9c54 f8c80663 nt!ObfDereferenceObject+0x5f&lt;/pre&gt;&lt;pre style="margin: 0px; color: red;"&gt;f8af9c7c 805767ff StringFilter!DriverEntry+0xf3&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8af9d4c 8057690f nt!IopLoadDriver+0x66d&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8af9d74 80534c12 nt!IopLoadUnloadDriver+0x45&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8af9dac 805c61ee nt!ExpWorkerThread+0x100&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8af9ddc 80541de2 nt!PspSystemThreadStartup+0x34&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;00000000 00000000 nt!KiThreadStartup+0x16&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Como de costume, o fonte do filtro que foi implementado neste post está disponível para download. Nosso filtro não faz quase nada, mas já servirá de base para posts futuros que darão mais funcionalidade a ele explicando como tais funcionalidades são implementadas.&lt;/p&gt;&lt;p&gt;Até mais!&lt;/p&gt;&lt;p&gt;Download &lt;a href="http://www.driverentry.com.br/samples/StringFilter.zip"&gt;StringFilter.zip&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-4208475676533334470?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2010/01/escrevendo-filtros.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>5</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-583517946433005279</guid><pubDate>Thu, 17 Dec 2009 12:37:00 +0000</pubDate><atom:updated>2009-12-17T15:05:39.336-02:00</atom:updated><title>Drivers de Boot no Windows</title><description>&lt;img style="FLOAT: right; MARGIN: 0px 10px;" src="http://www.driverentry.com.br/images/StartingWidows.png" border="0"/&gt;&lt;p&gt;Tenho acompanhado o trabalho do meu amigo Lesma, que em &lt;a href="http://www.caloni.com.br"&gt;seu blog&lt;/a&gt; tem descrito como o processo de boot transforma um apanhado de bytes no disco rígido em um sistema operacional vivo. Pegando carona nesse tema, vou aproveitar para comentar sobre a ordem de carga dos drivers durante este processo. Com isso posso tentar responder uma pergunta frequente dos leitores: &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;"Como fazer para que meu driver seja o primeiro a ser carregado?"&lt;/span&gt;&lt;/span&gt;. Talvez este post possa clarear um pouco as coisas neste sentido, ou não.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Eu primeiro! Eu primeiro!&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Um ponto importante a ser considerado no modelo &lt;span style="font-weight:bold;"&gt;Legacy&lt;/span&gt; quando escrevemos um driver é o referente ao momento no qual seu driver é carregado. Isso é configurado no valor &lt;span style="font-style:italic;"&gt;"Start"&lt;/span&gt; na chave do driver no registro. Quatro valores configuram o momento da carga do seu driver, sendo eles:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Boot (0) - Drivers são carregados durante o boot, antes mesmo sistema operacional estar completamente pronto para execução.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;System (1) - Drivers são carregados depois dos drivers de boot, quando o Kernel já está completamente funcional.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Automatic (2) - Neste grupo os drivers são carregados quando os subsistemas forem carregados. Basicamente junto com os serviços de &lt;span style="font-style:italic;"&gt;User Mode&lt;/span&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Manual (3) - Nenhuma carga automática é realizada aqui, o driver é carregado somente quando alguém, ou algum componente, solicita sua carga.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Disabled (4)- Mesmo que o driver seja solicitado, sua carga é negada.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;&lt;span style="font-style:italic;"&gt;"Bom, então para meu driver ser o primeiro a ser carregado ele precisa ser iniciado como boot e pronto?"&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Na verdade seu driver vai disputar um lugar na fila de drivers que querem ser iniciados no boot. Vários drivers estão configurados para ser iniciados nesse momento e o seu será apenas mais um. Mesmo entre os drivers de boot, uma ordem de carga precisa ser seguida para que certos drivers possam contar com os serviços de outros drivers. Por esse motivo, drivers se separam em grupos. Um grupo de cada vez vai sendo iniciado até que todos os drivers de boot passem por esse processo.&lt;/p&gt;&lt;p&gt;Drivers identificam seu grupo pelo valor &lt;span style="font-style:italic;"&gt;"Group"&lt;/span&gt; encontrado em sua chave de registro. Esta chave deve conter o nome do grupo ao qual o driver pertence. Os nomes de todos os grupos podem ser encontrados na chave &lt;span style="font-weight:bold;"&gt;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ServiceGroupOrder&lt;/span&gt;. Nela existe um valor do tipo &lt;span style="font-style:italic;"&gt;REG_MULTI_SZ&lt;/span&gt; chamado &lt;span style="font-style:italic;"&gt;"List"&lt;/span&gt; que contém a lista de todos os grupos existentes dispostos em sua ordem de carga.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/ServiceGroupOrder.png"/&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;&lt;span style="font-style:italic;"&gt;"Tudo bem, meu driver está configurado para ser iniciado em Boot e está configurado para iniciar com o primeiro grupo de drivers. Pronto agora?"&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Quase. Quando falamos em iniciar grupos de drivers, já fica sub-entendido que mais de um driver será carregado. A ordem que tais drivers são carregados dentro de cada grupo também pode ser determinada.&lt;/p&gt;&lt;p&gt;A chave &lt;span style="font-weight:bold;"&gt;KHEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GroupOrderList&lt;/span&gt; mantém uma série de valores, cada um com o nome de um grupo. O valor é do tipo &lt;span style="font-style:italic;"&gt;REG_BINARY&lt;/span&gt; e sua interpretação é um array de conjuntos de quatro bytes. O primeiro conjunto indica quantas tags estão contidas naquele buffer binário. Os demais conjuntos são as representações numéricas de cada tag. Dessa forma, a interpretação do buffer exibido na figura abaixo nos dá a informação de que temos seis tags, sendo elas: 1, 2, 3, 4, 5 e 6.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/GroupOrderList.png"/&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;&lt;span style="font-style:italic;"&gt;"Mas o que é uma tag?"&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Uma tag é a identificação numérica de um driver dentro de um determinado grupo. Um driver se identifica pelo valor &lt;span style="font-style:italic;"&gt;"Tag"&lt;/span&gt; que podemos encontrar na chave do driver no registro.&lt;/p&gt;&lt;p&gt;Apesar de o exemplo nos mostrar uma ordem crescente de tags, o valor da tag não determina a ordem de carga dos drivers. A ordem é determinada por sua posição dentro do buffer binário.&lt;/p&gt;&lt;p&gt;Ficou com nojinho de mexer em buffers binários? Você pode utilizar o &lt;a href="http://www.osronline.com/article.cfm?article=157"&gt;OSR Driver Loader&lt;/a&gt; que configura tudo isso pra você quando utilizado para instalar um driver.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/DrvReinitLdr.png"/&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Colocar sua tag como primeiro na lista de tags classifica sua ordem de carga dentro de um determinado grupo, mas ainda não é fator determinante para ter seu driver carregado antes de todos os drivers do universo. Um novo grupo sempre pode ser criado e ter sua ordem de carga configurada para antes do seu grupo.&lt;/p&gt;&lt;p&gt;Todas essas regras sobre grupos, tags e afins não fazem sentido nos drivers gerenciados pelo &lt;span style="font-weight:bold;"&gt;Plug-And-Play (Pnp) Manager&lt;/span&gt;, já que a carga de tais drivers é solicitada quando o dispositivo ao qual seu driver está relacionado é detectado pelo driver de barramento.&lt;/p&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;&lt;span style="font-style:italic;"&gt;"Aff! Fernando, pega leve e tenta explicar isso de novo."&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Tudo bem, vamos lá. Quando você instala um driver Pnp, você o associa a um determinado dispositivo. Apenas como exemplo, digamos que esse dispositivo seja um conversor USB/Serial. Seu driver será carregado automagicamente quando seu dispositivo for detectado e será descarregado quando o dispositivo for removido.&lt;/p&gt;&lt;p&gt;Para que ele seja detectado, outros dispositivos precisam ser detectados antes, tais como controladora PCI, controladora USB e hub USB. Essa lista de dependência cria a &lt;a href="http://msdn.microsoft.com/en-us/library/ms794302.aspx"&gt;pilha de dispositivos USB&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;A controladora PCI, ao ser detectada, tem seu driver carregado e este enumera seus dispositivos filhos, já que PCI é um barramento. Para cada disposivito detectado, esse driver utiliza o barramento para detectar a identidade de cada dispositivo e cria um &lt;span style="font-weight:bold;"&gt;Phisical Device Object&lt;/span&gt; (PDO) para cada um deles. O Pnp Manager carrega o driver de cada dispositivo atachado a esse barramento. Esse driver criará o &lt;span style="font-weight:bold;"&gt;Functional Device Object&lt;/span&gt; (FDO) do dispositivo, dando funcionalidade a ele.&lt;/p&gt;&lt;p&gt;Um desses dispositivos é a controladora do barramento USB. Seguindo o ritual, o driver de barramento USB enumera seus dispositivos filhos, criando novos PDOs. Assim, os hubs USB são detectados e seu driver será carregado. Este driver criará um novo FDO para cada hub. O driver de hub USB vai enumerar seus dispositivos filhos e é nesse momento que seu dispositivo é detectado. O driver que você escreveu será carregado e o Pnp Manager irá chamar sua rotina &lt;a href="http://msdn.microsoft.com/en-us/library/ms795509.aspx"&gt;AddDevice&lt;/a&gt;, que receberá o PDO que o driver de hub criou referente ao seu dispositivo.&lt;/p&gt;&lt;p&gt;Ufa! Tudo bem, tenham calma. O assunto Plug-And-Play não é o foco deste post e já está na minha lista de posts futuros.&lt;/p&gt;&lt;p&gt;Toda essa atividade que age recursivamente serve para montar a &lt;a href="http://msdn.microsoft.com/en-us/library/aa489660.aspx"&gt;árvore de dispositivos do sistema&lt;/a&gt;. Sabendo que esta árvore é formada pelos nossos drivers e seus devices, fica explícito aqui que no fundo &lt;a href="http://www.youtube.com/watch?v=JqIeVYIUKxk"&gt;"As árveres somos nozes"&lt;/a&gt;. A figura abaixo dá uma idéia de como a árvore de dispositivos é organizada.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/DeviceTree.png"/&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Ainda falando sobre ordem de carga de drivers, não faria sentido ter seu driver carregado antes de todos os outros drivers, já que os componentes básicos para a comunicação com seu dispositivo ainda não foram carregados, e por isso, não possuem funcionalidade nenhuma. Além do mais, ter seu driver carregado muito cedo lhe trará problemas em lidar com outros componentes do sistema que ainda não estarão prontos para atender seu pedido. Mais detalhes &lt;a href="http://www.driverentry.com.br/blog/2007/06/comear-de-novo.html"&gt;neste post&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Depurando no Boot&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Outro assunto curioso e que pode gerar alguma confusão é o referente ao debug de drivers que são carregados no boot. Apesar de a conexão de Debug usar o meio &lt;a href="http://www.driverentry.com.br/blog/2006/12/step-into-kernel-serial.html"&gt;serial&lt;/a&gt;, &lt;a href="http://www.driverentry.com.br/blog/2008/07/step-into-kernel-firewire.html"&gt;firewire&lt;/a&gt; ou mesmo &lt;a href="http://www.driverentry.com.br/blog/2008/03/step-into-kernel-vista-usb2.html"&gt;USB&lt;/a&gt;, os drivers referentes a estes meios não precisam estar carregados para que você possa realizar o debug do sistema. Em outras palavras, o driver de porta serial não é utilizado para fazer debug do sistema quando se usa o meio serial. Isso seria um problema se pensarmos que alguns drivers são carregados e inciados antes do driver de porta serial. Como estes drivers seriam depurados?&lt;/p&gt;&lt;p&gt;O fato é que o algoritmo que lida com os meios de depuração do sistema são definidos no próprio Kernel (mais precisamente no módulo ntoskrnl.exe e seus irmãos). Este módulo lida diretamente com o hardware responsável pelo meio utilizado. Essa é também a explicação para outra pergunta frequente: &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;"Meu computador não tem porta serial. Posso usar um conversor USB/Serial do lado Target para fazer debug do sistema?"&lt;/span&gt;&lt;/span&gt;. Como acabamos de ver, um conversor USB/Serial depende de toda uma pilha de dispositivos para que a porta serial esteja disponível. Tal funcionalidade não está implementada no algoritmo de debug do sistema, e como comentei &lt;a href="http://www.driverentry.com.br/blog/2006/10/serial-killers.html"&gt;neste outro post&lt;/a&gt;, sistemas mais novos implementam novas funcionalidades de debug no Kernel.&lt;/p&gt;&lt;p&gt;Mesmo que seu driver seja de boot, ele ainda pode ser depurado. &lt;a href="http://www.driverentry.com.br/blog/2007/07/bug-em-meu-driver-de-boot-j-posso.html"&gt;Este outro post&lt;/a&gt; mostra ainda como fazer mapeamento de um driver de boot pelo WinDbg. Não sabe do que estou falando? É sobre ter seu driver substituído por uma nova versão automaticamente no lado target quando este for carregado. Vale a pena dar uma olhada.&lt;/p&gt;&lt;p&gt;Have fun!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-583517946433005279?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2009/12/drivers-de-boot-no-windows.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>5</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-5003926784526586187</guid><pubDate>Fri, 27 Nov 2009 14:34:00 +0000</pubDate><atom:updated>2009-11-30T17:10:42.735-02:00</atom:updated><title>Mais um Engenheiro à solta</title><description>&lt;img style="FLOAT: right; MARGIN: 0px 10px;" src="http://www.driverentry.com.br/images/GraduationHat.jpg" border="0"/&gt;&lt;p&gt;Ufa! Quarta-feira passada foi minha última prova da universidade. Parece mentira mas finalmente o curso de Engenharia da Computação chegou ao fim. Ainda temos que entregar uma papelada do projeto de conclusão de curso, mas são apenas papéis. Acabaram as provas, os trabalhos, os relatórios e agora um baita peso começa a sair das minhas costas à medida que as notas vão sendo liberadas no site da universidade. Quem me segue no &lt;a href="http://twitter.com/driverentry"&gt;Twitter&lt;/a&gt; teve uma idéia da correria que tem sido. Esse ano foi especialmente apertado por conta das dependêcias que tive que cursar, do estágio que tive que fazer, e por fim, do projeto de formatura que tivemos que apresentar.&lt;/p&gt;&lt;p&gt;O projeto foi o maior responsável pela minha ausência no blog, foram muitos testes, sustos, sensores queimados, apresentações e feiras.&lt;/p&gt;&lt;p&gt;&lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;Pera aí! Você disse sensores queimados?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Isso mesmo. Murphy pode ser considerado um integrante do nosso grupo já que esteve sempre presente no desenvolvimento do projeto. Nas vésperas da apresentação para a banca avaliadora, um mal contato nos reguladores de tensão, originado pela trepidação do motor do helimodelo, fez com que quase tudo se queimasse. Foi um desespero geral, já que os sensores eram importados. Sem falar da grana que teríamos que desembolsar, ainda teríamos que aguardar a entrega que ocorre em média em duas semanas. Nesse momento Murphy nos deu uma trégua. Particularmente acho que ele ficou com pena de nós. Digo isso porque no dia seguinte encontramos uma &lt;a href="http://www.multilogica-shop.com/"&gt;revenda&lt;/a&gt; de sensores similares aos que estávamos usando no projeto aqui em Santo André (cidade onde moro). Quando entramos em contato com a revenda, descobrimos que eles tinham os sensores em estoque. Inacreditável não?&lt;/p&gt;&lt;p&gt;Depois da apresentação que fizemos para a banca avaliadora, ainda fomos convidados a participar da apresentação de gala da universidade, onde os melhores projetos do ano foram exibidos.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/HeliISA2009.JPG"/&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Também ficamos em segundo lugar no concurso "Melhor Aplicação Acadêmica Baseada em PC" promovido pela &lt;a href="http://www.ni.com/"&gt;National Instruments&lt;/a&gt;, e com isso fomos convidados a expor nosso trabalho no stand da National no &lt;a href="http://www.isashow.com.br/"&gt;ISA Show 2009&lt;/a&gt;. O mais legal é que o primeiro lugar deste concurso foi conquistado por um grupo também da minha sala na &lt;a href="http://www.usjt.br/"&gt;Universidade São Judas Tadeu&lt;/a&gt;. Isso evidenciou que além de ficarmos na frente de várias outras universidades, mostrou que nossa sala né brinquedo não.&lt;/p&gt;&lt;p&gt;A National Instruments produziu este vídeo explicativo onde eu falo sobre a idéia básica do projeto. O vídeo foi feito na chácara de um dos componentes do grupo, onde ficamos internados nas últimas semanas antes das apresentações finais.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/DyWCRKHU17E&amp;hl=en_US&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/DyWCRKHU17E&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;p&gt;Este é apenas um post de &lt;span style="font-style:italic;"&gt;"Ei, estou voltando do coma!"&lt;/span&gt;. Agora vou parar de falar sobre o projeto e voltar a falar sobre o que realmente interessa: &lt;a href="http://pt.wikipedia.org/wiki/BSOD"&gt;"Telas Azuis da Morte"&lt;/a&gt;. O meu grande dilema agora é se devo também comentar algo sobre firmware e hardware. Desenvolvi muitas atividades com software embarcado não só durante a produção do projeto.&lt;/p&gt;&lt;p&gt;Durante o projeto, sobretudo desenvolvendo protocolo USB em um &lt;a href="http://pt.wikipedia.org/wiki/Intel_8051"&gt;8051&lt;/a&gt; utilizando o &lt;a href="http://www.keil.com/"&gt;Keil&lt;/a&gt;, além de ler sensores e trabalhar com barramentos em um &lt;a href="http://pt.wikipedia.org/wiki/Microcontrolador_PIC"&gt;PIC&lt;/a&gt; utilizando o compilador &lt;a href="http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&amp;nodeId=1406&amp;dDocName=en010014"&gt;C18&lt;/a&gt; no &lt;a href="http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&amp;nodeId=1406&amp;dDocName=en019469&amp;part=SW007002"&gt;MPLAB&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Além disso, tive a oportunidade de contribuir no desenvolvimento de um projeto do &lt;a href="http://www.ipei.com.br/"&gt;IPEI&lt;/a&gt;. O hardware deste projeto mede os resultados de uma máquina de tração e os envia a um computador via USB. Contribui no desenvolvimento do firmware PIC implementando o protocolo &lt;a href="http://en.wikipedia.org/wiki/Human_interface_device"&gt;HID&lt;/a&gt; mais uma vez utilizando o MPLAB.&lt;/p&gt;&lt;p&gt;Por último e não menos importante, também contribui no desenvolvimento de firmware &lt;a href="http://www.freescale.com/"&gt;FreeScale&lt;/a&gt; durante meu estágio na &lt;a href="http://www.commodity.com.br/"&gt;Commodity&lt;/a&gt;. Este firmware também lida com protocolo USB, e conta com o auxílio de um chip que dispoê desta interface, já que o DSP que compunha a solução não o fazia. Desta vez tive que utilizar o &lt;a href="http://www.freescale.com/codewarrior"&gt;Code Warrior&lt;/a&gt; no desenvolvimento, e revivi o tempo em que trabalhei na &lt;a href="http://www.provectus.com.br/"&gt;Provectus&lt;/a&gt; desenvolvendo um Bootloader para a atualização do próprio firmware via rede 485, só que dessa vez foi através da USB.&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px;" src="http://www.driverentry.com.br/images/USBComplete.jpg" border="0"/&gt;&lt;/p&gt;&lt;p&gt;Tanta interação com a USB de ambos os lados do cabo é principalmente fruto dos conhecimentos adquiridos com o livro &lt;a href="http://www.amazon.com/USB-Complete-Everything-Develop-Peripherals/dp/B001TJQQ0O/ref=sr_1_6?ie=UTF8&amp;s=books&amp;qid=1259596967&amp;sr=1-6"&gt;"USB Complete"&lt;/a&gt; de Jan Alexlson. Não me lembro se já comentei sobre esse livro aqui antes, mas vale a pena falar dele principalmente sobre do ponto de vista eletrônico/firmware. O livro é simplesmente ótimo, mas diz não cobrir o desenvolvimento de drivers para USB pois tal assunto daria um livro à parte. Com isso ele indica um velho conhecido nosso, o &lt;a href="http://www.amazon.com/Programming-Microsoft%C2%AE-Windows%C2%AE-Driver-Second/dp/0735618038/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1259596816&amp;sr=8-1"&gt;"Programming the Windows Driver Model"&lt;/a&gt; de Walter Oney.&lt;/p&gt;&lt;p&gt;De qualquer forma, aguardem por reformas neste blog. Durante muito tempo tenho trabalhado no limite para conseguir postar e ainda dar conta de todo o resto. Agora meu tempo extra me permitirá fazer posts com mais frequência e finalmente montar turmas abertas do Curso de Drivers para Windows.&lt;/p&gt;&lt;p&gt;Como alguns de vocês já sabem, mantenho uma lista de interessados no curso e que serão notificados por e-mail quando as turmas forem abertas. Independente disso, gostaria de receber e-mails sobre preferências de horários e especialidades, tais como WDM/KMFD/UMDF/File Systems ou ainda sobre ênfase em USB/PCI.&lt;/p&gt;&lt;p&gt;É isso aí. Para um post que não tinha nada a dizer, até que esse disse muito.&lt;br&gt;Até breve!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-5003926784526586187?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2009/11/final-de-curso-mais-um-engenheiro-solta.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>3</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-5799639373011183591</guid><pubDate>Tue, 08 Sep 2009 15:19:00 +0000</pubDate><atom:updated>2010-01-26T12:00:58.953-02:00</atom:updated><title>Um Helimodelo no XV Simpósio Multidisciplinar</title><description>&lt;p&gt;Esse é mais um daqueles posts Off-Topic que não tem nada a ver com nada. Ou quase nada. Acho que a maioria de vocês já esta cansada de saber que este é meu último ano da universidade e que este ano estou todo enrolado com nosso Trabalho de Graduação (TG). Quem acompanha &lt;a href="http://twitter.com/driverentry"&gt;meu Twitter&lt;/a&gt; tem uma idéia de como isso tem tomado meu tempo. Você pode estar se perguntando: &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;"O que faz este projeto?"&lt;/span&gt;&lt;/span&gt;. Em uma palavra: &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;"Nada"&lt;/span&gt;&lt;/span&gt;. Estamos trabalhando a mais de um ano desenvolvendo hardware, firmware, driver, software e controle para que o projeto não faça nada. Na verdade, o objetivo é que não aconteça nada com um helimodelo em voo. Tá, tudo bem, desde o começo agora.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Era uma vez um helimodelo&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Alguns de vocês já sabem que além de programador retardado, também tento gastar o tempo que não tenho como helimodelista. Um helimodelo é um helicóptero em escala reduzida, equipado com motor à combustão ou mesmo elétrico. Com as funcionalidades de um helicóptero convencional, é capaz de realizar voos com as mesmas características e liberdade de movimentos. Na verdade, quanto à capacidade de voo, um helimodelo pode fazer mais que um helicóptero real. Não é incomum ver um helimodelo voando de cabeça para baixo.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/RaptorTitan.jpg"/&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Um helimodelo é controlado por um sistema de rádio controle que determina os movimentos de servomotores instalados na aeronave. Cada servo tem seu papel específico dentro do helimodelo. Um controla o acelerador, outro a inclinação das pás do rotor de cauda e assim por diante.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;É fácil controlar um helimodelo?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Não mesmo. O rádio controle possui dois sticks, e assim, são 4 os movimentos que você deve comandar ao mesmo tempo, isso além das chaves adicionais. O grande problema é que para ganhar os reflexos necessários para fazer a correção leva um certo tempo de treino. Acredite, você não vai querer um helimodelo voando desgovernado perto de você. Além do risco de se ferir, é quase certo que o helimodelo acertará algo e se dividirá em vários pedaços. Posso dizer que este não é um equipamento barato e cada queda pode significar centenas de reais para colocar tudo para funcionar novamente. Para começar com esse hobby normalmente utilizamos um simulador.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/SimuladorG45.PNG"/&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Um simulador é muito parecido com um vídeo-game. Com ele você recebe um joystick especial que é uma réplica de um rádio controle, mas que faz interface USB com seu computador. Você utiliza tal joystick para controlar um helimodelo na tela do seu computador. Assim, a cada queda que o modelo sofrer, basta apertar um botão de reset no próprio controle para que você possa tentar novamente. Quando eu estava começando no hobby, todos me indicaram um simulador até que comprei um. Fiquei pensando: &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;"Que dificil que nada... Pra cima de mim? Só se for pra esses coroas. Tenho mais de vinte anos de vídeo-game nas costas".&lt;/span&gt;&lt;/span&gt; Quando comecei a brincar com o simulador ví que não era tão fácil assim. Dezenas e dezenas de quedas. Os simuladores são realmente um excelente início. Eles conseguem reproduzir com grandes detalhes o comportamento de um modelo.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/AulaVoo.PNG"/&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Depois do simulador, você normalmente faz aulas de voo. E para isso você contrata um instrutor. Mas o que pode fazer um instrutor além de lhe desejar boa sorte? O que acontece é que rádios controles podem ser ligados por um cabo de treinamento. Esse cabo permite que o instrutor possa controlar o helimodelo, e com o mudar de uma chave, o controle passa para a mão do aluno. Se o aluno perder o controle sobre o modelo, o instrutor pode tomar o controle de volta e evitar que seu helimodelo novinho se transforme num monte de lixo.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Legal, mas e o projeto?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Nosso projeto tem como objetivo controlar um helimodelo em voo de forma a estabilizá-lo. Por isso digo que nosso projeto vai fazer nada. Houve professor que ainda disse: &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;"Caramba! Vocês vão fazer tudo isso para fazer um helicóptero ficar parado?"&lt;/span&gt;&lt;/span&gt;. Se você é um helimodelista, sabe que manter o helimodelo parado é o primeiro desafio de um piloto. Mesmo em ambientes sem vento, estabilizar um helimodelo requer uma boa quantidade de experiência. Nosso objetivo final seria descrever um plano de voo simples onde ele decole, estabilize no ar, faça alguns movimentos e finalmente pouse. Não queremos dar um passo maior que a perna. Vamos ver o que conseguimos até o dia da apresentação.&lt;/p&gt;&lt;p&gt;Para detectar os movimentos do helimodelo, nós instalamos alguns sensores no helimodelo, os dados dos sensores são reunidos por um microcontrolador que está instalado numa placa também a bordo do helimodelo. Depois de reunidos, os dados são enviados à uma outra placa em solo através de um módulo &lt;a href="http://pt.wikipedia.org/wiki/ZigBee"&gt;ZigBee&lt;/a&gt;. Na foto abaixo pode-se ver nossa plaquinha. Essa placa em solo é &lt;a href="http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3185"&gt;um kit da Atmel&lt;/a&gt; que tem suporte a diversas interfaces, sendo a USB uma delas. Assim, escrevemos o &lt;a href="http://pt.wikipedia.org/wiki/Firmware"&gt;firmware&lt;/a&gt; e o driver USB para fazer com que tais leituras agora fossem recebidas pelo nosso software de controle.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/DiagramaTG.PNG"/&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Para fazer o controle, utilizaremos o novo Toolkit de &lt;a href="http://pt.wikipedia.org/wiki/L%C3%B3gica_difusa"&gt;lógica Fuzzy&lt;/a&gt; que está disponível no novo &lt;a href="http://www.ni.com./labview/"&gt;LabVIEW 2009&lt;/a&gt; da &lt;a href="http://www.ni.com/"&gt;National Instruments&lt;/a&gt;. Para quem não conhece, o LabVIEW é uma das principais ferramentas de controle utilizadas pela engenharia moderna. É possível fazer programas de controle apenas desenhando e arrastando componentes sobre a tela, pode parecer contraditório um desenvolvedor de baixo nível dizer isso, mas toda essa abstração nos dá tempo para dedicar às coisas que realmente precisam de tempo. A ferramenta também dispõe de interfaces de I/O permitindo trabalhar com sensores e atuadores diretamente sobre o meio eletrônico.&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px;" src="http://www.driverentry.com.br/images/BrunoNI.PNG" border="0"/&gt;&lt;p&gt;Uma outra informação interessante é que o Toolkit de PID e Lógica Fuzzy foi completamente re-escrito e reformulado com a ajuda de um brasileiro. Isso mesmo, &lt;a href="http://www.linkedin.com/pub/bruno-cesar/5/517/832"&gt;Bruno Cesar&lt;/a&gt; (na foto ao lado) trabalha na National Instruments Brasil e foi um dos responsáveis por esse desenvolvimento. Acha isso conhecidência? Então o que você me diria ao saber que ele também se formou na Universidade São Judas Tadeu? Bruno esteve semana passada no campus da Mooca dando uma palestra sobre o novo módulo de lógica Fuzzy. Isso nos deu mais certeza de que a lógica Fuzzy é a ideal para nosso problema de controle, já que ela é perfeita para lidar com problemas complexos onde não se tem o modelo matemático que descreva o comportamento de um helimodelo. A lógica Fuzzy se baseia na experiência de um operador para atuar sobre os controles. É bem interessante.&lt;/p&gt;&lt;p&gt;O LabVIEW também nos permite que façamos chamadas à DLLs. Essa foi uma maneira simples que conseguimos para fazer com os dados que estavam no driver chagar ao software de controle. Assim, criamos uma DLL que abstraísse muitas das complicações de se interagir com um dispositivo USB. O LabVIEW apenas chama funções do tipo &lt;span style="font-style:italic;"&gt;LeAmostra()&lt;/span&gt; que já retorna o dado pronto para o uso. Todo aquele código com &lt;a href="http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx"&gt;CreateFile()&lt;/a&gt; e &lt;a href="http://msdn.microsoft.com/en-us/library/aa363216(VS.85).aspx"&gt;DeviceIoControl()&lt;/a&gt; ficou por conta da DLL, além de outras funções auxiliares.&lt;/p&gt;&lt;p&gt;Agora vocês podem estar se perguntando: &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;"Mas como o LabVIEW vai atuar sobre o helimodelo?"&lt;/span&gt;&lt;/span&gt;. Lembra daquele cabo de treinamento? Nossa placa USB vai receber as ações de controle do LabVIEW através da mesma DLL. Para fazer com que tais comandos sejam aplicados sobre o helimodelo, fizemos com que nossa placa USB se comporte como um rádio controle, que utilizará o cabo de treinamento para aplicar o controle sobre o helimodelo. Obviamente ainda teremos um piloto segurando o rádio no papel de instrutor, principalmente para evitar acidentes, onde um mal funcionamento de nosso projeto poderia transformar meu helimodelo num monte de lixo.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;O Simpósio Multidisciplinar&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px;" src="http://www.driverentry.com.br/images/XV_SIMPOSIO.JPG" border="0"/&gt;&lt;p&gt;Desde que comecei a escrever sobre o projeto neste blog, algumas pessoas ficaram curiosas e disseram: &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;"Me avise quando houver alguma apresentação"&lt;/span&gt;&lt;/span&gt;. Bom, esta é a sua chance de dar uma olhada em alguns projetos. Nosso projeto foi aceito para ter um espaço neste evento que vai acontecer de 18 a 25 de setembro na &lt;a href="http://www.usjt.br"&gt;Universidade São Judas Tadeu&lt;/a&gt;. Serão várias apresentações curtas de 15 cada. Os projetos ainda não estão terminados, e dessa forma, você ainda não verá nenhum helimodelo voando sozinho por lá, mas estaremos com nosso equipamento dispostos a responder algumas perguntas. O simpósio é aberto à visitação pública. Não é necessário ser aluno para participar, basta se inscrever gratuitamente no site da universidade e pronto. Se quiser discutir um pouco sobre sensores, microcontroladores, firmwares, drivers, controle ou ainda helimodelismo, mesmo que durante um café, é só aparecer.&lt;/p&gt;&lt;p&gt;Bom, é isso aí. Já escrevi demais. Agora preciso voltar ao meu projeto.&lt;br&gt;Até mais!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-5799639373011183591?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2009/09/um-helimodelo-no-xv-simposio.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>4</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-4356179197378366746</guid><pubDate>Tue, 18 Aug 2009 19:32:00 +0000</pubDate><atom:updated>2009-08-20T13:11:29.516-03:00</atom:updated><title>Notificando eventos à aplicação</title><description>&lt;p&gt;Há algumas semanas, cá estava eu todo enrolado com meu projeto da faculdade. Com toda essa atividade, o que tenho comentado com meus amigos é que meu &lt;a href="http://twitter.com/driverentry"&gt;Twitter&lt;/a&gt; mais parece um cronograma. Mas em fim, em meio a tanta correria, recebi a seguinte dúvida do leitor Júlio César (Rio de Janeiro - RJ):&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"Como implementar a comunicação entre um driver e uma aplicação de modo que o driver inicie a comunicação? Ou seja, não quero que a aplicação envie uma mensagem ao driver, mas sim que o driver envie uma mensagem à aplicação."&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Minha resposta curta, porém grossa, é que não existem meios de um driver simplesmente acordar numa manhã ensolarada, coçar a barriga enquanto se espreguiça e dizer a si mesmo: &lt;span style="font-style:italic;"&gt;"Hoje vou fazer uma surpresa ao meu amigo notepad.exe. Vou mandar lhe um cartão postal da Kernel-lândia."&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Um modelo Cliente-Servidor&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;O Windows funciona num modelo Cliente-Servidor, onde o lado Servidor seria o Kernel, que atende às requisições de seus clientes, que nesse caso são as aplicações. Nenhuma atividade é iniciada pelo Kernel por vontade própria. Sempre são as aplicações, que utilizando a API nativa do sistema, solicitam notificações do sistema de uma série de eventos.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"Mas Fernando, como ficam as notificações do plug-and-play às aplicações em user-mode?"&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Na verdade elas são solicitadas pelas aplicações utilizando a rotina &lt;a href="http://msdn.microsoft.com/en-us/library/aa363431(VS.85).aspx"&gt;RegisterDeviceNotification()&lt;/a&gt;. Esse assunto é bem legal para se comentar num post futuro. Deixa eu anotar aqui na minha lista de posts a escrever.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"Mas Fernando, quando o sistema inicia, as coisas não começam automagicamente?"&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;O Boot é um procedimento especial no qual o Kernel inicia apenas o Gerenciador de Sessão na User-lândia, também conhecido pelos mais chegados como &lt;span style="font-weight:bold;"&gt;Smss&lt;/span&gt;. O &lt;span style="font-style:italic;"&gt;Smss&lt;/span&gt; é um processo nativo (que utiliza apenas API nativa) e é considerado um componente de confiança. Ele não utiliza API Windows porque o Subsistema Windows (&lt;span style="font-weight:bold;"&gt;Csrss&lt;/span&gt;) ainda não existe. Daí em diante ocorre uma série de inicializações originadas pelo &lt;span style="font-style:italic;"&gt;Smss&lt;/span&gt; e seus processos filhos, mas vou deixar os detalhes sobre isso com o &lt;a href="http://www.caloni.com.br/blog/archives/o-boot-no-windows-sem-windows"&gt;Lesma&lt;/a&gt;. Isso me fez lembrar que &lt;span style="font-style:italic;"&gt;Csrss&lt;/span&gt; se extende por &lt;span style="font-style:italic;"&gt;"Client Server Run-Time Subsystem"&lt;/span&gt;.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"Mas Fernando, e quanto aos serviços?"&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Serviços são iniciados por um processo chamado &lt;span style="font-style:italic;"&gt;Services.exe&lt;/span&gt;, que por sua vez também foi iniciado por outro componente durante o processo de Boot.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"Mas Fernando, e quanto aos drivers de boot?"&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;A carga de drivers não é considerada uma notificação para o user-mode.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"Mas Fernando, setembro chove?"&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Bom, chega né? Vamos falar do que interessa agora.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Operações de I/O pendentes&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Já vimos em um &lt;a href="http://www.driverentry.com.br/blog/2008/06/criando-e-usando-ioctls.html"&gt;outro post&lt;/a&gt; que uma aplicação pode solicitar serviços ao driver. Para dar a impressão de que o driver enviou uma notificação à aplicação, podemos utilizar uma operação que ficaria pendente até que o evento desejado ocorra. Tal como uma operação de leitura numa porta serial, que ficaria presa na chamada &lt;a href="http://msdn.microsoft.com/en-us/library/aa365467(VS.85).aspx"&gt;ReadFile()&lt;/a&gt; até que um ou mais caracteres fossem recebidos.&lt;/p&gt;&lt;p&gt;Isso funciona razoavelmente bem, mas teríamos algumas complicações caso o evento nunca ocorra e sua aplicação precise sair porque deixou o feijão no fogo ou coisa assim. Dessa forma, teríamos que adotar uma solução &lt;span style="font-style:italic;"&gt;multi-threaded&lt;/span&gt;, onde uma segunda thread avisaria à thread pendente de que é tarde demais, que não adianta mais esperar pelo evento, já era, miou, esquece, cai na real.&lt;/p&gt;&lt;p&gt;Para as pessoas que sofrem de &lt;span style="font-style:italic;"&gt;"thread-fobia"&lt;/span&gt;, uma solução utilizando &lt;a href="http://msdn.microsoft.com/en-us/library/ms686358(VS.85).aspx"&gt;Overlapped I/O&lt;/a&gt; cairia como uma luva, mas não vou falar sobre isso hoje. Na verdade isso já está na minha lista, mas não vai ser hoje.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Compartilhando um evento&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;A maneira que mais gosto de trabalhar é compartilhando um evento. Todos sabem o que é um evento? Pode parecer besteira, mas tem muita gente não sabe direito o que é um &lt;span style="font-style:italic;"&gt;handle &lt;/span&gt;e quer programar o Kernel. Isso me preocupa um pouco. Que tipo de drivers essas pessoas podem gerar? Me permitam abrir um parenteses aqui para fazer uma pergunta: O que vocês acham de além de eu oferecer posts de drivers, eu oferecer posts sobre &lt;span style="font-style:italic;"&gt;System Programming&lt;/span&gt;? Coisas como Processos, Threads, Objetos, Handles, Memória Virtual, Heaps, Dispatch Objects, Sincronismo e por aí vai. Me mandem e-mails com sugestões, que serão muito bem vindas.&lt;/p&gt;&lt;p&gt;Voltando ao que interessa, se uma aplicação cria um evento e manda seu handle para o driver, este poderá sinalizar a existência de uma informação relevante à aplicação. Assim a aplicação pode esperar por este evento, e quando este for sinalizado, a aplicação faz o I/O para buscar tal informação usando os meios de comunicação que já vimos em &lt;a href="http://www.driverentry.com.br/blog/2008/10/buffered-direct-ou-neither-em-ioctls.html"&gt;outros posts&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Image Notifier&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Para exemplificar a recepção de eventos gerados por um driver, vamos ver hoje um driver que nos avisará sempre que uma imagem for mapeada em um processo.&lt;/p&gt;&lt;p&gt;Primeiro vamos definir uma interface para essa comunicação. A aplicação precisará enviar o handle de um evento para o driver, isso também vai avisar o driver que a aplicação deseja receber notificações sobre o mapeamento de imagens. Para isso vamos definir nossas IOCTLs como já vimos &lt;a href="http://www.driverentry.com.br/blog/2008/06/criando-e-usando-ioctls.html"&gt;neste outro post&lt;/a&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//-f--&amp;gt; Este ser&amp;#225; o IOCTL para notificar o driver de que uma&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; aplica&amp;#231;&amp;#227;o est&amp;#225; interessada nos eventos de carga de&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; imagens. Este IOCTL dever&amp;#225; levar o handle do evento&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; a ser sinalizado quando houver dados para a aplica&amp;#231;&amp;#227;o.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IOCTL_IMG_START_NOTIFYING&amp;nbsp;&amp;nbsp; CTL_CODE(FILE_DEVICE_UNKNOWN,&amp;nbsp;&amp;nbsp; \&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x800&lt;/span&gt;,&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; \&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; METHOD_BUFFERED,&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; \&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; FILE_ANY_ACCESS)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//-f--&amp;gt; Sei que &amp;#233; besteira criar uma estrutura com um s&amp;#243; membro,&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; mas isso al&amp;#233;m de ser mais did&amp;#225;tico, facilita para aquela&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; galera que vai fazer &amp;quot;Copy and Paste&amp;quot; do meu c&amp;#243;digo para&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; outros projetos. Depois eles v&amp;#227;o querer mandar mais dados&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; ao driver e v&amp;#227;o se enrolar com isso. A&amp;#237; j&amp;#225; viu de quem &amp;#233;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; a culpa: &amp;quot;Peguei esse c&amp;#243;digo no blog daquela besta!&amp;quot;.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;typedef&lt;/span&gt; &lt;span style="color: blue;"&gt;struct&lt;/span&gt; _IMG_START_NOTIFYING&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; HANDLE&amp;nbsp; hEvent;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;} IMG_START_NOTIFYING, *PIMG_START_NOTIFYING;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//-f--&amp;gt; Este ser&amp;#225; o IOCTL que a aplica&amp;#231;&amp;#227;o lan&amp;#231;ar&amp;#225; ao driver para&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; obter os detalhes sobre a carga de imagens num processo.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IOCTL_IMG_GET_IMAGE_DETAIL&amp;nbsp; CTL_CODE(FILE_DEVICE_UNKNOWN,&amp;nbsp;&amp;nbsp; \&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x801&lt;/span&gt;,&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; \&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; METHOD_BUFFERED,&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; \&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; FILE_ANY_ACCESS)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui vou definir um path m&amp;#225;ximo de 260 caracteres, mas&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; podem haver casos de paths mais longos. N&amp;#227;o vou tratar&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; todos os casos e nem otimizar o transporte deste buffer&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; pegando apenas os bytes v&amp;#225;lidos.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IMG_MAX_IMAGE_NAME&amp;nbsp; &lt;span style="color: purple;"&gt;260&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui segue path da imagem que o driver obter&amp;#225;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; antes de notificar a aplica&amp;#231;&amp;#227;o.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;typedef&lt;/span&gt; &lt;span style="color: blue;"&gt;struct&lt;/span&gt; _IMG_IMAGE_DETAIL&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; CHAR&amp;nbsp;&amp;nbsp;&amp;nbsp; ImageName[IMG_MAX_IMAGE_NAME];&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;} IMG_IMAGE_DETAIL, *PIMG_IMAGE_DETAIL;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui a aplica&amp;#231;&amp;#227;o diz que n&amp;#227;o est&amp;#225; mais interessada nas&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; notifica&amp;#231;&amp;#245;es sobre imagens. Isso far&amp;#225; com que o driver&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; libere a refer&amp;#234;ncia que fez ao handle.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;#define&lt;/span&gt; IOCTL_IMG_STOP_NOTIFYING&amp;nbsp;&amp;nbsp;&amp;nbsp; CTL_CODE(FILE_DEVICE_UNKNOWN,&amp;nbsp;&amp;nbsp; \&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0x802&lt;/span&gt;,&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; \&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; METHOD_BUFFERED,&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; \&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; FILE_ANY_ACCESS)&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Não vou colocar todo o código aqui no post, mas está tudo disponível no exemplo para download ao final deste post. Lembrem-se que nove em cada dez dentistas recomendam a leitura dos comentários para o melhor entendimento do exemplo. A aplicação basicamente criará um evento e enviará seu handle para o driver através de um IOCTL.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Cria o evento que ser&amp;#225; compartilhado.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; hNotificationEvt = CreateEvent(NULL,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; TRUE,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; FALSE,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; NULL);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; _ASSERT(hNotificationEvt);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; printf(&lt;span style="color: #a31515;"&gt;&amp;quot;Requesting device to start notifying.\n&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Copiamos o handle do evento para a estrutura&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; que ser&amp;#225; enviada ao driver. Como sabemos, handles&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; s&amp;#227;o v&amp;#225;lidos apenas no contexto deste processo,&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; ent&amp;#227;o estamos admitindo que nosso driver estar&amp;#225;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; no topo da device stack.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; StartNotifying.hEvent = hNotificationEvt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (!DeviceIoControl(hDevice,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IOCTL_IMG_START_NOTIFYING,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;amp;StartNotifying,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;sizeof&lt;/span&gt;(StartNotifying),&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; NULL,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: purple;"&gt;0&lt;/span&gt;,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;amp;dwBytes,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; NULL))&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Respira fundo e abre o WinDbg...&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; dwError = GetLastError();&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; printf(&lt;span style="color: #a31515;"&gt;&amp;quot;Error #%d on starting device notification.\n&amp;quot;&lt;/span&gt;,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; dwError);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;__leave&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Quando o driver receber este IOCTL, este irá adquirir uma referência ao objeto apontado pelo handle. Notem que para isso o driver utiliza a rotina &lt;a href="http://msdn.microsoft.com/en-us/library/ms802942.aspx"&gt;ObReferenceObjectByHandle()&lt;/a&gt; do Object Manager, que além de incrementar o contador de referência do objeto, também certifica que o handle é do tipo de objeto que você espera receber. Isso evitaria que, por algum motivo, o handle de um outro objeto tenha sido passado no lugar do handle do evento. O resultado dessa chamada será um ponteiro para um evento recebido pelo driver. Como sabemos, objetos têm seu header num formato padrão, mas o corpo do objeto varia dependendo do seu tipo. Imagine que alguém enviasse um handle para uma thread no lugar de um handle para um evento, poderiamos usar as rotinas de evento para manipular uma thread e a chance de tudo ficar azul é alta. Por isso o uso do parâmetro &lt;span style="font-style:italic;"&gt;ObjectType&lt;/span&gt;, apesar de opcional, é muito recomendado.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Obt&amp;#233;m uma refer&amp;#234;ncia ao objeto&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; nts =&amp;nbsp; ObReferenceObjectByHandle(pStartNotifying-&amp;gt;hEvent,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; EVENT_ALL_ACCESS,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; *ExEventObjectType,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; UserMode,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (PVOID*)&amp;amp;g_pEvent,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; NULL);&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"Fernando, isso é mesmo necessário? Minha aplicação é a única que vai usar esse driver, e ela sempre vai enviar um handle para evento."&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Esse tipo de precaução evita que um programa engraçadinho envie qualquer coisa para seu driver produzindo uma tela azul propositalmente.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;span style="font-weight:bold;font-style:italic;"&gt;"Fernando, na minha opinião você gosta mesmo é de complicar as coisas. Eu não poderia simplesmente fazer uma cópia do handle e usar as rotinas do tipo &lt;a href="http://msdn.microsoft.com/en-us/library/dd851996.aspx"&gt;ZwSetEvent()&lt;/a&gt; que recebem o handle do evento como parâmetro?"&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Veja bem, o handle é válido apenas dentro do processo que o obteve. No nosso caso, tal handle é válido apenas no contexto da nossa aplicação de teste. As notificações de imagens rodam em contexto arbitrário, ou seja, sabe Deus em qual contexto de processo. Por isso teremos que obter uma referência que seja válida em qualquer contexto. O ponteiro obtido pela rotina &lt;span style="font-style:italic;"&gt;ObReferenceObjectByHandle()&lt;/span&gt; é válido em qualquer contexto, pois aponta para o próprio objeto que reside em &lt;span style="font-weight:bold;"&gt;System Space&lt;/span&gt;. Se você não sabe o que significa &lt;span style="font-style:italic;"&gt;System Space&lt;/span&gt;, então dê uma passeada por &lt;a href="http://www.driverentry.com.br/blog/2008/09/uma-pitada-de-memria-virtual.html"&gt;este post&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Bom, depois disso a aplicação vai ficar aguardando o evento ser sinalizado pelo driver. No código abaixo, dois eventos são monitorados, um deles é sinalizado pelo driver enquanto o outro é sinalizado pela própria aplicação no momento de encerrar sua atividade.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui criamos um array de handles para a espera&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; por m&amp;#250;ltiplos objetos.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; hObjects[&lt;span style="color: purple;"&gt;0&lt;/span&gt;] = hFinishEvt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; hObjects[&lt;span style="color: purple;"&gt;1&lt;/span&gt;] = hNotificationEvt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;do&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Espera ou por um sinal do device indicando a&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; presen&amp;#231;a de dados no driver, ou um sinal da&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; thread prim&amp;#225;ria dizendo aquela baboseira de&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; novela e tals.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; dwWait = WaitForMultipleObjects(&lt;span style="color: purple;"&gt;2&lt;/span&gt;,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; hObjects,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; FALSE,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; INFINITE);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;switch&lt;/span&gt;(dwWait)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;case&lt;/span&gt; WAIT_FAILED:&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; P&amp;#244; Murphy, d&amp;#225; um tempo!&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; dwError = GetLastError();&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; printf(&lt;span style="color: #a31515;"&gt;&amp;quot;Error #%d on waiting for device notification.\n&amp;quot;&lt;/span&gt;,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; dwError);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;__leave&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;case&lt;/span&gt; WAIT_OBJECT_0 + &lt;span style="color: purple;"&gt;1&lt;/span&gt;:&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Opa! O driver tem algo para n&amp;#243;s, vamos buscar.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (GetImageDetail(hDevice) != ERROR_SUCCESS)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;__leave&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;break&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Ficaremos nisso enquanto o evento de finaliza&amp;#231;&amp;#227;o&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; n&amp;#227;o for sinalizado pela thread prim&amp;#225;ria.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;span style="color: blue;"&gt;while&lt;/span&gt;(dwWait != WAIT_OBJECT_0);&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Quando o evento é sinalizado, a aplicação enviará um IOCTL para obter os dados do driver. Nossa aplicação de teste também imprime esse dado na tela por pura diversão. Vamos dar uma olhada no código do driver para saber como isso acontece.&lt;/p&gt;&lt;p&gt;Durante a inicialização, o driver chama a rotina &lt;a href="http://msdn.microsoft.com/en-us/library/ms802949.aspx"&gt;PsSetLoadImageNotifyRoutine()&lt;/a&gt; para registrar uma rotina de callback que é chamada sempre que uma imagem for mapeada para algum processo.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Registra rotina de callback para receber&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; as notifica&amp;#231;&amp;#245;es de imagens mapeadas para&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; processos.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; nts = PsSetLoadImageNotifyRoutine(OnLoadImage);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ASSERT(NT_SUCCESS(nts));&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Nossa rotina de callback converte o path da imagem mapeada de Unicode para ANSI. Mais detalhes sobre conversão de strings &lt;a href="http://www.driverentry.com.br/blog/2009/07/strings-no-kernel.html"&gt;neste post&lt;/a&gt;. Em seguida a rotina coloca esse path numa lista e seta o evento enviado pela aplicação. Se você ainda não sabe brincar de listas ligadas no kernel do Widows, então leia &lt;a href="http://www.driverentry.com.br/blog/2007/02/listas-ligadas-no-ddk.html"&gt;este post&lt;/a&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;VOID&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;OnLoadImage(IN PUNICODE_STRING&amp;nbsp; pusFullImageName,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; IN HANDLE&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; hProcessId,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; IN PIMAGE_INFO&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; pImageInfo)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PIMG_EVENT_NODE pNode;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ANSI_STRING&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; asImageName;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; NTSTATUS&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; nts;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Vamos adquirir o controle das vari&amp;#225;veis&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; compartilhadas por diferentes threads.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; nts = KeWaitForMutexObject(&amp;amp;g_EventMtx,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; UserRequest,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; KernelMode,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; FALSE,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; NULL);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ASSERT(NT_SUCCESS(nts));&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;__try&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Verifica se a aplica&amp;#231;&amp;#227;o est&amp;#225; interessada neste&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; evento.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (!g_pEvent)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;__leave&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Aloca um n&amp;#243; para a lista de paths de imagens&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; pNode = (PIMG_EVENT_NODE)ExAllocatePoolWithTag(PagedPool,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;sizeof&lt;/span&gt;(IMG_EVENT_NODE),&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; IMG_TAG);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (!pNode)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Ops!&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; ASSERT(FALSE);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;__leave&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Inicializa uma ANSI_STRING para usar na convers&amp;#227;o&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; do path da imagem. Vamos fornecer sempre um byte&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; a menos para nos reservar espa&amp;#231;o para adicionar um&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; terminador nulo.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; RtlInitEmptyAnsiString(&amp;amp;asImageName,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; pNode-&amp;gt;ImageDetail.ImageName,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;sizeof&lt;/span&gt;(pNode-&amp;gt;ImageDetail.ImageName) - &lt;span style="color: purple;"&gt;1&lt;/span&gt;);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Faz a convers&amp;#227;o sem aloca&amp;#231;&amp;#227;o do resultado.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; nts = RtlUnicodeStringToAnsiString(&amp;amp;asImageName,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; pusFullImageName,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; FALSE);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Ops!&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; ASSERT(FALSE);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; ExFreePool(pNode);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;__leave&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Coloca o terminador nulo para que a aplica&amp;#231;&amp;#227;o de&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; teste possa contar com ele na hora de fazer o print.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; asImageName.Buffer[asImageName.Length] = &lt;span style="color: purple;"&gt;0&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Insere o n&amp;#243; na lista.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; InsertTailList(&amp;amp;g_ListHead,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;amp;pNode-&amp;gt;Entry);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Setamos o evento informando a aplica&amp;#231;&amp;#227;o que existem&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; dados na lista a serem lidos.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; KeSetEvent(g_pEvent,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; IO_NO_INCREMENT,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; FALSE);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;__finally&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Por fim, libera o mutex e corre pro abra&amp;#231;o.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; KeReleaseMutex(&amp;amp;g_EventMtx,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; FALSE);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Quando o evento é sinalizado, a aplicação acorda de seu sono profundo e descobre que o driver tem dados para ela. Então ela envia um IOCTL para obter tais dados. Este IOCTL vai executar a rotina abaixo removendo o primeiro elemento da lista e verificar se ainda existem mais dados a serem coletados pela aplicação. Caso a lista esvazie nesta chamada, o driver reseta o evento para que a aplicação volte a dormir esperando pelos registros de novas imagens mapeadas.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;NTSTATUS&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;OnGetImageDetail(PIMG_IMAGE_DETAIL&amp;nbsp; pImageDetail)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; NTSTATUS&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; nts;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PLIST_ENTRY&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pEntry;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PIMG_EVENT_NODE pNode;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Adquire o mutex&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; nts = KeWaitForMutexObject(&amp;amp;g_EventMtx,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; UserRequest,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; KernelMode,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; FALSE,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; NULL);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ASSERT(NT_SUCCESS(nts));&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Verifica se a lista est&amp;#225; vazia. Sempre&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; use esta rotina antes de tentar remover&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; um elemento da lista.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (!IsListEmpty(&amp;amp;g_ListHead))&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Obt&amp;#233;m o endere&amp;#231;o do Entry&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; pEntry = RemoveHeadList(&amp;amp;g_ListHead);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Obt&amp;#233;m o endere&amp;#231;o do n&amp;#243;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; pNode = CONTAINING_RECORD(pEntry,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; IMG_EVENT_NODE,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; Entry);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Copia para o buffer da aplica&amp;#231;&amp;#227;o.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; RtlCopyMemory(pImageDetail,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;amp;pNode-&amp;gt;ImageDetail,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &lt;span style="color: blue;"&gt;sizeof&lt;/span&gt;(IMG_IMAGE_DETAIL));&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Libera o n&amp;#243; e balezia&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; ExFreePool(pNode);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; nts = STATUS_SUCCESS;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;else&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; nts = STATUS_NO_MORE_ENTRIES;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Pode ser que nesta chamada a lista tenha&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; ficado vazia. Ent&amp;#227;o verificamos novamente&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; e resetamos o evento para que a aplica&amp;#231;&amp;#227;o&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; n&amp;#227;o volte aqui.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (IsListEmpty(&amp;amp;g_ListHead))&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; KeResetEvent(g_pEvent);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Libera o mutex e pronto.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; KeReleaseMutex(&amp;amp;g_EventMtx,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; FALSE);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; nts;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;O resultado de tanto bla-bla-bla&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Depois que o driver for compilado, instalado e iniciado, poderemos executar nossa aplicação de teste e esperar que algo seja executado. Quando um processo é criado, tanto seu módulo como as DLLs que ele depende são mapeadas no sistema. Isso vai disparar nossa rotina de callback no driver e fazer a coisa toda funcionar. Se você não sabe como compilar, instalar e iniciar um driver, &lt;a href="http://www.driverentry.com.br/blog/2006/09/getting-started.html"&gt;este post&lt;/a&gt; pode te ajudar.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/ImgNotifier.png"/&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;A imagem acima é o resultado da execução do &lt;span style="font-style:italic;"&gt;notepad.exe&lt;/span&gt; enquanto nossa aplicação de teste esperava por eventos, mas qualquer outro processo poderia disparar tais eventos. Este post além de nos fornecer este exemplo de chamada invertida, também nos mostra como brincar com &lt;span style="font-weight:bold;"&gt;Mutex Objects&lt;/span&gt;, que foi a dúvida de outro leitor, Ismael Rocha (Brasília - DF).&lt;/p&gt;&lt;p&gt;Agora deixa eu voltar para o meu projeto da faculdade.&lt;br&gt;Até mais!&lt;/p&gt;&lt;p&gt;Download &lt;a href="http://www.driverentry.com.br/samples/ImgNotifier.zip"&gt;ImgNotifier.zip&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-4356179197378366746?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2009/08/notificando-eventos-aplicacao.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>6</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-1734150893048229269</guid><pubDate>Tue, 07 Jul 2009 15:01:00 +0000</pubDate><atom:updated>2009-08-19T13:52:35.397-03:00</atom:updated><title>Strings no Kernel</title><description>&lt;p&gt;Tem coisa mais besta que manipular strings? Creio que a resposta correta seria "&lt;span style="font-style:italic;"&gt;Depende&lt;/span&gt;". Quando eu tinha terminado meu curso técnico de Informática Industrial em 1995, eu pensava que sabia muito de linguagem C. Afinal de contas eu já sabia manipular strings. Copiar, concatenar, inverter, fazer buscas por palavras... O que mais um programador deveria saber? Quando comecei a fazer meu estágio e a pegar programas do mundo real, descobri que eu não sabia nada. Mas uma coisa é certa, eu sabia manipular strings. Este post deve ajudar bastante os programadores C++ que manjam tudo de &lt;a href="http://en.wikipedia.org/wiki/Template_(programming)"&gt;Templates&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Smart_pointer"&gt;Smart Pointers&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Standard_Template_Library"&gt;STL&lt;/a&gt; e coisas mágicas que abstraem a realidade facilitando a vida do programador. Com todos esses recursos de contadores de referências e sobrecarga de tudo que é operador, as coisas começam a ficar nebulosas e você começa a se perguntar: &lt;span style="font-style:italic;"&gt;"Mas onde está o buffer mesmo?"&lt;/span&gt;. Hoje vamos apenas dar uma passadinha de leve nas estruturas &lt;a href="http://msdn.microsoft.com/en-us/library/aa492030.aspx"&gt;UNICODE_STRING&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/aa491546.aspx"&gt;ANSI_STRING&lt;/a&gt; e algumas funções de conversão entre elas. Pode parecer besteira, mas se você não souber brincar de strings, pra quê aprender o resto? Vai tudo acabar em tela azul mesmo.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;E no prézinho...&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Nós aprendemos com a tia da escolinha que strings são cadeias de caracteres. Assim poderíamos contar a história de nossas vidas apenas colocando um caractere na frente do outro.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;CHAR&amp;nbsp;&amp;nbsp;&amp;nbsp; szExemplo[] = &lt;span style="color: #a31515;"&gt;&amp;quot;Tava ruim l&amp;#225; na Bahia, profiss&amp;#227;o de b&amp;#243;ia-fria\n&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #a31515;"&gt;&amp;quot;Trabalhando noite e dia, num era isso que eu queria\n&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #a31515;"&gt;&amp;quot;Eu vim-me embora pra \&amp;quot;Sum Paulo\&amp;quot;,\n&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #a31515;"&gt;&amp;quot;Eu vim no lombo dum jumento com pouco conhecimento\n&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #a31515;"&gt;&amp;quot;Enfrentando chuva e vento e dando uns peido fedorento (vish)\n&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #a31515;"&gt;&amp;quot;At&amp;#233; minha bunda fez um calo\n&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #a31515;"&gt;&amp;quot;Chegando na capital, uns puta predi&amp;#227;o legal\n&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #a31515;"&gt;&amp;quot;As mina pagando um pau, mas meu jumento tava mal\n&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #a31515;"&gt;&amp;quot;Precisando reformar\n&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #a31515;"&gt;&amp;quot;Fiz a pintura, importei quatro ferradura\n&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #a31515;"&gt;&amp;quot;Troquei at&amp;#233; dentadura e pra completar a belezura\n&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #a31515;"&gt;&amp;quot;Eu instalei um Road-Star!&amp;quot;&lt;/span&gt;;&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style="font-style:italic;"&gt;Jumento Celestino / Mamonas Assassinas&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Duas coisas são obrigatórias que você saiba para que você possa continuar lendo este post. Uma delas é que estes caracteres precisam estar armazenados em algum lugar, seja em uma variável local, alocadas no heap ou mesmo no segmento de dados inicializados. A outra coisa é que strings normalmente são terminadas por um caractere NULL, mas a falta dele não descaracteriza uma string. Isso significa que podemos ter strings sem terminadores onde seu tamanho é indicado por uma outra variável. Deixo aqui um &lt;a href="http://www.caloni.com.br/blog/archives/strings"&gt;gancho&lt;/a&gt; para o Lesma explicar essas coisas aos meninos e meninas interessados.&lt;/p&gt;&lt;p&gt;Uma outra característica importante é que nem sempre um caractere é igual a um Byte. Existem strings compostas por caracteres largos. Caracteres largos, ao contrário do que se pensa, não são caracteres sortudos, mas sim, caracteres formados por valores de 16 bits. Com strings formadas por tais caracteres pode-se expressar palavras em qualquer idioma. Isso explica como o Windows consegue lidar com nomes de arquivos alienígenas quando você tenta instalar os drivers do &lt;a href="http://www.youtube.com/watch?v=M3mbLGjTfhU"&gt;HiPhone&lt;/a&gt; que você comprou no China.&lt;/p&gt;&lt;p&gt;Além disso, imagine que ocorra um erro durante o processo de criptografia do seu disco rídigo. Seria vital que uma mensagem de erro detalha lhe fosse exibida independente da nacionalidade do produto. Se a informação vai ajudar já é outro assunto.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/chinese_error.jpg" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Quatro tipos de strings&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Destes temos duas strings que já estamos acostumados a ver em user-Mode, ambas terminadas com caractere NULL.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;CHAR&amp;nbsp;&amp;nbsp;&amp;nbsp; szString[] = &lt;span style="color: #a31515;"&gt;&amp;quot;Uma string de CHAR&amp;quot;&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;WCHAR&amp;nbsp;&amp;nbsp; wzString[] = L&lt;span style="color: #a31515;"&gt;&amp;quot;Uma string de WCHAR&amp;quot;&lt;/span&gt;;&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p&gt;As outras duas strings são as que normalmente vemos em kernel-mode.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;typedef&lt;/span&gt; &lt;span style="color: blue;"&gt;struct&lt;/span&gt; _UNICODE_STRING {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; USHORT&amp;nbsp; Length;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; USHORT&amp;nbsp; MaximumLength;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; PWSTR&amp;nbsp; Buffer;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;} UNICODE_STRING, *PUNICODE_STRING;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;typedef&lt;/span&gt; &lt;span style="color: blue;"&gt;struct&lt;/span&gt; _STRING {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; USHORT&amp;nbsp; Length;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; USHORT&amp;nbsp; MaximumLength;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; PCHAR&amp;nbsp; Buffer;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;} ANSI_STRING, *PANSI_STRING;&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Estas strings são definidas por estruturas com três membros, os quais descrevo abaixo. O comportamento das rotinas e macros que às manipulam é muito similar. Por isso vou concentrar meus exemplos em UNICODE_STRING já que o sub-sistema Win32 converte tudo para unicode quando passa uma chamada para o kernel.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Buffer:&lt;/strong&gt; E um ponteiro para a região de memória onde os caracteres são armazenados.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Length:&lt;/strong&gt; Indica a quantidade de bytes válidos da string. Este tamanho é sempre expresso em bytes, mesmo que esta seja uma string de WCHAR.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;MaximumLength: &lt;/strong&gt;Indica o tamanho máximo que esta string pode ter.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Para entender de como estes membros são interpretados, dê uma olhada no exemplo abaixo:&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;void&lt;/span&gt; OsTresMembros(&lt;span style="color: blue;"&gt;void&lt;/span&gt;)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; WCHAR&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; wsUmArray[&lt;span style="color: purple;"&gt;200&lt;/span&gt;];&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; UNICODE_STRING&amp;nbsp; usString;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//--f-&amp;gt; Indico onde os caracteres desta string&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ser&amp;#227;o armazenados.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; usString.Buffer = wsUmArray;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Ainda n&amp;#227;o escrevemos nada no buffer,&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; por isso, nada do que esteja no buffer&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;#233; v&amp;#225;lido.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; usString.Length = &lt;span style="color: purple;"&gt;0&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Embora o buffer n&amp;#227;o esteja inicializado&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ele ainda est&amp;#225; l&amp;#225; e pode conter uma string&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; de no m&amp;#225;ximo seu tamanho em bytes.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; usString.MaximumLength = &lt;span style="color: blue;"&gt;sizeof&lt;/span&gt;(wsUmArray);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Aqui vemos uma string unicode vazia, pois tem zero bytes válidos, mas sua capacidade de armazenar é de até 200 caracteres. Notem que os caracteres são armazenados em um array que está na pilha. Isso significa que nenhum &lt;span style="font-style:italic;"&gt;leak&lt;/span&gt; de memória será causado com o termino desta rotina.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Inicializando Strings&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Podemos ainda utilizar algumas macros que fazem a inicialização destas estruturas.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;void&lt;/span&gt; InitString(&lt;span style="color: blue;"&gt;void&lt;/span&gt;)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; UNICODE_STRING&amp;nbsp; usOutraConstante;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; UNICODE_STRING&amp;nbsp; usVazia;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; WCHAR&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; wzBuffer[&lt;span style="color: purple;"&gt;30&lt;/span&gt;] = L&lt;span style="color: #a31515;"&gt;&amp;quot;Isso n&amp;#227;o ser&amp;#225; considerado.&amp;quot;&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Inicaliza uma string na sua cria&amp;#231;&amp;#227;o.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; UNICODE_STRING&amp;nbsp; usConstante = RTL_CONSTANT_STRING(L&lt;span style="color: #a31515;"&gt;&amp;quot;Uma string.&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Inicializa uma string constante.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; RtlInitUnicodeString(&amp;amp;usOutraConstante,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; L&lt;span style="color: #a31515;"&gt;&amp;quot;Uma outra string constante que n&amp;#227;o muda.&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Inicializa uma string vazia para uso posterior&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; RtlInitEmptyUnicodeString(&amp;amp;usVazia,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; wzBuffer,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;sizeof&lt;/span&gt;(wzBuffer));&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Reparem nos valores destas estruturas.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;kd&amp;gt; ?? usConstante&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;struct _UNICODE_STRING&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;quot;Uma string.&amp;quot;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp; +0x000 Length&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; : 0x16&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp; +0x002 MaximumLength&amp;nbsp;&amp;nbsp;&amp;nbsp; : 0x18&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp; +0x004 Buffer&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; : 0xf8cd8600&amp;nbsp; &amp;quot;Uma string.&amp;quot;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;kd&amp;gt; db 0xf8cd8600 L0x18&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8cd8600&amp;nbsp; 55 00 6d 00 61 00 20 00-73 00 74 00 72 00 69 00&amp;nbsp; U.m.a. .s.t.r.i.&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8cd8610&amp;nbsp; 6e 00 67 00 2e 00 00 00&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; n.g.....&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Embora a capacidade máxima desta string seja de 0x18 caracteres, somente 0x16 bytes são válidos. Isso porque a macro desconsidera o terminador NULL que o compilador C/C++ deixou de brinde.&lt;/p&gt;&lt;p&gt;A macro &lt;a href="http://msdn.microsoft.com/en-us/library/aa906838.aspx"&gt;RTL_CONSTANT_STRING()&lt;/a&gt; nos permite inicializar a string na sua criação, mas um outro notável recurso dela é que ela ferra o IntelliSence do Visual Studio (2008 pelo menos). Então se você gosta mesmo do IntelliSence, prefira utilizar a macro &lt;a href="http://msdn.microsoft.com/en-us/library/ms802969.aspx"&gt;RtlInitUnicodeString()&lt;/a&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;kd&amp;gt; ?? usVazia&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;struct _UNICODE_STRING&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;quot;Isso n&amp;#227;o ser&amp;#225; considerado.&amp;quot;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp; +0x000 Length&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; : 0&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp; +0x002 MaximumLength&amp;nbsp;&amp;nbsp;&amp;nbsp; : 0x3c&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp; +0x004 Buffer&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; : 0xf8ae5c34&amp;nbsp; &amp;quot;Isso n&amp;#227;o ser&amp;#225; considerado.&amp;quot;&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Oops! Como pode uma string com zero bytes válidos ser exibida pelo WinDbg? Na verdade, o que acontece é que o WinDbg desmonta a estrutura UNICODE_STRING e mostra cada um dos mebros aqui. No caso, existe um array de WCHAR com valores bem comportados aqui. Não culpe o coitadinho. Você é que está mal acostumado com o Visual Studio.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;kd&amp;gt; db 0xf8ae5c34 L0x3c&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8ae5c34&amp;nbsp; 49 00 73 00 73 00 6f 00-20 00 6e 00 e3 00 6f 00&amp;nbsp; I.s.s.o. .n...o.&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8ae5c44&amp;nbsp; 20 00 73 00 65 00 72 00-e1 00 20 00 63 00 6f 00&amp;nbsp;&amp;nbsp; .s.e.r... .c.o.&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8ae5c54&amp;nbsp; 6e 00 73 00 69 00 64 00-65 00 72 00 61 00 64 00&amp;nbsp; n.s.i.d.e.r.a.d.&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8ae5c64&amp;nbsp; 6f 00 2e 00 00 00 00 00-00 00 00 00&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; o...........&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Mas estes dados só estão aí por acaso. Eles não são considerados como parte válida de uma string unicode. Isso atrapalha um pouco na hora de fazer o debug pois mesmo a janela de variáveis locais também mostra esse conteúdo inválido.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/usLocals.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;A extensão &lt;a href="http://msdn.microsoft.com/en-us/library/cc266940.aspx"&gt;!ustr&lt;/a&gt; mostra apenas os dados válidos de uma string unicode.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;kd&amp;gt; !ustr usConstante&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;String(22,24) at f899fc6c: Uma string.&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;kd&amp;gt; !ustr usVazia&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;String(0,60) at f8ae5c1c:&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;O terminador é necessário?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px;" src="http://www.driverentry.com.br/images/terminator.png" border="0" /&gt;&lt;p&gt;Não mesmo! Você pode bem observar que nos exemplos anteriores, a macro deixou o terminador de fora dos bytes válidos. Considerar o terminador como byte válido é um erro e pode gerar confusão. Imagine comparar duas strings distintas que carregam o mesmo conteúdo, mas uma delas considera o terminador como informação válida. Tais strings serão diferentes, já que possuem comprimento diferente.&lt;/p&gt;&lt;p&gt;Creio que o importante mesmo é não contar com o terminador nas strings que você recebe de outros componentes. É fato que na maioria das vezes, o buffer possui um terminador, e que apesar de não ser considerado informação válida, o terminador ainda está lá. Nunca conte com isso a menos que haja alguma nota na documentação. Já vi pessoas que utilizarem o membro &lt;span style="font-style:italic;"&gt;Buffer&lt;/span&gt; como parâmetro para uma chamada à rotina &lt;a href="http://msdn.microsoft.com/en-us/library/78zh94ax(VS.80).aspx"&gt;wcslen()&lt;/a&gt; por exemplo. Além do risco de obter a informação incorreta, ainda tem um plus de poder gerar uma tela azul.&lt;/p&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;&lt;span style="font-style:italic;"&gt;"Mas Fernando, eu já testei isso em vários sistemas operacionais e sempre funcionou."&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Isso não justifica nada, você não pode se apoiar em testes, mas em documentações. Quando seu produto se espalha no mercado, ele enfrenta muitos ambientes diferentes, com os mais diversos filtros, anti-virus, monitores e por aí vai. Não se pode ter certeza da implementação de qualquer software. O melhor que podemos esperar deles é que se apoiem na documentação.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Manipulando Strings&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Os membros da estrutura UNICODE_STRING são basicamente utilizados por rotinas de manipulação a fim de verificar se o buffer existente é suficiente para a operação desejada. Portanto, antes de utilizar uma string, certifique-se de esta foi inicializada corretamente. No caso de uma cópia de strings, a string de destino precisará ser inicilizada mesmo que vazia.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;void&lt;/span&gt; CopyString(&lt;span style="color: blue;"&gt;void&lt;/span&gt;)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; UNICODE_STRING&amp;nbsp; usSource;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; UNICODE_STRING&amp;nbsp; usTarget;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; WCHAR&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; wzTarget[&lt;span style="color: purple;"&gt;10&lt;/span&gt;];&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui inicializamos nossa string de origem.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; RtlInitUnicodeString(&amp;amp;usSource,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; L&lt;span style="color: #a31515;"&gt;&amp;quot;12345678901234567890&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui inicializamos nossa string de destino.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; RtlInitEmptyUnicodeString(&amp;amp;usTarget,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; wzTarget,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;sizeof&lt;/span&gt;(wzTarget));&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Realiza a c&amp;#243;pia&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; RtlCopyUnicodeString(&amp;amp;usTarget,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;amp;usSource);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Aqui vemos o exemplo de uma cópia de string onde a string de origem é maior que a de destino. Nessa situação não teremos uma violação de acesso, mas o buffer de destino será preenchido completamente. Notem que a rotina não nos deixou o confortável terminador.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;kd&amp;gt; !ustr usTarget&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;String(20,20) at f89a3c6c: 1234567890&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;kd&amp;gt; ?? usTarget&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;struct _UNICODE_STRING&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;quot;1234567890&amp;quot;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp; +0x000 Length&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; : 0x14&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp; +0x002 MaximumLength&amp;nbsp;&amp;nbsp;&amp;nbsp; : 0x14&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp; +0x004 Buffer&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; : 0xf89a3c54&amp;nbsp; &amp;quot;1234567890&amp;quot;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;kd&amp;gt; db 0xf89a3c54 L0x20&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f89a3c54&amp;nbsp; 31 00 32 00 33 00 34 00-35 00 36 00 37 00 38 00&amp;nbsp; 1.2.3.4.5.6.7.8.&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f89a3c64&amp;nbsp; 39 00 30 00 98 5d 5f 00-14 00 14 00 54 3c 9a f8&amp;nbsp; 9.0..]_.....T&amp;lt;..&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Diferentes da rotina &lt;a href="http://msdn.microsoft.com/en-us/library/ms803010.aspx"&gt;RtlCopyUnicodeString()&lt;/a&gt;, algumas outras rotinas nos retornam STATUS_BUFFER_TOO_SMALL quando o buffer é insuficiente.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;VOID &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; RtlCopyUnicodeString(&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN OUT PUNICODE_STRING&amp;nbsp; DestinationString,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN PCUNICODE_STRING&amp;nbsp; SourceString&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;NTSTATUS &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; RtlAppendUnicodeStringToString(&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN OUT PUNICODE_STRING&amp;nbsp; Destination,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN PUNICODE_STRING&amp;nbsp; Source&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Existe uma série de rotinas de manipulação de strings no WDK, mas sempre fica faltando alguma rotina se comparado com a ampla biblioteca de rotinas da biblioteca padrão C/C++. Rotinas como &lt;a href="http://msdn.microsoft.com/en-us/library/ftw0heb9(VS.71).aspx"&gt;strrchr()&lt;/a&gt; por exemplo. Quando necessário teremos que construir uma versão que manipule estruturas UNICODE_STRING da mesma maneira. Aqui está uma &lt;a href="http://msdn.microsoft.com/en-us/library/ms796247.aspx"&gt;lista básica de rotinas de string&lt;/a&gt; que o WDK suporta. Outras funções são listadas &lt;a href="http://msdn.microsoft.com/en-us/library/aa489561.aspx"&gt;aqui&lt;/a&gt;, mas falaremos delas mais tarde.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Mas onde está o buffer mesmo?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;A estrutura UNICODE_STRING não armazena buffer, mas sim um ponteiro para ele. Dessa forma, a maneira de descartar uma string varia dependendo da maneira que você a obteve. Nos exemplos que vimos até agora, os bufferes utilizados estão em dados inicializados ou de arrays locais da função de exemplo. Existem rotinas que inicializam e alocam strings como forma de retornar a informação desejada. Nestes casos, é necessário liberar o buffer que você recebeu. É o caso das rotinas que fazem a conversão de ANSI_STRING para UNICODE_STRING e vice versa. São elas &lt;a href="http://msdn.microsoft.com/en-us/library/ms803001.aspx"&gt;RtlUnicodeStringToAnsiString()&lt;/a&gt; e &lt;a href="http://msdn.microsoft.com/en-us/library/ms804316.aspx"&gt;RtlAnsiStringToUnicodeString().&lt;/a&gt;&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;NTSTATUS &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; RtlAnsiStringToUnicodeString(&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN OUT PUNICODE_STRING&amp;nbsp; DestinationString,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN PANSI_STRING&amp;nbsp; SourceString,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN BOOLEAN&amp;nbsp; AllocateDestinationString&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;NTSTATUS &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; RtlUnicodeStringToAnsiString(&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN OUT PANSI_STRING&amp;nbsp; DestinationString,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN PUNICODE_STRING&amp;nbsp; SourceString,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN BOOLEAN&amp;nbsp; AllocateDestinationString&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;O exemplo abaixo faz uma conversão de ANSI para UNICODE com alocação do resultado, e em seguida, libera o buffer recebido.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;void&lt;/span&gt; ConvertString(&lt;span style="color: blue;"&gt;void&lt;/span&gt;)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ANSI_STRING&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; asString;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; UNICODE_STRING&amp;nbsp; usString;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui inicializamos nossa string de origem.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; RtlInitAnsiString(&amp;amp;asString,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #a31515;"&gt;&amp;quot;Um exemplo simples.&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Neste caso n&amp;#227;o precisaremos inicializar a&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string de destino. A rotina far&amp;#225; isso por n&amp;#243;s.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; RtlAnsiStringToUnicodeString(&amp;amp;usString,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;amp;asString,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TRUE);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Imprime a string resultante&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; DbgPrint(&lt;span style="color: #a31515;"&gt;&amp;quot;String convertida: %wZ\n&amp;quot;&lt;/span&gt;,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;amp;usString);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Liberamos o buffer alocado na convers&amp;#227;o.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; RtlFreeUnicodeString(&amp;amp;usString);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Você mesmo pode escrever uma rotina que gere UNICODE_STRINGs alocando o buffer dinamicamente. O buffer pode ser alocado dinamicamente utilizando &lt;a href="http://msdn.microsoft.com/en-us/library/ms796989.aspx"&gt;ExAllocatePoolWithTag()&lt;/a&gt; ou uma de suas irmãs. No entanto, na hora de liberar o buffer desta string, utilize a função adequada, que neste exemplo seria &lt;a href="http://msdn.microsoft.com/en-us/library/ms796854.aspx"&gt;ExFreePoolWithTag()&lt;/a&gt;. Não saia utilizando &lt;a href="http://msdn.microsoft.com/en-us/library/ms804310.aspx"&gt;RtlFreeUnicodeString()&lt;/a&gt; a torto e a direito. Aprecie com moderação. Apenas utilize essa rotina para liberar strings que foram obtidas por funções como &lt;span style="font-style:italic;"&gt;RtlAnsiStringToUnicodeString()&lt;/span&gt;, cuja documentação indica o uso de &lt;span style="font-style:italic;"&gt;RtlFreeUnicodeString()&lt;/span&gt;.&lt;/p&gt;&lt;p&gt;&lt;span style="font-style:italic;"&gt;&lt;blockquote&gt;&lt;span style="font-weight:bold;"&gt;"..., the caller must deallocate the buffer by calling RtlFreeUnicodeString."&lt;/span&gt;&lt;/blockquote&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Safe Strings em Kernel&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Uma parte das rotinas de manipulação de strings da biblioteca padrão do C/C++, tais como &lt;a href="http://msdn.microsoft.com/en-us/library/kk6xf663.aspx"&gt;strcpy()&lt;/a&gt; e &lt;a href="http://msdn.microsoft.com/en-us/library/ybk95axf(VS.71).aspx"&gt;sprintf()&lt;/a&gt;, também estão disponíveis em kernel, mas a crescente preocupação com a seguraça na manipulação de buffers fez com que as funções seguras fossem disponibilizadas tanto para user-mode como para kernel-mode. Para ter detalhes sobre o uso destas funções consulte &lt;a href="http://msdn.microsoft.com/en-us/library/aa489591.aspx"&gt;este link&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Mais um driver de exemplo&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Este &lt;a href="http://www.driverentry.com.br/blog/2007/06/nos-queremos-exemplos.html"&gt;outro post&lt;/a&gt; traz o exemplo de um driver que guarda uma lista de strings em memória. Já neste &lt;a href="http://www.driverentry.com.br/blog/2007/08/usando-fileobject-e-fscontext.html"&gt;outro post&lt;/a&gt;, esse mesmo exemplo foi evoluido para que diferentes listas fossem mantidas sob diferentes contextos. Agora vou evoluir esse exemplo novamente. A aplicação continuará enviando strings com terminadores NULL durante a escrita, o driver criará ANSI_STRINGs a partir delas e às converterão em UNICODE_STRINGs antes de colocá-las na lista.&lt;/p&gt;&lt;p&gt;A maior parte das modificações estão nas rotinas de leitura e escrita, então vou apenas exibir o código dessas rotinas aqui. De qualquer forma, todo o projeto incluindo o driver e uma aplicação de teste estão disponíveis para download ao final deste post. Vamos começar com a rotina de escrita que envia as strings ao driver. Como sempre, toda informação revelante estão nos comentários.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;/****&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;***&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; OnWrite&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;**&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;**&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; A aplica&amp;#231;&amp;#227;o est&amp;#225; enviando uma string.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;NTSTATUS&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;OnWrite(IN PDEVICE_OBJECT&amp;nbsp; pDeviceObj,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN PIRP&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; pIrp)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PIO_STACK_LOCATION&amp;nbsp; pStack;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ANSI_STRING&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; asString;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PSTRING_LIST&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pStringList;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PSTRING_REG&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pStringReg;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ULONG&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ulBytes;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; KIRQL&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; kIrql;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; NTSTATUS&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; nts;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Obtemos a Stack Location corrente.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pStack = IoGetCurrentIrpStackLocation(pIrp);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Obt&amp;#233;m a ponta da lista.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pStringList = (PSTRING_LIST)pStack-&amp;gt;FileObject-&amp;gt;FsContext;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; O que temos no buffer de sistema aqui &amp;#233; um array&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; de bytes terminados com NULL. Vamos inicializar&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; uma ANSI_STRING com este buffer.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; RtlInitAnsiString(&amp;amp;asString,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (PCSZ)pIrp-&amp;gt;AssociatedIrp.SystemBuffer);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui alocamos o n&amp;#243; que vai ser colocado na lista&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pStringReg = (PSTRING_REG) ExAllocatePoolWithTag(NonPagedPool,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;sizeof&lt;/span&gt;(STRING_REG),&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; STR_LST_TAG);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Para fazer a convers&amp;#227;o para UNICODE_STRING, vamos&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; solicitar que a rotina fa&amp;#231;a a aloca&amp;#231;&amp;#227;o do bufer&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; resultante. Por essa raz&amp;#227;o, n&amp;#227;o precisaremos inicializar&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; a string de sa&amp;#237;da.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; nts = RtlAnsiStringToUnicodeString(&amp;amp;pStringReg-&amp;gt;usString,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;amp;asString,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TRUE);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Ops! Provavelmente n&amp;#227;o tivemos mem&amp;#243;ria para isso.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Vamos sinalizar a falha e informar ao IoManager&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; que zero bytes foram copiados.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ExFreePoolWithTag(pStringReg, STR_LST_TAG);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pIrp-&amp;gt;IoStatus.Information = &lt;span style="color: purple;"&gt;0&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;else&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Vamos segurar o spinlock para evitar concorr&amp;#234;ncia&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; no acesso &amp;#224; lista.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; KeAcquireSpinLock(&amp;amp;pStringList-&amp;gt;SpinLock,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;amp;kIrql);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Insere o n&amp;#243; na lista.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; InsertTailList(&amp;amp;pStringList-&amp;gt;ListHead,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;amp;pStringReg-&amp;gt;Entry);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Libera o spinlock.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; KeReleaseSpinLock(&amp;amp;pStringList-&amp;gt;SpinLock,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; kIrql);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui informamos ao IoManager que todos os bytes&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; enviados pela aplica&amp;#231;&amp;#227;o foram recebidos com&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sucesso pelo driver.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pIrp-&amp;gt;IoStatus.Information = pStack-&amp;gt;Parameters.Write.Length;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; O campo de Information j&amp;#225; foi preenchido, vamos apenas&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; copiar o status da opera&amp;#231;&amp;#227;o e completar a IRP.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pIrp-&amp;gt;IoStatus.Status = nts;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IoCompleteRequest(pIrp, IO_NO_INCREMENT);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; nts;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Agora vejamos a recuperação das strings na leitura.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;/****&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;***&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; OnRead&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;**&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;**&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; A aplica&amp;#231;&amp;#227;o est&amp;#225; querendo receber as strings eviadas&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;**&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; por ela.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;NTSTATUS&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;OnRead(IN PDEVICE_OBJECT&amp;nbsp; pDeviceObj,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN PIRP&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; pIrp)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ANSI_STRING&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; asString;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PIO_STACK_LOCATION&amp;nbsp; pStack;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PSTRING_LIST&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pStringList;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PSTRING_REG&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pStringReg;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PLIST_ENTRY&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pEntry;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; KIRQL&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; kIrql;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; NTSTATUS&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; nts;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Vamos deixar este campo com zero at&amp;#233; que tenhamos&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; certeza de que a c&amp;#243;pia foi feita paro o buffer da&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; aplica&amp;#231;&amp;#227;o&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pIrp-&amp;gt;IoStatus.Information = &lt;span style="color: purple;"&gt;0&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Obtemos a Stack Location corrente.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pStack = IoGetCurrentIrpStackLocation(pIrp);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Obt&amp;#233;m a ponta da lista.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pStringList = (PSTRING_LIST)pStack-&amp;gt;FileObject-&amp;gt;FsContext;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Vamos retornar para aplica&amp;#231;&amp;#227;o apenas um array de CHAR&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; com terminador NULL. As strings est&amp;#227;o armexadas como&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; UNICODE_STRING. Vamos convert&amp;#234;-las para ANSI_STRING.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Aqui vamos inicializar a ANSI_STRING que receber&amp;#225;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; o resultado da convers&amp;#227;o de UNICODE_STRING.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Vamos oferecer Lengh-1 para reservar um byte para&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; o terminador null ap&amp;#243;s convers&amp;#227;o.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; RtlInitEmptyAnsiString(&amp;amp;asString,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; (PCHAR)pIrp-&amp;gt;AssociatedIrp.SystemBuffer,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; (USHORT)pStack-&amp;gt;Parameters.Read.Length - &lt;span style="color: purple;"&gt;1&lt;/span&gt;);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui vamos adquirir o spinlock para evitar concorr&amp;#234;ncia&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; no acesso &amp;#224; lista.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; KeAcquireSpinLock(&amp;amp;pStringList-&amp;gt;SpinLock,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;amp;kIrql);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Verifica se a lista est&amp;#225; vazia.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (IsListEmpty(&amp;amp;pStringList-&amp;gt;ListHead))&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Sinaliza erro na leitura e j&amp;#225; libera o spinlock.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nts = STATUS_NO_MORE_ENTRIES;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; KeReleaseSpinLock(&amp;amp;pStringList-&amp;gt;SpinLock,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; kIrql);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;else&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Remove o registro da lista e j&amp;#225; libera o spinlock.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pEntry = RemoveHeadList(&amp;amp;pStringList-&amp;gt;ListHead);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; KeReleaseSpinLock(&amp;amp;pStringList-&amp;gt;SpinLock,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; kIrql);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pStringReg = CONTAINING_RECORD(pEntry,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; STRING_REG,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Entry);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui convertemos a string. Reparem que n&amp;#227;o solicitamos&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; a aloca&amp;#231;&amp;#227;o do buffer. Estamos utilizando o buffer de&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sistema para receber o resultado da convers&amp;#227;o. Neste&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; caso a string de destino deve estar inicializada.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nts = RtlUnicodeStringToAnsiString(&amp;amp;asString,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;amp;pStringReg-&amp;gt;usString,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; FALSE);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (NT_SUCCESS(nts))&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Aqui usamos aquele byte que reservamos e assim&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; o terminador null tamb&amp;#233;m &amp;#233; copiado pelo IoManager&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; do SystemBuffer para o buffer da aplica&amp;#231;&amp;#227;o&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; asString.Buffer[asString.Length] = &lt;span style="color: purple;"&gt;0&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Precisamos informar ao IoManager a quantidade de&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; bytes que ser&amp;#227;o copiados para o buffer da aplica&amp;#231;&amp;#227;o.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Esse tamanho &amp;#233; o tamanho da string convertida mais&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; um byte ocupado pelo terminador NULL que colocamos.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; pIrp-&amp;gt;IoStatus.Information = asString.Length+&lt;span style="color: purple;"&gt;1&lt;/span&gt;;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Neste exemplo, n&amp;#227;o estamos lidando com o caso de erro&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; na convers&amp;#227;o, isso pode acontecer se a aplica&amp;#231;&amp;#227;o mandar&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; um buffer pequeno para a string. Caso algum erro ocorra&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; durante a convers&amp;#227;o, vamos simplesmente descartar a string&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; Libera o buffer da string e em seguida o registro que ela&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ocupava na lista.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RtlFreeUnicodeString(&amp;amp;pStringReg-&amp;gt;usString);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ExFreePoolWithTag(pStringReg, STR_LST_TAG);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//-f--&amp;gt; O campo de Information j&amp;#225; foi preenchido, vamos apenas&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green;"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; copiar o status da opera&amp;#231;&amp;#227;o e completar a IRP.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pIrp-&amp;gt;IoStatus.Status = nts;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IoCompleteRequest(pIrp, IO_NO_INCREMENT);&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; nts;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Com este driver rodando, já posso pensar em alguns exemplos de filtros. Já venho pensando em exemplos de filtros há algum tempo, além de receber sujestões de post sobre esse assunto. O fato é que não tinhamos base para tal. O importante é termos um driver simples o suficiente para o fácil entendimento das coisas. Não adianta eu fazer um post com um filtro de disco rígido, um filtro de rede ou qualquer outro driver com plug-and-play e gerenciamento de energia. Estes precisariam de muito conhecimento acumulado e não caberiam num post.&lt;/p&gt;&lt;p&gt;Enfim, mais uma vêz espero ter ajudado. E se alguma dúvida surgir é só me mandar um e-mail.&lt;/p&gt;&lt;p&gt;Have fun!&lt;/p&gt;&lt;p&gt;Download &lt;a href="http://www.driverentry.com.br/samples/StringList.zip"&gt;StringList.zip&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-1734150893048229269?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2009/07/strings-no-kernel.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>10</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-1172842590293909203</guid><pubDate>Thu, 18 Jun 2009 18:46:00 +0000</pubDate><atom:updated>2009-08-19T13:55:36.680-03:00</atom:updated><title>Levando a tela azul pra casa</title><description>&lt;p&gt;Nada melhor que uma bela choraderia para começar este post. Meu rítmo está baixo por conta da universidade estar sugando todas as minhas energias vitais. Se você tem acompanhado meu blog nos últimos posts, já sabe do que estou falando. No meu tempo livre estive correndo com meu projeto, meu estágio e meu emprego. Meu blog também participa dessa lista de tarefas, mas o coitadinho tem menos prioridade aqui. Alguns de vocês devem saber que sou helimodelista por hobby, mas como eu disse ao meu amigo Heldai outro dia: &lt;em&gt;"Hobby é o nome que se dá àquilo que fazemos para ocupar o tempo que temos livre, mas ainda estou para descobrir o nome que daríamos àquilo que gostaríamos de fazer se tivéssemos tempo livre..."&lt;/em&gt;. Enfim, como isso não tem nada a ver com o post de hoje, vamos mudar de assunto.&lt;/p&gt;&lt;p&gt;Entre uma coisa e outra, estive tentando pensar em algo simples para um post pequeno. Foi então que a dúvida do leitor Ismael Rocha (Brasília - DF) gerou este post.&lt;/p&gt;&lt;em&gt;&lt;strong&gt;&lt;blockquote&gt;"Existe uma maneira de salvar as BSOD's para posteriormente verificar eventuais problemas?"&lt;/blockquote&gt;&lt;/strong&gt;&lt;/em&gt;&lt;p&gt;Salvar uma tela azul? Salvar o quê? A máquina já morreu meu amigo! Já era! Acabou! O que você ainda pode tentar salvar é seu emprego.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Brincadeiras à parte, existe sim.&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;O sistema operacional está pré-configurado para reiniciar automagicamente quando uma falha crítica acontece. Falha crítica é a maneira polida de se dizer que a casa caiu, a vaca foi pro brejo, o jacaré te abraçou, o tambor girou, o ferro berrou, o tempo fechou, ficou pequeno pra você... enfim, uma tela azul aconteceu. &lt;a href="http://www.driverentry.com.br/blog/2007/09/como-assim-eu-no-gosto-de-tela-azul.html"&gt;Não que eu não goste de telas azuis&lt;/a&gt;, mas do efeito colateral que ela nos traz. Pela norma mundial dos consumidores de drivers de terceiros, se você é o autor de um driver que estiver instalado em uma máquina no momento da falha, esteja ele rodando ou não, então a culpa da falha é sua até que se prove o contrário. É triste, mas é a realidade. A partir do momento que uma tela azul acontece, você é o culpado padrão e terá que ficar aguentando piadinhas pelo resto da eternidade. Gostaria de aproveitar o contexto para mandar um abraço pro meu amigo Heldai.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Exibindo a tela azul&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Na tentativa de salvar sua dignidade, você tenta provar que a culpa não é sua. Dizer que o reset da máquina é uma &lt;em&gt;feature&lt;/em&gt; do seu driver e que felizmente funcionou muito bem não vai colar, não na segunda vez. Mas o que você pode fazer se a tela azul é apenas um flash de informações enquanto a máquina não reinicia? Felizmente você pode mudar isso. Clicando com o botão direito do mouse sobre o &lt;em&gt;"Meu computador"&lt;/em&gt;, selecionando &lt;em&gt;"Propriedades"&lt;/em&gt;. Daí em diante é só dar uma olhada na figura abaixo para descobrir que você pode evitar que a máquina reinicie automaginamente.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/DumpSettings.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Você terá de desmarcar a opção "Reiniciar automaticamente", e assim ter todo o tempo que for necessário para mostrar a todos que o problema não é seu. Na maioria das vezes o sistema consegue detectar o driver que provavelmente é o causador de toda essa dor de cabeça e exibir o nome do arquivo na tela azul como podemos ver na figura abaixo.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Desmontando uma tela azul&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/UselessBSOD.png" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;"Nossa! Então o Windows tem um algorítmo de inteligência artificial, que provalvelmente usa nanotecnologia de alguma forma para descobrir o driver culpado?"&lt;/p&gt;&lt;p&gt;Na verdade é um pouco mais simples que isso, o Windows simplesmemte pega a imagem do driver que lançou uma exceção que não foi manipulada ou que voluntariamente derrubou o sistema por detectar alguma incoerência. Por isso, nem sempre o nome do driver exibido é de fato o nome do driver culpado. Se pensarmos no simples exemplo onde o driver &lt;em&gt;MetralhadoraGiratoria.sys&lt;/em&gt; escreve onde não deveria corrompendo algum &lt;em&gt;Pool&lt;/em&gt; de alocações, esse erro mais tarde pode ser detectado pelo driver &lt;em&gt;Laranja.sys&lt;/em&gt; que, na hora de fazer uma alocação de memória, chama uma rotina de sistema que por sua vez chama a rotina &lt;a href="http://msdn.microsoft.com/en-us/library/ms801645.aspx"&gt;KeBugCheckEx()&lt;/a&gt; ao detectar tal incoerência. Consegue adivinhar o nome do driver que aparecerá no BO?&lt;/p&gt;&lt;p&gt;Outras informações ainda podem ser obtidas da tela azul. Se é o nome do seu driver que aparece na tela, então você ainda pode obter o endereço da instrução onde a desgraça ocorreu. Em nosso exemplo o endereço é o &lt;em&gt;0xF8DD8A415&lt;/em&gt; partir daí podemos chegar na função que estava sendo executada no momento da falha se tivermos o arquivo de mapa gerado pelo linker. Também é possível obter a data da imagem do arquivo e tirar aquela dúvida de que realmente era a versão certa que estava sendo executada. A data do arquivo é obtida no campo &lt;em&gt;DateStamp&lt;/em&gt; e é expressa em um valor hexadecimal de 32 bits representando a quantidade de segundos deste meia noite de primeiro de Janeiro de 1970. Difícil mesmo é achar alguém com paciência suficiente para calcular isso diante de uma tela azul. Existem meios bem menos trabalhosos de descobrir que a culpa foi sua mesmo.&lt;/p&gt;&lt;p&gt;Na minha opinião, a informação mais relevante que a tela azul oferece é o &lt;strong&gt;Stop Code&lt;/strong&gt;. Como o nome já sugere, &lt;em&gt;Stop Code&lt;/em&gt; é um código que vai indicar o motivo da falha do sistema. você pode consultar a lista de Stop Codes neste &lt;a href="http://msdn.microsoft.com/en-us/library/ms789516.aspx"&gt;link&lt;/a&gt; ou ainda dar uma olhada no arquivo &lt;em&gt;C:\WinDDK\6001.18002\inc\api\BugCodes.h&lt;/em&gt; que vem no WDK.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/BugCheckCodes.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;&lt;em&gt;Stop Codes&lt;/em&gt; vêm com até quatro parâmetros que trazem informações adicionais ao código de parada. A interpretação destes valores dependerá do código de falha, que em nosso exemplo é 0x7E. Consultando no link que informei a pouco, teremos a seguinte interpretação para os valores que nos foi apresentado.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/BugCheck7E.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Mas existe um jeito de salvar a BSOD ou não?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Tá tá tá... É que começo a escrever e acabo me empolgando. Mas enfim, quando uma falha crítica ocorre, o sistema cria um arquivo conhecido como &lt;strong&gt;Crash Dump&lt;/strong&gt;. Existem três opções de &lt;em&gt;crash dumps&lt;/em&gt; que podem ser geradas.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dump Completo:&lt;/strong&gt; Nesta opção, todo o conteúdo da memória física no momento da falha será copiado em um arquivo. Obviamente o tamanho deste arquivo será a quantidade de memória presente na máquina com um acréssimo de 1MB de header. Essa opção não aparece nas máquinas que possuam mais de 2GB de memória física, mas ainda é possivel configurar o dump completo sem utilizar essa interface gráfica escrevendo diretamente no registro. Esse método é também conhecido como &lt;em&gt;"configurar na unha"&lt;/em&gt;. O dump completo é muito útil quando a informação presente em páginas de memória em &lt;em&gt;User Space&lt;/em&gt; for relevante para o problema, tal como situações de &lt;em&gt;Dead Locks&lt;/em&gt;. Se você não sabe o que significa &lt;em&gt;User Space&lt;/em&gt;, &lt;a href="http://www.driverentry.com.br/blog/2008/09/uma-pitada-de-memria-virtual.html"&gt;este post&lt;/a&gt; pode ajudar.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dump de Kernel:&lt;/strong&gt; Aqui somente as páginas em &lt;em&gt;System Space&lt;/em&gt; serão copiadas para disco. O tamanho deste arquivo vai variar dependendo de quantidade de memória física a máquina tem instalada, mas não existe uma proporção exata. Muito do balanceamento de páginas utilizado pelo gerenciador de memória virtual vai determinar o tamanho deste arquivo, mas ele fica pela ordem de 200MB num sistema com 4GB de memória total (já dá pra levar no pen drive). Essa opção é normalmente a mais viável, já que só carrega a informação mais relevante para um crash de sistema.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dump Mínimo:&lt;/strong&gt; Aqui um arquivo de 64KB será gerado para sistemas 32 bits ( 128KB para sistemas 64 bits). Neste arquivo temos apenas o &lt;em&gt;Stop Code&lt;/em&gt; e seus parâmetros, a lista de drivers carregados no momento da falha, informações sobre o processo e thread corrente e o &lt;em&gt;Call Stack&lt;/em&gt; da thread que causou a falha.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Na mesma janela onde você configura o reinicio automático do sistema, existem dois outros campos que vão configurar o tipo de dump desejado e o caminho onde este será gerado. Agora você já pode levar sua tela azul no coração e depurar onde você quiser. Em casa, no trabalho, no trêm, no metrô... Você pode ainda pedir que clientes enviem seus &lt;em&gt;crash dumps&lt;/em&gt; para que você possa diagnosticar o problema ser ter que se deslocar através de rios e montanhas sob o frio e a chuva.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Tenho o &lt;em&gt;Crash Dump&lt;/em&gt;, e agora?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Agora que você é um feliz proprietário de um maravilhoso arquivo de &lt;em&gt;Crash Dump&lt;/em&gt;, o que mais você poderia querer da vida? Talvez ser capaz descobrir a causa do problema já seria um bom começo. Para isso vamos utilizar o depurador nativo do sistema operacional. Se você ainda não conhece o &lt;a href="www.microsoft.com/whdc/DevTools/Debugging/default.mspx"&gt;WinDbg&lt;/a&gt;, então dê uma olhada &lt;a href="http://www.driverentry.com.br/blog/2006/12/step-into-kernel-serial.html"&gt;neste post&lt;/a&gt; para que você sabia do que estamos falando aqui.&lt;/p&gt;&lt;p&gt;Admitindo que você tenha &lt;em&gt;Windbg&lt;/em&gt; instalado em sua máquina de desenvolvimento, e que este esteja com o servidor de símbolos configurado, tudo que temos a fazer agora é abrir o &lt;em&gt;WinDbg&lt;/em&gt;, selecionar o ítem &lt;em&gt;"Open Crash Dump..."&lt;/em&gt; no menu &lt;em&gt;"File"&lt;/em&gt; e apontar o caminho do arquivo de dump que você copiou da pobre máquina que ousou rodar seu driver. O texto abaixo é o resultado exibido na janela de comandos quando o &lt;em&gt;Crash Dump&lt;/em&gt; é aberto.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;Microsoft (R) Windows Debugger Version 6.11.0001.404 AMD64&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Copyright (c) Microsoft Corporation. All rights reserved.&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Loading Dump File [Z:\Sources\MEMORY.DMP]&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Kernel Summary Dump File: Only kernel address space is available&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Symbol search path is: srv*&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Executable search path is: &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Windows XP Kernel Version 2600 (Service Pack 3) UP Free x86 compatible&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Product: WinNt, suite: TerminalServer SingleUserTS&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Built by: 2600.xpsp.080413-2111&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Machine Name:&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Kernel base = 0x804d7000 PsLoadedModuleList = 0x80553fc0&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Debug session time: Thu Jun 18 14:46:24.969 2009 (GMT-3)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;System Uptime: 0 days 0:03:20.375&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Loading Kernel Symbols&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;...............................................................&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;.........................................................&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Loading User Symbols&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Loading unloaded module list&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;...........&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;*******************************************************************************&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;*&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; *&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;*&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; Bugcheck Analysis&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; *&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;*&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; *&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;*******************************************************************************&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Use !analyze -v to get detailed debugging information.&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;BugCheck 7E, {c0000005, f8d9f415, f8af1bb4, f8af18b0}&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Probably caused by : Useless.sys ( Useless!DriverEntry+5 )&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Followup: MachineOwner&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;---------&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Agora se simplesmente executarmos o comando sugerido, já teremos uma boa descrição do que aconteceu com máquina que sofreu a falha crítica.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;div style="font-family: Courier New; font-size: 9pt; color: black; background: white;"&gt;&lt;pre style="margin: 0px;"&gt;kd&amp;gt; !analyze -v&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;*******************************************************************************&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;*&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; *&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;*&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; Bugcheck Analysis&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; *&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;*&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; *&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;*******************************************************************************&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;SYSTEM_THREAD_EXCEPTION_NOT_HANDLED (7e)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;This is a very common bugcheck.&amp;nbsp; Usually the exception address pinpoints&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;the driver/function that caused the problem.&amp;nbsp; Always note this address&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;as well as the link date of the driver/image that contains this address.&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Arguments:&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Arg1: c0000005, The exception code that was not handled&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Arg2: f8d9f415, The address that the exception occurred at&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Arg3: f8af1bb4, Exception Record Address&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Arg4: f8af18b0, Context Record Address&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Debugging Details:&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;------------------&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;memory at 0x%08lx. The memory could not be %s.&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;FAULTING_IP: &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Useless!DriverEntry+5 [z:\sources\driverentry\useless\useless.c @ 7]&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8d9f415 c7050000000000000000 mov dword ptr ds:[0],0&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;EXCEPTION_RECORD:&amp;nbsp; f8af1bb4 -- (.exr 0xfffffffff8af1bb4)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;ExceptionAddress: f8d9f415 (Useless!DriverEntry+0x00000005)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp; ExceptionCode: c0000005 (Access violation)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp; ExceptionFlags: 00000000&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;NumberParameters: 2&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp; Parameter[0]: 00000001&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp; Parameter[1]: 00000000&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Attempt to write to address 00000000&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;CONTEXT:&amp;nbsp; f8af18b0 -- (.cxr 0xfffffffff8af18b0)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;eax=07263867 ebx=00000000 ecx=bb40e64e edx=1be10003 esi=e19feea8 edi=81eb41d0&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;eip=f8d9f415 esp=f8af1c7c ebp=f8af1c7c iopl=0&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nv up ei ng nz na po nc&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;cs=0008&amp;nbsp; ss=0010&amp;nbsp; ds=0023&amp;nbsp; es=0023&amp;nbsp; fs=0030&amp;nbsp; gs=0000&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; efl=00010282&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Useless!DriverEntry+0x5:&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8d9f415 c7050000000000000000 mov dword ptr ds:[0],0&amp;nbsp; ds:0023:00000000=????????&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Resetting default scope&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;PROCESS_NAME:&amp;nbsp; System&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;at 0x%08lx. The memory could not be %s.&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;EXCEPTION_PARAMETER1:&amp;nbsp; 00000001&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;EXCEPTION_PARAMETER2:&amp;nbsp; 00000000&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;WRITE_ADDRESS:&amp;nbsp; 00000000 &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;FOLLOWUP_IP: &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Useless!DriverEntry+5 [z:\sources\driverentry\useless\useless.c @ 7]&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8d9f415 c7050000000000000000 mov dword ptr ds:[0],0&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;BUGCHECK_STR:&amp;nbsp; 0x7E&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;DEFAULT_BUCKET_ID:&amp;nbsp; NULL_DEREFERENCE&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;LAST_CONTROL_TRANSFER:&amp;nbsp; from 8057677f to f8d9f415&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;STACK_TEXT:&amp;nbsp; &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8af1c7c 8057677f 81eb41d0 81d46000 00000000 Useless!DriverEntry+0x5&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;[z:\sources\driverentry\useless\useless.c @ 7]&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8af1d4c 8057688f 80000360 00000001 00000000 nt!IopLoadDriver+0x66d&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8af1d74 80534c02 80000360 00000000 823c68b8 nt!IopLoadUnloadDriver+0x45&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8af1dac 805c6160 b29accf4 00000000 00000000 nt!ExpWorkerThread+0x100&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;f8af1ddc 80541dd2 80534b02 00000001 00000000 nt!PspSystemThreadStartup+0x34&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;FAULTING_SOURCE_CODE:&amp;nbsp; &lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3: NTSTATUS DriverEntry(IN PDRIVER_OBJECT&amp;nbsp; pDriverObject,&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4:&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; IN PUNICODE_STRING pusRegistryPath)&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 5: {&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 6:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //-f--&amp;gt; Diga ol&amp;#225; &amp;#224; BSOD e v&amp;#225; se acostumando com ela...&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 7:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; *(PVOID*)0x00000000 = 0;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 8:&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 9:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //-f--&amp;gt; N&amp;#227;o vamos viver para ver isso.&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return STATUS_SUCCESS;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 11: }&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;SYMBOL_STACK_INDEX:&amp;nbsp; 0&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;SYMBOL_NAME:&amp;nbsp; Useless!DriverEntry+5&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;FOLLOWUP_NAME:&amp;nbsp; MachineOwner&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;MODULE_NAME: Useless&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;IMAGE_NAME:&amp;nbsp; Useless.sys&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;DEBUG_FLR_IMAGE_TIMESTAMP:&amp;nbsp; 4a3844ef&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;STACK_COMMAND:&amp;nbsp; .cxr 0xfffffffff8af18b0 ; kb&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;FAILURE_BUCKET_ID:&amp;nbsp; 0x7E_Useless!DriverEntry+5&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;BUCKET_ID:&amp;nbsp; 0x7E_Useless!DriverEntry+5&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;Followup: MachineOwner&lt;/pre&gt;&lt;pre style="margin: 0px;"&gt;---------&lt;/pre&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Se a máquina que está abrindo o arquivo de dump for a máquina de desenvolvimento do seu driver, o &lt;em&gt;Windbg&lt;/em&gt; será capaz de automagicamente achar os fontes do seu driver e apontar a causa da falha com grandes detalhes. Então certifique-se que seu gerente não esteja por perto nesse momento. Isso já é muito mais informação do que você poderia obter simplesmente olhando para a tela azul do computador. Neste exemplo utilizei o driver de exemplo do post &lt;a href="http://www.driverentry.com.br/blog/2006/09/getting-started.html"&gt;Getting Started&lt;/a&gt; para reproduzir a tela azul. Mas não se preocupe com isso, mesmo sendo um programador novato em drivers, uma das primeiras coisas que você aprenderá é como gerar telas azuis.&lt;/p&gt;&lt;p&gt;Mais uma vez espero ter ajudado.&lt;br&gt;Have fun!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-1172842590293909203?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2009/06/levando-tela-azul-pra-casa.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>2</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-3962993921506189504</guid><pubDate>Fri, 01 May 2009 13:23:00 +0000</pubDate><atom:updated>2009-05-03T23:18:44.654-03:00</atom:updated><title>Enumerando dispositivos</title><description>&lt;p&gt;Dentre minhas tarefas atuais, estava a de ler as amostras de um giroscópio utilizando uma porta serial e gerar um arquivo com elas. Este arquivo seria lido por um driver semelhante ao demonstrado &lt;a href="http://www.driverentry.com.br/blog/2009/04/lendo-arquivos.html"&gt;no post anterior a este&lt;/a&gt;. No &lt;a href="http://pt.wikipedia.org/wiki/Datasheet"&gt;datasheet&lt;/a&gt; do giroscópio há uma descrição do protocolo, sem falar do exemplo feito em Visual Basic que existe no &lt;a href="http://www.sparkfun.com/"&gt;site do fabricante&lt;/a&gt;, que demonstra a &lt;a href="http://pt.wikipedia.org/wiki/Acelera%C3%A7%C3%A3o_angular"&gt;aceleração angular&lt;/a&gt; em cada eixo bem como as &lt;a href="http://pt.wikipedia.org/wiki/Acelera%C3%A7%C3%A3o"&gt;acelerações lineares&lt;/a&gt; deles como mostra abaixo.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/IMU6DOF.png"/&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Toda a interface de configuração e obtenção dos dados do giroscópio é realizada através da serial. Mesmo sem uma aplicação dedicada pode-se utilizar um programa qualquer, como o HyperTerminal, para ter acesso aos menus oferecidos pelo dispositivo. Utilizando uma porta serial, qualquer kit de microcontrolador que não tenha display e nem mesmo um teclado pode oferecer uma interface bem amigável.&lt;/p&gt;&lt;p&gt;Escrever protocolos seriais não é algo que me assuste. Duro é ter que lembrar como programar uma aplicação &lt;span style="font-style:italic;"&gt;User-Mode&lt;/span&gt; que tenha janelas, botões e outros controles, mas nada que um pouco de &lt;a href="http://msdn.microsoft.com/en-us/library/ms632587(VS.85).aspx"&gt;MSDN&lt;/a&gt; não resolva. Programadores de drivers normalmente testam tudo que podem utilizando aplicações console, então desenvolvi um pequeno software de terminal para lidar diretamente com o dispositivo. O fonte dele segue abaixo e fica de brinde para quem precisar brincar com portas seriais qualquer dia.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&amp;nbsp;
&lt;span class="comment"&gt;/****
***     main
**
**      E lá vamos nós...
*/&lt;/span&gt;
&lt;span class="keyword"&gt;int&lt;/span&gt; _tmain(&lt;span class="keyword"&gt;int&lt;/span&gt; argc, _TCHAR* argv[])
{
    DWORD           dwBytes,
                    dwError = ERROR_SUCCESS;
    HANDLE          hCom;
    ULONG           i;
    UCHAR           ucByteIn[512], ucByteOut;
    DCB             dcb;
    COMMTIMEOUTS    CommTimeouts;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui abrimos aporta serial desejada
&lt;/span&gt;    hCom = CreateFile(L&lt;span class="literal"&gt;"COM3"&lt;/span&gt;,
                      GENERIC_READ | GENERIC_WRITE,
                      0,
                      NULL,
                      OPEN_EXISTING,
                      0,
                      NULL);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Verifica se obtivemos sucesso
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (hCom == INVALID_HANDLE_VALUE) 
    {
        &lt;span class="comment"&gt;//-f--&gt; Oops!
&lt;/span&gt;        dwError = GetLastError();
        printf(&lt;span class="literal"&gt;"CreateFile failed with error %d.\n"&lt;/span&gt;,
                dwError);
        &lt;span class="keyword"&gt;return&lt;/span&gt; dwError;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Existem 7 kilos de configurações da porta serial.
&lt;/span&gt;    &lt;span class="comment"&gt;//      Ao invés de configirar cada uma delas, vamos apenas
&lt;/span&gt;    &lt;span class="comment"&gt;//      obter as configurações padrão do sistema e modificar
&lt;/span&gt;    &lt;span class="comment"&gt;//      apenas as que nos interessa.
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (!GetCommState(hCom, &amp;amp;dcb))
    {
        &lt;span class="comment"&gt;//-f--&gt; Oops!
&lt;/span&gt;        dwError = GetLastError();
        printf (&lt;span class="literal"&gt;"GetCommState failed with error %d.\n"&lt;/span&gt;,
                dwError);
        CloseHandle(hCom);
        &lt;span class="keyword"&gt;return&lt;/span&gt; dwError;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui modificamos as configurações para setar as
&lt;/span&gt;    &lt;span class="comment"&gt;//      configurações exigidas pelo giroscópio.
&lt;/span&gt;    &lt;span class="comment"&gt;//      57600, 8 N 1 (sem controle de fluxo)
&lt;/span&gt;    dcb.DCBlength = &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(DCB);
    dcb.BaudRate = CBR_57600;
    dcb.ByteSize = 8;
    dcb.Parity = NOPARITY;
    dcb.StopBits = ONESTOPBIT;
    dcb.fDtrControl = DTR_CONTROL_DISABLE;
    dcb.fRtsControl = RTS_CONTROL_DISABLE;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui aplicamos as configurações que modificamos
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (!SetCommState(hCom, &amp;amp;dcb))
    {
        &lt;span class="comment"&gt;//-f--&gt; Oops!
&lt;/span&gt;        dwError = GetLastError();
        printf (&lt;span class="literal"&gt;"SetCommState failed with error %d.\n"&lt;/span&gt;,
                dwError);
        CloseHandle(hCom);
        &lt;span class="keyword"&gt;return&lt;/span&gt; dwError;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Vou configurar os timeouts de forma que uma
&lt;/span&gt;    &lt;span class="comment"&gt;//      leitura seja completada depois de 100ms mesmo
&lt;/span&gt;    &lt;span class="comment"&gt;//      nenhum caracter seja recebido pela aplicação.
&lt;/span&gt;    &lt;span class="comment"&gt;//      Isso evita da aplicação ficar travada em ReadFile
&lt;/span&gt;    &lt;span class="comment"&gt;//      até que um byte seja recebido pela porta serial
&lt;/span&gt;    CommTimeouts.ReadIntervalTimeout = 0;
    CommTimeouts.ReadTotalTimeoutMultiplier = 0;
    CommTimeouts.ReadTotalTimeoutConstant = 100;
    CommTimeouts.WriteTotalTimeoutConstant = 0;
    CommTimeouts.WriteTotalTimeoutMultiplier = 0;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aplica as configurações de timeouts
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (!SetCommTimeouts(hCom, &amp;amp;CommTimeouts))
    {
        &lt;span class="comment"&gt;//-f--&gt; Oops!
&lt;/span&gt;        dwError = GetLastError();
        printf (&lt;span class="literal"&gt;"SetCommTimeouts failed with error %d.\n"&lt;/span&gt;,
                dwError);
        CloseHandle(hCom);
        &lt;span class="keyword"&gt;return&lt;/span&gt; dwError;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui começa o loop infinito que vai executar
&lt;/span&gt;    &lt;span class="comment"&gt;//      para todo o sempre até o fim dos dias.
&lt;/span&gt;    &lt;span class="comment"&gt;//      Na verdade, se você teclar [ESC] ele termina.
&lt;/span&gt;    &lt;span class="keyword"&gt;while&lt;/span&gt;(1)
    {
        &lt;span class="comment"&gt;//-f--&gt; Verifica se há um byte a ser recebido pela
&lt;/span&gt;        &lt;span class="comment"&gt;//      aplicação no buffer de teclado.
&lt;/span&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt; (_kbhit())
        {
            &lt;span class="comment"&gt;//-f--&gt; Obtém a tecla
&lt;/span&gt;            ucByteOut = _getch();
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Verifica se a tecla recebida é [ESC]
&lt;/span&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; (ucByteOut == 27)
                &lt;span class="keyword"&gt;break&lt;/span&gt;;
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Envia o byte recebido para a porta serial
&lt;/span&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; (!WriteFile(hCom,
                           &amp;amp;ucByteOut,
                           1,
                           &amp;amp;dwBytes,
                           NULL))
            {
                &lt;span class="comment"&gt;//--&gt; Oops!
&lt;/span&gt;                dwError = GetLastError();
                &lt;span class="keyword"&gt;break&lt;/span&gt;;
            }
        }
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Verifica se algum byte foi recebido pela
&lt;/span&gt;        &lt;span class="comment"&gt;//      porta serial. Observe que neste loop não
&lt;/span&gt;        &lt;span class="comment"&gt;//      existe o típico Sleep() para não levar a CPU
&lt;/span&gt;        &lt;span class="comment"&gt;//      a 100%. Esta espera é realizada dentro da
&lt;/span&gt;        &lt;span class="comment"&gt;//      chamada à ReadFile(). A função espera por um
&lt;/span&gt;        &lt;span class="comment"&gt;//      byte por até 100ms, conforme configurado nos
&lt;/span&gt;        &lt;span class="comment"&gt;//      timeouts.
&lt;/span&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt; (!ReadFile(hCom,
                      ucByteIn,
                      &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(ucByteIn),
                      &amp;amp;dwBytes,
                      NULL))
        {
            &lt;span class="comment"&gt;//-f--&gt; Oops!
&lt;/span&gt;            dwError = GetLastError();
            &lt;span class="keyword"&gt;break&lt;/span&gt;;
        }
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Imprime na tela a sequência de caracteres recebida
&lt;/span&gt;        &lt;span class="keyword"&gt;for&lt;/span&gt; (i=0; i&amp;lt;dwBytes; i++)
        {
            &lt;span class="keyword"&gt;if&lt;/span&gt; (ucByteIn[i] == 0x0d)
                puts(&lt;span class="literal"&gt;""&lt;/span&gt;);
            &lt;span class="keyword"&gt;else&lt;/span&gt;
                printf(&lt;span class="literal"&gt;"%c"&lt;/span&gt;, ucByteIn[i]);
        }
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Bom se chegamos neste ponto é porque chegamos
&lt;/span&gt;    &lt;span class="comment"&gt;//      ao fim dos dias ou a tecle ESC foi pressionada.
&lt;/span&gt;    &lt;span class="comment"&gt;//      Vamos fechar a porta serial e sair correndo.
&lt;/span&gt;    CloseHandle(hCom);
    &lt;span class="keyword"&gt;return&lt;/span&gt; dwError;
}&lt;/pre&gt;&lt;p&gt;Abrir uma porta serial é a parte mais simples desta história, o problema é decidir qual porta abrir. Como qualquer programa decente, deveria haver um combo list com as portas seriais disponíveis no computador, onde o usuário escolheria uma e pronto. A partir daí é só obter a porta selecionada pelo usuário e montar uma chamada para a rotina &lt;a href="http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx"&gt;CreateFile()&lt;/a&gt; como ilustrado no cógido acima.&lt;/p&gt;&lt;p&gt;Tudo bem, arrastei o controle para a janela que eu estava programando e agora é só preenchê-lo. Então pensei: &lt;span style="font-style:italic;"&gt;"Deve haver alguma função do tipo EnumerateCommPorts() na API"&lt;/span&gt;, mas não foi o que a &lt;a href="http://msdn.microsoft.com/en-us/library/aa363194(VS.85).aspx"&gt;página da referência&lt;/a&gt; me mostrava. - Como assim não tem? O &lt;a href="http://www.google.com"&gt;Google&lt;/a&gt; deve saber algo a respeito. - Acabei descobrindo que esta é uma dúvida bem comum por aí. Uns resolvem este problema fazendo um loop que tenta abrir as portas seriais em sequência (COM1, COM2,... ), outros utilizam a função &lt;a href="http://msdn.microsoft.com/en-us/library/aa365461(VS.85).aspx"&gt;QueryDosDevice()&lt;/a&gt; e filtram os &lt;span style="font-style:italic;"&gt;Symbolic Links&lt;/span&gt; que iniciam com "COM(n)", mas o método que vou mostrar aqui é capaz de enumerar qualquer tipo de interface utilizando a &lt;a href="http://msdn.microsoft.com/en-us/library/ms791318.aspx"&gt;SetupAPI&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Setup quem?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;A SetuAPI é uma parte do &lt;span style="font-weight:bold;"&gt;Plug-And-Play&lt;/span&gt; que fornece serviços às aplicações &lt;span style="font-style:italic;"&gt;User-Mode&lt;/span&gt;. O &lt;span style="font-style:italic;"&gt;Plug-And-Play&lt;/span&gt; tem como um dos seus objetivos, unificar a configuração, uso e a enumeração de dispositivos e serviços semelhantes. Desta forma todos os fabricantes de placas que oferecem serviços de porta serial podem ter seus dispositivos configurados de uma única maneira. Dispositivos que oferecem serviços de porta serial devem implementar uma interface pré-definida, ou seja, devem se mostrar dispostos a receber &lt;span style="font-weight:bold;"&gt;IOCTLs&lt;/span&gt; e responder a eles de maneira prevista na &lt;a href="http://msdn.microsoft.com/en-us/library/ms800933.aspx"&gt;documentação&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;O driver que desejar criar devices que implementem a interface de porta serial deverá declarar isso através da chamada à rotina &lt;a href="http://msdn.microsoft.com/en-us/library/aa490533.aspx"&gt;IoRegisterDeviceInterface()&lt;/a&gt;. Aqui um device é associado à uma classe de interface de dispositivo, a qual é identificada por um &lt;span style="font-weight:bold;"&gt;GUID&lt;/span&gt;. Existem diversas classes de interfaces de dispositivos pré-definidas no sistema, como listado &lt;a href="http://msdn.microsoft.com/en-us/library/bb663138.aspx"&gt;aqui&lt;/a&gt;, e a &lt;a href="http://msdn.microsoft.com/en-us/library/bb663174.aspx"&gt;classe de portas seriais&lt;/a&gt; é uma delas. A partir daí, seu device será enumerado por rotinas do &lt;span style="font-style:italic;"&gt;Plug-and-Play&lt;/span&gt; como provedor de uma determinada interface.&lt;/p&gt;&lt;p&gt;Então tá. O que temos que fazer é utilizar estas funções de enumeração de interfaces para descobrir quais os dispositivos que implementam a interface de porta serial. A &lt;span style="font-style:italic;"&gt;SetupAPI&lt;/span&gt; vai nos ajudar com essa tarefa. Mas antes de darmos uma olhada no fonte, vamos resolver uma coisinha. Tenho visto diferentes maneiras de usar &lt;span style="font-weight:bold;"&gt;GUID_DEVINTERFACE_COMPORT&lt;/span&gt;, uns incluem o header de &lt;span style="font-weight:bold;"&gt;Kernel-Mode&lt;/span&gt; &lt;span style="font-style:italic;"&gt;Ntddser.h&lt;/span&gt;, outros definem o &lt;span style="font-style:italic;"&gt;GUID&lt;/span&gt; na unha, mas qual seria o jeito correto?&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Definindo o GUID de interface&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Tudo começa com a chamada à rotina &lt;a href="http://msdn.microsoft.com/en-us/library/ms792959.aspx"&gt;SetupDiGetClassDevs()&lt;/a&gt; que vai reunir informações sobre o grupo de dispositivos que correspondem aos critérios de busca adotados nos parâmetros. Vamos querer dispositivos que implementem a interface identificada por GUID_DEVINTERFACE_COMPORT, mas se simplesmente fizermos a chamada como mostra abaixo obteremos o erro exibido em seguida.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;        hDevInfoSet = SetupDiGetClassDevs(&amp;amp;GUID_DEVINTERFACE_COMPORT,
                                          NULL,
                                          NULL,
                                          DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);&lt;/pre&gt;&lt;br&gt;&lt;pre class="code" id="codemain"&gt;1&gt;z:\sources\samples\enumserialport.obj : error LNK2001: unresolved external
 symbol _GUID_DEVINTERFACE_COMPORT&lt;/pre&gt;&lt;p&gt;Isso ocorre porque GUID_DEVINTERFACE_COMPORT está declarado em &lt;span style="font-style:italic;"&gt;WinIoCtl.h&lt;/span&gt;. Lembre-se que este header é indiretamente incluído por &lt;span style="font-style:italic;"&gt;Windows.h&lt;/span&gt; quando o símbolo WIN32_LEAN_AND_MEAN não é definido, como já comentei neste &lt;a href="http://www.driverentry.com.br/blog/2008/10/buffered-direct-ou-neither-em-ioctls.html"&gt;outro post&lt;/a&gt;. Mas mesmo incluindo este header ainda temos o mesmo problema. Vamos olhar isso um pouco mais de perto.&lt;/p&gt;&lt;p&gt;No header &lt;span style="font-style:italic;"&gt;WinIoCtl.h&lt;/span&gt; temos:&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;DEFINE_GUID(GUID_DEVINTERFACE_COMPORT, 0x86e0d1e0L, 0x8089, 0x11d0,
            0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73);&lt;/pre&gt;&lt;p&gt;Mas o que é DEFINE_GUID afinal?&lt;/p&gt;&lt;p&gt;Em GuidDef.h temos:&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="keyword"&gt;#ifdef&lt;/span&gt; INITGUID&lt;span class="keyword"&gt;
#define&lt;/span&gt; DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
        EXTERN_C &lt;span class="keyword"&gt;const&lt;/span&gt; GUID DECLSPEC_SELECTANY name \
                = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }&lt;span class="keyword"&gt;
#&lt;span class="keyword"&gt;else&lt;/span&gt;&lt;/span&gt;&lt;span class="keyword"&gt;
#define&lt;/span&gt; DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
    EXTERN_C &lt;span class="keyword"&gt;const&lt;/span&gt; GUID FAR name&lt;span class="keyword"&gt;
#endif&lt;/span&gt; &lt;span class="comment"&gt;// INITGUID&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;Então agora nos vem o sonoro "Aaaah táaa!". GUID_DEVINTERFACE_COMPORT é uma constante que é definida quando o símbolo INITGUID é definido, caso contrário, esta constante é apenas declarada. O símbolo INITGUID é definido no header &lt;span style="font-style:italic;"&gt;InitGuid.h&lt;/span&gt;. Assim, quando você quiser utilizar os GUIDs declarados com DEFINE_GUID, você terá que em um dos seus módulos incluir o header &lt;span style="font-style:italic;"&gt;Initguid.h&lt;/span&gt; antes de &lt;span style="font-style:italic;"&gt;Windows.h&lt;/span&gt;. Como nosso exemplo só tem um módulo, então fica fácil. Como de costume, todo código fonte está contido num exemplo disponível para download.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;//-f--&gt; Vamos ter que colocar estes includes na ordem certa
&lt;/span&gt;&lt;span class="comment"&gt;//      para que o GUID que identifica a interface
&lt;/span&gt;&lt;span class="comment"&gt;//      GUID_DEVINTERFACE_COMPORT seja definido.
&lt;/span&gt;&lt;span class="keyword"&gt;#include&lt;/span&gt; &amp;lt;InitGuid.h&gt;&lt;span class="keyword"&gt;
#include&lt;/span&gt; &amp;lt;Windows.h&gt;&lt;span class="keyword"&gt;
#include&lt;/span&gt; &amp;lt;SetupApi.h&gt;&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Enumerando interfaces&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;/****
***     EnumSerialInterfaces
**
**      Rotina que enumera dispositivos que
**      implementam a interface de porta serial
*/&lt;/span&gt;
&amp;nbsp;
DWORD EnumSerialInterfaces(&lt;span class="keyword"&gt;void&lt;/span&gt;)
{
    CHAR                        szFriendlyName[100];
    HDEVINFO                    hDevInfoSet = NULL;
    SP_DEVICE_INTERFACE_DATA    DevInterfaceData;
    SP_DEVINFO_DATA             DevInfoData;
    DWORD                       dwReturn,
                                dwInterfaceIndex = 0;
    &lt;span class="keyword"&gt;try&lt;/span&gt;
    {
        &lt;span class="comment"&gt;//-f--&gt; Reunindo informações sobre dispositivos que implementam
&lt;/span&gt;        &lt;span class="comment"&gt;//      a interface desejada que estejam presentes no
&lt;/span&gt;        &lt;span class="comment"&gt;//      momento em que esta rotina é chamada.
&lt;/span&gt;        hDevInfoSet = SetupDiGetClassDevs(&amp;amp;GUID_DEVINTERFACE_COMPORT,
                                          NULL,
                                          NULL,
                                          DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
&amp;nbsp;
        &lt;span class="keyword"&gt;if&lt;/span&gt; (hDevInfoSet == INVALID_HANDLE_VALUE)
            &lt;span class="keyword"&gt;throw&lt;/span&gt; GetLastError();
&amp;nbsp;
        DevInterfaceData.cbSize = &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(SP_DEVICE_INTERFACE_DATA);
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Agora enumera cada uma das interfaces
&lt;/span&gt;        &lt;span class="keyword"&gt;while&lt;/span&gt; (SetupDiEnumDeviceInterfaces(hDevInfoSet,
                                           0,
                                           &amp;amp;GUID_DEVINTERFACE_COMPORT,
                                           dwInterfaceIndex++,
                                           &amp;amp;DevInterfaceData))
        {
            DevInfoData.cbSize = &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(SP_DEVINFO_DATA);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Para cada uma das interfaces, obtemos o
&lt;/span&gt;            &lt;span class="comment"&gt;//      device que a implementa.
&lt;/span&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; (SetupDiGetDeviceInterfaceDetail(hDevInfoSet,
                                                &amp;amp;DevInterfaceData,
                                                NULL,
                                                0,
                                                NULL,
                                                &amp;amp;DevInfoData))
                &lt;span class="keyword"&gt;throw&lt;/span&gt; GetLastError();
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Agora apenas obtemos o nome camarada do dispositivo
&lt;/span&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; (!SetupDiGetDeviceRegistryProperty(hDevInfoSet,
                                                  &amp;amp;DevInfoData,
                                                  SPDRP_FRIENDLYNAME,
                                                  NULL,
                                                  (PBYTE)szFriendlyName,
                                                  &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(szFriendlyName),
                                                  NULL))
                &lt;span class="keyword"&gt;throw&lt;/span&gt; GetLastError();
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Printf neles...
&lt;/span&gt;            printf(&lt;span class="literal"&gt;"%d) %s\n"&lt;/span&gt;,
                   dwInterfaceIndex,
                   szFriendlyName);
        }
    }
    &lt;span class="keyword"&gt;catch&lt;/span&gt;(DWORD dwError)
    {
        &lt;span class="comment"&gt;//-f--&gt; Oops!

&lt;/span&gt;        printf(&lt;span class="literal"&gt;"Error %d on trying enumerate device interfaces.\n"&lt;/span&gt;,
               dwError);
&amp;nbsp;
        dwReturn = dwError;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Libera as informações obtidas
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (hDevInfoSet)
        SetupDiDestroyDeviceInfoList(hDevInfoSet);
&amp;nbsp;
    &lt;span class="keyword"&gt;return&lt;/span&gt; dwReturn;
}&lt;/pre&gt;&lt;p&gt;Com essa implementação, teremos a seguinte saída.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/FriendlyName.png"/&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Legal, mas não era bem isso...&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Muito bem. As portas foram enumeradas, mas como eu passaria uma string dessas para a função &lt;a href="http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx"&gt;CreateFile()&lt;/a&gt;? Terei que ficar interpretando essa string para pegar a parte "COM1" que está entre parênteses?&lt;/p&gt;&lt;p&gt;Na verdade existem meios de você obter o &lt;span style="font-style:italic;"&gt;Symbolic Link&lt;/span&gt; dos dispositivos também utilizando funções da &lt;span style="font-style:italic;"&gt;SetupAPI&lt;/span&gt;, mas eu imagino que o que você queria é o mesmo que eu quero. Preencher um &lt;span style="font-style:italic;"&gt;Combo Box&lt;/span&gt; com o nome simples das portas seriais, como qualquer programa normal.&lt;/p&gt;&lt;p&gt;O port name, que é a string "COMx" que estamos procurando, está escrito como um valor de registro na chave do dispotivo. Todo driver de porta serial tem que ter esse valor conforme mostra &lt;a href="http://msdn.microsoft.com/en-us/library/ms800601.aspx"&gt;esta página&lt;/a&gt;. Para obter a chave de registro referente ao dispositivo, utilizaremos uma outra rotina da &lt;span style="font-style:italic;"&gt;SetupAPI&lt;/span&gt;. O fonte abaixo vai enumerar as portas seriais da maneira que estamos querendo.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;/****
***     EnumSerialPorts
**
**      Rotina que enumera dispositivos que
**      implementam a interface de porta serial e
**      imprime um nome que não é camarada mas que
**      ainda servem para alguma coisa.
*/&lt;/span&gt;
&amp;nbsp;
DWORD EnumSerialPorts(&lt;span class="keyword"&gt;void&lt;/span&gt;)
{
    CHAR                        szPortName[10];
    HDEVINFO                    hDevInfoSet = NULL;
    SP_DEVICE_INTERFACE_DATA    DevInterfaceData;
    SP_DEVINFO_DATA             DevInfoData;
    DWORD                       dwReturn,
                                dwSize,
                                dwInterfaceIndex = 0;
    HKEY                        hKey;
&amp;nbsp;
    &lt;span class="keyword"&gt;try&lt;/span&gt;
    {
        &lt;span class="comment"&gt;//-f--&gt; Reunindo informações sobre dispositivos que implementam
&lt;/span&gt;        &lt;span class="comment"&gt;//      a interface desejada que estejam presentes no
&lt;/span&gt;        &lt;span class="comment"&gt;//      momento em que esta rotina é chamada.

&lt;/span&gt;        hDevInfoSet = SetupDiGetClassDevs(&amp;amp;GUID_DEVINTERFACE_COMPORT,
                                          NULL,
                                          NULL,
                                          DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
&amp;nbsp;
        &lt;span class="keyword"&gt;if&lt;/span&gt; (hDevInfoSet == INVALID_HANDLE_VALUE)
            &lt;span class="keyword"&gt;throw&lt;/span&gt; GetLastError();
&amp;nbsp;
        DevInterfaceData.cbSize = &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(SP_DEVICE_INTERFACE_DATA);
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Agora enumera cada uma das interfaces
&lt;/span&gt;        &lt;span class="keyword"&gt;while&lt;/span&gt; (SetupDiEnumDeviceInterfaces(hDevInfoSet,
                                           0,
                                           &amp;amp;GUID_DEVINTERFACE_COMPORT,
                                           dwInterfaceIndex++,
                                           &amp;amp;DevInterfaceData))
        {
            DevInfoData.cbSize = &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(SP_DEVINFO_DATA);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Para cada uma das interfaces, obtemos o
&lt;/span&gt;            &lt;span class="comment"&gt;//      device que a implementa.
&lt;/span&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; (SetupDiGetDeviceInterfaceDetail(hDevInfoSet,
                                                &amp;amp;DevInterfaceData,
                                                NULL,
                                                0,
                                                NULL,
                                                &amp;amp;DevInfoData))
                &lt;span class="keyword"&gt;throw&lt;/span&gt; GetLastError();
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Aqui obtemos a chave de registro do
&lt;/span&gt;            &lt;span class="comment"&gt;//      device que implementa a interface.
&lt;/span&gt;            hKey = SetupDiOpenDevRegKey(hDevInfoSet,
                                        &amp;amp;DevInfoData,
                                        DICS_FLAG_GLOBAL,
                                        0,
                                        DIREG_DEV,
                                        KEY_QUERY_VALUE);
&amp;nbsp;
            &lt;span class="keyword"&gt;if&lt;/span&gt; (hKey == INVALID_HANDLE_VALUE)
                &lt;span class="keyword"&gt;throw&lt;/span&gt; GetLastError();
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Aqui obtemos o valor PortName do Registry
&lt;/span&gt;            dwSize = &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(szPortName);
            dwReturn = RegQueryValueEx(hKey,
                                       &lt;span class="literal"&gt;"PortName"&lt;/span&gt;,
                                       NULL,
                                       NULL,
                                       (LPBYTE)szPortName,
                                       &amp;amp;dwSize);
            RegCloseKey(hKey);
&amp;nbsp;
            &lt;span class="keyword"&gt;if&lt;/span&gt; (dwReturn != ERROR_SUCCESS)
                &lt;span class="keyword"&gt;throw&lt;/span&gt; dwReturn;
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Printf neles...
&lt;/span&gt;            printf(&lt;span class="literal"&gt;"%d) %s\n"&lt;/span&gt;,
                   dwInterfaceIndex,
                   szPortName);
        }
    }
    &lt;span class="keyword"&gt;catch&lt;/span&gt;(DWORD dwError)
    {
        &lt;span class="comment"&gt;//-f--&gt; Oops!
&lt;/span&gt;        printf(&lt;span class="literal"&gt;"Error %d on trying enumerate device interfaces.\n"&lt;/span&gt;,
               dwError);
&amp;nbsp;
        dwReturn = dwError;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Libera as informações obtidas
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (hDevInfoSet)
        SetupDiDestroyDeviceInfoList(hDevInfoSet);
&amp;nbsp;
    &lt;span class="keyword"&gt;return&lt;/span&gt; dwReturn;
}&lt;/pre&gt;&lt;p&gt;Agora sim...&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/PortName.png" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Colocar isso num combo box já é assunto para um outro blog. Até que para um desenvolvedor de drivers, a janela abaixo não está tão ruim assim.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/GyroReader.png" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Até mais! ;-)&lt;/p&gt;&lt;p&gt;Download &lt;a href="http://www.driverentry.com.br/samples/EnumSerialPort.zip"&gt;EnumSerialPort.zip&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-3962993921506189504?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2009/05/enumerando-dispositivos.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>2</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-8107556137667664672</guid><pubDate>Wed, 01 Apr 2009 17:28:00 +0000</pubDate><atom:updated>2009-04-02T09:02:59.432-03:00</atom:updated><title>Lendo Arquivos</title><description>&lt;p&gt;Como vocês viram em meu último post, meu trabalho de graduação utilizará uma ferramenta chamada &lt;a href="http://en.wikipedia.org/wiki/LabVIEW"&gt;LabView&lt;/a&gt; para receber e tratar os dados de uma placa &lt;a href="http://en.wikipedia.org/wiki/Usb"&gt;USB&lt;/a&gt;. Estes dados serão coletados de um dispositivo chamado &lt;a href="http://pt.wikipedia.org/wiki/Girosc%C3%B3pio"&gt;giroscópio&lt;/a&gt; usando TTL 232, que é um &lt;a href="http://en.wikipedia.org/wiki/Rs232"&gt;RS 232&lt;/a&gt; com tensões de 0 e 5 volts. Mas como uma &lt;a href="http://www.caloni.com.br/blog/archives/provas-de-conceito-yes"&gt;prova de conceito&lt;/a&gt;, teríamos que fazer o driver simular a recepção de dados para enviar ao &lt;span style="font-style:italic;"&gt;LabView&lt;/span&gt;. Utilizamos um circuito que transforma TTL 232 em RS 232 apenas para permitir que os dados pudessem ser lidos de uma porta serial convencional. Então fiz um programinha idiota que grava em um arquivo tudo que recebe pela porta serial. Como o firmware ainda não estava nem começado, resolvi fazer um driver que lesse esse arquivo e repassasse os dados para a camada de aplicação. Perguntas sobre como manipular arquivos são especialmente frequentes. Muitos leitores gostariam de saber como criar, ler, escrever e até mesmo apagar arquivos em Kernel Mode. Talvez eu possa desapontá-los um pouco ao dizer que não é assim tão diferente de User Mode, mas já que estamos aqui sem fazer nada, por que não demonstrar?&lt;/p&gt;&lt;p&gt;Acredito que a maior diferença esteja no passo onde obtemos o handle para o arquivo. Vamos começar dando uma olhada na rotina &lt;a href="http://msdn.microsoft.com/en-us/library/ms804358.aspx"&gt;ZwCreateFile()&lt;/a&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;NTSTATUS  
  ZwCreateFile(
    OUT PHANDLE  FileHandle,
    IN ACCESS_MASK  DesiredAccess,
    IN POBJECT_ATTRIBUTES  ObjectAttributes,
    OUT PIO_STATUS_BLOCK  IoStatusBlock,
    IN PLARGE_INTEGER  AllocationSize  OPTIONAL,
    IN ULONG  FileAttributes,
    IN ULONG  ShareAccess,
    IN ULONG  CreateDisposition,
    IN ULONG  CreateOptions,
    IN PVOID  EaBuffer  OPTIONAL,
    IN ULONG  EaLength
    );&lt;/pre&gt;&lt;p&gt;A parte interessante desse passo é que a rotina não tem o clássico parâmetro &lt;span style="font-style:italic;"&gt;FileName &lt;/span&gt;que vimos na API equivalente &lt;a href="http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx"&gt;CreateFile()&lt;/a&gt; para User Mode. O nome do arquivo é descrito na estrutura &lt;a href="http://msdn.microsoft.com/en-us/library/aa491657.aspx"&gt;OBJECT_ATTRIBUTES&lt;/a&gt; descrita abaixo.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="keyword"&gt;typedef&lt;/span&gt; &lt;span class="keyword"&gt;struct&lt;/span&gt; _OBJECT_ATTRIBUTES {
    ULONG  Length;
    HANDLE  RootDirectory;
    PUNICODE_STRING  ObjectName;
    ULONG  Attributes;
    PVOID  SecurityDescriptor;
    PVOID  SecurityQualityOfService;
&amp;nbsp;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
&amp;nbsp;
&lt;span class="keyword"&gt;typedef&lt;/span&gt; CONST OBJECT_ATTRIBUTES *PCOBJECT_ATTRIBUTES;&lt;/pre&gt;&lt;p&gt;Para preencher esta estrutura utilizamos a macro &lt;a href="http://msdn.microsoft.com/en-us/library/ms802947.aspx"&gt;InitializeObjectAttributes()&lt;/a&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;VOID 
  InitializeObjectAttributes(
    OUT POBJECT_ATTRIBUTES  InitializedAttributes,
    IN PUNICODE_STRING  ObjectName,
    IN ULONG  Attributes,
    IN HANDLE  RootDirectory,
    IN PSECURITY_DESCRIPTOR  SecurityDescriptor
    );&lt;/pre&gt;&lt;p&gt;O caminho do arquivo é descrito no parâmetro &lt;span style="font-style:italic;"&gt;ObjectName&lt;/span&gt;, que é um ponteiro para uma &lt;a href="http://msdn.microsoft.com/en-us/library/aa380518(VS.85).aspx"&gt;UNICODE_STRING&lt;/a&gt;. Em Kernel, o caminho completo para um arquivo seria descrito como "\Device\HarddiskVolume0\Diretorio\Arquivo.ext" por exemplo. Isso acontece porque a parte "C:", que normalmente utilizamos no caminho de arquivo em User Mode, é um &lt;span style="font-style:italic;"&gt;Symbolic Link&lt;/span&gt;. &lt;span style="font-weight:bold;"&gt;&lt;span style="font-style:italic;"&gt;Symbolic quem?&lt;/span&gt;&lt;/span&gt; Um &lt;span style="font-style:italic;"&gt;Symbolic Link&lt;/span&gt; seria como um atalho para o nome em Kernel Mode. Aplicações em User Mode não podem abrir qualquer objeto do Kernel assim de cara. Então cada driver cria &lt;span style="font-style:italic;"&gt;Symbolic Links&lt;/span&gt; para os objetos que desejam torná-los disponíveis para User Mode. Quando uma aplicação quer abrir o arquivo "C:\Temp\Test.txt", o sub-sistema &lt;span style="font-style:italic;"&gt;Win32&lt;/span&gt; prefixa este caminho com "\??\", que é o diretório inicial desta busca, resultando em "\??\C:\Temp\Test.txt". Quando este nome chega ao &lt;a href="http://en.wikipedia.org/wiki/Object_Manager"&gt;Object Manager&lt;/a&gt;, o prefixo indica que a busca deve iniciar no diretório "\DosDevices", o mesmo que utilizamos na chamada à API &lt;a href="http://msdn.microsoft.com/en-us/library/aa490622.aspx"&gt;IoCreateSymbolicLink()&lt;/a&gt;. Enfim, pulando alguns detalhes para terminar esse post ainda nessa vida, o prefixo vai nos levar ao diretório "\GLOBAL??\". Depois que o prefixo "\??" foi processado, a próxima parte a ser processada é "C:". Utilizando a ferramenta &lt;a href="http://technet.microsoft.com/en-us/sysinternals/bb896657.aspx"&gt;WinObj&lt;/a&gt; da &lt;a href="http://technet.microsoft.com/en-us/sysinternals/default.aspx"&gt;Systernals&lt;/a&gt; ilustrada na figura abaixo, vemos que aqui em minha máquina "C:" será substituído por "\Device\HarddiskVolume3", que neste caso é o caminho para o device que receberá o restante da string a ser processada. Depois de realizada esta substituição, a string agora é "\Device\HarddiskVolume3\Temp\Test.txt". O &lt;span style="font-style:italic;"&gt;Object Manager&lt;/span&gt; agora recomeça a processar a string e encontra o device nela descrito.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/WinObj_DriveC.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Depois disso, sabendo que se trata de um device de volume de dados, o sistema consulta uma estrutura chamada &lt;span style="font-style:italic;"&gt;Volume Parameter Block&lt;/span&gt; (VPB). Ela cria um link que vai nos informar se o volume indicado foi montado por algum driver de &lt;a href="http://en.wikipedia.org/wiki/File_System"&gt;File System&lt;/a&gt;. No meu caso, o NTFS seria este driver. O device que ele criou faria o restante do tratamento da string para encontrar o arquivo desejado. Você não vai ter que percorrer todo esse caminho para abrir o arquivo. Basta colocar o prefixo "\??\" no caminho do arquivo que desejar abrir e todos os seus problemas &lt;a href="http://pt.wikipedia.org/wiki/Seu_Creysson"&gt;se acabaram-se&lt;/a&gt;. Se você quiser mais detalhes sobre as traduções de nomes que ocorrem durante a abertura de um arquivo, &lt;a href="http://www.osronline.com/article.cfm?id=381"&gt;este artigo&lt;/a&gt; da &lt;a href="http://www.osronline.com"&gt;OSR Online&lt;/a&gt; é ótimo. Este é o &lt;a href="http://msdn.microsoft.com/en-us/library/ms794736.aspx"&gt;link&lt;/a&gt; para a referência que fala sobre isso.&lt;/p&gt;&lt;p&gt;Depois de aberto, ler o arquivo fica fácil com a rotina &lt;a href="http://msdn.microsoft.com/en-us/library/ms804355.aspx"&gt;ZwReadFile()&lt;/a&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;NTSTATUS 
  ZwReadFile(
    IN HANDLE  FileHandle,
    IN HANDLE  Event  OPTIONAL,
    IN PIO_APC_ROUTINE  ApcRoutine  OPTIONAL,
    IN PVOID  ApcContext  OPTIONAL,
    OUT PIO_STATUS_BLOCK  IoStatusBlock,
    OUT PVOID  Buffer,
    IN ULONG  Length,
    IN PLARGE_INTEGER  ByteOffset  OPTIONAL,
    IN PULONG  Key  OPTIONAL
    );&lt;/pre&gt;&lt;p&gt;Os passos necessários para se abrir e ler um arquivo podem ser resumidos neste pequeno exemplo a seguir.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;/****
***     ReadTestFile
**
**      Rotina que demostra de maneira simples como abrir e
**      ler um arquivo.
*/&lt;/span&gt;
&amp;nbsp;
NTSTATUS ReadTestFile(PVOID     pBuffer,
                      ULONG     cbBuffer,
                      PULONG    pulBytesRead)
{
    UNICODE_STRING      usFileName;
    OBJECT_ATTRIBUTES   ObjAttributes;
    IO_STATUS_BLOCK     IoStatusBlock;
    HANDLE              hFile = NULL;
    NTSTATUS            nts = STATUS_SUCCESS;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Montamos o UNICODE_STRING contendo o caminho
&lt;/span&gt;    &lt;span class="comment"&gt;//      do arquivo que desejamos abrir
&lt;/span&gt;    RtlInitUnicodeString(&amp;amp;usFileName,
                         L&lt;span class="literal"&gt;"\\??\\C:\\Temp\\Test.txt"&lt;/span&gt;);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui a macro nos ajuda com a estrutura
&lt;/span&gt;    &lt;span class="comment"&gt;//      OBJECT_ATTRIBUTES
&lt;/span&gt;    InitializeObjectAttributes(&amp;amp;ObjAttributes,
                               &amp;amp;usFileName,
                               OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
                               NULL,
                               NULL);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui abrimos o arquivo.
&lt;/span&gt;    nts = ZwCreateFile(&amp;amp;hFile,
                       GENERIC_READ | SYNCHRONIZE,
                       &amp;amp;ObjAttributes,
                       &amp;amp;IoStatusBlock,
                       NULL,
                       FILE_ATTRIBUTE_NORMAL,
                       FILE_SHARE_READ,
                       FILE_OPEN,
                       FILE_SYNCHRONOUS_IO_NONALERT,
                       NULL,
                       0);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Retorna o erro em caso de falha.
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
        &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Uma simples leitura no arquivo.
&lt;/span&gt;    nts = ZwReadFile(hFile,
                     NULL,
                     NULL,
                     NULL,
                     &amp;amp;IoStatusBlock,
                     Buffer,
                     cbBuffer,
                     NULL,
                     NULL);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Em caso de falha, fecha o arquivo, retorna o
&lt;/span&gt;    &lt;span class="comment"&gt;//      erro e finge que não é com você.
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
    {
        ZwClose(hFile);
        &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui obtemos a quantidade de bytes lidos
&lt;/span&gt;    *pulBytesRead = IoStatusBlock.Information;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Fecha o handle do arquivo
&lt;/span&gt;    ZwClose(hFile);
    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;A prova de conceito&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Agora que todos nós sabemos como ler um arquivo, fica mais fácil de explicar como fiz um driver que simularia as leituras de um giroscópio apenas lendo o conteúdo de um arquivo. Durante a existência deste blog, já vimos &lt;a href="http://www.driverentry.com.br/blog/2006/09/getting-started.html"&gt;como criar um projetinho do zero&lt;/a&gt;, &lt;a href="http://www.driverentry.com.br/blog/2006/11/kernel-visual-studio-2005.html"&gt;como compilar drivers utilizando o Visual Studio&lt;/a&gt;, já vimos também &lt;a href="http://www.driverentry.com.br/blog/2007/02/legal-mas-o-que-uma-irp.html"&gt;o que é uma IRP&lt;/a&gt;, &lt;a href="http://www.driverentry.com.br/blog/2007/06/nos-queremos-exemplos.html"&gt;como oferecer serviços de leitura e escrita&lt;/a&gt;, &lt;a href="http://www.driverentry.com.br/blog/2007/08/usando-fileobject-e-fscontext.html"&gt;como utilizar o FsContext&lt;/a&gt; para manter o contexto entre diferentes operações, e como debug é parte do desenvolvimento, também vimos &lt;a href="http://www.driverentry.com.br/blog/2006/12/step-into-kernel-serial.html"&gt;como depurar drivers&lt;/a&gt; mesmo &lt;a href="http://www.driverentry.com.br/blog/2007/05/step-into-kernel-vmwarewindbg.html"&gt;em uma máquina virtual&lt;/a&gt;. Vamos utilizar toda essa tranqueirada para montar um driver que abra um arquivo, armazene seu handle em uma área de contexto, e que conforme realizamos leituras ao device criado e exportado por ele, este retorne os dados de um arquivo em disco. Isso vai servir direitinho para simular leituras contínuas que o &lt;span style="font-style:italic;"&gt;LabView&lt;/span&gt; fará ao meu driver USB.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Abrindo o Device e Arquivo&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;O driver receberá uma chamada de &lt;a href="http://msdn.microsoft.com/en-us/library/ms806162.aspx"&gt;IRP_MJ_CREATE&lt;/a&gt; quando um handle para o device for aberto. Vou aproveitar esse evento para já abrir o arquivo e guardar seu handle resultante no &lt;span style="font-style:italic;"&gt;FsContext&lt;/span&gt; do &lt;a href="http://msdn.microsoft.com/en-us/library/aa906961.aspx"&gt;FILE_OBJECT&lt;/a&gt; que receberei. Se você está boiando, dê uma olhada nos posts indicados anteriormente.&lt;/p&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;&lt;span style="font-style:italic;"&gt;E se o arquivo não existir?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Bom, caso tais eventos infelizes ocorram, vou retornar o erro pela própria IRP recebida. Assim, caso o arquivo não exista ou você não tenha permissão para abri-lo, o código de erro poderá ser verificado através da rotina &lt;a href="http://msdn.microsoft.com/en-us/library/ms679360(VS.85).aspx"&gt;GetLastError()&lt;/a&gt; caso obtivermos INVALID_HANDLE_VALUE como retorno da abertura do handle do device.&lt;/p&gt;&lt;p&gt;Dêem uma olhada como ficou a abertura do handle do device, que na mesma operação, abre o handle para o arquivo. Atenção, não misture as coisas. A aplicação vai obter o handle para o device, e através dele, fará leituras ao device. O device por sua vez utilizará o handle do arquivo para fazer leituras e retornar os dados à aplicação.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;/****
***     OnCreate
**
**      A aplicação está chamado CreateFile com o path
**      do nosso device.
*/&lt;/span&gt;
&amp;nbsp;
NTSTATUS OnCreate(PDEVICE_OBJECT    pDeviceObj,
                  PIRP              pIrp)
{
    UNICODE_STRING      usFileName;
    OBJECT_ATTRIBUTES   ObjAttributes;
    IO_STATUS_BLOCK     IoStatusBlock;
    PIO_STACK_LOCATION  pStack;
    NTSTATUS            nts = STATUS_SUCCESS;
    HANDLE              hFile = NULL;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Montamos o UNICODE_STRING contendo o caminho
&lt;/span&gt;    &lt;span class="comment"&gt;//      do arquivo que desejamos abrir
&lt;/span&gt;    RtlInitUnicodeString(&amp;amp;usFileName,
                         L&lt;span class="literal"&gt;"\\??\\C:\\Temp\\Test.txt"&lt;/span&gt;);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui a macro nos ajuda com a estrutura
&lt;/span&gt;    &lt;span class="comment"&gt;//      OBJECT_ATTRIBUTES
&lt;/span&gt;    InitializeObjectAttributes(&amp;amp;ObjAttributes,
                               &amp;amp;usFileName,
                               OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
                               NULL,
                               NULL);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui abrimos o arquivo. Como vamos repassar qualquer
&lt;/span&gt;    &lt;span class="comment"&gt;//      erro para a aplicação, então podemos utilizar a estrutura
&lt;/span&gt;    &lt;span class="comment"&gt;//      IO_STATUS_BLOCK da nossa IRP. Caso contrário poderiamos
&lt;/span&gt;    &lt;span class="comment"&gt;//      utilizar uma criada como variavel local.
&lt;/span&gt;    nts = ZwCreateFile(&amp;amp;hFile,
                       GENERIC_READ | SYNCHRONIZE,
                       &amp;amp;ObjAttributes,
                       &amp;amp;pIrp-&gt;IoStatus,
                       NULL,
                       FILE_ATTRIBUTE_NORMAL,
                       FILE_SHARE_READ,
                       FILE_OPEN,
                       FILE_SYNCHRONOUS_IO_NONALERT,
                       NULL,
                       0);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Vamos guardar o handle do arquivo em nossa área de
&lt;/span&gt;    &lt;span class="comment"&gt;//      contexto. Isso permite que várias aplicações de teste
&lt;/span&gt;    &lt;span class="comment"&gt;//      possam ser executas ao mesmo tempo. Para isso teremos
&lt;/span&gt;    &lt;span class="comment"&gt;//      obter a Stack Location atual.
&lt;/span&gt;    pStack = IoGetCurrentIrpStackLocation(pIrp);
    pStack-&gt;FileObject-&gt;FsContext = (PVOID)hFile;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Agora é só ler o arquivo, mas vamos fazer isso na IRP
&lt;/span&gt;    &lt;span class="comment"&gt;//      de leitura, só pra...
&lt;/span&gt;    IoCompleteRequest(pIrp,
                      IO_NO_INCREMENT);
    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;Repare que a API &lt;span style="font-style:italic;"&gt;ZwCreateFile()&lt;/span&gt; pede um ponteiro para &lt;a href="http://msdn.microsoft.com/en-us/library/ms806097.aspx"&gt;IO_STATUS_BLOCK&lt;/a&gt;. Utilizei a mesma estrutura que está contida na IRP que recebemos. Assim eu não tenho que repassar o status de uma operação para a outra. A aplicação continua obtendo o handle para o device da mesma maneira como sempre foi feito, mas lembre-se que se houver uma falha nessa obtenção, o erro pode ter sido gerado por um problema ao abrir o handle do arquivo. Confira como a aplicação vai utilizar o driver.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;/****
***     main
**
**      Ponto de entrada da aplicação
**
*/&lt;/span&gt;
&amp;nbsp;
&lt;span class="keyword"&gt;int&lt;/span&gt; __cdecl main(&lt;span class="keyword"&gt;int&lt;/span&gt; argc,
                 &lt;span class="keyword"&gt;char&lt;/span&gt;* argv[])
{
    &lt;span class="keyword"&gt;char&lt;/span&gt;    szBuffer[4096];
    HANDLE  hDevice = NULL;
    DWORD   dwError = ERROR_SUCCESS,
            dwBytes,
            i;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Obtendo um handle para o device
&lt;/span&gt;    printf(&lt;span class="literal"&gt;"Opening the device \"\\\\.\\FileReader\"...\n"&lt;/span&gt;);
&amp;nbsp;
    hDevice = CreateFile(&lt;span class="literal"&gt;"\\\\.\\FileReader"&lt;/span&gt;,
                         GENERIC_ALL,
                         0,
                         NULL,
                         OPEN_EXISTING,
                         0,
                         NULL);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Verifica se o handle foi aberto.
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (hDevice == INVALID_HANDLE_VALUE)
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops!
&lt;/span&gt;        dwError = GetLastError();
        printf(&lt;span class="literal"&gt;"Error #%d opening device...\n"&lt;/span&gt;,
               dwError);
        &lt;span class="keyword"&gt;return&lt;/span&gt; dwError;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Realiza as leituras no device
&lt;/span&gt;    &lt;span class="keyword"&gt;while&lt;/span&gt; (ReadFile(hDevice,
                    szBuffer,
                    &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(szBuffer),
                    &amp;amp;dwBytes,
                    NULL))
    {
        &lt;span class="comment"&gt;//-f--&gt; Exibe dados na tela
&lt;/span&gt;        &lt;span class="comment"&gt;//      Tá tá, eu sei que não é a maneira mais eficiente do mundo.
&lt;/span&gt;        &lt;span class="keyword"&gt;for&lt;/span&gt; (i = 0; i &amp;lt; dwBytes; i++)
            printf(&lt;span class="literal"&gt;"%c"&lt;/span&gt;, szBuffer[i]);
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Qualquer falha da chamada à função ZwReadFile é repassada para
&lt;/span&gt;    &lt;span class="comment"&gt;//      a estrutura IO_STATUS_BLOCK da IRP. Por isso é que vemos este
&lt;/span&gt;    &lt;span class="comment"&gt;//      erro aqui.
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; ((dwError = GetLastError()) != ERROR_NO_MORE_ITEMS)
        printf(&lt;span class="literal"&gt;"\n\n Error #%d reading device...\n"&lt;/span&gt;);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Põe a casa em ordem.
&lt;/span&gt;    printf(&lt;span class="literal"&gt;"Closing device...\n"&lt;/span&gt;);
    CloseHandle(hDevice);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Fim de festa! Chega!
&lt;/span&gt;    &lt;span class="keyword"&gt;return&lt;/span&gt; dwError;
}&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Lendo o Device e o Arquivo&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Faremos a leitura do arquivo de forma similar à abertura. Vamos obter o handle do arquivo a partir do &lt;span style="font-style:italic;"&gt;FsContext&lt;/span&gt;. Este ponteiro foi originalmente disponibilizado para que o driver pudesse armazenar nele o endereço de uma estrutura definida pelo desenvolvedor. Este ponteiro sempre será o mesmo para todas as operações que utilizam o mesmo &lt;span style="font-style:italic;"&gt;FILE_OBJECT&lt;/span&gt; até que a operação de &lt;a href="http://msdn.microsoft.com/en-us/library/ms795859.aspx"&gt;IRP_MJ_CLOSE&lt;/a&gt; seja chamada. Como um handle é algo muito pequeno, podemos gravar seu valor ao invés de um ponteiro para uma estrutura alocada em memória que contenha o valor do handle.&lt;/p&gt;&lt;p&gt;Aqui também vamos utilizar a repassagem da estrutura &lt;span style="font-style:italic;"&gt;IO_STATUS_BLOCK&lt;/span&gt; para transferir o status da operação de leitura do arquivo para a aplicação.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;/****
***     OnRead
**
**      Rotina que realiza leitura do arquivo já
**      aberto.
*/&lt;/span&gt;
&amp;nbsp;
NTSTATUS OnRead(PDEVICE_OBJECT    pDeviceObj,
                PIRP              pIrp)
{
    NTSTATUS            nts = STATUS_SUCCESS;
    PIO_STACK_LOCATION  pStack;
    HANDLE              hFile;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Obtemos a stack location atual
&lt;/span&gt;    pStack = IoGetCurrentIrpStackLocation(pIrp);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui recuperamos o handle do arquivo.
&lt;/span&gt;    ASSERT(pStack-&gt;FileObject-&gt;FsContext != NULL);
    hFile = (HANDLE)pStack-&gt;FileObject-&gt;FsContext;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Uma simples leitura no arquivo.
&lt;/span&gt;    nts = ZwReadFile(hFile,
                     NULL,
                     NULL,
                     NULL,
                     &amp;amp;pIrp-&gt;IoStatus,
                     pIrp-&gt;AssociatedIrp.SystemBuffer,
                     pStack-&gt;Parameters.Read.Length,
                     NULL,
                     NULL);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; STATUS_END_OF_FILE não é repassado para a camada aplicação
&lt;/span&gt;    &lt;span class="comment"&gt;//      como uma falha de leitura, então vamos usar um erro mais
&lt;/span&gt;    &lt;span class="comment"&gt;//      fácil de detectar.
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (pIrp-&gt;IoStatus.Status == STATUS_END_OF_FILE)
        nts = pIrp-&gt;IoStatus.Status = STATUS_NO_MORE_ENTRIES;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Completa a IRP.
&lt;/span&gt;    IoCompleteRequest(pIrp,
                      IO_NO_INCREMENT);
    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;Se nosso driver retornar &lt;span style="font-style:italic;"&gt;STATUS_END_OF_FILE&lt;/span&gt; para a aplicação, a API &lt;a href="http://msdn.microsoft.com/en-us/library/aa365467(VS.85).aspx"&gt;ReadFile()&lt;/a&gt; não sinalizará uma falha, mas apenas informará que zero bytes foram lidos. Para facilitar a detecção do fim de arquivo lá na aplicação, vou retornar um código de erro diferente, assim a rotina &lt;span style="font-style:italic;"&gt;ReadFile()&lt;/span&gt; retornará FALSE e o loop de leitura será interrompido.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Fechando o handle do Device e do Arquivo&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Agora fica muito fácil. Vamos fechar o handle do arquivo quando o handle do device for fechado. Sem muitas novidades por aqui.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;/****
***     OnCleanup
**
**      O handle para nosso device foi fechado. Vamos
**      aproveitar e fechar o handle do arquivo também.
*/&lt;/span&gt;
&amp;nbsp;
NTSTATUS OnCleanup(PDEVICE_OBJECT    pDeviceObj,
                   PIRP              pIrp)
{
    PIO_STACK_LOCATION  pStack;
    HANDLE              hFile;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Obtemos a stack location atual
&lt;/span&gt;    pStack = IoGetCurrentIrpStackLocation(pIrp);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui recuperamos o handle do arquivo.
&lt;/span&gt;    ASSERT(pStack-&gt;FileObject-&gt;FsContext != NULL);
    hFile = (HANDLE)pStack-&gt;FileObject-&gt;FsContext;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Fecha o handle
&lt;/span&gt;    ZwClose(hFile);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Completa a IRP normalmente
&lt;/span&gt;    pIrp-&gt;IoStatus.Status = STATUS_SUCCESS;
    pIrp-&gt;IoStatus.Information = 0;
    IoCompleteRequest(pIrp,
                      IO_NO_INCREMENT);
&amp;nbsp;
    &lt;span class="keyword"&gt;return&lt;/span&gt; STATUS_SUCCESS;
}&lt;/pre&gt;&lt;p&gt;Se vocês já são leitores deste blog há algum tempo, verão que o restante do driver contém código elementar e que já foi comentado em outros posts. De qualquer forma, tanto o fonte do driver quando o fonte da aplicação de teste estão disponíveis para download. Caso vocês tenham alguma dúvida é só mandar um e-mail. Normalmente eu digo que vocês só precisarão torcer para eu saber a resposta, mas ultimamente vocês terão que torcer para eu também ter tempo de responder.&lt;/p&gt;&lt;p&gt;Como sempre, espero ter ajudado.&lt;br&gt;Have fun!&lt;/p&gt;&lt;p&gt;Download &lt;a href="http://www.driverentry.com.br/samples/FileReader.zip"&gt;FileReader.zip&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-8107556137667664672?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2009/04/lendo-arquivos.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>4</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-4372841544338494748</guid><pubDate>Fri, 20 Mar 2009 03:54:00 +0000</pubDate><atom:updated>2009-03-20T02:38:56.732-03:00</atom:updated><title>110% de CPU</title><description>&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/busy_person.jpg" border="0" /&gt;&lt;p&gt;Eu não queria postar algo que começasse com choradeiras dizendo que estou sem tempo, que tive que buscar minha tia no aeroporto e coisa e tal, mas estou vendo que não vai ter jeito. Até comecei a escrever uns posts para tirar dúvidas de leitores, mas aí eles começam a tomar tempo quando tenho que montar um exemplo, fazer uma figura, etc. Por fim, já tenho dois posts começados, mas que foram abandonados durante sua concepção por falta de tempo. Tenho recebido alguns e-mails do tipo &lt;span style="font-style:italic;"&gt;"Você ainda está vivo por aí?"&lt;/span&gt; de alguns leitores. As coisas estão realmente complicadas para mim esse ano, mas não vou deixar de postar. Só estou precisando arrumar um tempinho pra sentar. Já tentei escrever posts enquanto almoçava ou tomava banho, mas não deu muito certo. De fato eu consegui escrever um post enquanto dormia, mas quando acordei, por algum motivo, o post não estava publicado. &lt;span style="font-style:italic;"&gt;Nota: "Reclamar com o pessoal do blogger"&lt;/span&gt;. Mas enfm, na falta de tempo suficiente para escrever algo que os ajudem a desenvolver algo, neste post vou apenas relatar o que tenho feito. De repente vocês precisem de algo parecido e queiram me pedir alguma ajuda ou referência.&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;O Último Ano...&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Parece mentira, mas este é meu último ano da minha graduação em Engenharia da Computação, e por ser o último ano, três agravantes aparecem para disputar meu tempo (como se eu tivesse algum), sendo eles: Dependências, Trabalho de graduação (TG) e o Estágio.&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/UniversityCap_Diploma.jpg" border="0" /&gt;&lt;p&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;As Dependências...&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Para quem não lembra ou não sabe o que são dependências, são matérias as quais você não conseguiu ser aprovado no ano em que você estudou, mas consegue passar de ano mesmo assim, só que você terá que estudar estas matérias em separado, seja de sábado ou num horário em que você tenha livre (dá até vontade de rir...). Enfim, cheguei ao sexto ano com três matérias para fazer em meu horário livre. Assim, parte do meu sábado foi e será dedicado a essa atividade. Para fazer uma dependência, é necessário que a universidade agrupe alunos com a mesma matéria para fechar uma turma. Duas das três dependências já formaram turmas, mas ainda preciso rezar para que a terceira turma se forme. Dessa forma eu poderei me matricular. Caso contrário, terei que vir para universidade mais um ano para estudar a matéria restante. Isola...(Toc, toc, toc) vai dar tudo certo.&lt;/p&gt;&lt;p&gt;
&lt;span style="font-size:130%;"&gt;&lt;strong&gt;O Estágio...&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Em paralelo à essa aventura de estudar três matérias adicionais e ainda ter que passar em tudo, terei que fazer o estágio. Estágio é uma das matérias que preciso fazer para me formar. Terei que cumprir 192 horas de estágio para passar nesta matéria. Felizmente, meu curso pode ter o estágio tanto com ênfase eu software como em hardware, já que a Engenharia da Computação desenvolve as duas especialidades no estudante.&lt;/p&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Mas Fernando, se me lembro bem, você trabalha na IBM e seu estágio pode sair sem que você tenha que mover um único músculo. Basta alguém assinar seu estágio e pronto.&lt;/span&gt;&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/chip.jpg" border="0" /&gt;&lt;p&gt;Pois é, acho que tenho sérias tendências para querer me enrolar. É como meu amigo Heldai me disse uma vez: &lt;span style="font-style:italic;"&gt;"Ê seu Fêrnando, o senhor é como eu. Não pode ver uma corda que já quer enrolar no pescoço"&lt;/span&gt;. O fato é que eu gostaria imensamente de desenvolver meu lado mais fraco, e sabendo que já brinco de fazer telas azuis há um tempo razoável, meu objetivo seria arrumar um estágio em hardware. Mas como isso seria possível se já trabalho numa empresa de software durante a semana?&lt;/p&gt;&lt;p&gt;Bom, há um ano atrás, eu dei um curso de desenvolvimento de drivers para Windows em uma empresa que projeta hardware. Uma empresa dessa não se acha assim em qualquer esquina. A &lt;a href="http://www.commodity.com.br"&gt;Commodity&lt;/a&gt; é uma empresa que desenvolve um hardware que faz interface USB, cujo firmware é escrito em linguagem C e roda num chip da &lt;a href="http://www.freescale.com"&gt;Freescale&lt;/a&gt;, e como se não fosse o bastante, o hardware ainda faz compressão de audio utilizando um chip da &lt;a href="http://www.altera.com"&gt;Altera&lt;/a&gt; com &lt;a href="http://en.wikipedia.org/wiki/Field-programmable_gate_array"&gt;FPGA&lt;/a&gt;. Quem entende de eletrônica sabe que este seria um excelente lugar para fazer estágio e aprender muito. O problema é que eu não poderia fazer o estágio de domingo, que é meu único dia disponível, já que tenho aulas aos sábados e trabalho durante toda a semana. Já que não posso fazer o estágio de domingo, então posso trabalhar de domingo. Mais uma grande vantagem de se fazer &lt;a href="http://www.driverentry.com.br/blog/2008/07/agora-que-virei-home.html"&gt;Home Office&lt;/a&gt;. A solução foi me oferecer para fazer o estágio que seria cumprido apenas de segunda-feira. Estes dias seriam compensados trabalhando de domingo. Pedi autorização para meus gerentes do Brasil e dos Estados Unidos e pronto. Sabendo que um estagiário que aparece uma vez por semana mais atrapalha que ajuda, facilitei minha entrada na empresa solicitando um estágio não remunerado. Como eles já me conheciam, ficou fácil.&lt;/p&gt;&lt;p&gt;Apesar de eu não receber nem um centavo e ainda pagar minha gasolina e refeição, tenho certeza de que fiz a escolha certa. Meu estágio começou há duas semanas e tenho tido contato com coisas bem interessantes. Para dar uma acelerada no acúmulo de horas, trabalharei no estágio durantes todos os dias da semana enquanto eu estiver de férias da IBM. Juntando tudo, meu estágio terminará no início de agosto.&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;O TG...&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Bom, eu já não tinha os sábados e agora não tenho mais os domingos. Só preciso dar um jeito que acabar com qualquer fragmento de tempo livre que eu puder encontrar. Para isso, a universidade nos abençoou com o trabalho de graduação. Nós teremos que montar um projeto que utilize as especialidades que vimos durante o curso de engenharia da computação. Como meta pessoal, não importa o que o projeto faça, terá que haver um driver. Afinal de contas é a única coisa que sei fazer direito. Não posso perder essa oportunidade. Nosso projeto tem como objetivo estabilizar um helimodelo em vôo.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/RaptorTitan.jpg" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Ah tá, e onde o driver entra na história mesmo?&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Para resumir muito (mas muito mesmo), faremos leituras de um &lt;a href="http://pt.wikipedia.org/wiki/Girosc%C3%B3pio"&gt;giroscópio&lt;/a&gt; embarcado num &lt;a href="http://pt.wikipedia.org/wiki/Helimodelismo"&gt;helimodelo&lt;/a&gt; à distância utilizando o protocolo &lt;a href="http://pt.wikipedia.org/wiki/ZigBee"&gt;ZigBee&lt;/a&gt;. Tais leituras serão realizadas por um &lt;a href="http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3185"&gt;kit de desenvolvimento&lt;/a&gt; com &lt;a href="http://pt.wikipedia.org/wiki/Microcontrolador"&gt;microcontrolador&lt;/a&gt; que fará interface USB com um computador. O driver encaminhará as leituras de movimentos para uma ferramenta chamada &lt;a href="http://pt.wikipedia.org/wiki/LabVIEW"&gt;LabView&lt;/a&gt;. Em resposta à percepção dos movimentos, o sistema reagirá enviando comandos via USB para a placa. A mesma placa fará interface com o rádio controle do helimodelo a fim de corrigir o curso.&lt;/p&gt;&lt;p&gt;Em linhas gerais é isso que faremos. Eu poderia escrever muito à respeito de como estamos fazendo isso. Começamos a trabalhar no projeto no ano passado por que já sabíamos que não seria nada fácil. Aproveitei uma das minhas idas para os Estados Unidos para comprar os sensores e kits necessários. O importante é que o projeto está indo bem até aqui, mas ainda vamos apanhar muito. Estou escrevendo o &lt;a href="http://pt.wikipedia.org/wiki/Firmware"&gt;firmware&lt;/a&gt; do microcontrolador e obviamente o driver que fará interface com ele, mas nosso principal desafio será fechar a malha de controle e fazer o helimodelo ficar parado no ar.&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/at89stk_nl_11_05.jpg" border="0" /&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Parado? Você não tem vergonha de dizer que está fazendo tudo isso para fazer um helimodelo ficar parado?&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Nosso sonho dourado é definir dois pontos distintos e fazer o trajeto completo com decolagem, delsocamento e pouso, mas quem é helimodelista como eu sabe que fazer um helimodelo ficar parado é um excelente primeiro passo. Nosso orientador já nos disse que nosso projeto é o mais desafiador das engenharias e que se nós fizéssemos apenas a parte da leitura à distância já seria um bom trabalho de graduação.&lt;/p&gt;&lt;p&gt;Eu escreveria muito mais sobre o projeto, mas isso vai ficar para depois. Este post já está ficando longo para mais um &lt;span style="font-style:italic;"&gt;Off-Topic&lt;/span&gt;. Meus agradecimentos à prefeitura municipal de São Paulo por ter criado o &lt;a href="http://pt.wikipedia.org/wiki/Rod%C3%ADzio_de_ve%C3%ADculos_de_S%C3%A3o_Paulo"&gt;rodízio municipal de veículos&lt;/a&gt;, e por consequência, ter me trazido para a faculdade com duas horas de antecedência. Isso me deu a oportunidade de começar este post. Agora são 01:45 da manhã e ainda tenho que revisar e publicar esse post.&lt;/p&gt;&lt;p&gt;Vou tentar escrever mais. Talvêz minhas aventuras com o TG ou com o estágio me tragam assuntos interessantes e curtos o suficiente para publicar aqui.&lt;/p&gt;&lt;p&gt;Até mais mais. :-)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-4372841544338494748?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2009/03/110-de-cpu.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>2</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-7493945212540504610</guid><pubDate>Tue, 18 Nov 2008 17:20:00 +0000</pubDate><atom:updated>2008-11-19T10:33:20.653-02:00</atom:updated><title>Gerenciando paginação do driver</title><description>&lt;p&gt;Depois de tanto falar sobre memória virtual e paginação, recebi uma pergunta que coincidentemente tem tudo a ver com o assunto dos últimos posts. &lt;span style="font-style:italic;"&gt;"Para que servem os &lt;strong&gt;pragma alloc_text&lt;/strong&gt; que vemos nos exemplos do WDK?"&lt;/span&gt; (Thiago Cardoso, Recife-PE). Essa pergunta já deve ter passado pela cabeça de muitos que já deram uma olhada nos exemplos do WDK. Visto que todos os exemplos do WDK que conheço utilizam este pragma, até que demorou para alguém perguntar sobre isso. Mas enfim, vamos ao que interessa.&lt;/p&gt;&lt;p&gt;Como &lt;a href="http://www.driverentry.com.br/blog/2008/09/uma-pitada-de-memria-virtual.html"&gt;já vimos&lt;/a&gt;, páginas de memória podem estar tanto nos chips de RAM como em disco. Também já vimos que threads que estão em alta prioridade de execução não podem acessar dados que são pagináveis. Mas como saberiamos quais dados são pagináveis ou não?&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Controlando Paginação de Dados&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Quando alocamos memória dinamicamente, podemos escolher se a área de memória a ser alocada será paginável ou não. A função &lt;a href="http://msdn.microsoft.com/en-us/library/ms796846.aspx"&gt;ExAllocatePool&lt;/a&gt;, e suas irmãs (&lt;a href="http://msdn.microsoft.com/en-us/library/ms796989.aspx"&gt;ExAllocatePoolWithTag&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/ms797113.aspx"&gt;ExAllocatePoolWithQuota&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/ms797114.aspx"&gt;ExAllocatePoolWithQuotaTag&lt;/a&gt; e &lt;a href="http://msdn.microsoft.com/en-us/library/aa490023.aspx"&gt;ExAllocatePoolWithTagPriority&lt;/a&gt;), recebem um parâmetro do tipo POOL_TYPE que define se a memória a ser alocada será paginável ou não.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="keyword"&gt;typedef&lt;/span&gt; &lt;span class="keyword"&gt;enum&lt;/span&gt; _POOL_TYPE {
  &lt;font color="#ff0000"&gt;NonPagedPool,
  PagedPool,&lt;/font&gt;
  NonPagedPoolMustSucceed,
  DontUseThisType,
  NonPagedPoolCacheAligned,
  PagedPoolCacheAligned,
  NonPagedPoolCacheAlignedMustS
} POOL_TYPE;
&amp;nbsp;
&amp;nbsp;
PVOID 
  ExAllocatePool(
    IN POOL_TYPE  PoolType,
    IN SIZE_T  NumberOfBytes
    );&lt;/pre&gt;&lt;p&gt;Você terá que gerenciar quais alocações serão acessadas por diferentes prioridades de execução. Por exemplo: Uma determinada lista ligada é consultada apenas por funções que rodam somente em IRQL baixa, logo, todos os seus elementos podem ser alocados em memória paginável (PagedPool). Por outro lado, uma lista ligada que é consultada por funções que rodam em IRQLs altas, deve ter seus elementos alocados em memória não paginável (NonPagedPool).&lt;/p&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Legal Fernando, mas nem tudo é alocado dinamicamente. Como ficam as variáveis estáticas?&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Por padrão, todas as variáveis globais são não pagináveis. Isso é ruim se seu driver possui muitas variáveis globais, o que exigiria mais memória não paginável para que seu driver pudesse ser carregado, e como também já foi visto, memória não paginável deve ser poupada. Felizmente, podemos definir que um conjunto de variáveis globais possa ser paginável, desde que estas sejam apenas acessadas por threads em baixa IRQL.&lt;/p&gt;&lt;p&gt;Dentro de um módulo, seja uma aplicação, uma DLL ou mesmo um driver, o controle de paginação da memória é aplicado nas sessões que os compõem. Para que um grupo de variáveis seja definido em uma sessão paginável, podemos utilizar o &lt;a href="http://msdn.microsoft.com/en-us/library/thfhx4st.aspx"&gt;pragma data_seg&lt;/a&gt;. Porém, não é todo compilador que nos permite fazer isso. Para saber se o compilador que estamos utilizando suporta o uso deste pragma, contamos com os headers do WDK, que definem o símbolo &lt;span style="font-weight:bold;"&gt;ALLOC_DATA_PRAGMA&lt;/span&gt; quando o compilador oferece suporte a este recurso. Isso, obviamente, tem se tornado menos significante, já que o aconselhável é utilizar o compilador do próprio WDK, mas não custa nada prever que seu código possa ser compilado por algum outro compilador. Veja o exemplo abaixo que define tanto variáveis não pagináveis como pagináveis.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;//-f--&gt; Estas são variáveis definidas em uma sessão não paginável
&lt;/span&gt;PDEVICE_OBJECT  g_pControlDeviceObj;
PDRIVER_OBJECT  g_pDriverObj;
&amp;nbsp;
&lt;span class="comment"&gt;//-f--&gt; Aqui eu verifico se o compilador que estou utilizando
&lt;/span&gt;&lt;span class="comment"&gt;//      suporta o uso do #pragma data_seg.
&lt;/span&gt;&lt;span class="comment"&gt;//      Se sim, eu abro a sessão PAGEDATA que é uma sessão de
&lt;/span&gt;&lt;span class="comment"&gt;//      dados paginável.
&lt;/span&gt;&lt;span class="keyword"&gt;#ifdef&lt;/span&gt; ALLOC_DATA_PRAGMA&lt;span class="keyword"&gt;
    #pragma&lt;/span&gt; data_seg(&lt;span class="literal"&gt;"PAGEDATA"&lt;/span&gt;)&lt;span class="keyword"&gt;
#endif&lt;/span&gt;
&amp;nbsp;
&lt;span class="comment"&gt;//-f--&gt; Todas as variáveis declaradas aqui serão pagináveis.
&lt;/span&gt;&lt;span class="comment"&gt;//      Assim, apenas threads que rodam em baixo nível de
&lt;/span&gt;&lt;span class="comment"&gt;//      prioridades poderão acessar tais variáveis
&lt;/span&gt;&amp;nbsp;
&lt;span class="comment"&gt;//-f--&gt; Definimos um buffer gigante. Ainda bem que é paginável
&lt;/span&gt;UCHAR   g_Buffer[100000];
&amp;nbsp;
&lt;span class="comment"&gt;//-f--&gt; Aqui marcamos o fim da sessão paginável. As variáveis
&lt;/span&gt;&lt;span class="comment"&gt;//      definidas após este #pragma serão não pagináveis.
&lt;/span&gt;&lt;span class="keyword"&gt;#ifdef&lt;/span&gt; ALLOC_DATA_PRAGMA&lt;span class="keyword"&gt;
    #pragma&lt;/span&gt; data_seg()&lt;span class="keyword"&gt;
#endif&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Controlando Paginação de Código&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Memória é memória, seja para armazenar dados ou código. Podemos também controlar onde as funções que você escreve serão definidas. Assim, podemos colocar todas as funções que executam em baixa prioridade em sessões de código pagináveis. Aqui usaremos o &lt;span style="font-style:italic;"&gt;pragma&lt;/span&gt; que originou a dúvida do Thiago, o &lt;a href="http://msdn.microsoft.com/en-us/library/sw8ty6zf.aspx"&gt;pragma alloc_text&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Este &lt;span style="font-style:italic;"&gt;pragma&lt;/span&gt; tem duas limitações. A primeira delas é que o &lt;span style="font-style:italic;"&gt;pragma&lt;/span&gt; deve ser aplicado depois da declaração da função, mas antes da definição da mesma. A outra é que este &lt;span style="font-style:italic;"&gt;pragma&lt;/span&gt; não é aplicável em funções C++, ou seja, métodos de classes ou funções com sobrecargas não terão esse luxo de ser pagináveis. Se você é como eu, que prefere utilizar a tipagem forte do C++ em drivers, mesmo que você só escreva simples funções, você deverá utilizar o modificador &lt;span style="font-weight:bold;"&gt;extern "C"&lt;/span&gt; nas declarações das funções.&lt;/p&gt;&lt;p&gt;Esse &lt;span style="font-style:italic;"&gt;pragma&lt;/span&gt; não é obrigatoriamente suportado por todos os compiladores, e assim como o &lt;span style="font-style:italic;"&gt;data_seg&lt;/span&gt;, os headers do WDK definem o símbolo &lt;span style="font-weight:bold;"&gt;ALLOC_PRAGMA&lt;/span&gt; para sinalizar que o compilador utilizado suporta esse recurso. Segue mais um exemplo.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;//-f--&gt; Normalmente esta declaração é feita em um arquivo de
&lt;/span&gt;&lt;span class="comment"&gt;//      header. Observe que se estivermos compilando em C++,
&lt;/span&gt;&lt;span class="comment"&gt;//      teremos que usar o extern "C" para poder definir a
&lt;/span&gt;&lt;span class="comment"&gt;//      sessão onde as funções aqui declaradas serão definidas.
&lt;/span&gt;&lt;span class="keyword"&gt;#ifdef&lt;/span&gt; __cplusplus
&lt;span class="keyword"&gt;extern&lt;/span&gt; &lt;span class="literal"&gt;"C"&lt;/span&gt;
{&lt;span class="keyword"&gt;
#endif&lt;/span&gt;
    &lt;span class="comment"&gt;//-f--&gt; Declara uma
&lt;/span&gt;    ULONG SumOne(IN ULONG ulParam);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Declara outra
&lt;/span&gt;    ULONG SumTwo(IN ULONG ulParam);
&amp;nbsp;&lt;span class="keyword"&gt;
#ifdef&lt;/span&gt; __cplusplus
}&lt;span class="keyword"&gt;
#endif&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;
&lt;span class="comment"&gt;//-f--&gt; A parte a seguir fica no mesmo módulo onde a função é
&lt;/span&gt;&lt;span class="comment"&gt;//      definida, e deve ficar antes da definição da função.
&lt;/span&gt;&lt;span class="comment"&gt;//      Repare que aqui testamos se este pragma é suportado, e
&lt;/span&gt;&lt;span class="comment"&gt;//      se sim, cada função deve receber seu pragma alloc_text
&lt;/span&gt;&lt;span class="keyword"&gt;#ifdef&lt;/span&gt; ALLOC_PRAGMA&lt;span class="keyword"&gt;
    #pragma&lt;/span&gt; alloc_text(PAGE, SumOne);&lt;span class="keyword"&gt;
    #pragma&lt;/span&gt; alloc_text(PAGE, SumTwo);&lt;span class="keyword"&gt;
#endif&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;
&lt;span class="comment"&gt;//-f--&gt; Depois disso, podemos definir as funções normalmente
&lt;/span&gt;&amp;nbsp;
ULONG SumOne(IN ULONG ulParam);
{
    &lt;span class="comment"&gt;//--f-&gt; Função SumOne que foi escrita por alguém.
&lt;/span&gt;&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Esse comentário só tem graça em Inglês.
&lt;/span&gt;    &lt;span class="comment"&gt;//
&lt;/span&gt;    &lt;span class="comment"&gt;//      Function SumOne that has been written by someone
&lt;/span&gt;&amp;nbsp;
    PAGED_CODE();
&amp;nbsp;
    &lt;span class="keyword"&gt;return&lt;/span&gt; ulParam + 1;
}
&amp;nbsp;
ULONG SumTwo(IN ULONG ulParam);
{
    &lt;span class="comment"&gt;//-f--&gt; Função desgraçada. (que não tem graça nenhuma)
&lt;/span&gt;    PAGED_CODE();
&amp;nbsp;
    &lt;span class="keyword"&gt;return&lt;/span&gt; SumOne(SomeOne(ulParam));
}&lt;/pre&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Fernando, para quê servem estas macros &lt;a href="http://msdn.microsoft.com/en-us/library/aa489904.aspx"&gt;PAGED_CODE&lt;/a&gt; que você usou no exemplo?&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Bom, lá vem histórinha. Uma coisa desconfortável é que problemas com paginação de memória vão ocorrer apenas quando a página que você está acessando estiver em disco. Isso significa que você pode escrever um driver com problema, testá-lo, e se por "sorte", as páginas estiverem todas em RAM durante o teste, você não verá nenhum problema. Uma das coisas que ajuda bastante é essa macro PAGED_CODE. Em &lt;span style="font-weight:bold;"&gt;Checked Build&lt;/span&gt;, esta macro é traduzida para uma função que irá verificar se a prioridade corrente é baixa suficiente para executar código paginável. Caso não seja, uma exceção de &lt;span style="font-style:italic;"&gt;breakpoint&lt;/span&gt; será lançada. Espero que você esteja com o depurador atachado para ver isso acontecer. Caso contrário, não se preocupe, uma linda tela azul irá aparecer e você vai acabar conectando o depurador mais cedo ou mais tarde. Quando compilado em &lt;span style="font-weight:bold;"&gt;Free Build&lt;/span&gt;, essa macro é traduzida para nada, evitando perda de performance. Concluindo, isso evita de você chamar uma função paginável em IRQL alta e tudo funcionar por "sorte".&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;...
&amp;nbsp;&lt;span class="keyword"&gt;
#elif&lt;/span&gt; DBG
&amp;nbsp;&lt;span class="keyword"&gt;
#define&lt;/span&gt; PAGED_CODE() {                                                       \
    &lt;span class="keyword"&gt;if&lt;/span&gt; (KeGetCurrentIrql() &gt; APC_LEVEL) {                                    \
        KdPrint((&lt;span class="literal"&gt;"EX: Pageable code called at IRQL %d\n"&lt;/span&gt;, KeGetCurrentIrql())); \
        NT_ASSERT(FALSE);                                                    \
    }                                                                        \
}
&amp;nbsp;
...
&amp;nbsp;&lt;span class="keyword"&gt;
#&lt;span class="keyword"&gt;else&lt;/span&gt;&lt;/span&gt;
&amp;nbsp;&lt;span class="keyword"&gt;
#define&lt;/span&gt; PAGED_CODE()        NOP_FUNCTION;
&amp;nbsp;
...
&amp;nbsp;&lt;span class="keyword"&gt;
#endif&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;Uma excelente maneira de pegar problemas de paginação de cógido é utilizar o &lt;a href="http://msdn.microsoft.com/en-us/library/ms792872.aspx"&gt;Driver Verifier&lt;/a&gt; com a opção de &lt;span style="font-weight:bold;"&gt;Force IRQL Checking&lt;/span&gt; habilitada. Esta opção, além de verificar se você está chamando as funções da API nas prioridades corretas, também força a paginação de tudo que seja paginável em seu driver toda vez que a IRQL subir para DISPATCH_LEVEL ou superior. Isso acaba com aquela "sorte" de utilizar um recurso paginável em IRQL alta quando o recurso já estiver em RAM.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Sessão Descartável&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Além da sessão de código paginável &lt;span style="font-weight:bold;"&gt;PAGE&lt;/span&gt; que vimos, uma outra sessão também é vista com grande frequência nos exemplos do WDK. A sessão &lt;span style="font-weight:bold;"&gt;INIT&lt;/span&gt; é descartada quando a chamada à função &lt;span style="font-style:italic;"&gt;DriverEntry&lt;/span&gt; do seu driver retornar ao sistema, e se for o caso do seu driver, depois que qualquer função de reinicialização for terminada. Se você não sabe o que é uma função de reinicialização, então dê uma passada por &lt;a href="http://www.driverentry.com.br/blog/2007/06/comear-de-novo.html"&gt;este post&lt;/a&gt;. Assim, se você tem funções que são somente utilizadas durante a inicialização do seu driver (que neste caso significa: funções chamadas pela &lt;span style="font-style:italic;"&gt;DriverEntry&lt;/span&gt;, ou funções chamadas por funções que foram chamadas pela &lt;span style="font-style:italic;"&gt;DriverEntry&lt;/span&gt;, ou ainda funções chamadas por funções que foram chamadas por funções... Ah! Eu acho que você entendeu), vocé pode utilizar o mesmo procedimento para defini-las na sessão INIT da mesma maneira como foi feito anteriormente. Mas não custa deixar um exemplo.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;//-f--&gt; As funções aqui declaradas serão removidas da RAM quando
&lt;/span&gt;&lt;span class="comment"&gt;//      a chamada à função DriverEntry retornar. Não tente chamá-las
&lt;/span&gt;&lt;span class="comment"&gt;//      depois disso, porque estas já foram para o céu das funções
&lt;/span&gt;&lt;span class="comment"&gt;//      de inicialização.
&lt;/span&gt;&amp;nbsp;&lt;span class="keyword"&gt;
#ifdef&lt;/span&gt; ALLOC_PRAGMA&lt;span class="keyword"&gt;
    #pragma&lt;/span&gt; alloc_text(INIT, DriverEntry);&lt;span class="keyword"&gt;
    #pragma&lt;/span&gt; alloc_text(INIT, FunctionCalledByDriverEntry);&lt;span class="keyword"&gt;
    #pragma&lt;/span&gt; alloc_text(INIT, AnotherFunctionCalledByDriverEntry);
&amp;nbsp;&lt;span class="keyword"&gt;
    #pragma&lt;/span&gt; alloc_text(PAGE, SumOne);&lt;span class="keyword"&gt;
    #pragma&lt;/span&gt; alloc_text(PAGE, SumTwo);&lt;span class="keyword"&gt;
#endif&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;Isso é especialmente útil em &lt;span style="font-weight:bold;"&gt;Legacy Drivers&lt;/span&gt;, que realizam muitos passos na inicialização. Legacy drivers, além de  procurar o hardware que vão controlar, ainda precisam fazer a associação dos recursos disponíveis (portas, interrupções, canais de DMA e etc), e isso acaba consumido uma quantidade expressiva de código. Por outro lado, drivers de &lt;span style="font-weight:bold;"&gt;WDM&lt;/span&gt; recebem tudo mastigadinho do &lt;span style="font-weight:bold;"&gt;Plug-and-Play Manager&lt;/span&gt;. O hardware foi detectado e a associação de recursos já foi negociada. Coisa linda de Deus!&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Paginável ou não paginável, eis a questão&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Supondo que você tenha muitas funções que rodem em IRQL alta, e que por isso, devem estar em memória não paginável, isso faria com que uma grande quantidade de memória não paginável seja utilizada para manter tais funções, mesmo que ninguém esteja utilizando o driver. Também podemos definir nossas próprias sessões e assim torná-las pagináveis ou não pagináveis quando nos for conveniente. Isso permitiria que todas aquelas funções não pagináveis sejam pagináveis enquanto ninguém obtiver uma referência para nosso driver. Desta forma, quando recebermos um &lt;a href="http://msdn.microsoft.com/en-us/library/ms806162.aspx"&gt;IRP_MJ_CREATE&lt;/a&gt; ou quando programarmos o hardware para disparar interrupções, podemos dizer ao sistema que agora precisaremos tornar a sessão onde tais funções foram definidas como não paginável.&lt;/p&gt;&lt;p&gt;Primeiro de tudo, teremos que criar nossas sessões do coração, e faremos isso utilizando o &lt;span style="font-style:italic;"&gt;pragma alloc_text&lt;/span&gt; para definir sessões que devem ter seu nome no formato com PAGExxxx, onde xxxx seja um nome único no seu driver. Veja o exemplinho de desencargo de consciência.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;//-f--&gt; As funções aqui declaradas serão definidas em uma sessão
&lt;/span&gt;&lt;span class="comment"&gt;//      que pode ser não paginável quando nos for conveniente.
&lt;/span&gt;&amp;nbsp;&lt;span class="keyword"&gt;
#ifdef&lt;/span&gt; ALLOC_PRAGMA&lt;span class="keyword"&gt;
    #pragma&lt;/span&gt; alloc_text(PAGEABCD, FunctionOne);&lt;span class="keyword"&gt;
    #pragma&lt;/span&gt; alloc_text(PAGEABCD, FunctionTwo);&lt;span class="keyword"&gt;
#endif&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;
&lt;span class="comment"&gt;//-f--&gt; As funções aqui declaradas serão definidas em uma outra 
&lt;/span&gt;&lt;span class="comment"&gt;//      sessão que pode ser não paginável. Assim podemos definir
&lt;/span&gt;&lt;span class="comment"&gt;//      diferentes grupos de funções em diferentes sessões.
&lt;/span&gt;&amp;nbsp;&lt;span class="keyword"&gt;
#ifdef&lt;/span&gt; ALLOC_PRAGMA&lt;span class="keyword"&gt;
    #pragma&lt;/span&gt; alloc_text(PAGE1234, FunctionThree);&lt;span class="keyword"&gt;
    #pragma&lt;/span&gt; alloc_text(PAGE1234, FunctionFour);&lt;span class="keyword"&gt;
#endif&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;Agora que já definimos quais funções estarão definidas nessa sessão, podemos torná-la não paginável somente quando tais funções forem utilizadas. Para tal devemos utilizar a função &lt;a href="http://msdn.microsoft.com/en-us/library/ms802002.aspx"&gt;MmLockPagableCodeSection&lt;/a&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&amp;nbsp;
PVOID 
  MmLockPagableCodeSection(
    IN PVOID  AddressWithinSection
    );&lt;/pre&gt;&lt;p&gt;Para identificar qual sessão será marcada como não paginável, teremos que passar um endereço que esteja dentro da sessão. Um nome de função definida na sessão já resolve o problema, mas lembre-se que todas as funções dentro da mesma sessão serão marcadas como não paginável.&lt;/p&gt;&lt;p&gt;A função &lt;span style="font-style:italic;"&gt;MmLockPagableCodeSection&lt;/span&gt; nos retorna um endereço opaco que pode ser utilizado como parâmetro para a chamada à função &lt;a href="http://msdn.microsoft.com/en-us/library/ms801974.aspx"&gt;MmUnlockPagableImageSection&lt;/a&gt;, que marca a sessão novamente como paginável. Essa função é normalmente chamada antes do driver ser descarregado. Seguindo a linha do nosso exemplo, poderiamos chamar esta função ao receber um &lt;a href="http://msdn.microsoft.com/en-us/library/ms806166.aspx"&gt;IRP_MJ_CLOSE&lt;/a&gt;. O mesmo endereço retornado por &lt;span style="font-style:italic;"&gt;MmLockPagableCodeSection&lt;/span&gt; pode ser utilizado como parâmetro às chamadas à função &lt;a href="http://msdn.microsoft.com/en-us/library/ms802008.aspx"&gt;MmLockPagableSectionByHandle&lt;/a&gt; para novamente tornar uma sessão não paginável, o que é muito mais rápido que a chamada à &lt;span style="font-style:italic;"&gt;MmLockPagableCodeSection&lt;/span&gt;. Assim, devemos chamar &lt;span style="font-style:italic;"&gt;MmLockPagableCodeSection&lt;/span&gt; pelo menos uma vez para obter o ponteiro opaco, e depois disso, podemos chamar &lt;span style="font-style:italic;"&gt;MmLockPagableSectionByHandle&lt;/span&gt; e &lt;span style="font-style:italic;"&gt;MmUnlockPagableImageSection&lt;/span&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;VOID 
  MmLockPagableSectionByHandle(
    IN PVOID  ImageSectionHandle
    );
&amp;nbsp;
VOID 
  MmUnlockPagableImageSection(
    IN PVOID  ImageSectionHandle
    );&lt;/pre&gt;&lt;p&gt;O mesmo pode ser feito em sessões customizadas que definem dados, mas devemos utilizar a função &lt;a href="http://msdn.microsoft.com/en-us/library/ms801971.aspx"&gt;MmLockPagableDataSection&lt;/a&gt; para obter o ponteiro opaco que identifica a sessão.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Minha função pode ser paginável?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Fernando, se minha função é chamada em PASSIVE_LEVEL, então ela pode ser paginável?&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Não é bem assim. Sua função, mesmo sendo chamada em PASSIVE_LEVEL, pode conter intervalos de código que precisam ser não pagináveis. Se você chamar funções que elevam a IRQL, tais como &lt;a href="http://msdn.microsoft.com/en-us/library/ms801656.aspx"&gt;KeAcquireSpinLock&lt;/a&gt;, sua função não pode ser definida em sessão paginável.&lt;/p&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Mas Fernando, acompanhe o meu raciocínio. Se a função foi chamada e está em execução no momento, não é obvio que a página à qual ela está contida está em RAM?&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Você pode até não acreditar, mas uma função é composta por uma cadeia de bytes que podem estar nas fronteiras de páginas. Isso significa que o inicio da sua função pode estar no final de uma página, que de fato foi paginada para RAM quando a chamada foi feita, mas não sabemos onde uma nova página pode começar. Esta nova página pode estar em disco, e se for acessada em IRQL alta, a paginação causará uma tela azul. Veja o código abaixo para ter uma idéia do que estou falando e não esqueça de ler os comentários.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;/****
***     FunctionCalledAtPassiveLevel
**
**      Rotina que é chamada em PASSIVE_LEVEL,
**      mas tem elevação de IRQL durante a chamada.
*/&lt;/span&gt;
&amp;nbsp;
PLIST_ENTRY
FunctionCalledAtPassiveLevel(VOID)
{
    KIRQL       Irql;
    PLIST_ENTRY pEntry = NULL;
&amp;nbsp;
    PAGED_CODE();
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui nossa IRQL vai para as alturas, se o codigo que
&lt;/span&gt;    &lt;span class="comment"&gt;//      vier depois desta chamada estiver na próxima página
&lt;/span&gt;    &lt;span class="comment"&gt;//      de memória que estiver em disco, então (CABUM !!!)
&lt;/span&gt;    KeAquireSpinLock(&amp;amp;g_SpinLock, &amp;amp;Irql);
&amp;nbsp;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt;    -------======= Fronteira de página =======-------
&lt;/span&gt;&amp;nbsp;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; O código aqui é executado em DISPATCH_LEVEL, o que
&lt;/span&gt;    &lt;span class="comment"&gt;//      impede esta função de ser definida em uma sessão
&lt;/span&gt;    &lt;span class="comment"&gt;//      paginável.
&lt;/span&gt;&amp;nbsp;
    &lt;span class="keyword"&gt;if&lt;/span&gt; (!IsListEmpty(&amp;amp;g_NonPagedList))
    {
        pEntry = RemoveHeadList(&amp;amp;g_NonPagedList);
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Tudo bem que você acesse apenas dados não pagináveis,
&lt;/span&gt;    &lt;span class="comment"&gt;//      mas o código que você usa para tal acesso também precisa
&lt;/span&gt;    &lt;span class="comment"&gt;//      estar em memória não paginável. Afinal, esse código está
&lt;/span&gt;    &lt;span class="comment"&gt;//      sendo executado em IRQL alta e recuperar uma página de
&lt;/span&gt;    &lt;span class="comment"&gt;//      código em disco resultaria em coisas horríveis.
&lt;/span&gt;&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Voltamos a PASSIVE_LEVEL
&lt;/span&gt;    KeReleaseSpinLock(Irql);
&amp;nbsp;
    &lt;span class="keyword"&gt;return&lt;/span&gt; pEntry;
}&lt;/pre&gt;&lt;p&gt;Se você possui funções que são grandes, mas que contém intervalos pontuais com elevação de IRQL, então separe tais intervalos em funções isoladas que podem ser definidas em sessões não pagináveis, permitindo assim que você defina sua grande e complexa funcão em uma sessão paginável.&lt;/p&gt;&lt;p&gt;Ufa! Eu ainda poderia escrever mais alguns comentários sobre este assunto, mas se eu já estou cansado de escrever, imagino como vocês estão. O assunto é tratado com todos detalhes na &lt;a href="http://msdn.microsoft.com/en-us/library/aa489514.aspx"&gt;referência&lt;/a&gt;. Mas se tiverem qualquer dúvida a respeito, é só me enviar um e-mail e torcer pra eu saber responder.&lt;/p&gt;&lt;p&gt;Até mais!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-7493945212540504610?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2008/11/gerenciando-paginao-do-driver.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>3</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-2668040272947531224</guid><pubDate>Thu, 16 Oct 2008 14:07:00 +0000</pubDate><atom:updated>2008-10-27T09:46:44.676-02:00</atom:updated><title>Buffered, Direct ou Neither em IOCTLs</title><description>&lt;p&gt;Depois de &lt;a href="http://www.driverentry.com.br/blog/2008/09/uma-pitada-de-memria-virtual.html"&gt;uma pitada de memória virtual&lt;/a&gt; para entendermos os conceitos mais relevantes e darmos uma boa passeada nos &lt;a href="http://www.driverentry.com.br/blog/2008/10/buffered-direct-ou-neither.html"&gt;métodos de transferências de dados&lt;/a&gt; entre aplicação e driver, hoje vamos fechar essa trilogia falando sobre os métodos de transferências de dados em IOCTLs. Se você não sabe criar ou utilizar IOCTLs, este &lt;a href="http://www.driverentry.com.br/blog/2008/06/criando-e-usando-ioctls.html"&gt;outro post&lt;/a&gt; pode ajudar.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Flags não ajudam aqui&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;No post referente aos métodos de transferências de dados foi visto que definimos o método de transferência através de uma máscara de bits que está localizada no campo &lt;strong&gt;Flags&lt;/strong&gt; em um &lt;a href="http://msdn.microsoft.com/en-us/library/aa491677.aspx"&gt;DEVICE_OBJECT&lt;/a&gt;. O método escolhido aqui define como o I/O Manager vai manipular os dados nas operações de leitura (&lt;a href="http://msdn.microsoft.com/en-us/library/ms806158.aspx"&gt;IRP_MJ_READ&lt;/a&gt;) e escrita (&lt;a href="http://msdn.microsoft.com/en-us/library/ms806163.aspx"&gt;IRP_MJ_WRITE&lt;/a&gt;) do driver. O  método escolhido é aplicado para ambas as operações. Não podemos ter escritas utilizando um método enquanto as leituras são realizadas utilizando outro. No caso das IOCTLs, a história é diferente. O método de transferência é escolhido quando se define o &lt;strong&gt;control code&lt;/strong&gt; utilizando a macro &lt;strong&gt;CTL_CODE&lt;/strong&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&amp;nbsp;&lt;span class="keyword"&gt;
#define&lt;/span&gt; IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)&lt;/pre&gt;&lt;p&gt;Para se ter uma explicação mais detalhada sobre o uso desta macro, visite &lt;a href="http://www.driverentry.com.br/blog/2008/06/criando-e-usando-ioctls.html"&gt;este post&lt;/a&gt;, ou dê uma olhada na &lt;a href="http://msdn.microsoft.com/en-us/library/ms795909.aspx"&gt;referência&lt;/a&gt;. Aqui comentarei apenas sobre os métodos de transfêrencias de dados que é selecionado pelo parâmetro &lt;strong&gt;Method&lt;/strong&gt; desta macro. A utilização desta macro para definir IOCTLs é normalmente feita em um arquivo de header que será compartilhado entre a aplicação e o driver. A definição desta macro é obtida a partir do header &lt;strong&gt;Windows.h&lt;/strong&gt; para &lt;em&gt;User-Mode&lt;/em&gt; e &lt;strong&gt;Ntddk.h&lt;/strong&gt; para &lt;em&gt;Kernel-Mode&lt;/em&gt;. Abaixo segue a definição dos IOCTLs que implementaremos neste post.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&amp;nbsp;
&lt;span class="comment"&gt;//-f--&gt; Aqui definimos os IOCTLs de cópia utilizando os
&lt;/span&gt;&lt;span class="comment"&gt;//      deferentes métodos de transferência de dados entre
&lt;/span&gt;&lt;span class="comment"&gt;//      aplicação e driver.
&lt;/span&gt;&amp;nbsp;
&lt;span class="comment"&gt;//-f--&gt; Utilizando cópia de sistema
&lt;/span&gt;&lt;span class="keyword"&gt;#define&lt;/span&gt; IOCTL_COPY_BUFFERED CTL_CODE(FILE_DEVICE_UNKNOWN,   \
                                     0x800,                 \
                                     METHOD_BUFFERED,       \
                                     FILE_ANY_ACCESS)
&amp;nbsp;
&lt;span class="comment"&gt;//-f--&gt; Travando as páginas da aplicação
&lt;/span&gt;&lt;span class="keyword"&gt;#define&lt;/span&gt; IOCTL_COPY_DIRECT   CTL_CODE(FILE_DEVICE_UNKNOWN,   \
                                     0x801,                 \
                                     METHOD_OUT_DIRECT,     \
                                     FILE_ANY_ACCESS)
&amp;nbsp;
&lt;span class="comment"&gt;//-f--&gt; Seja o que Deus quiser
&lt;/span&gt;&lt;span class="keyword"&gt;#define&lt;/span&gt; IOCTL_COPY_NEITHER  CTL_CODE(FILE_DEVICE_UNKNOWN,   \
                                     0x802,                 \
                                     METHOD_NEITHER,        \
                                     FILE_ANY_ACCESS)&lt;/pre&gt;&lt;p&gt;Como se pode observar, podemos ter diferentes métodos de transferência de dados para diferentes IOCTLs. Neste post vou criar um driver que ofereça três IOCTLs que simplesmente copiam os dados recebidos no buffer de entrada para buffer de saída. O que teremos que fazer inicialmente é utilizar a macro CTL_CODE para criar as IOCTLs dos serviços que nosso driver de exemplo irá oferecer. O código completo do driver de exemplo está disponível para download ao final deste post.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Pô Fernando, eu incluí o &lt;em&gt;Windows.h&lt;/em&gt; em minha aplicação de teste, mas ainda está faltando a definição da macro CTL_CODE e recebo a mensagem de erro abaixo. Estou usando um &lt;em&gt;Windows.h&lt;/em&gt; incompleto?&lt;/strong&gt;&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;Z:\sources\testapp.cpp(45) : error C3861: 'CTL_CODE': identifier not found&lt;/pre&gt;&lt;p&gt;O negócio é o seguinte: O Wizard das versões mais recentes do Visual Studio cria o arquivo &lt;em&gt;StdAfx.h&lt;/em&gt; contendo, entre outras, as seguintes linhas:&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="keyword"&gt;#define&lt;/span&gt; WIN32_LEAN_AND_MEAN             &lt;span class="comment"&gt;// Exclude rarely-used stuff from Windows headers
&lt;/span&gt;&lt;span class="comment"&gt;// Windows Header Files:
&lt;/span&gt;&lt;span class="keyword"&gt;#include&lt;/span&gt; &amp;lt;windows.h&gt;&lt;/pre&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/Lemming.PNG" border="0" /&gt;&lt;p&gt;Observe que o símbolo &lt;strong&gt;WIN32_LEAN_AND_MEAN&lt;/strong&gt; é definido antes da inclusão do arquivo &lt;em&gt;Windows.h&lt;/em&gt;. A fim de ganhar velocidade de compilação, este símbolo evita a declaração de algumas toneladas de definições que raramente são utilizadas por aplicações. O que está acontecendo é que a macro CTL_CODE é uma destas coisas raramente utilizadas. É rapaz, interagir com drivers não é pra qualquer um não. Enfim, para resolver este problema é só comentar a definição deste símbolo e todos viverão felizes para sempre.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Calma lá Fernando! Todos menos eu, que não usei o Wizard do Visual Studio. Estou utilizando o arquivo SOURCES para compilar minha aplicação. O fato é que em meu fonte não existe nenhuma definição desse tal de "Win32 &lt;a href="http://en.wikipedia.org/wiki/Lemmings_(video_game)"&gt;Lemming&lt;/a&gt;". Qual é a desculpinha agora?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Se você está utilizando o arquivo SOURCES para compilar sua aplicação de teste, assim como estou fazendo no exemplo deste post, você precisará adicionar a linha em destaque abaixo para que o símbolo WIN32_LEAN_AND_MEAN não seja definido pelo makefile padrão do WDK.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;TARGETNAME=TestApp
TARGETTYPE=PROGRAM
USE_LIBCMT=1
UMTYPE=console
&lt;font color="#ff0000"&gt;NOT_LEAN_AND_MEAN=1&lt;/font&gt;
&amp;nbsp;
SOURCES=TestApp.cpp&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Utilizando um buffer de sistema&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;O primeiro método que veremos aqui é o Buffered I/O, definido pela utilização do valor &lt;strong&gt;METHOD_BUFFERED&lt;/strong&gt; como parâmetro da macro CTL_CODE. Aqui não teremos grandes novidades para quem leu o &lt;a href="http://www.driverentry.com.br/blog/2008/10/buffered-direct-ou-neither.html"&gt;post anterior&lt;/a&gt;. A grande diferença aqui é que na mesma chamada ao driver, dois bufferes são passados para a função &lt;a href="http://msdn.microsoft.com/en-us/library/aa363216(VS.85).aspx"&gt;DeviceIoControl&lt;/a&gt;, um de entrada e outro de saída. Aqui o I/O Manager vai alocar um único buffer de sistema com o tamanho igual ao maior deles. Complicou? Um exemplo ajuda. Numa chamada em que a aplicação ofereça o buffer de entrada com 50 bytes e um buffer de saída com 100 bytes, o buffer de sistema será alocado com 100 bytes. O I/O Manager vai copiar os 50 bytes do buffer de entrada da aplicação para o buffer de sistema. A IRP é enviada ao driver, e ao ser completada, o I/O Manager copia o conteúdo do buffer de sistema para o buffer de saída da aplicação. Quantidade de bytes copiada de volta à aplicação é determinada pelo campo &lt;strong&gt;pIrp-&gt;IoStatus.Information&lt;/strong&gt;, assim como no post anterior.&lt;/p&gt;&lt;p&gt;Uma coisa importante a ser notada aqui é que já que o buffer de sistema é único tanto para a entrada como para saída dos dados, o driver precisa ler os dados de entrada antes de começar a escrever os dados de saída, que sobrescreveriam o buffer de entrada.&lt;/p&gt;&lt;p&gt;Conforme já comentei, nosso driver de exemplo vai copiar o buffer de entrada para o buffer de saída. Vamos dar uma olhada na implementação da nossa rotina que vai tratar o IOCTL que usará um buffer de sistema. Leiam os comentários.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;/****
***     OnCopyBuffered
**
**      Rotina de tratamento da IOCTL responsável por
**      fazer a cópia do buffer de entrada para o buffer
**      de saída. (usando METHOD_BUFFERED)
*/&lt;/span&gt;
&amp;nbsp;
NTSTATUS
OnCopyBuffered(IN PDEVICE_OBJECT    pDeviceObj,
               IN PIRP              pIrp)
{
    NTSTATUS            nts;
    PIO_STACK_LOCATION  pStack;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Obtemos um ponteiro para a stack location
&lt;/span&gt;    &lt;span class="comment"&gt;//      corrente.
&lt;/span&gt;    pStack = IoGetCurrentIrpStackLocation(pIrp);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Output dos valores obtidos.
&lt;/span&gt;    &lt;span class="comment"&gt;//      Reparem que o buffer de entrada e o buffer
&lt;/span&gt;    &lt;span class="comment"&gt;//      de saída é o mesmo. Isso significa que você
&lt;/span&gt;    &lt;span class="comment"&gt;//      não pode escrever no buffer de saída até que
&lt;/span&gt;    &lt;span class="comment"&gt;//      tenha lido todos os bytes do buffer de entrada.
&lt;/span&gt;    DbgPrint(&lt;span class="literal"&gt;"========== OnCopyBuffered ===========\n"&lt;/span&gt;
             &lt;span class="literal"&gt;"Input buffer address: 0x%p\n"&lt;/span&gt;
             &lt;span class="literal"&gt;"Input buffer size:    %d\n"&lt;/span&gt;
             &lt;span class="literal"&gt;"Output buffer addres: 0x%p\n"&lt;/span&gt;
             &lt;span class="literal"&gt;"Output buffer size:   %d\n\n"&lt;/span&gt;,
             pIrp-&gt;AssociatedIrp.SystemBuffer,
             pStack-&gt;Parameters.DeviceIoControl.InputBufferLength,
             pIrp-&gt;AssociatedIrp.SystemBuffer,
             pStack-&gt;Parameters.DeviceIoControl.OutputBufferLength);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Vamos verificar se o buffer de entrada cabe no buffer
&lt;/span&gt;    &lt;span class="comment"&gt;//      de saída antes de fazer a cópia.
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (pStack-&gt;Parameters.DeviceIoControl.OutputBufferLength &amp;lt;
        pStack-&gt;Parameters.DeviceIoControl.InputBufferLength)
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops!
&lt;/span&gt;        nts = STATUS_BUFFER_TOO_SMALL;
        pIrp-&gt;IoStatus.Information = 0;
    }
    &lt;span class="keyword"&gt;else&lt;/span&gt;
    {
        &lt;span class="comment"&gt;//-f--&gt; Copiar pra quê?
&lt;/span&gt;        &lt;span class="comment"&gt;//      Como o buffer de entrada e o buffer de saída
&lt;/span&gt;        &lt;span class="comment"&gt;//      oferecidos pela aplicação são copiados para um
&lt;/span&gt;        &lt;span class="comment"&gt;//      único buffer de sistema, não precisamos fazer
&lt;/span&gt;        &lt;span class="comment"&gt;//      nenhuma cópia. O I/O Manager já fará isso por
&lt;/span&gt;        &lt;span class="comment"&gt;//      nós. Vamos apenas informar à aplicação quantos
&lt;/span&gt;        &lt;span class="comment"&gt;//      bytes são válidos no buffer de saída.
&lt;/span&gt;        nts = STATUS_SUCCESS;
        pIrp-&gt;IoStatus.Information =
            pStack-&gt;Parameters.DeviceIoControl.InputBufferLength;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Fecha a conta e passa a régua.
&lt;/span&gt;    pIrp-&gt;IoStatus.Status = nts;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Travando a memória da Aplicação&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Apesar de o IOCTL ser uma solicitação normalmente utilizada para controle, nada nos impede de utilizar este meio de comunicação para obter ou enviar dados para o driver. Se nestas leituras ou escritas, grandes volumes de dados forem trocados, o método Buffered passa a ser pouco eficiente. Utilizando o método Direct não haverá cópias intermediárias em buffer de sistema. Nada muito diferente do que já vimos no &lt;a href="http://www.driverentry.com.br/blog/2008/10/buffered-direct-ou-neither.html"&gt;post anterior&lt;/a&gt;, mas aqui temos dois bufferes em uma só chamada. O buffer de entrada faltou na escola bem no dia da aula sobre MDLs, e por isso ainda chega ao driver utilizando um buffer de sistema. É isso mesmo! Igualzinho ao método buffered. Por esta razão, não usaremos o buffer de entrada para enviar grandes quantidades de dados ao driver.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Mas e se eu quiser enviar uma grande quantidade de dados para o driver através de um IOCTL?&lt;/strong&gt; Aqui a conversa entorta um pouco. Repare que para utilizar o método Direct em IOCTLs, podemos usar tanto o parâmetro &lt;strong&gt;METHOD_IN_DIRECT&lt;/strong&gt; como o &lt;strong&gt;METHOD_OUT_DIRECT&lt;/strong&gt;. Com o método Direct, você pode utilizar o buffer de saída como entrada para o driver. &lt;strong&gt;Hein?&lt;/strong&gt; Tá bom, vamos mais devagar. Ambas as opções criam uma MDL para descrever as páginas que compõem o buffer de saída oferecido pela aplicação. Quando a IRP chega ao driver, você utiliza a função &lt;a href="http://msdn.microsoft.com/en-us/library/ms802005.aspx"&gt;MmGetSystemAddressForMdlSafe&lt;/a&gt; para obter um ponteiro de &lt;em&gt;System Space&lt;/em&gt; que mapeia as mesmas páginas físicas oferecidas pela aplicação. Isso significa que o ponteiro que você recebe vai escrever diretamente nas páginas oferecidas pela aplicação. &lt;strong&gt;Já sei! Se o ponteiro aponta para as mesmas páginas da aplicação, então podemos ler os dados contidos nestas páginas?&lt;/strong&gt; É exatamente isso. Podemos enviar dados ao driver preenchendo o buffer de saída antes de chamar a função &lt;em&gt;DeviceIoControl&lt;/em&gt;. Assim, quando o driver receber a IRP, ele pode ler estes dados. Isso permite que o driver receba grandes quantidades de dados de entrada, mas utilizando o buffer de saída. O parâmetro METHOD_IN_DIRECT sinaliza ao I/O Manager que o buffer que será utilizado para montar a MDL será utilizado para leitura, assim o buffer é testado para leituras no processo de criação da MDL. Alternativamente, o parâmetro METHOD_OUT_DIRECT indica que o buffer receberá &lt;strong&gt;leituras e escritas&lt;/strong&gt; do driver.&lt;/p&gt;&lt;p&gt;Lembre-se que METHOD_IN_DIRECT ou METHOD_OUT_DIRECT define apenas o tipo de teste que será feito sobre o buffer de saída, permitindo do driver ler o buffer de saída. O buffer de entrada sempre virá por intermédio de um buffer de sistema. &lt;strong&gt;Tá tá tá, vamos ao código por favor?&lt;/strong&gt;&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;/****
***     OnCopyDirect
**
**      Rotina de tratamento da IOCTL responsável por
**      fazer a cópia do buffer de entrada para o buffer
**      de saída. (usando METHOD_DIRECT_XXX)
*/&lt;/span&gt;
&amp;nbsp;
NTSTATUS
OnCopyDirect(IN PDEVICE_OBJECT  pDeviceObj,
             IN PIRP            pIrp)
{
    NTSTATUS            nts;
    PIO_STACK_LOCATION  pStack;
    PVOID               pOutputBuffer;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Obtemos um ponteiro para a stack location
&lt;/span&gt;    &lt;span class="comment"&gt;//      corrente.
&lt;/span&gt;    pStack = IoGetCurrentIrpStackLocation(pIrp);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; O ponteiro de saída vem de uma MDL criada
&lt;/span&gt;    &lt;span class="comment"&gt;//      pelo I/O Manager.
&lt;/span&gt;    pOutputBuffer = MmGetSystemAddressForMdlSafe(pIrp-&gt;MdlAddress,
                                                 LowPagePriority);
&amp;nbsp;
    &lt;span class="keyword"&gt;if&lt;/span&gt; (!pOutputBuffer)
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops! Estamos sem recursos para mapear as
&lt;/span&gt;        &lt;span class="comment"&gt;//      páginas descritas pelo MDL em System Space.
&lt;/span&gt;        pIrp-&gt;IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
        pIrp-&gt;IoStatus.Information = 0;
        IoCompleteRequest(pIrp, IO_NO_INCREMENT);
        &lt;span class="keyword"&gt;return&lt;/span&gt; STATUS_INSUFFICIENT_RESOURCES;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Já o ponteiro de entrada sempre vem por um
&lt;/span&gt;    &lt;span class="comment"&gt;//      buffer de sistema como no método Buffered
&lt;/span&gt;    DbgPrint(&lt;span class="literal"&gt;"=========== OnCopyDirect ============\n"&lt;/span&gt;
             &lt;span class="literal"&gt;"Input buffer address: 0x%p\n"&lt;/span&gt;
             &lt;span class="literal"&gt;"Input buffer size:    %d\n"&lt;/span&gt;
             &lt;span class="literal"&gt;"Output buffer addres: 0x%p\n"&lt;/span&gt;
             &lt;span class="literal"&gt;"Output buffer size:   %d\n\n"&lt;/span&gt;,
             pIrp-&gt;AssociatedIrp.SystemBuffer,
             pStack-&gt;Parameters.DeviceIoControl.InputBufferLength,
             pOutputBuffer,
             pStack-&gt;Parameters.DeviceIoControl.OutputBufferLength);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Vamos verificar de o buffer de entrada cabe no buffer
&lt;/span&gt;    &lt;span class="comment"&gt;//      de saída antes de fazer a cópia.
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (pStack-&gt;Parameters.DeviceIoControl.OutputBufferLength &amp;lt;
        pStack-&gt;Parameters.DeviceIoControl.InputBufferLength)
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops!
&lt;/span&gt;        nts = STATUS_BUFFER_TOO_SMALL;
        pIrp-&gt;IoStatus.Information = 0;
    }
    &lt;span class="keyword"&gt;else&lt;/span&gt;
    {
        &lt;span class="comment"&gt;//-f--&gt; Neste caso teremos que fazer a cópia, já que o buffer
&lt;/span&gt;        &lt;span class="comment"&gt;//      de entrada e o buffer de saída são fisicamente distintos
&lt;/span&gt;        RtlCopyMemory(pOutputBuffer,
                      pIrp-&gt;AssociatedIrp.SystemBuffer,
                      pStack-&gt;Parameters.DeviceIoControl.InputBufferLength);
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Sinaliza sucesso ao I/O Manager e informa à aplicação a
&lt;/span&gt;        &lt;span class="comment"&gt;//      quantidade de bytes válidos no buffer de saída.
&lt;/span&gt;        nts = STATUS_SUCCESS;
        pIrp-&gt;IoStatus.Information =
            pStack-&gt;Parameters.DeviceIoControl.InputBufferLength;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Fecha a conta e passa a régua.
&lt;/span&gt;    pIrp-&gt;IoStatus.Status = nts;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Nem Buffered I/O nem Direct I/O
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Isso pode parecer repetitivo para você, mas neste método, indicado pelo parâmetro &lt;strong&gt;METHOD_NEITHER&lt;/strong&gt;, o I/O Manager não fará nada por você. Assim, você terá que testar o contexto do processo e também testar o acesso aos bufferes como vimos no &lt;a href="http://www.driverentry.com.br/blog/2008/10/buffered-direct-ou-neither.html"&gt;post anterior&lt;/a&gt;. Mais uma vez, a grande diferença aqui é que teremos dois bufferes. O buffer de entrada virá por &lt;strong&gt;pStack-&gt;Parameters.DeviceIoControl.Type3InputBuffer&lt;/strong&gt; e o buffer de saída é obtido por &lt;strong&gt;pIrp-&gt;UserBuffer&lt;/strong&gt;. O buffer de entrada deve ser testado com &lt;a href="http://msdn.microsoft.com/en-us/library/aa489870.aspx"&gt;ProbeForRead&lt;/a&gt;, já que o driver fará leituras neste buffer, e o buffer de saída deve ser testado com &lt;a href="http://msdn.microsoft.com/en-us/library/ms797108.aspx"&gt;ProbeForWrite&lt;/a&gt;. Acho que o resto o código de exemplo é capaz de explicar. Para testar o contexto do processo, utilizamos a função que já foi explicada no &lt;a href="http://www.driverentry.com.br/blog/2008/10/buffered-direct-ou-neither.html"&gt;post anterior&lt;/a&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;/****
***     OnCopyNeither
**
**      Rotina de tratamento da IOCTL responsável por
**      fazer a cópia do buffer de entrada para o buffer
**      de saída. (usando METHOD_NEITHER)
*/&lt;/span&gt;
&amp;nbsp;
NTSTATUS
OnCopyNeither(IN PDEVICE_OBJECT pDeviceObj,
              IN PIRP           pIrp)
{
    NTSTATUS            nts;
    PIO_STACK_LOCATION  pStack;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Obtemos um ponteiro para a stack location
&lt;/span&gt;    &lt;span class="comment"&gt;//      corrente.
&lt;/span&gt;    pStack = IoGetCurrentIrpStackLocation(pIrp);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Output dos valores obtidos.
&lt;/span&gt;    DbgPrint(&lt;span class="literal"&gt;"=========== OnCopyNeither ===========\n"&lt;/span&gt;
             &lt;span class="literal"&gt;"Input buffer address: 0x%p\n"&lt;/span&gt;
             &lt;span class="literal"&gt;"Input buffer size:    %d\n"&lt;/span&gt;
             &lt;span class="literal"&gt;"Output buffer addres: 0x%p\n"&lt;/span&gt;
             &lt;span class="literal"&gt;"Output buffer size:   %d\n\n"&lt;/span&gt;,
             pStack-&gt;Parameters.DeviceIoControl.Type3InputBuffer,
             pStack-&gt;Parameters.DeviceIoControl.InputBufferLength,
             pIrp-&gt;UserBuffer,
             pStack-&gt;Parameters.DeviceIoControl.OutputBufferLength);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Como estamos utilizando o método Neither, temos que
&lt;/span&gt;    &lt;span class="comment"&gt;//      estar executando no mesmo contexto do processo que
&lt;/span&gt;    &lt;span class="comment"&gt;//      gerou a IRP., pois vamos acessar o User Space em Kernel-Mode.
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (!EstouNoContextoDoProcessoQueGerouEssaIrp(pIrp))
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops!
&lt;/span&gt;        pIrp-&gt;IoStatus.Status = STATUS_INVALID_ADDRESS;
        pIrp-&gt;IoStatus.Information = 0;
        IoCompleteRequest(pIrp, IO_NO_INCREMENT);
        &lt;span class="keyword"&gt;return&lt;/span&gt; STATUS_INVALID_ADDRESS;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui já sabemos que estamos no contexto certo, mas ainda
&lt;/span&gt;    &lt;span class="comment"&gt;//      precisamos testar os bufferes oferecidos pela aplicação.
&lt;/span&gt;    &lt;span class="comment"&gt;//      Não queremos que uma aplicação infeliz envie um ponteiro
&lt;/span&gt;    &lt;span class="comment"&gt;//      inválido e o sistema termine em tela azul por conta disso.
&lt;/span&gt;    __try
    {
        &lt;span class="comment"&gt;//-f--&gt; O driver fará leituras no buffer de entrada
&lt;/span&gt;        ProbeForRead(pStack-&gt;Parameters.DeviceIoControl.Type3InputBuffer,
                     pStack-&gt;Parameters.DeviceIoControl.InputBufferLength,
                     1);
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; E fará escritas no buffer de saída
&lt;/span&gt;        ProbeForWrite(pIrp-&gt;UserBuffer,
                      pStack-&gt;Parameters.DeviceIoControl.OutputBufferLength,
                      1);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        &lt;span class="comment"&gt;//-f--&gt; Ahaaa!!
&lt;/span&gt;        nts = GetExceptionCode();
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Completa a IRP e xinga a mãe do cara que escreveu
&lt;/span&gt;        &lt;span class="comment"&gt;//      a aplicação (a menos que tenha sido você mesmo).
&lt;/span&gt;        pIrp-&gt;IoStatus.Status = nts;
        pIrp-&gt;IoStatus.Information = 0;
        IoCompleteRequest(pIrp, IO_NO_INCREMENT);
        &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Vamos verificar de o buffer de entrada cabe no buffer
&lt;/span&gt;    &lt;span class="comment"&gt;//      de saída antes de fazer a cópia.
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (pStack-&gt;Parameters.DeviceIoControl.OutputBufferLength &amp;lt;
        pStack-&gt;Parameters.DeviceIoControl.InputBufferLength)
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops!
&lt;/span&gt;        nts = STATUS_BUFFER_TOO_SMALL;
        pIrp-&gt;IoStatus.Information = 0;
    }
    &lt;span class="keyword"&gt;else&lt;/span&gt;
    {
        &lt;span class="comment"&gt;//-f--&gt; Feche os olhos, diga "Sangue de Jesus tem poder",
&lt;/span&gt;        &lt;span class="comment"&gt;//      acredita em São Walter Oney e copia o buffer de
&lt;/span&gt;        &lt;span class="comment"&gt;//      entrada para o buffer de saída.
&lt;/span&gt;        RtlCopyMemory(pIrp-&gt;UserBuffer,
                      pStack-&gt;Parameters.DeviceIoControl.Type3InputBuffer,
                      pStack-&gt;Parameters.DeviceIoControl.InputBufferLength);
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Ufa! Todos vivos?
&lt;/span&gt;        &lt;span class="comment"&gt;//      Sinaliza sucesso ao I/O Manager e informa à aplicação a
&lt;/span&gt;        &lt;span class="comment"&gt;//      quantidade de bytes válidos no buffer de saída.
&lt;/span&gt;        nts = STATUS_SUCCESS;
        pIrp-&gt;IoStatus.Information =
            pStack-&gt;Parameters.DeviceIoControl.InputBufferLength;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Fecha a conta e passa a régua.
&lt;/span&gt;    pIrp-&gt;IoStatus.Status = nts;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Compilando o exemplo&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Para compilar o exemplo disponível para download, você pode utilizar o atalho de ambiente instalado pelo WDK e chamar o &lt;strong&gt;Build&lt;/strong&gt; do diretório raiz do exemplo. Repare que em um único passo compilamos o driver e a aplicação de teste. A figura abaixo ilustra isso. Ou você pode usar o &lt;a href="http://www.osronline.com/article.cfm?article=43"&gt;DDKBUILD&lt;/a&gt; como eu já expliquei neste &lt;a href="http://www.driverentry.com.br/blog/2006/11/kernel-visual-studio-2005.html"&gt;outro post&lt;/a&gt; para compilar a partir do Visual Studio.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/BuildIoctlCopy.PNG"/&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;&lt;strong&gt;Fernando, mais uma dúvida antes de você sumir na névoa. Na tabela de &lt;em&gt;Dispatch Routines&lt;/em&gt;, que preenchemos na estrutura &lt;a href="http://msdn.microsoft.com/en-us/library/ms805988.aspx"&gt;DRIVER_OBJECT&lt;/a&gt;, contém apenas uma entrada para &lt;a href="http://msdn.microsoft.com/en-us/library/ms796116.aspx"&gt;IRP_MJ_DEVICE_CONTROL&lt;/a&gt;. Como você criou uma rotina para cada método?&lt;/strong&gt; Essa eu vou deixar o código de exemplo abaixo responder, mas se ainda assim você tiver alguma dúvida, é só me mandar um e-mail, que está em meu &lt;a href="http://www.blogger.com/profile/12893657520734789532"&gt;perfil do Blogger&lt;/a&gt;, e aí a gente sai na porrada.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;/****
***     OnDeviceControl
**
**      Aqui recebemos todos os DeviceIoControl
**      enviados para o driver e separamos em rotinas
**      específicas para o tratamento de cada IOCTL.
**      Todo o tratamento de todas as IOCTLs poderiam
**      estar em uma só função, mas não custa nada ser
**      organizado de vez em quando.
*/&lt;/span&gt;
&amp;nbsp;
NTSTATUS
OnDeviceControl(IN PDEVICE_OBJECT   pDeviceObj,
                IN PIRP             pIrp)
{
    PIO_STACK_LOCATION  pStack;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Obtemos um ponteiro para a stack location
&lt;/span&gt;    &lt;span class="comment"&gt;//      corrente.
&lt;/span&gt;    pStack = IoGetCurrentIrpStackLocation(pIrp);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Obtém o código do IOCTL para encaminhar
&lt;/span&gt;    &lt;span class="comment"&gt;//      para a rotina certa, ou não. :-)
&lt;/span&gt;    &lt;span class="keyword"&gt;switch&lt;/span&gt;(pStack-&gt;Parameters.DeviceIoControl.IoControlCode)
    {
    &lt;span class="keyword"&gt;case&lt;/span&gt; IOCTL_COPY_BUFFERED:
        &lt;span class="keyword"&gt;return&lt;/span&gt; OnCopyBuffered(pDeviceObj,
                              pIrp);
&amp;nbsp;
    &lt;span class="keyword"&gt;case&lt;/span&gt; IOCTL_COPY_DIRECT:
        &lt;span class="keyword"&gt;return&lt;/span&gt; OnCopyDirect(pDeviceObj,
                            pIrp);
&amp;nbsp;
    &lt;span class="keyword"&gt;case&lt;/span&gt; IOCTL_COPY_NEITHER:
        &lt;span class="keyword"&gt;return&lt;/span&gt; OnCopyNeither(pDeviceObj,
                             pIrp);
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Ops! Recebemos um IOCTL diferente dos que
&lt;/span&gt;    &lt;span class="comment"&gt;//      estávamos esperando.
&lt;/span&gt;    pIrp-&gt;IoStatus.Status = STATUS_NOT_IMPLEMENTED;
    pIrp-&gt;IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
&amp;nbsp;
    &lt;span class="keyword"&gt;return&lt;/span&gt; STATUS_NOT_IMPLEMENTED;
}&lt;/pre&gt;&lt;p&gt;Hoje vou me despedir ao estilo &lt;a href="http://www.linkedin.com/pub/7/7a3/268"&gt;mr4nd3r50n&lt;/a&gt;, que é um amigo que trabalhou comigo na &lt;a href="http://www.scua.com.br"&gt;SCUA&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Intel mais, já vou Windows!&lt;br&gt;:-)&lt;/p&gt;&lt;p&gt;Download &lt;a href="http://www.driverentry.com.br/samples/IoctlCopy.zip"&gt;IoctlCopy.zip&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-2668040272947531224?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2008/10/buffered-direct-ou-neither-em-ioctls.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>2</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-6569006779888744965</guid><pubDate>Wed, 01 Oct 2008 19:43:00 +0000</pubDate><atom:updated>2008-10-06T10:07:15.692-03:00</atom:updated><title>Buffered, Direct ou Neither</title><description>&lt;p&gt;No &lt;a href="http://www.driverentry.com.br/blog/2008/09/uma-pitada-de-memria-virtual.html"&gt;post passado&lt;/a&gt; apresentei uma breve introdução sobre alguns pontos referentes à memória virtual que considero ser os mais relevantes aos desenvolvedores de drivers. Com essa pequena carga de conhecimento, ficará mais simples de explicar as diferenças entre os métodos de transferências de dados entre aplicações e drivers, assim como outras questões, tais como o atendimento de interrupções, mapeamento de memória e contexto de execução.&lt;/p&gt;&lt;p&gt;Se formos pensar de maneira bem simplificada (e bota simplificada nisso), drivers são basicamente os módulos que extraem dados de dispositivos e os disponibilizam para as aplicações e vice-versa. Deste ponto de vista, parece mesmo que escrever drivers seja fácil. Parece até uma maneira de fazer um &lt;em&gt;memcpy&lt;/em&gt; de software para hardware. Hoje veremos um pouco sobre as maneiras as quais um driver pode optar para fazer essa transferência de dados.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Utilizando um Buffer de sistema&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;O primeiro método é o chamado &lt;strong&gt;Buffered I/O&lt;/strong&gt;. Este método utiliza um buffer de sistema para fazer a transferência de dados entre a aplicação e o driver. Quando uma aplicação chama a função &lt;a href="http://msdn.microsoft.com/en-us/library/aa365747(VS.85).aspx"&gt;WriteFile&lt;/a&gt; passando os dados a serem enviados para o driver, o I/O Manager faz uma cópia dos dados da aplicação para um buffer de sistema. &lt;strong&gt;Mas o que é um buffer de sistema?&lt;/strong&gt; Se trata de uma alocação em &lt;em&gt;System Space&lt;/em&gt; que foi feita em &lt;em&gt;Kernel-Mode&lt;/em&gt;, e por isso, é acessível em qualquer contexto de processo. Já falamos disso no &lt;a href="http://www.driverentry.com.br/blog/2008/09/uma-pitada-de-memria-virtual.html"&gt;post passado&lt;/a&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;BOOL WINAPI WriteFile(
  __in         HANDLE hFile,
  __in         LPCVOID lpBuffer,
  __in         DWORD nNumberOfBytesToWrite,
  __out_opt    LPDWORD lpNumberOfBytesWritten,
  __inout_opt  LPOVERLAPPED lpOverlapped
);&lt;/pre&gt;&lt;p&gt;O I/O Manager obtém o tamanho do buffer a ser alocado do parâmetro &lt;strong&gt;nNumberOfBytesToWrite&lt;/strong&gt;, então realiza uma alocação de memória do tipo não paginada e faz a cópia dos dados que estão em &lt;em&gt;User Space&lt;/em&gt; para &lt;em&gt;System Space&lt;/em&gt;. Se você estiver derrapando nos termos &lt;em&gt;System Space&lt;/em&gt;, &lt;em&gt;User Space&lt;/em&gt; e tipos de alocação de memória, dê uma lida &lt;a href="http://www.driverentry.com.br/blog/2008/09/uma-pitada-de-memria-virtual.html"&gt;neste post&lt;/a&gt; para que as coisas fiquem menos obscuras para você.&lt;/p&gt;&lt;p&gt;No driver, o ponteiro para este buffer é obtido pela IRP e seu tamanho é obtido pela stack location da IRP. &lt;a href="http://www.driverentry.com.br/blog/2007/02/legal-mas-o-que-uma-irp.html"&gt;Legal, mas o que é uma IRP?&lt;/a&gt; Dê uma olhada neste mini exemplo abaixo para ter uma idéia, ou você pode ver o exemplo utilizado neste &lt;a href="http://www.driverentry.com.br/blog/2007/06/nos-queremos-exemplos.html"&gt;outro post&lt;/a&gt; que também utiliza o método &lt;em&gt;Buffered&lt;/em&gt; para enviar e receber strings de um driver de exemplo.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;//-f--&gt; Rotina de tratamento da IRP_MJ_WRITE.
&lt;/span&gt;&lt;span class="comment"&gt;//      Exemplo de obtenção do buffer de sistema
&lt;/span&gt;&lt;span class="comment"&gt;//      alocado pelo I/O Manager no método Buffered
&lt;/span&gt;&amp;nbsp;
NTSTATUS
OnDispatchWrite(__in PDEVICE_OBJECT pDeviceObj,
                __in PIRP           pIrp)
{
    PIO_STACK_LOCATION  pStack;
    PVOID               pBuffer;
    ULONG               ulLength;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Obtém a stack corrente da IRP
&lt;/span&gt;    pStack = IoGetCurrentIrpStackLocation();
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui obtemos o ponteiro para o buffer
&lt;/span&gt;    pBuffer = pIrp-&gt;AssociatedIrp.SystemBuffer;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui o tamanho do buffer
&lt;/span&gt;    ulLength = pStack-&gt;Parameters.Write.Length;
&amp;nbsp;
    ...
}&lt;/pre&gt;&lt;p&gt;Nas operações de leitura, o método é bem parecido, mas apesar de neste caso o I/O Manager também fazer a locação em memória não paginada, ele não faz nenhuma cópia para o buffer de sistema. O buffer de sistema é recebido pelo driver também através de &lt;strong&gt;pIrp-&gt;AssociatedIrp.SystemBuffer&lt;/strong&gt;, mas o seu tamanho é obtido em &lt;strong&gt;pStack-&gt;Parameters.Read.Length&lt;/strong&gt;. O driver preencherá o buffer com os dados vindos do dispositivo, o I/O Manager fará a cópia do buffer de &lt;em&gt;System Space&lt;/em&gt; para &lt;em&gt;User Space&lt;/em&gt; quando a IRP for completada, preenchendo o buffer da aplicação.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Opa opa! Até onde eu sei, drivers normalmente completam IRPs em contexto arbitrário, o que significa que &lt;em&gt;"só Deus sabe em qual contexto de processo"&lt;/em&gt;. Tudo bem se o driver escrever em um buffer de sistema, o qual é válido para qualquer processo, mas o buffer da aplicação está em &lt;em&gt;User Space&lt;/em&gt; e só é acessível no contexto do próprio processo. Como o I/O Manager faria a cópia do buffer de sistema para o buffer da aplicação em contexto arbitrário?&lt;/strong&gt; Nossa, essa realmente foi uma excelente pergunta. Acho que nem eu teria imaginado uma pergunta tão boa. As IRPs podem ser tratadas tanto sincronamente como assincronamente. O I/O Manager sabe como a IRP foi processada, e no caso síncrono, o I/O Manager já está no contexto do processo que fez a solicitação, e neste caso ele tem acesso a ambos os bufferes, já que o I/O Manager roda em &lt;em&gt;Kernel-Mode&lt;/em&gt;. Quando a IRP é tratada assincronamente, o I/O Manager enfila uma &lt;strong&gt;APC&lt;/strong&gt; (&lt;a href="http://msdn.microsoft.com/en-us/library/ms681951.aspx"&gt;Asynchronous Procedure Call&lt;/a&gt;), que é uma chamada assincrona executada no contexto de uma dada thread. Essa thread é justamente a thread que iniciou a operação, e que portanto, estará no contexto do processo certo para acessar o &lt;em&gt;User Space&lt;/em&gt; da aplicação.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;BOOL WINAPI ReadFile(
  __in         HANDLE hFile,
  __out        LPVOID lpBuffer,
  __in         DWORD nNumberOfBytesToRead,
  __out_opt    LPDWORD lpNumberOfBytesRead,
  __inout_opt  LPOVERLAPPED lpOverlapped
);&lt;/pre&gt;&lt;p&gt;Na chamada da função &lt;a href="http://msdn.microsoft.com/en-us/library/aa365467(VS.85).aspx"&gt;ReadFile&lt;/a&gt;, o parâmetro &lt;em&gt;nNumberOfBytesToRead&lt;/em&gt; indica o tamanho do buffer que a aplicação está oferecendo ao driver. O driver recebe este valor como quantidade máxima de bytes que podem ser retornados à aplicação. Supondo que a aplicação tenha oferecido 1000 bytes, o I/O Manager faz uma alocação de 1000 bytes e repassa o buffer para o driver. Vamos supor que o driver tenha apenas 500 bytes a serem retornados à aplicação, neste caso, o I/O Manager terá de copiar para o buffer da aplicação apenas 500 dos 1000 bytes alocados. O I/O Manager recebe este valor através do campo &lt;strong&gt;pIrp-&gt;IoStatus.Information&lt;/strong&gt;, que é preenchido pelo driver antes da IRP ser completada. Desta forma, o I/O Manager copia somente os bytes válidos do buffer de sistema para o buffer da aplicação. Este mesmo valor é retornado à aplicação através do parâmetro &lt;em&gt;lpNumberOfBytesRead&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Utilizar memória não paginada para o manter o buffer de sistema nos assegura que as páginas não serão removidas da RAM por paginação. Isso permite que a página seja acessada mesmo a partir de threads que estejam rodando em alto nível de prioridade. Contudo, o método &lt;em&gt;Buffered&lt;/em&gt; é indicado apenas para pequenas movimentações de dados. Imagine que uma aplicação queira fazer uma escrita de 10 MB de uma só vez. O I/O Manager teria que fazer uma alocação em &lt;em&gt;System Space&lt;/em&gt; de 10 MB de memória não paginada, o que não seria nada adequado, pois memória não paginada é um recurso escasso. Se o I/O Manager conseguir fazer a alocação, ele ainda terá que fazer uma cópia de 10 MB do buffer da aplicação para o buffer de sistema. Isso até funcionaria, mas teriamos sérios problemas de performace. Neste caso, o mais indicado seria utilizar o método que é visto em seguida.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Travando a memória da Aplicação&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;No método chamado &lt;strong&gt;Direct I/O&lt;/strong&gt;, como o nome já sugere, o driver faz acesso diretamente às páginas de memória da aplicação sem utilizar um buffer intermedirário. Desta forma, o I/O Manager não faz uma alocação em memória não paginada e também não tem que ficar no BPL-BPC (&lt;em&gt;Buffer pra lá - Buffer pra cá&lt;/em&gt;). Ao invés disso, o I/O Manager testa as páginas de memória que compõem o buffer oferecido pela aplicação, cria uma &lt;strong&gt;MDL&lt;/strong&gt; e trava as páginas de memória na RAM. &lt;strong&gt;Nossa! Calma aí meu amigo, vamos devagar!&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Testa as páginas de memória&lt;/strong&gt; - Nada impede um programador de fazer besteira. Um buffer inválido pode ser passado para o I/O Manager. Pode ser que o buffer não tenha sido alocado, ou que o buffer seja menor que o valor indicado na chamada às funções &lt;em&gt;ReadFile&lt;/em&gt; ou &lt;em&gt;WriteFile&lt;/em&gt;, pode ser também que as páginas de memória oferecidas à &lt;em&gt;ReadFile&lt;/em&gt; estejam protegidas contra escrita, pode ser também que algumas das páginas utilizadas pelo buffer tenham sido paginadas para o disco. Se um driver tenta acessar uma página inválida ou protegida, uma exceção é gerada e uma tela azul sugirá das trevas. &lt;strong&gt;Mas como o I/O Manager vai testar a memória?&lt;/strong&gt; Se o buffer é passado como parâmetro para a função &lt;em&gt;ReadFile&lt;/em&gt;, então o driver fará escritas neste buffer. Para ganhar tempo, o I/O Manager fará uma escrita de um byte de cada página de RAM apenas para testar o acesso a elas. Essa escrita é feita sob um manipulador de exceção. Se o buffer for inválido ou protegido, o manipulador de exceção tratará isso e devolverá um erro para a aplicação. Se o buffer é passado para a função &lt;em&gt;WriteFile&lt;/em&gt;, então o buffer será lido pelo driver, e neste caso, o teste seria a leitura de um byte de cada página.&lt;/li&gt;&lt;br&gt;&lt;li&gt;&lt;strong&gt;Cria uma MDL&lt;/strong&gt; - Uma MDL (&lt;a href="http://msdn.microsoft.com/en-us/library/aa491609.aspx"&gt;Memory Descriptor List&lt;/a&gt;) é uma estrutura de dados que descreve as várias páginas de memória que compõem um buffer. Estas páginas são as mesmas páginas físicas utilizadas pela aplicação, ou seja, quando o driver escrever nestas páginas, este já estará escrevendo diretamente no buffer da aplicação. Assim o I/O Manager não precisará fazer nenhum BPL-BPC.&lt;/li&gt;&lt;br&gt;&lt;li&gt;&lt;strong&gt;Trava as páginas na RAM&lt;/strong&gt; - Esse passo faz com que as páginas de memória que compõem o buffer da aplicação se tornem não pagináveis. Assim, estas poderão ser acessadas pelo driver em threads que estejam sendo executadas em alto nível de prioridade.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Depois de todo esse ritual, o driver agora recebe o buffer através de &lt;strong&gt;pIrp-&gt;MdlAddress&lt;/strong&gt;, mas o que teremos aqui é um ponteiro para uma MDL. &lt;strong&gt;Mas o que eu faço com uma MDL?&lt;/strong&gt; Na maioria das vezes, você vai passar como argumento em um serviço oferecido por outro driver ou componente do sistema. Alguns exemplos são drivers de DMA (&lt;a href="http://en.wikipedia.org/wiki/Direct_memory_access"&gt;Direct Memory Access&lt;/a&gt;) que utilizam MDL na chamada para a função &lt;a href="http://msdn.microsoft.com/en-us/library/ms806324.aspx"&gt;MapTransfer&lt;/a&gt;, ou mesmo quando uma MDL é repassada para rotinas de controladores USB (&lt;a href="http://en.wikipedia.org/wiki/USB"&gt;Universal Serial Bus&lt;/a&gt;), tais como &lt;a href="http://msdn.microsoft.com/en-us/library/ms790516.aspx"&gt;UsbBuildInterruptOrBulkTransferRequest&lt;/a&gt;. MDLs são estruturas opacas, mas se você quer ter acesso ao buffer da aplicação, então devemos chamar a função &lt;a href="http://msdn.microsoft.com/en-us/library/ms802005.aspx"&gt;MmGetSystemAddressForMdlSafe&lt;/a&gt; para conseguir o endereço para o buffer a ser escrito/lido. Reparem que o endereço retornado por esta função está em &lt;em&gt;System Space&lt;/em&gt;, e assim, acessível em qualquer contexto de processo. &lt;strong&gt;Mas o buffer da aplicação não está em &lt;em&gt;User Space&lt;/em&gt;?&lt;/strong&gt; Sim, mas o que temos aqui é uma página de memória física sendo mapeada tanto para o espaço de endereçamento da aplicação quanto para o espaço de endereçamemto de sistema.&lt;/p&gt;&lt;p&gt;O tamanho do buffer descrito pela MDL também é obtido pela stack location da IRP, assim como no método &lt;em&gt;Buffered&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Nem Buffered I/O nem Direct I/O&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;O terceiro método é simplesmente o não uso dos dois primeiros. No método chamado &lt;strong&gt;Neither&lt;/strong&gt;, o I/O Manager não faz uma cópia em um buffer de sistema nem monta uma MDL para descrever o buffer da aplicação. Quando a IRP chega ao seu driver, você tem acesso ao endereço virtual do buffer oferecido pela aplicação por &lt;strong&gt;pIrp-&gt;UserBuffer&lt;/strong&gt;. Este endereço aponta para &lt;em&gt;User Space&lt;/em&gt;, e por isso, lembre-se que este endereço só é valido no contexto do processo que solicitou o I/O. O uso deste método requer mais cuidado, pois seu driver precisa ser o primeiro driver na pilha de dispositivos, e dessa forma, garantir que a IRP chegue ao seu driver no contexto do prcesso que fez a solicitação de I/O.&lt;/p&gt;&lt;p&gt;Você pode verificar se você está no contexto do processo que solicitou a operação fazendo o teste abaixo.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;/****
***     EstouNoContextoDoProcessoQueGerouEssaIrp
**
**      Função com nome ridículo que verifica se o
**      estamos no contexto do processo que gerou
**      a IRP passada como parâmetro.
*/&lt;/span&gt;
&amp;nbsp;
BOOLEAN EstouNoContextoDoProcessoQueGerouEssaIrp(__in PIRP pIrp)
{
    PETHREAD    pEThread;
    PEPROCESS   pEProcess;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Obtém a thread que gerou a IRP
&lt;/span&gt;    pEThread = pIrp-&gt;Tail.Overlay.Thread;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Obtém o processo referente a thread
&lt;/span&gt;    pEProcess = IoThreadToProcess(pEThread);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui comparamos o processo corrente com
&lt;/span&gt;    &lt;span class="comment"&gt;//      o processo que gerou a IRP
&lt;/span&gt;    &lt;span class="keyword"&gt;return&lt;/span&gt; (PsGetCurrentProcess() == pEProcess);
}&lt;/pre&gt;&lt;p&gt;O fato de estar no contexto do processo correto não garante que o buffer passado como parâmetro seja válido. Diferente do método &lt;em&gt;Direct&lt;/em&gt;, neste ponto o I/O Manager não testou o buffer antes de passar a IRP para o driver. Teremos que fazer isso por nós mesmos. Lembre-se que, assim como em &lt;em&gt;User-Mode&lt;/em&gt;, ao acessar um buffer inválido o driver receberá uma exceção que deve ser manipulada, caso contrário, tudo azul.&lt;/p&gt;&lt;p&gt;Para realizar o teste, teremos que acessar um byte de cada página do buffer que nos foi passado e verificar se o mundo acaba. As rotinas &lt;a href="http://msdn.microsoft.com/en-us/library/aa489870.aspx"&gt;ProbeForRead&lt;/a&gt; e &lt;a href="http://msdn.microsoft.com/en-us/library/ms797108.aspx"&gt;ProbeForWrite&lt;/a&gt; fazem isso por nós, mas estas devem ser chamadas dentro de um manipulador de exceção. Vale lembrar que em uma operação de leitura, a aplicação nos envia um buffer onde o driver escreverá dados para a aplicação. Já que o driver fará uma escrita neste buffer, teremos que realizar um teste de escrita (&lt;em&gt;ProbeForWrite&lt;/em&gt;) nas operações de leitura (&lt;em&gt;ReadFile&lt;/em&gt;). De maneira análoga, o driver deverá fazer um teste de leitura (&lt;em&gt;ProbeForRead&lt;/em&gt;) nas operações de escrita (&lt;em&gt;WriteFile&lt;/em&gt;). Dê uma olhada no exemplo de rotina de leitura que segue abaixo. Como você já deve estar acostumado, leia os comentários que complementam o texto.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;&lt;span class="comment"&gt;/****
***     OnDispatchRead
**
**      Outra função com nominho besta de exemplo.
**      Valida o buffer enviado pela aplicação pelo
**      método Neither e escreve uma seqüência numérica
**      no buffer da aplicação.
*/&lt;/span&gt;
&amp;nbsp;
NTSTATUS OnDispatchRead(__in PDEVICE_OBJECT pDeviceObj,
                        __in PIRP           pIrp)
{
    PIO_STACK_LOCATION  pStack;
    PVOID               pBuffer;
    ULONG               ulLength;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Verifica se estamos no contexto do processo
&lt;/span&gt;    &lt;span class="comment"&gt;//      que gerou esta IRP
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (!EstouNoContextoDoProcessoQueGerouEssaIrp(pIrp))
    {
        &lt;span class="comment"&gt;//-f--&gt; Sinaliza ao I/O Manager que a casa caiu
&lt;/span&gt;        pIrp-&gt;IoStatus.Status = STATUS_INVALID_PARAMETER;
        pIrp-&gt;IoStatus.Information = 0;
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Completa a IRP com falha
&lt;/span&gt;        IoCompleteRequest(pIrp, IO_NO_INCREMENT);
        &lt;span class="keyword"&gt;return&lt;/span&gt; STATUS_INVALID_PARAMETER;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Obtém a stack location corrente
&lt;/span&gt;    pStack = IoGetCurrentIrpStackLocation(pIrp);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui obtemos o endereço do buffer oferecido
&lt;/span&gt;    &lt;span class="comment"&gt;//      pela aplicação bem como seu tamanho.
&lt;/span&gt;    &lt;span class="comment"&gt;//      Note que este buffer deve estar em User Space
&lt;/span&gt;    pBuffer = pIrp-&gt;UserBuffer;
    ulLength = pStack-&gt;Parameters.Read.Length;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; As funções que testam o buffer lançam exceções
&lt;/span&gt;    &lt;span class="comment"&gt;//      no caso de o buffer ser inválido. Por isso temos
&lt;/span&gt;    &lt;span class="comment"&gt;//      que fazer o teste dentro de um manipulador de exceções
&lt;/span&gt;    __try
    {
        &lt;span class="comment"&gt;//-f--&gt; Se você der uma olhada na referência, verá que
&lt;/span&gt;        &lt;span class="comment"&gt;//      esta rotina retorna VOID, por isso a única
&lt;/span&gt;        &lt;span class="comment"&gt;//      maneira de saber se o buffer é inválido é
&lt;/span&gt;        &lt;span class="comment"&gt;//      manipulando a exceção que será gerada.
&lt;/span&gt;        ProbeForWrite(pBuffer,
                      ulLength,
                      1);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        NTSTATUS    nts;
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Ops! Buffer inválido.
&lt;/span&gt;        &lt;span class="comment"&gt;//      Vamos obter o código da exceção e dar um
&lt;/span&gt;        &lt;span class="comment"&gt;//      fim nesse sofrimento
&lt;/span&gt;        nts = GetExceptionCode();
&amp;nbsp;
        pIrp-&gt;IoStatus.Status = nts;
        pIrp-&gt;IoStatus.Information = 0;
        IoCompleteRequest(pIrp, IO_NO_INCREMENT);
        &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui sabemos que o buffer está seguro para receber
&lt;/span&gt;    &lt;span class="comment"&gt;//      escritas. Vamos apenas escrever uma seqüência numérica
&lt;/span&gt;    &lt;span class="comment"&gt;//      só pra...
&lt;/span&gt;    &lt;span class="keyword"&gt;for&lt;/span&gt; (ULONG i = 0; i &amp;lt; ulLength, i++)
        ((PUCHAR)pBuffer)[i] = (UCHAR)i;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui informamos que a operação foi realizada com
&lt;/span&gt;    &lt;span class="comment"&gt;//      sucesso.
&lt;/span&gt;    pIrp-&gt;IoStatus.Status = STATUS_SUCCESS;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Apesar de no método Neither o I/O Manager não
&lt;/span&gt;    &lt;span class="comment"&gt;//      utilizar este número para fazer BPL-BPC, este
&lt;/span&gt;    &lt;span class="comment"&gt;//      número ainda é retornado pela função ReadFile
&lt;/span&gt;    &lt;span class="comment"&gt;//      para informar à aplicação quantos bytes do buffer
&lt;/span&gt;    &lt;span class="comment"&gt;//      são válidos para a aplicação ler.
&lt;/span&gt;    pIrp-&gt;IoStatus.Information = ulLength;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Dá um Fatality na IRP
&lt;/span&gt;    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    &lt;span class="keyword"&gt;return&lt;/span&gt; STATUS_SUCCESS;
}&lt;/pre&gt;&lt;p&gt;Para todos os métodos, é importante notar que setar o campo &lt;strong&gt;pIrp-&gt;IoStatus.Information&lt;/strong&gt; diz à aplicação a quantidade de bytes foram lidos ou escritos no buffer, independente de haver ou não a cópia de buffer de sistema como no caso do método &lt;em&gt;Buffered&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Outra coisa que não vai mudar para os diferentes métodos é a obtenção do tamanho do buffer oferecido pela aplicação. Este valor sempre vem pela stack location como já foi visto nos métodos já discutidos.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Como selecionar o método&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Faz todo o sentido que a escolha do método seja feita antes da primeira IRP chegar ao driver. Isso é feito logo depois que o device é criado. Depois que a chamada à função &lt;a href="http://msdn.microsoft.com/en-us/library/aa490468.aspx"&gt;IoCreateDevice &lt;/a&gt; termina, recebemos o novo device através de um ponteiro de saída. O membro &lt;strong&gt;Flags&lt;/strong&gt; da estrutura &lt;a href="http://msdn.microsoft.com/en-us/library/aa491677.aspx"&gt;DEVICE_OBJECT&lt;/a&gt; é uma máscara de bits e os bits &lt;strong&gt;DO_BUFFERED_IO&lt;/strong&gt; e &lt;strong&gt;DO_DIRECT_IO&lt;/strong&gt; configuram o método de transferência.&lt;/p&gt;&lt;p&gt;Então é fácil assim. Para configurar o método &lt;em&gt;Buffered&lt;/em&gt;, setamos o bit DO_BUFFERED_IO, para configurar o método &lt;em&gt;Direct&lt;/em&gt;, setamos o bit DO_DIRECT_IO, e finalmente para setar o método &lt;em&gt;Neither&lt;/em&gt;, não setamos nenhum destes bits. Já li em algum livro, que não encontro agora, que o comportamento não é previsto se você setar ambos os bits.&lt;/p&gt;&lt;p&gt;Segue mais um exemplinho besta de como setar o device que acaba de ser criado para transferências no método &lt;em&gt;Buffered&lt;/em&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain"&gt;    &lt;span class="comment"&gt;//-f--&gt; Cria o device que irá receber as IRPs
&lt;/span&gt;    nts = IoCreateDevice(pDriverObj,
                         0,
                         &amp;amp;usDeviceName,
                         FILE_DEVICE_UNKNOWN,
                         0,
                         FALSE,
                         &amp;amp;pDeviceObj);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Verifica se o device foi criado
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
        &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui já podemos configurar o método
&lt;/span&gt;    &lt;span class="comment"&gt;//      de transferência desejado, que neste
&lt;/span&gt;    &lt;span class="comment"&gt;//      exemplo é o Buffered I/O
&lt;/span&gt;    pDeviceObj-&gt;Flags |= DO_BUFFERED_IO;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Mas e se uma IRP for entregue ao driver antes de setarmos estes bits?&lt;/strong&gt; Outra excelente pergunta. As coisas acontecem assim. Sempre quando um novo device é criado, o bit &lt;a href="http://msdn.microsoft.com/en-us/library/ms790739.aspx"&gt;DO_DEVICE_INITIALIZING&lt;/a&gt; é setado. As IRPs só começam a ser entregues a este device quando o driver baixar este bit. Isso nos permite inicializar o device antes que qualquer IRP chegue.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Boa tentativa espertão, mas &lt;a href="http://www.driverentry.com.br/blog/2007/06/nos-queremos-exemplos.html"&gt;seu exemplo&lt;/a&gt; não baixa este bit. Como você explica isso?&lt;/strong&gt; Você hoje está impossível! Ao termino da rotina &lt;em&gt;DriverEntry&lt;/em&gt;, quando o controle volta ao I/O Manager, ele varre a lista de devices criados pelo driver e baixa este bit por nós. É importante lembrar que ainda precisamos baixar este bit quando criamos um novo device depois que a rotina &lt;em&gt;DriverEntry&lt;/em&gt; terminou. Um exemplo muito comum são os devices de WDM, que são criados na rotina &lt;a href="http://msdn.microsoft.com/en-us/library/ms795509.aspx"&gt;AddDevice&lt;/a&gt;, mas isso vai ficar para uma outra vez. Esse post já ficou muito longo.&lt;/p&gt;&lt;p&gt;Até mais! :-)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-6569006779888744965?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2008/10/buffered-direct-ou-neither.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>5</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-2576821292209997781</guid><pubDate>Thu, 11 Sep 2008 18:42:00 +0000</pubDate><atom:updated>2008-11-17T12:34:26.707-02:00</atom:updated><title>Uma pitada de Memória Virtual</title><description>&lt;a href="http://www.amazon.com/Microsoft-Windows-Internals-4th-Server/dp/0735619174/ref=pd_bbs_sr_1?ie=UTF8&amp;s=books&amp;qid=1221163140&amp;sr=8-1"&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/Winternals.jpg" border="0" /&gt;&lt;/a&gt;&lt;p&gt;Agora vamos deixar de conversinha mole e vamos logo ao que interessa. O exemplo que descrevi em &lt;a href="http://www.driverentry.com.br/blog/2007/06/nos-queremos-exemplos.html"&gt;outro post&lt;/a&gt; mostra como implementar um driver bem simples que armazena uma lista de strings que são enviadas ao driver através de operações de escrita (&lt;a href="http://msdn.microsoft.com/en-us/library/aa365747(VS.85).aspx"&gt;WriteFile&lt;/a&gt;), e as mesmas strings são retornadas em subseqüentes operações de leitura (&lt;a href="http://msdn.microsoft.com/en-us/library/aa365467(VS.85).aspx"&gt;ReadFile&lt;/a&gt;). Esse é um bom exemplo de como os dados das aplicações vão para os drivers e vice-versa. Existem três maneiras de acessar os dados oferecidos por aplicações a partir de um driver. Neste post vamos dar uma olhadinha em memória virtual para que possamos ter uma base para posts futuros, onde poderei explicar as diferenças entre aquelas tais três maneiras. Vamos nos ater a apenas algumas características básicas da memória virtual para que as coisas que explicarei em seguida façam mais sentido para você, mas se você quiser mais detalhes a respeito de Memória Virtual, você pode ler o Capítulo 7, &lt;em&gt;"Memory Management"&lt;/em&gt;, do &lt;a href="http://www.amazon.com/Microsoft-Windows-Internals-4th-Server/dp/0735619174/ref=pd_bbs_sr_1?ie=UTF8&amp;s=books&amp;qid=1221163140&amp;sr=8-1"&gt;Windows Internals&lt;/a&gt;, ou dar uma olhadinha em &lt;a href="http://www.1bit.com.br/downloads/por_dentro_do_windows_memoria.ppt"&gt;uma apresentação&lt;/a&gt; feita pelo meu amigo &lt;a href="http://www.1bit.com.br/content.1bit/weblog"&gt;Strauss&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;O que é uma página de memória&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Página de memória é uma unidade utilizada pelo hardware nas proteções de acesso à memória. Mais sobre proteções de páginas de memória &lt;a href="http://msdn.microsoft.com/en-us/library/aa366785(VS.85).aspx"&gt;aqui&lt;/a&gt;. Essa é uma unidade definida pelo hardware que ajuda a subdividir a memória em espaços menores e gerenciáveis. Apesar de existirem dois tamanhos de páginas (uma pequena de 4KB bytes, e outra grande de 4MB), toda referência ao tamanho de página vista na documentação são referentes apenas às páginas pequenas. O tamanho da página de memória varia com a plataforma de hardware. Em sistemas &lt;a href="http://en.wikipedia.org/wiki/X86"&gt;x86&lt;/a&gt; e &lt;a href="http://en.wikipedia.org/wiki/X64"&gt;x64&lt;/a&gt;, as paginas são de 4KB enquanto que em sistemas &lt;a href="http://en.wikipedia.org/wiki/IA64"&gt;IA64&lt;/a&gt; a página é de 8KB. O tamanho da página pode ser obtido pela constante &lt;strong&gt;PAGE_SIZE&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Espaço de endereçamento&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;De uma maneira bem resumida, memória virtual é um mecanismo, que em um trabalho conjunto entre hardware e software, permite que processos que rodem no Windows tenham seu próprio espaço de endereçamento. &lt;strong&gt;Espaço quem?&lt;/strong&gt; Espaço de endereçamento, ou &lt;em&gt;Address Space&lt;/em&gt; como normalmente vimos na referência, permite que cada processo tenha uma visão privada da memória que esteja utilizando. Ou seja, um processo não pode ler ou escrever em páginas de memória de outro processo. Isso evita que um programa mal escrito possa erroneamente escrever em páginas de memória de outro processo ou mesmo em páginas do sistema operacional, comprometendo assim a estabilidade de todo o sistema. Portanto, podemos assumir que o espaço de endereçamento de um processo só é acessível pelo processo ao qual ele pertence.&lt;/p&gt;&lt;blockquote&gt;Nota: É possível compartilhar memória entre processos, mas mesmo neste caso, a mesma página física de memória é referenciada por dois ou mais espaços de endereçamentos diferentes.&lt;/blockquote&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Memória Virtual e Memória Física&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Logo após a descoberta do fogo, a memória era endereçada diretamente em &lt;a href="http://en.wikipedia.org/wiki/Real_mode"&gt;modo real&lt;/a&gt;. Isso significa que um ponteiro utilizado por uma aplicação acessava o dado exatamente onde ele se encontrava fisicamente nos chips de memória RAM. Páginas de memória virtual associadas aos processos refletem páginas físicas de memória através de um endereçamento virtual. Os bits que compõem o endereço virtual mapeam a memória física que se deseja acessar. Ao deferenciar um ponteiro que contem um endereço virtual, o processador, de maneria transparente ao processo, consulta estruturas preenchidas pelo sistema operacional de forma a traduzir o endereço virtual em real, e dessa forma, encontra a página física onde o dado realmente se encontra.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/VirtualFisica.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;A disposição das páginas de memória virtual não tem relação com sua disposição física. Isso significa que você quando você faz uma grande alocação de memória, que é composta por várias páginas de memória, você ganha um ponteiro que, do ponto de vista da aplicação, aponta para páginas de memória que estão linearmente distribuídas (uma seguida da outra), enquanto que as páginas de memória física podem estar todas espalhadas pelos chips de RAM.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;A memória pode não estar na RAM&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;A memória utilizada pelas aplicações não se limita à quantidade de memória oferecida pelos chips de RAM instalados em sua placa mãe. Quando os processos, cada vez mais sedentos por memória, vão sendo executados, o espaço disponível em RAM vai sendo compartilhado entre os vários processos. Páginas de memória acessadas com menos frequência são removidas dos chips de RAM e vão para disco (para o &lt;strong&gt;&lt;em&gt;Pagefile.sys&lt;/em&gt;&lt;/strong&gt; para ser mais preciso), dando lugar à uma outra página de memória que é necessária naquele momento. Esse processo recebe o nome de paginação. Quando a aplicação finalmente acessar o dado que está naquela página agora em disco, o sistema aloca espaço em RAM física para trazer de volta a página do disco para a RAM. Isso pode resultar em outras páginas que estavam em RAM a serem paginadas para o disco.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/Paging.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Para as aplicações, o processo de paginação é completamente transparente. O desenvolvedor não está nem um pouco preocupado se a região de memória que o programa vai acessar está em disco ou está em RAM. É só acessar o dado e o processador junto com o gerenciador de memória resolve isso para você. Infelizmente, a história não é tão florida para os desenvolvedores de drivers. Aplicações sempre rodam em uma prioridade de thread baixa (&lt;strong&gt;PASSIVE_LEVEL&lt;/strong&gt;) e ao acessar um dado que esteja em uma página em disco, a thread é interrompida pelo gerenciador de memória para que este tenha a oportunidade de acionar os drivers de &lt;em&gt;File System&lt;/em&gt; e por conseqüência os drivers de disco, aguardar o I/O ser realizado (apesar de levar um cacalhésimo de segundo, ainda temos que esperar) e então a thread é liberada. &lt;a href="http://www.driverentry.com.br/blog/2008/06/matria-mais-difcil.html"&gt;Embora haja pessoas que não acreditem nessas coisas&lt;/a&gt;, a plataforma NT é completamente preemptiva. Isso significa que uma thread pode ser interrompida por outra de maior prioridade. Em &lt;em&gt;Kernel Mode&lt;/em&gt;, uma thread pode estar em prioridades superiores à &lt;strong&gt;APC_LEVEL&lt;/strong&gt;, que é a prioridade em que as paginações são realizadas. Se a thread que acessará o dado estiver em um nível de prioridade muito alta (maior ou igual a &lt;strong&gt;DISPATCH_LEVEL&lt;/strong&gt;), o processador não vai conseguir fazer a paginação e uma tela azul irá aflorar aos seus olhos. Um exemplo típico de execução em alta prioridade é o tratamento de interrupções, mas não vamos nos desviar do assunto.&lt;/p&gt;&lt;img style="FLOAT: left; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/MonkeyComputerMoney.jpg" border="0" /&gt;&lt;p&gt;Uma página pode ser acessada não só por um software, mas também por hardware. &lt;strong&gt;Hein?&lt;/strong&gt; Uma aplicação pode passar o ponteiro de uma região de memória para um driver, que por sua vez, irá utilizar um canal de &lt;a href="http://en.wikipedia.org/wiki/Direct_memory_access"&gt;DMA&lt;/a&gt; para preencher este buffer. A cópia de um buffer através do processo de DMA não utiliza ciclos de CPU para ser realizada. Preencher um buffer é algo tão simples que até um chimpanzé autista pode fazer. Assim um grupo de chimpanzés autistas engenheiros criaram um chip que faz isso, mas isso não vem ao caso agora. Este chip pode estar na placa mãe ou na própria placa controlada pelo driver. O chip é programado pelo driver e uma transferência é iniciada. Durante a cópia, nem a aplicação, nem o sistema operacional e nem mesmo o processador ficam cientes do que está acontecendo. O sistema pode então determinar que as páginas utilizadas na transferência, que não estão sendo acessadas por nenhum processo, devem ser paginadas para disco. Se isso ocorrer, o chip de DMA continuará tranferido bytes para aquele endereço físico de RAM e... Bom, já sabe né? Para evitar isso, existem meios de avisar o gerenciador de memória que uma ou mais páginas não devem ser paginadas.&lt;/p&gt;&lt;p&gt;Durante o desenvolvimento de um driver, você pode querer alocar um buffer que será utilizado por uma ISR (&lt;a href="http://msdn.microsoft.com/en-us/library/ms892408.aspx"&gt;Interrupt Service Routine&lt;/a&gt;). Como uma ISR é executada em alta prioridade, podemos fazer uma alocação de memória solicitando um buffer que não seja paginável, e assim, garantir que o buffer obtido por esta alocação esteja sempre em RAM. Mas vá com calma, memória não paginável é um recurso escasso e deve ser utilizado com parcimônia. Caso contrário, algumas operações vão deixar de ser realizadas pelo sistema por falta de RAM, mesmo que ainda haja muita memória paginável disponível.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/NonPaged.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;User Space e System Space&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Adotando um sistema de 32 bits como exemplo, um ponteiro é capaz de endereçar &lt;strong&gt;4 GB&lt;/strong&gt; de memória. Destes, &lt;strong&gt;2 GB&lt;/strong&gt; são reservados para endereçar páginas privadas para cada processo, ou seja, vão compor o espaço de endereçamento privado de cada aplicação. Esta faixa, que vai de &lt;strong&gt;0x00000000&lt;/strong&gt; ao &lt;strong&gt;0x7FFFFFFF&lt;/strong&gt;, recebe o nome de &lt;em&gt;&lt;strong&gt;User Space&lt;/strong&gt;&lt;/em&gt; e são os endereços que aplicações acessam em &lt;em&gt;User Mode&lt;/em&gt;. Conforme vimos, esta faixa compõe o espaço de endereçamento privado de um processo, protegido contra acessos indevidos de outros processos. Um exemplo seria: Somente as threads do &lt;em&gt;Processo A&lt;/em&gt; terão acesso ao &lt;em&gt;User Space&lt;/em&gt; do &lt;em&gt;Processo A&lt;/em&gt;. O mesmo endereço aponta para diferentes páginas físicas de RAM em diferentes processos, dependendo do contexto do processo que faz o acesso. Os outros 2 GB restantes do endereçamento de 32 bits são reservados ao endereçamento de páginas de sistema. A faixa de endereços de &lt;strong&gt;0x80000000&lt;/strong&gt; ao &lt;strong&gt;0xFFFFFFFF&lt;/strong&gt; define o então chamado &lt;em&gt;&lt;strong&gt;System Space&lt;/strong&gt;&lt;/em&gt;, que diferente do &lt;em&gt;User Space&lt;/em&gt;, endereçam páginas de memória que &lt;strong&gt;não&lt;/strong&gt; são privados a um determinado processo. Isso significa que o mesmo dado (na mesma página física) pode ser acessado através do mesmo endereço virtual em diferentes processos. Um dado em &lt;em&gt;System Space&lt;/em&gt; pode ser acessado apenas em &lt;em&gt;Kernel Mode&lt;/em&gt;, enquanto que um dado em &lt;em&gt;User Space&lt;/em&gt; pode ser acessado tanto em &lt;em&gt;Kernel Mode&lt;/em&gt; quanto em &lt;em&gt;User Mode&lt;/em&gt;.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/UserSystemSpace.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;&lt;strong&gt;Cagamba! Isso foi uma metralhadora de conceitos?&lt;/strong&gt; Se você apresentar sintomas de visão turva ou vômito seguido de diarréia e tremedeira, larga a mão de ser frouxo e se prepare para os próximos posts. Caso você não tenha apresentado nenhum destes sintomas, não se preocupe, para algumas pessoas o processo é mais demorado. Em posts futuros vou explicar como os drivers acessam os endereços virtuais oferecidos pelas aplicações, como lidam com a paginação de memória e ainda testam buffers oferecidos pelas aplicações. Um driver mal escrito pode permitir que um parâmetro inválido em uma aplicação cause uma tela azul.&lt;/p&gt;&lt;p&gt;Por hoje chega, mas vale lembrar que o que foi apresentado aqui é apenas a ponta do &lt;em&gt;iceberg&lt;/em&gt;. Essa pontinha é o que acredito ser o mais relevante para o desevolvimento de drivers, mas ainda existem toneladas de detalhes referentes à memória virtual.&lt;/p&gt;&lt;p&gt;Have fun! :-)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-2576821292209997781?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2008/09/uma-pitada-de-memria-virtual.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>9</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-4611321437015601455</guid><pubDate>Sun, 24 Aug 2008 18:45:00 +0000</pubDate><atom:updated>2008-08-25T09:11:47.125-03:00</atom:updated><title>Recife Office</title><description>&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/BoaViagem.jpg" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Pode uma pessoa trabalhar do outro lado do país e ainda assim dar um curso de Drivers para Windows? A resposta para essa pergunta é: "Depende se essa pessoa é Home suficiente para isso". O fato de ter recebido a oportunidade de trabalhar em casa me abriu a grande flexibilidade de poder trabalhar fora de casa. Trabalhar pela Internet não significa necessariamente trabalhar em casa.&lt;/p&gt;&lt;p&gt;Fui convidado a ministrar o curso de Drivers em um centro de estudos que fica em Recife - PE. Eu nunca poderia aceitar um convite desses em condições normais de temperatura e pressão, já que trabalho regularmente em uma empresa em São Paulo, e como se não bastasse, ainda estou me graduando em Engenharia da Computação no período noturno. Mas &lt;a href="http://www.driverentry.com.br/2008/07/agora-que-virei-home.html"&gt;agora que virei Home&lt;/a&gt;, as coisas são diferentes. Aproveitando o período de férias da universidade, fui fazer meu Home Office em Recife e dar o curso durante o período noturno.&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/SlidePPT.png" border="0" /&gt;&lt;p&gt;Uma empresa multi-nacional do ramo de eletrônicos contratou os serviços deste centro de estudo para o desenvolvimento e manutenção dos drivers que se comunicariam via USB com telefones celulares. O driver original foi escrito por ninguém menos que &lt;a href="http://www.oneysoft.com/"&gt;Walter Oney&lt;/a&gt;. Fiquei com a missão de ensinar o caminho das telas azuis para este grupo de 10 alunos, minha maior turma até agora. Quantas pessoas que trabalham com drivers você conhece? O número de alunos não foi problema em momento algum, pensei que seria mais difícil gerenciar isso. Todos interessados, esforçados e com vontade de aprender. Difícil mesmo foi ter que ficar duas semanas inteiras hospedado em um Hotel à beira-mar na praia de Boa Viagem e não poder dar um único mergulho (A primeira foto deste post foi tirada da janela do hotel). Um dos motivos é a constante presença de tubarões nas praias Pernambucanas, com algumas exceções. Outro fato foi a carga horária que foi adotada. Um curso básico de drivers é normalmente ministrado em 40 horas, o que é perfeito para duas semanas, sendo 10 aulas de 4 horas. Mas neste caso havia a necessidade de uma especialização em dispositivos USB. Isso nos tomou dois sábados, meio domingo e muita energia. Um curso de três semanas foi dado em duas. Não sei se foi o mais produtivo, mas minha agenda não me permitiu o luxo de descansar aos finais de semana.&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/Tubarao.png" border="0" /&gt;&lt;p&gt;Uma pergunta freqüente em meu blog é "Quando será formada a próxima turma?". No começo desse ano eu imaginei poder montar uma turma aberta durante o período de férias, mas a correria que veio em seguida, acentuada com pela minha viagem, não permitiu que eu me organizasse com relação a infra-estrutura e divulgação. O curso destas férias não existiria, mas no caso de Recife, já havia uma turma fechada e a disponibilidade dos recursos oferecidos por eles. A pergunta ainda persiste e a resposta agora será que estou me organizando para montar uma turma aberta para o início de Janeiro. Obviamente vou publicar qualquer novidade referente a isso aqui no blog, mas gostaria de já ter uma noção dos recursos que terei que reservar para isso. Assim, se você tem interesse de participar dessa turma, me envie um e-mail sem compromisso ou deixe um comentário neste post. Meu endereço de e-mail que ninguém encontra, fica na página apontada por este &lt;a href="http://www.blogger.com/profile/12893657520734789532"&gt;link&lt;/a&gt;, que é a página do meu perfil do blogger.&lt;/p&gt;&lt;p&gt;Até mais...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-4611321437015601455?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2008/08/recife-office_24.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-7634273384502241046</guid><pubDate>Sat, 12 Jul 2008 10:34:00 +0000</pubDate><atom:updated>2009-07-04T12:31:35.272-03:00</atom:updated><title>Agora que virei Home</title><description>&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/Ballet.jpg" border="0" /&gt;&lt;p&gt;De repente meu gerente me liga e pergunta - &lt;em&gt;"Você é Home, certo?"&lt;/em&gt;. Fiquei meio constrangido com aquela situação, mas o que eu podia fazer? Eu tinha que responder àquela pergunta. &lt;em&gt;"Olha, ainda não sou completamente, mas estou interessado em virar"&lt;/em&gt;, respondi. &lt;em&gt;"Então preencha os formulários que eu aprovo"&lt;/em&gt;. Foi basicamente assim que virei &lt;strong&gt;Home Office&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Como funcionário regular, eu ainda tinha o direito de fazer Home Office 2 dias por semana. No início vem um sentimento de culpa. Não dá pra aceitar, assim de cara, que não terei que levantar às cinco e meia da manhã e enfrentar quase duas horas de trânsito para sair de Santo André, pegar a Avenida dos Estados e finalmente chegar ao prédio onde trabalho. Confesso que não sair de casa tão cedo e me sentar em frente ao notebook da empresa usando um moletom causa um certo desconforto no início. Dá a impressão de que não estamos trabalhando, de que estamos fazendo alguma coisa errada.&lt;/p&gt;&lt;p&gt;Depois do sentimento de culpa vem o relaxo. Até hoje eu continuo levantando cedo para poder levar minha esposa até a estação de trem, que fica à exatos 7 minutos de minha casa. Nas primeiras semanas, eu voltava para a cama e dava mais uma dormida até às nove da manhã. Já consegui chegar no trabalho com 30 minutos atraso mesmo estando a vinte metros dele. Tomar café da manhã em frente ao notebook virou rotina. Mas isso passou. Acho que conforme o tempo foi passando, o corpo e a mente vão ficando mais descançados, até uma hora que não dá mais vontade de voltar para a cama. Além do mais, eu ainda tenho uma lista razoável de livros para ler. O tempo que antes eu gastava no trânsito, agora eu gasto lendo e isso está dando muito certo.&lt;/p&gt;&lt;p&gt;Meus vizinhos agora devem pensar que fiquei desempregado e que sou sustentado pela minha esposa. Tem uma pessoa que além limpar minha casa, prepara meu almoço. Assim, eu consigo almoçar em 15 minutos e não vou voltar correndo para o notebook depois disso. Eu preciso fazer minha &lt;a href="http://gl.wikipedia.org/wiki/Fotos%C3%ADntese"&gt;fotosíntese&lt;/a&gt;. Para isso vou tomar um café expresso em uma padaria que fica láááááááá em cima e aproveito para tomar sol caminhando até a padaria. Ter horário para tomar sol até parece programa de presidiário. Às vezes eu levo meus cães para passear durante meu horário de almoço. Eu não culpo meus vizinhos. O que você pensaria se visse uma pessoa de uns trinta anos de idade, com a barba de dias na cara, passeando com cachorros ao meio dia? &lt;em&gt;"Esse aí leva cães para passear para poder conseguir o dinheiro da pinga"&lt;/em&gt;.&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/TrabalheEmCasa.PNG" border="0" /&gt;&lt;p&gt;Bom, eu estou mesmo babando e andando para os vizinhos. Complicado é o que seus parentes pensam de você. Para tomar café, eu passo em frente à casa de uma tia. Sabe aquela tia que tudo o que ela conseguiu entender sobre o que você faz é que você trabalha com computadores, ela só não tem certeza se é limpando ou vendendo. Numa dessas idas e vindas do café, acabei me encontrando com ela. &lt;em&gt;"E aí Fernando, está de folga?"&lt;/em&gt; - ela pergunta. &lt;em&gt;"Não tia, agora eu trabalho em casa"&lt;/em&gt; - eu respondo todo satisfeito, mas olhando sua expressão, dá pra imaginar as engrenagens na cabeça dela - &lt;em&gt;"Vixe tadinho, deve estar fazendo aqueles bicos do tipo &lt;strong&gt;Trabalhe em casa, pergunte-me como&lt;/strong&gt;"&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Mais difícil que se convencer de que você pode trabalhar em casa, é convencer os outros de que você está trabalhando em casa. Na época em que eu fazia Home Office apenas 2 vezes por semana, minha esposa sempre me perguntava - &lt;em&gt;"Você vai trabalhar amanhã?"&lt;/em&gt;, como se eu não trabalhasse em casa. Meu irmão uma vez me disse, &lt;em&gt;" ...mas você não vai ficar em casa amanhã? Então, eu passo lá e a gente vai ver aquela peça do carro"&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Trabalhar em casa exige disciplina, embora eu possa fazer meu próprio horário, eu prefiro ter horário fixo para almoçar, começar e parar de trabalhar. Não estou falando apenas de trabalhar menos. Trabalhar demais é um risco maior. Você já está alí com um pepino pra resolver, falta só mais aquele último teste, que é o último há uns 5 testes atrás, você não tem que pegar trânsito, já está no conforto do seu lar e quando você vê já são nove da noite.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Não muda nada para a empresa?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/homeoffice.JPG" border="0" /&gt;&lt;p&gt;Eu trabalho em um time global. Uns aqui, outros nos Estados Unidos e India. O gerente do time de desenvolvimento está em outro hemisfério. Você acha mesmo que isso vai fazer alguma diferença pra ele? Mesmo quando eu estava trabalhando no prédio, tudo que eu precisava era de um ponto de rede. Apesar de eu ter um notebook, este é utilizado para abrir uma sessão remota em uma das máquinas que servem os laboratórios externos (Brasil e India). As máquinas de teste são virtuais em sua maioria, as reais também são acessadas remotamente. Todas as reuniões são feitas via &lt;em&gt;Web Conference&lt;/em&gt;. Seu ramal é desviado para uma ferramenta VOIP no notebook. Você disca um ramal pra falar com alguém do outro lado do planeta. Tudo através de um ponto de rede.&lt;/p&gt;&lt;p&gt;Quando você vira Home Office, a empresa te dá um limite de reembolso para comprar móveis, aparelho e conta telefônica, Internet banda larga, impressora e material de escritório. Obviamente tudo tem que ser devolvido quando você deixar de ser Home Office. Felizmente minha casa já tinha todos os requisitos necessários, e assim, não vou ficar usando uma mesa ou cadeira que não são minhas mesmo. O bom disso é que eu posso trocá-los quando eu quiser.&lt;/p&gt;&lt;p&gt;O que principalmente vai mudar para a empresa, é que agora eles terão mais uma baia livre. Uma empresa deste tamanho não cabe nos prédios onde estamos (isso mesmo, no plural). A empresa incentiva o Home Office. Uma das cláusulas da transição para Home Office é a de que você deve permanecer no mínimo um ano nesta condição. Fiquei preocupado quando li essa parte, então fui perguntar às pessoas que já eram Home Office o que eu não estava conseguindo ler nas entrelinhas. O que acontece é que na empresa que trabalho, a grande maioria dos funcionários são vendedores. Vendedor é o tipo de profissional extrovertido, que fala bastante e preza muito o contato com as pessoas. Para esse tipo de pessoa, ficar enclausurado dentro de casa é a morte. Tanto que muitas pessoas que são Home Office deixam suas casas para disputar baias vagas aos tapas lá no prédio. Minha esposa mesmo, se ficasse em casa, morreria louca mordendo o braço do sofá em menos de um mês.&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/LivroIntrovertido.jpg" border="0" /&gt;&lt;p&gt;Pois é, existem pessoas e pessoas. Como você já deve ter percebido, sou desenvolvedor de software. Sou do tipo de pessoa que prefere ter seu tempo para fazer as coisas, gosto de ter meu espaço. Não estou dizendo que tenho alergia a pessoas e que ando com um saco de papel enfiado na cabeça. Só estou dizendo que não me importo de ficar trabalhando sozinho em casa, onvindo meu som preferido enquanto faço tudo pela internet. Aliás, isso me dá um gancho pra falar de um livro que li e que imaginei nunca ter a oportunidade de comentar a respeito neste blog técnico. &lt;a href="http://publifolha.folha.com.br/catalogo/livros/145101/"&gt;Este livro&lt;/a&gt; fala justamente das diferenças entre pessoas extrovertidas e introvertidas. Explica que apesar de 75% das pessoas serem extrovertidas, não há nada errado em ser introvertido. O livro explica que pessoas introvertidas tem lá suas vantagens. Elas se concentram com maior facilidade. Conhecem menos assuntos, mas com mais profundidade. Tem menos amigos, mas são amizades mais intensas. Achei o livro interessante, com suas estatísticas e fundamentos para explicar o por quê do comportamento das pessoas.&lt;/p&gt;&lt;p&gt;Ainda não falei tudo que poderia falar sobre minha transição para Home Office, mas este post já está ficando longo demais para um &lt;em&gt;Off Topic&lt;/em&gt;.&lt;br&gt;
Até mais...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-7634273384502241046?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2008/07/agora-que-virei-home.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>3</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-3056670469352328464</guid><pubDate>Wed, 02 Jul 2008 17:54:00 +0000</pubDate><atom:updated>2008-07-02T16:40:15.833-03:00</atom:updated><title>Step into Kernel (Firewire)</title><description>&lt;p&gt;Já sei! Seu computador de teste não tem porta serial e você precisa fazer debug de Kernel nele. Creio que depois da porta serial, a maneira mais utilizada para depurar o Kernel do Windows seja utilizando uma interface &lt;a href="http://pt.wikipedia.org/wiki/FireWire"&gt;firewire&lt;/a&gt;. Ainda existe a opção de se fazer o debug de Kernel utilizando USB 2.0, mas isso ainda é para poucos, já que além de apenas ser suportado pelo Windows Vista, ainda é necessário ter um cabo especial. Os detalhes sobre debug de Kernel pela porta USB podem ser encontrados &lt;a href="http://www.driverentry.com.br/blog/2008/03/step-into-kernel-vista-usb2.html"&gt;neste post&lt;/a&gt;. Hoje a história é outra.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Mas eu não tenho porta firewire&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Larga a mão de ser chorão. O que importa aqui não é o fato de você ter ou não uma porta firewire em seu micro de desenvolvimento, mas sim o fato da máquina do cliente ter uma porta firewire. Você sabe muito bem que pela &lt;a href="http://pt.wikipedia.org/wiki/Lei_de_Murphy"&gt;Lei de Murphy&lt;/a&gt;, aquele problema que você nem sabia que existia só acontece naquela máquina que não tem portas seriais. Então melhor você estar preparado para encontrar esse tipo de coisa. Elas realmente acontecem. Sua reclamação poderia ainda ser diferente: &lt;em&gt;"Mas eu não tenho porta serial"&lt;/em&gt;. Alguns notebooks que não possuem portas seriais oferecem portas firewire, mas idependente disso, existem placas tanto &lt;a href="http://pt.wikipedia.org/wiki/Peripheral_Component_Interconnect"&gt;PCI&lt;/a&gt; quanto &lt;a href="http://pt.wikipedia.org/wiki/Pcmcia"&gt;PCMCIA&lt;/a&gt; capazes que disponibilizar a interface &lt;a href="http://pt.wikipedia.org/wiki/FireWire"&gt;IEEE 1394&lt;/a&gt;. Desta forma, seja sua máquina um desktop ou um notebook, existem meios delas ganharem portas firewire.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Configurando o lado TARGET&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Se você ainda não sabe o que significa lado HOST/TARGET e está completamente perdido sobre o assunto, leia &lt;a href="http://www.driverentry.com.br/blog/2006/12/step-into-kernel-serial.html"&gt;este post introdutório&lt;/a&gt; antes de continuar. Configurar o lado TARGET não é muito diferente do que já vimos em outros posts desta série. Podemos editar o aquivo boot.ini, como já vimos &lt;a href="http://www.driverentry.com.br/blog/2006/12/step-into-kernel-serial.html"&gt;neste post&lt;/a&gt; para adicionar as seguintes configurações de debug.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;[boot loader]
timeout=10
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)&amp;lt;&amp;lt;...&gt;&gt;/fastdetect &lt;font color="#ff0000"&gt;/debugport=1394 /channel=44&lt;/font&gt;
multi(0)disk(0)&amp;lt;&amp;lt;...&gt;&gt;/fastdetect&lt;/pre&gt;&lt;p&gt;O número do canal a ser utilizado pode ser qualquer um, mas o valor deve ser o mesmo em ambos os lados TARGET e HOST. Caso você esteja querendo depurar um Windows Vista, o método de configurar as mesmas coisas mudaram um pouco como vimos &lt;a href="http://www.driverentry.com.br/blog/2008/03/step-into-kernel-vista-usb2.html"&gt;neste outro post&lt;/a&gt;. A figura a seguir mostra os passos para setar o modo de configuração do sistema para interface IEEE 1394.&lt;/p&gt;&lt;p&gt;Lembre-se que aqui estamos apenas configurando a maneira com a qual o sistema seria depurado caso exista uma entrada de debug na lista de boot da máquina. &lt;a href="http://www.driverentry.com.br/blog/2008/03/step-into-kernel-vista-usb2.html"&gt;Este post&lt;/a&gt; mostra os detalhes de como criar uma entrada adicional nesta lista e configurá-la para debug.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/Vista1394Cmd.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Só isso? Nem doeu!&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/Disable1394.png" border="0" /&gt;&lt;p&gt;Para se fazer Kernel Debug utilizando um cabo firewire, ambos os micros devem estar rodando Windows XP ou superior, não necessariamente a mesma versão em ambos os lados. Existe uma particularidade quanto utilizar sistemas anteriores ao Windows XP SP2 ou Windows 2003 Server sem service pack no lado TARGET da história, conforme informa &lt;a href="http://msdn.microsoft.com/en-us/library/cc501183.aspx"&gt;esta página&lt;/a&gt;. Para estes sistemas, deve-se desabilitar a controladora do barramento 1394. Isso é necessário porque o Windows, que está sendo depurado inconscientemente, pode querer tentar conversar com a interface Firewire durante o debug, e isso pode fazer com que a conexão com o depurador caia. Para desabilitar essa interface nos sistemas acima citados, você deve simplesmente selecionar o ítem &lt;em&gt;"Disable"&lt;/em&gt; no menu de contexto que aparece quando você clica com o botão direito do mouse sobre a controladora firewire.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Posso desabilitar a controladora Firewire independente da versão do Windows?&lt;/strong&gt; Não, se você desabilitar a controladora em sistemas posteriores aos acima citados, você poderá não conseguir depurar o sistema quando ele mudar entre os estados de energia do sistema. &lt;strong&gt;Estados de energia? Você está falando da &lt;a href="http://pt.wikipedia.org/wiki/Aura"&gt;aura&lt;/a&gt; do computador?&lt;/strong&gt; Supondo que você está querendo depurar seu driver durante as transições energia do sistema que são gerenciadas pelo Power Manager. O Power Manager determina qual barramento pode ser desligado para economizar energia. Assim, o sistema pode decidir desligar a interface firewire durante seu debug de um driver qualquer, e dessa forma, você não vai conseguir acompanhar as &lt;a href="http://www.driverentry.com.br/blog/2007/02/legal-mas-o-que-uma-irp.html"&gt;IRPs&lt;/a&gt; de gerenciamento de energia chegando ao seu driver.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Configurando o lado HOST
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Normalmente, para fazer iniciar uma sessão de debug do lado HOST, basta abrir o WinDbg, selecionar o ítem &lt;em&gt;"Kernel Debug..."&lt;/em&gt; do menu &lt;em&gt;File&lt;/em&gt;, clicar na aba 1394, preencher o número do canal que se deseja utilizar, clicar OK como mostra a figura abaixo e correr para o abraço.&lt;/p&gt;
&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/KrnDbg1394.png" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Tudo isso que acabei de descrever continua valendo, mas para que seja possível utilizar firewire do lado HOST, o WinDbg precisa instalar os drivers virtuais de acesso ao barramento IEEE 1394, como mostra &lt;a href="http://msdn.microsoft.com/en-us/library/cc266341.aspx"&gt;nesta página&lt;/a&gt;. O WinDbg faz isso automagicamente quando você seleciona as opções acima, mas os drivers só poderão ser instalados se você estiver logado como administrador do sistema. Caso contrário você receberá a seguinte mensagem.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/AdminNeeded.png" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;&lt;strong&gt;Pô Fernando, sou desenvolvedor de driver! Você acha mesmo que não sou administrador da minha máquina?&lt;/strong&gt; Tudo bem, você pode até ser, mas mesmo sendo um administrador no Windows Vista você precisará executar o WinDbg clicando com o botão direito do mouse sobre o ícone do WinDbg e selecionar o ítem &lt;em&gt;"Run as Administrator"&lt;/em&gt;. Aí sim, você repete o procedimento descrito acima para que os drivers virtuais sejam instalados. Esse procedimento é necessário somente na primeira vêz que você utiliza a porta firewire para debug, nas próximas vezes, os drivers já estarão instalados. Para quem estiver utilizando um sistema anterior ao Vista e já estiver utilizando uma conta admininstrativa, é como meu amigo &lt;a href="http://codebehind.wordpress.com/"&gt;Thiago&lt;/a&gt; diz: &lt;em&gt;"Sai na urina"&lt;/em&gt;. Ou seja, o driver virtual será instalado e você terá a saída como mostra a figura abaixo, que demonstra as mesmas operações realizadas no Windows Vista rodando o WinDbg como Administrador.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/Install1394OK.png" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Daí em diante é só debug mesmo.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Dump Racing&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Aproveitando que estamos todos aqui reunidos, vamos fazer um teste e verificar se a velocidade do firewire ajuda mesmo com relacão à fazer Kernel Debug. Vamos imaginar a situação onde você esteja em visita a um cliente onde obviamente seu driver não está funcionando adequadamente. Lembra daquele bug que você nem sabia que existia? Pois bem, se trata de um &lt;a href="http://pt.wikipedia.org/wiki/Deadlock"&gt;deadlock&lt;/a&gt;. Deadlock são especialmente queridos na hora de fazer debug, porque você está lá quando o problema acontece, mas &lt;a href="http://www.driverentry.com.br/blog/2007/09/como-assim-eu-no-gosto-de-tela-azul.html"&gt;tela azul que é bom&lt;/a&gt; nada. Nessa situação, você pode gerar um arquivo de dump da máquina e deixar para analizar o problema em casa, afinal de contas, roupa suja se lava em casa, e assim poder liberar a máquina do cliente para uso, já que normalmente nessas situações, ficam umas três pessoas em cima de você perguntando &lt;em&gt;"E aí? Descobriu o problema?"&lt;/em&gt; a cada 3 minutos. Configurei meu desktop aqui de casa para fazer debug do Windows Vista por uma porta serial. Esta máquina tem 2GB de memória RAM. Um arquivo de dump full é uma cópia de tudo que esta na memória do computador naquele instante, por isso, nada mais justo que este arquivo tenha aproximadamente 2GB de tamanho. Este arquivo pode ser gerado na máquina HOST durante uma sessão de debug utilizando o comando &lt;a href="http://msdn.microsoft.com/en-us/library/cc266761.aspx"&gt;.dump&lt;/a&gt;. Segue abaixo a saída deste comando quando utilizado sobre uma conexão serial.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;0: kd&gt; .dump /f c:\Temp\SERIAL.DMP
Creating a full kernel dump over the COM port is a &lt;font color="#ff0000"&gt;VERY VERY slow&lt;/font&gt; operation.
This command may take &lt;font color="#ff0000"&gt;many HOURS&lt;/font&gt; to complete.  Ctrl-C if you want to terminate the command.
Creating c:\Temp\SERIAL.DMP - Full kernel dump
Percent written 0
Percent written 1
Percent written 2
        :
        :&lt;/pre&gt;&lt;p&gt;Vocês repararam na mensagem ameaçadora que nos foi exibida? Particularmente penso que estes engenheiros de software são todos uns desesperados, provavelmente por causa quantidade de café que eles consomem por dia. Já posso até imaginar quantos nem esperam o dump começar para já pressionar CTRL+C e interromper o processo. São uns covardes mesmo. Bom, já que isso vai me custar algum tempo, vou aproveitar para dar uma mijada.&lt;/p&gt;&lt;p&gt;Muito, mas muito tempo mesmo depois...&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;        :
        :
Percent written 97
Percent written 98
Percent written 99
Dump successfully written
0: kd&gt;&lt;/pre&gt;&lt;p&gt;OK, tudo bem até aqui. Agora vamos repetir o processo em uma sessão de debug utilizando o cabo firewire. A mesma máquina com a mesma quantidade de memória e até o mesmo comando.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;1: kd&gt; .dump /f c:\Temp\FIREWIRE.DMP
Creating c:\Temp\FIREWIRE.DMP - Full kernel dump
Percent written 0
Percent written 5
Percent written 10
        :
        :
Percent written 90
Percent written 95
Dump successfully written
1: kd&gt;&lt;/pre&gt;&lt;p&gt;Tá, tudo bem, a contagem vai de cinco em cinco ao invés de um em um como foi com o cabo serial, grande coisa. Não é a toa que demore mais pela porta serial, eles gastam processamento com tudo. Agora podemos comparar as datas de criação e modificação nos atributos de cada arquivo para poder determinar quanto tempo levou para poder gerá-los.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/DumpRacing.PNG" /&gt;&lt;/div&gt;&lt;br&gt;
&lt;p&gt;Lembrando que o mês de junho termina no dia 30, podemos concluir que o dump pela porta serial levou aproximadamente 2 dias, 5 horas e 12 minutos. É uma pena essa janela não mostrar os segundos para termos mais precisão aqui. De qualquer forma, lembrando também que cada minuto tem 60 segundos, o mesmo dump gerado pela porta firewire levou aproximadamente 7 minutos. Nossa, foi quase! Se não fosse por essa pequena vantagem de 3187 minutos.&lt;/p&gt;&lt;p&gt;Até mais...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-3056670469352328464?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2008/07/step-into-kernel-firewire.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>2</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-4147677066616088458</guid><pubDate>Tue, 24 Jun 2008 19:23:00 +0000</pubDate><atom:updated>2008-06-25T14:24:53.354-03:00</atom:updated><title>A matéria mais difícil</title><description>&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/suicide-teddy.jpg" border="0" /&gt;&lt;p&gt;Como alguns de vocês já estão cansados de saber, estou cursando o quinto ano de Engenharia da Computação. No começo do curso, naturalmente pastei para poder aprender Cálculo, Álgebra, Física e aquelas outras matérias que tiram fumacinha da calculadora.&lt;/p&gt;&lt;p&gt;Como não pude sair do meu colegial técnico e ir direto para uma universidade, trabalhei um pouco na área até eu conseguir pagar a mensalidade de uma faculdade. Estagiário ganha pouco mesmo. Esse tempo, além de me trazer uma graninha extra por mês, também me trouxe um cadim de experiência. Não foi muita, mas o suficiente para identificar possíveis deslises de professores durante as aulas de programação. O curso de Engenharia da Computação tem duração de seis anos, e durante esse tempo eu ainda tinha que trabalhar, mesmo porque eu tinha que pagar a faculdade. Durante esse tempo acabei lendo um livro ou dois, ou três, quatro, cinco, enfim, alguns.&lt;/p&gt;&lt;p&gt;É natural os professores tenderem a não explicar exatamente como as coisas são ou funcionam, e assim, ganhar um pouco de tempo para poder abordar mais assuntos com menos detalhes. Afinal, você não aprende tudo o que precisa na universidade, mas você já consegue ter uma boa noção de onde você está se enfiando quando for trabalhar na área e por onde começar a buscar mais detalhes sobre determinado assunto. Eu mesmo, nos cursos de drivers que já dei, tento explicar o essencial, o que o aluno vai mesmo utilizar, mas sempre tento deixar claro que existem detalhes a serem vistos.&lt;/p&gt;&lt;p&gt;Durante o curso, eu mal podia esperar para ver as matérias técnicas, que me abririam os olhos para uma nova maneira de ver os dispositivos eletrônicos, circuitos integrados e os computadores. Depois de engolir algumas atravessadas de professores de Linguagem C e de Engenharia se Software, finalmente comecei a estudar algo realmente interessante. A matéria de Sistemas Operacionais é mesmo surpreendente. Nela estudamos diferentes sistemas operacionais que rodam sobre diferentes arquiteturas de computadores. Ufa! Pelo menos uma arquitetura e um sistema operacional eu conheço razoavelmente bem.&lt;/p&gt;&lt;p&gt;Tudo bem o professor dizer que &lt;span style="font-style:italic;"&gt;Thread&lt;/span&gt; e &lt;span style="font-style:italic;"&gt;Processo&lt;/span&gt; é a mesma coisa, explicar &lt;a href="http://en.wikipedia.org/wiki/Inter-process_communication"&gt;Inter Process Communication&lt;/a&gt; sem nem tocar no assunto &lt;a href="http://en.wikipedia.org/wiki/Virtual_memory"&gt;Memória Virtual&lt;/a&gt; e &lt;a href="http://en.wikipedia.org/wiki/Marshalling_%28computer_science%29"&gt;Marshaling&lt;/a&gt;, mas foi show tentar engolir a explicação sobre Sincronismo e Gerenciamento de Interrupções dos sistemas operacionais. Tudo bem, eu só posso dizer que o professor escorregou em Windows, eu não conheço todos os outros Sistemas Operacionais. Acho que ele só escolheu o sistema operacional errado na hora de dar um exemplo infeliz. Não me lembro das palavras exatas, mas meu professor nos ensinou que o Windows XP ainda não é um sistema operacional preemptivo, e que as chamadas ao sistema operacional não podiam acontecer concorrentemente. Primeiro uma, depois a outra e assim por diante. Particularmente acho que ele deve ter confundido XP com 9X. Afinal os dois possuem a letra X. Nessa hora eu tive que parar de ler o &lt;a href="http://www.amazon.com/Developing-Drivers-Windows-Foundation-Developer/dp/0735623740/ref=pd_bbs_sr_1?ie=UTF8&amp;s=books&amp;qid=1214337511&amp;sr=8-1"&gt;livro que eu estava lendo&lt;/a&gt; e pedir para ele repetir aquilo. Eu poderia ter ouvido errado, mas infelizmente não foi isso que aconteceu. Ele disse aquilo mesmo. Mais tarde na mesma aula ele nos disse que o Windows XP ainda não consegue gerenciar interrupções adequadamente, que se uma interrupção ocorre enquanto um processo está rodando, o sistema normalmente espera o processo inteiro terminar para que só depois ele possa atender à uma interrupção.&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/madagascar-penguins.JPG" border="0" /&gt;&lt;p&gt;Como em toda sala de aula universitária, sempre existe um grupinho de alunos que odeiam o Windows e defendem uma outra opção de Sistema Operacional. Nada contra eles, nem contra a opção deles, mas de vêz em quando me aparece cada figura que é um caso sério. Enfim, estes logo disseram ironicamente aos risos: &lt;span style="font-style:italic;"&gt;"É lógico! Porque é o mais inteligente a se fazer"&lt;/span&gt;. Assim todos na sala começaram a rir desse péssimo exemplo de gerenciamento de interrupções do Windows. Eu nunca falo nada, sou aquele cara que chega uns 30 minutos mais cedo na sala pra poder ler meu livro em paz. Não saio fazendo campanha a favor do Windows nem contra outros sistemas. Não fico explicando como completar uma &lt;a href="http://www.driverentry.com.br/blog/2007/02/legal-mas-o-que-uma-irp.html"&gt;IRP&lt;/a&gt; para cada pessoa que encontro no café, mas essa eu não podia deixar passar em branco. Perguntei ao professor: &lt;span style="font-style:italic;"&gt;"Nossa, que interessante isso! Onde foi mesmo que o senhor leu isso?"&lt;/span&gt;. &lt;span style="font-style:italic;"&gt;"Em qualquer livro de Sistemas Operacionais"&lt;/span&gt; ele me respondeu. Vejam só que inocência da minha parte, eu imaginava que os livros clássicos de Sistemas Operacionais tinham sido escritos quando o Windows XP ainda nem existia. Então eu disse: &lt;span style="font-style:italic;"&gt;"Não é por nada, é que não é isso que tenho visto nos livros que tenho lido. Talvêz eu esteja lendo os livros errados."&lt;/span&gt;&lt;/p&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.amazon.com/Programming-Microsoft-Windows-Driver-Second/dp/0735618038/ref=pd_bbs_sr_1?ie=UTF8&amp;s=books&amp;qid=1214412567&amp;sr=1-1"&gt;&lt;img src="http://www.driverentry.com.br/images/WDMBook.jpg" border="0"/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Windows Driver Model (1999)&lt;/span&gt;&lt;br&gt;&lt;blockquote&gt;&lt;span style="font-style:italic;"&gt;"The operating system can preempt any subroutine at any moment for an arbitrarily long period of time, so we cannot be sure of completing critical tasks without interfere or delay. Even if we take steps to prevent preemption, code executing simultaneously on other CPU in same computer can interfere with our code."&lt;/span&gt;&lt;/blockquote&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.amazon.com/Windows-Device-Development-Classic-Reprints/dp/0976717522/ref=pd_bbs_sr_2?ie=UTF8&amp;s=books&amp;qid=1214412624&amp;sr=1-2"&gt;&lt;img src="http://www.driverentry.com.br/images/devicedriversml.jpg" border="0"/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Windows NT Device Driver Development (1999)&lt;/span&gt;&lt;br&gt;&lt;blockquote&gt;&lt;span style="font-style:italic;"&gt;"As was mentioned in Chapter 1, Windows NT is a pre-emptive, multithreaded, and multitasking, operating system. It employs a traditional operating system technique to provide this multitasking capability by associating a quantum with each thread when it starts running."&lt;/span&gt;&lt;/blockquote&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.temporeal.com.br/produtos.php?id=153786"&gt;&lt;img src="http://www.driverentry.com.br/images/Desvendando.jpg" border="0"/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Desvendando o Windows NT (1998)&lt;/span&gt;&lt;br&gt;&lt;blockquote&gt;&lt;span style="font-style:italic;"&gt;"Como o Windows NT implementa um escalador preemptivo, se outro thread, com uma prioridade mais alta se aprontar para execução, o thread sendo executado atualmente é solicitado antes de terminar sua fatia de tempo."&lt;/blockquote&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.amazon.com/Microsoft-Windows-Internals-4th-Server/dp/0735619174/ref=pd_bbs_sr_1?ie=UTF8&amp;s=books&amp;qid=1214412699&amp;sr=1-1"&gt;&lt;img src="http://www.driverentry.com.br/images/Winternals.jpg" border="0"/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Windows Internals (2005):&lt;/span&gt;&lt;br&gt;&lt;blockquote&gt;&lt;span style="font-style:italic;"&gt;"Windows implements a priority-driven, preemptive scheduling system the highest priority runnable (ready) thread always runs, with the caveat that thread chosen to run might be limited by the processors on wich the thread is allowed to run, a phenomenom called Processor Afinitty."&lt;/span&gt;&lt;/blockquote&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.amazon.com/Developing-Drivers-Windows-Foundation-Developer/dp/0735623740/ref=pd_bbs_sr_1?ie=UTF8&amp;s=books&amp;qid=1214412741&amp;sr=1-1"&gt;&lt;img src="http://www.driverentry.com.br/images/WDFBook.jpg" border="0"/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Developing Drivers with Windows Driver Foundation (2007):&lt;/span&gt;&lt;br&gt;&lt;blockquote&gt;&lt;span style="font-style:italic;"&gt;"Windows is a preemptive multitasking operating system in wich multiple thread can try to aceess shared data or resources concurrently and multiple drivers functions can run concurrently."&lt;/span&gt;&lt;/blockquote&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;Enfim, é óbvio que não vou discutir com meu professor e tornar minha vida acadêmica um inferno. Mais uma vez vou respirar fundo e fazer dizer aquele sonoro &lt;span style="font-style:italic;"&gt;"Ahhhh tá..."&lt;/span&gt;.&lt;/p&gt;&lt;p&gt;É nestes momentos que eu fico preocupado sobre um outro aspecto. Felizmente posso ouvir e filtrar o que professores de programação em Linguagem C e Sistemas Operacionais dizem, mas nada posso fazer quanto aos professores de Eletrônica, Inteligência Artificial, Microcontroladores e outras tantas matérias que estamos aprendendo. Será que estamos ouvindo as mesmas barbaridades e não sabemos?&lt;/p&gt;&lt;p&gt;De qualquer forma, a matéria que pensei que seria a mais fácil, agora é uma das que me atormentam.&lt;br&gt;Leia sempre...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-4147677066616088458?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2008/06/matria-mais-difcil.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>7</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-8613671958810854749</guid><pubDate>Mon, 23 Jun 2008 19:38:00 +0000</pubDate><atom:updated>2008-06-24T11:39:51.958-03:00</atom:updated><title>Utilizando o Registry (Parte 2)</title><description>&lt;p&gt;Pois é, como eu estava dizendo na &lt;a href="http://www.driverentry.com.br/blog/2008/06/utilizando-o-registry-parte-1.html"&gt;primeira parte deste post&lt;/a&gt;. Isso é muito código. Todo esse negócio parece trabalhoso, não? Montar uma &lt;a href="http://msdn.microsoft.com/en-us/library/aa492030.aspx"&gt;UNICODE_STRING&lt;/a&gt; para o caminho da chave que se deseja acessar, montar um &lt;a href="http://msdn.microsoft.com/en-us/library/aa491657.aspx"&gt;OBJECT_ATTRIBUTES&lt;/a&gt; com ela, abrir handle, determinar o tamanho dos valores a serem lidos, alocar um buffer grande suficiente, ler o valor que se deseja, desalocar qualquer buffer temporário utilizado no processo, e por fim, fechar o handle para a chave do registro. Você pode conferir quanto código utilizamos em nosso &lt;a href="http://www.driverentry.com.br/samples/KernelReg.zip"&gt;exemplo do post anterior&lt;/a&gt; para se ler dois valores no registro. Hoje veremos um grupo de rotinas de manipulação de registro que facilita esse processo.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Rotinas RTL de Registro&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;São apenas cinco as rotinas que compõem esse grupinho. &lt;a href="http://msdn.microsoft.com/en-us/library/aa489620.aspx"&gt;Na referência&lt;/a&gt; você encontra uma descrição de cada uma delas. Esse grupo de rotinas tem como característica comum o fato de não termos que utilizar handles para as chaves. Os handles são abertos e fechados para cada operação. Outra característica comum é que você não precisa compor o nome completo da chave na qual estão os valores que você quer ler ou escrever. Essas rotinas trabalham com um esquema de nome de chave relativo a algum lugar pré-definido. Confuso? Vamos dar uma olhada numas das rotinas para que tenhamos um exemplo mais prático. Tentando seguir a mesma idéia do exemplo dado no post anterior, precisamos inicialmente verificar se a chave, onde os valores estão armazenados, existe de fato. Tem uma &lt;a href="http://msdn.microsoft.com/en-us/library/ms804317.aspx"&gt;funçãozinha&lt;/a&gt; justamente para isso.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;NTSTATUS 
  RtlCheckRegistryKey(
    IN ULONG  RelativeTo,
    IN PWSTR  Path
    );&lt;/pre&gt;&lt;p&gt;Notem que o primeiro parâmetro dessa rotina é uma constante que indica qual será a referência a ser aplicada ao segundo parâmetro. Observe a tabela abaixo que foi retirada da referência.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/RegRefTable.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Um exemplo seria o seguinte: Se usarmos RTL_REGISTRY_ABSOLUTE como primeiro parâmetro, o segundo deve ser o caminho completo da chave de registro. Afinal, temos que passar o caminho absoluto da chave. Por outro lado, se utilizarmos RTL_REGISTRY_SERVICES, o segundo parâmetro deveria ser apenas o complemento do caminho referente a chave &lt;em&gt;Services&lt;/em&gt;. Foi esta a opção que utilizei no exemplo deste post. Observe como ficou nossa rotina de criação dos parâmetros no registro.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="comment"&gt;/****
***     CreateParams
**
**      Esta rotina é chamada quando não for detectada a chave
**      que contém os parâmetros. Ela cria a chave e os parâmetros
**      com valores pré-definidos.
*/&lt;/span&gt;
NTSTATUS
CreateParams(VOID)
{
    NTSTATUS        nts = STATUS_SUCCESS;
    WCHAR           wzParam[] = L&lt;span class="literal"&gt;"DriverEntry.com.br"&lt;/span&gt;;
    ULONG           ulParam = 0x12345678;
&amp;nbsp;
    &lt;span class="keyword"&gt;__try&lt;/span&gt;
    {
        &lt;span class="comment"&gt;//-f--&gt; Cria a chave onde os parâmetros serão armazenados.
&lt;/span&gt;        &lt;span class="comment"&gt;//      Simples assim.
&lt;/span&gt;        nts = RtlCreateRegistryKey(RTL_REGISTRY_SERVICES,
                                   L&lt;span class="literal"&gt;"KernelReg2\\Parameters"&lt;/span&gt;);
        &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
            ExRaiseStatus(nts);
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Aqui escrevemos o valor numérico
&lt;/span&gt;        nts = RtlWriteRegistryValue(RTL_REGISTRY_SERVICES,
                                    L&lt;span class="literal"&gt;"KernelReg2\\Parameters"&lt;/span&gt;,
                                    L&lt;span class="literal"&gt;"DoubleWord"&lt;/span&gt;,
                                    REG_DWORD,
                                    &amp;amp;ulParam,
                                    &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(ulParam));
        &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
            ExRaiseStatus(nts);
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; E aqui a String. Notem que não foi necessário
&lt;/span&gt;        &lt;span class="comment"&gt;//      criar um UNICODE_STRING para isso.
&lt;/span&gt;        nts = RtlWriteRegistryValue(RTL_REGISTRY_SERVICES,
                                    L&lt;span class="literal"&gt;"KernelReg2\\Parameters"&lt;/span&gt;,
                                    L&lt;span class="literal"&gt;"String"&lt;/span&gt;,
                                    REG_SZ,
                                    wzParam,
                                    &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(wzParam));
    }
    &lt;span class="keyword"&gt;__except&lt;/span&gt;(EXCEPTION_EXECUTE_HANDLER)
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops!
&lt;/span&gt;        nts = GetExceptionCode();
        ASSERT(FALSE);
    }
&amp;nbsp;
    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;Mais uma vêz, o código demonstrado aqui está disponível para download ao final deste post.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;O fácil que complica&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Pelo que pudemos ver até aqui, esse grupo de rotinas já facilitou um pouco nossa vida. Mas é na rotina de leitura que podemos ver a economia de código acontecer de verdade. O custo disso fica por conta de entender como a &lt;a href="http://msdn.microsoft.com/en-us/library/ms803008.aspx"&gt;rotina de leitura&lt;/a&gt; funciona.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;NTSTATUS 
  RtlQueryRegistryValues(
    IN ULONG  RelativeTo,
    IN PCWSTR  Path,
    IN PRTL_QUERY_REGISTRY_TABLE  QueryTable,
    IN PVOID  Context,
    IN PVOID  Environment  OPTIONAL
    );&lt;/pre&gt;&lt;p&gt;Olhando assim nem parece que ela morde, mas se você der uma olhada na referência, verá que ela pode ser bem flexivel. Flexibilidade as vezes é traduzido por &lt;em&gt;"Difícil de acertar a maneira certa de utilizar"&lt;/em&gt;. Os dois primeiros parâmetros são os já conhecidos de antes. A coisa começa a mudar com o terceiro parâmetro, que é uma tabela que contém os detalhes dos valores a serem lidos do registro. Não vou entrar nos detalhes de cada parâmetro desta rotina neste post. Vou apenas fazer o código equivalente ao exemplo do post anterior.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="comment"&gt;/****
***     LoadParams
**
**      Esta rotina carrega as variaveis globais com os
**      valores recuperados do registro.
*/&lt;/span&gt;
NTSTATUS
LoadParams(VOID)
{
    NTSTATUS                    nts = STATUS_SUCCESS;
    RTL_QUERY_REGISTRY_TABLE    QueryTable[] =
    {
        { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED,
          L&lt;span class="literal"&gt;"DoubleWord"&lt;/span&gt;, &amp;amp;g_ulParam, REG_NONE, NULL, 0 },
        { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED,
          L&lt;span class="literal"&gt;"String"&lt;/span&gt;, &amp;amp;g_usParam, REG_NONE, NULL, 0 },
        { NULL, 0, NULL, NULL, REG_NONE, 0, NULL }
    };
&amp;nbsp;
    &lt;span class="keyword"&gt;__try&lt;/span&gt;
    {
        &lt;span class="comment"&gt;//-f--&gt; Caso esta rotina seja chamada mais de uma vez, vamos liberar
&lt;/span&gt;        &lt;span class="comment"&gt;//      o buffer deste parâmetro.
&lt;/span&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt; (g_usParam.Buffer)
            RtlFreeUnicodeString(&amp;amp;g_usParam);
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Aqui todos os valores da tabela são lidos
&lt;/span&gt;        nts = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
                                     L&lt;span class="literal"&gt;"KernelReg2\\Parameters"&lt;/span&gt;,
                                     QueryTable,
                                     NULL,
                                     NULL);
        &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
            ExRaiseStatus(nts);
    }
    &lt;span class="keyword"&gt;__except&lt;/span&gt;(EXCEPTION_EXECUTE_HANDLER)
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops!
&lt;/span&gt;        nts = GetExceptionCode();
        ASSERT(FALSE);
    }
&amp;nbsp;
    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;Não, não está faltando nada. É só isso mesmo. Vamos olhar mais de perto o que fizemos aqui. A tabela que preenchemos tem os dados das leituras a serem realizadas e é definida como mostra abaixo.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="keyword"&gt;typedef&lt;/span&gt; &lt;span class="keyword"&gt;struct&lt;/span&gt; _RTL_QUERY_REGISTRY_TABLE {
    PRTL_QUERY_REGISTRY_ROUTINE QueryRoutine;
    ULONG Flags;
    PWSTR Name;
    PVOID EntryContext;
    ULONG DefaultType;
    PVOID DefaultData;
    ULONG DefaultLength;
} RTL_QUERY_REGISTRY_TABLE, *PRTL_QUERY_REGISTRY_TABLE;&lt;/pre&gt;&lt;p&gt;Olhando cada membro dela temos:&lt;/p&gt;&lt;p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;QueryRoutine:&lt;/strong&gt; Aqui você tem um ponteiro de função que será chamado quando o este ítem da tabela for lido no registro. Não usamos este método em nosso exemplo. Existem algumas coisas interessantes sobre essa rotina de callback. Ela é chamada para cada ítem desta tabela, mas se o valor a ser lido for do tipo REG_MULTI_SZ, a rotina de callback é chamada uma vez para cada string contida neste valor. Consulte a referência para uma completa descrição destes detalhes.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Flags:&lt;/strong&gt; O flag que utilizamos aqui foi o RTL_REGISTRY_DIRECT que sinaliza que não usaremos o método do callback. Outro flag é o RTL_QUERY_REGISTRY_REQUIRED que indica que o valor descrito nesta linha da tabela é obrigatório. Caso o valor não seja encontrado, RtlQueryRegistryValues retorna erro e os demais valores não serão lidos.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Name:&lt;/strong&gt; Aqui vem o nome do valor a ser lido. Note que não é um UNICODE_STRING, mas um array de WCHAR terminado em zero.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;EntryContext:&lt;/strong&gt; Em nosso exemplo, este parâmetro é o buffer onde o valor lido será armazenado, mas nos casos onde recebemos a chama pela a rotina de callback, este valor é passado como um dos parâmetros.&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;Os parâmetros &lt;strong&gt;DefaultType&lt;/strong&gt;, &lt;strong&gt;DefaultData&lt;/strong&gt; e &lt;strong&gt;DefaultLengh&lt;/strong&gt; seriam utilizados caso o valor não existisse e não fosse um valor obrigatório.&lt;/p&gt;&lt;p&gt;Cada valor a ser lido é representado por uma linha nesta tabela. A rotina identifica o fim da tabela quando encontrar os membros &lt;em&gt;QueryRotuine&lt;/em&gt; e &lt;em&gt;Name&lt;/em&gt; forem NULL.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Calma aí Fernando! Muito bem, nada nessa manga, nada nessa e os valores são lidos? Espertinho você hein? Pensou que ia enganar todo mundo com essa conversinha? Muito bem! Onde é que eu indico o tamanho do buffer oferecido para a leitura?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Muito boa a sua pergunta! Acho até que eu mesmo não teria feito uma pergunta tão boa. O tamanho o buffer é indicado pelo próprio buffer. No caso da string, passamos um ponteiro para um UNICODE_STRING, o tamanho do buffer da string já é descrito por essa estrutura, mas se o buffer desta estrutura for NULL, a rotina aloca o buffer do tamanho necessário. Temos que desalocar esse buffer quando não for mais utilizá-lo. Para leituras onde o buffer resultante é menor ou igual ao sizeof(ULONG) então o valor resultante é armazenado diretamente no lugar apontado por EntryContext. Para demais detalhes consulte a referência.&lt;/p&gt;&lt;p&gt;Esta função é bem flexível e não vou conseguir colocar tudo que essa rotina oferece em um único post, mas sintam-se a vontade para mandar suas dúvidas em relação a ela.&lt;/p&gt;&lt;p&gt;Have fun!&lt;/p&gt;&lt;p&gt;Download &lt;a href="http://www.driverentry.com.br/samples/KernelReg2.zip"&gt;KernelReg2.zip&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32803974-8613671958810854749?l=www.driverentry.com.br%2Fblog' alt='' /&gt;&lt;/div&gt;</description><link>http://www.driverentry.com.br/blog/2008/06/utilizando-o-registry-parte-2.html</link><author>fernando@driverentry.com.br (Fernando Roberto da Silva)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-32803974.post-2278751614243422935</guid><pubDate>Tue, 17 Jun 2008 02:22:00 +0000</pubDate><atom:updated>2008-06-19T10:29:43.488-03:00</atom:updated><title>Utilizando o Registry (Parte 1)</title><description>&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/Registry.PNG" border="0" /&gt;&lt;p&gt;No último post, em resposta à uma dúvida de um leitor, comentei um pouco sobre &lt;a href="http://www.driverentry.com.br/blog/2008/06/criando-e-usando-ioctls.html"&gt;como criar e usar novas IOCTLs&lt;/a&gt;. Afinal de contas, o lema desse blog é &lt;em&gt;"Servir bem para servir sempre"&lt;/em&gt;. Dúvidas de leitores são ótimas fontes de sugestões para novos posts. Creio que como em qualquer outra especialidade, o desenvolvimento de drivers é um tópico que pode ser desmembrado em muitas e muitas partes. Não é a toa que o &lt;a href="http://www.amazon.com/Windows-2000-Device-Driver-Book/dp/0130204315/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1213670808&amp;sr=1-1"&gt;menor livro que eu conheço sobre desenvolvimento de drivers&lt;/a&gt; não tenha menos de quatrocentas páginas. Por isso é bom saber quais são as dificuldades dos leitores para saber sobre qual assunto postar aqui. Sintam-se à vontade para mandar novas sugestões ou dúvidas. Já há algum tempo, recebi um e-mail de Fábio Dias (Fortaleza, CE), que sugeriu um post sobre como acessar o &lt;a href="http://en.wikipedia.org/wiki/Windows_Registry"&gt;Registry&lt;/a&gt; a partir de um driver. Muito bem, vamos lá.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;HKEY_LOCAL_MACHINE é coisa se User Mode&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Antes de sair dizendo quais APIs você deve usar para acessar o registro, vamos antes dar uma olhada em como o registro está organizado. Creio que o primeiro passo aqui seja falar sobre como abrir uma chave do registro. Assim como em &lt;em&gt;User Mode&lt;/em&gt;, temos que ter uma string que informe o caminho da chave a qual queremos abrir, e assim obter um handle para ela. &lt;strong&gt;Opa! Eu disse handle?&lt;/strong&gt; Sendo assim, vale comentar que as chaves do registro também são recursos gerenciados pelo &lt;strong&gt;Object Manager&lt;/strong&gt;. As chaves do registro estão todas armazenadas sob o namespace &lt;strong&gt;"\Registry"&lt;/strong&gt;. Desta forma, enquanto usamos HKEY_LOCAL_MACHINE em &lt;em&gt;User Mode&lt;/em&gt; como nome de uma das divisões básicas do registro, em &lt;em&gt;Kernel Mode&lt;/em&gt; usamos &lt;strong&gt;"\Registry\Machine"&lt;/strong&gt;. E de maneira análoga à HKEY_USERS temos &lt;strong&gt;"\Registry\Users"&lt;/strong&gt;. Mais detalhes &lt;a href="http://msdn.microsoft.com/en-us/library/aa489583.aspx"&gt;aqui&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Qual é o CurrentControlSet?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;A rotina &lt;em&gt;DriverEntry&lt;/em&gt; de qualquer driver para Windows NT recebe dois parâmetros de entrada, sendo um deles um ponteiro para a estrutura &lt;a href="http://msdn.microsoft.com/en-us/library/ms805988.aspx"&gt;DRIVER_OBJECT&lt;/a&gt; que representa a instância do nosso driver, e o outro parâmetro é um &lt;a href="http://msdn.microsoft.com/en-us/library/aa492030.aspx"&gt;UNICODE_STRING&lt;/a&gt; contendo o caminho do registro que indica onde nosso driver está cadastrado. &lt;strong&gt;Mas como assim? Nosso driver pode não saber como ele foi cadastrado no registro?&lt;/strong&gt; Aqui na referência é simples:&lt;/p&gt;&lt;div align=left&gt;&lt;em&gt;&lt;strong&gt;&lt;blockquote&gt;"The registry path string pointed to by RegistryPath is of the form \Registry\Machine\System\CurrentControlSet\Services\DriverName"&lt;/blockquote&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/div&gt;&lt;p&gt;Tudo bem, vamos pegar carona em um driver qualquer e dar uma olhada nisso utilizando o &lt;a href="http://msdn.microsoft.com/en-us/library/cc266321.aspx"&gt;WinDbg&lt;/a&gt;. Vamos abrir um parênteses aqui para informar aos mais novos neste blog que estou utilizando uma máquina virtual para fazer os testes com o driver de exemplo, como explica &lt;a href="http://www.driverentry.com.br/blog/2007/05/step-into-kernel-vmwarewindbg.html"&gt;neste post&lt;/a&gt;. Isso permite que eu possa fazer Debug de Kernel sem necessariamente ter que utilizar duas máquinas. Para facilitar a substituição do driver a cada modificação que faço, estou utilizando o mapeamento de drivers do WinDbg, como explica &lt;a href="http://www.driverentry.com.br/blog/2007/07/bug-em-meu-driver-de-boot-j-posso.html"&gt;neste outro post&lt;/a&gt;. Este recurso permite que o WinDbg sempre carregue uma nova versão driver na máquina TARGET sem que eu tenha que necessariamente substituir o driver nela. Fecha parênteses.&lt;/p&gt;&lt;p&gt;Antes mesmo do driver ser carregado, coloquei um breakpoint na rotina &lt;em&gt;DriverEntry&lt;/em&gt;. &lt;strong&gt;Opa! Como você pode colocar um breakpoint em um driver que ainda não foi carregado?&lt;/strong&gt; Ah tá... Você pode fazer isso utilizando o comando &lt;strong&gt;bu&lt;/strong&gt; como mostra abaixo. Os breakpoints são setados quando o driver for carregado. Na linha seguinte, eu apenas listo os breakpoints, &lt;strong&gt;só pra...&lt;/strong&gt; Quando o driver é carregado, a execução pára em meu breakpoint e uso a extensão &lt;strong&gt;!ustr&lt;/strong&gt;, que mostra UNICODE_STRINGs, para ver o valor desse meu segundo parâmetro da &lt;em&gt;DriverEntry&lt;/em&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;font color="#ff0000"&gt;kd&gt; bu KernelReg!DriverEntry
kd&gt; bl&lt;/font&gt;
 0 eu             0001 (0001) (KernelReg!DriverEntry)
&amp;nbsp;
kd&gt; g
KD: Accessing 'Z:\Sources\DriverEntry\KernelReg\objchk_w2k_x86\i386\KernelReg.sys'
    (\??\C:\WINDOWS\system32\drivers\KernelReg.sys)
  File size 2K.
MmLoadSystemImage: Pulled \??\C:\WINDOWS\system32\drivers\KernelReg.sys from kd
Breakpoint 0 hit
KernelReg!DriverEntry:
f8d394a0 8bff            mov     edi,edi
&lt;font color="#ff0000"&gt;kd&gt; !ustr poi(pusRegistryPath)&lt;/font&gt;
String(114,114) at 82302000: \REGISTRY\MACHINE\SYSTEM\&lt;font color="#ff0000"&gt;ControlSet001&lt;/font&gt;\Services\KernelReg&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Mas não era pra ser CurrentControlSet?&lt;/strong&gt; Na verdade o &lt;em&gt;CurrentControlSet&lt;/em&gt; é um link para um outro &lt;em&gt;ControlSet&lt;/em&gt;. No caso observado acima, o &lt;em&gt;CurrentControlSet&lt;/em&gt; está refletindo o &lt;em&gt;ControlSet001&lt;/em&gt;, ou seja, as alterações feitas no &lt;em&gt;ControlSet001&lt;/em&gt; são vistas no &lt;em&gt;CurrentControlSet&lt;/em&gt; e vice-versa. O &lt;em&gt;CurrentControlSet&lt;/em&gt; pode refletir qualquer outro &lt;em&gt;ControlSet&lt;/em&gt;. Algumas regras determinam quando eles mudam, como em casos de mudanças de configurações de sistema ou instalações de drivers. Para saber para onde o &lt;em&gt;CurrentControlSet&lt;/em&gt; está apontando, dê uma olhada no valor &lt;strong&gt;Current&lt;/strong&gt; que está na chave &lt;em&gt;"\Registry\Machine\System\Select"&lt;/em&gt; como mostra a figura abaixo.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/RegCurrent.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Mas isso é apenas para efeito de curiosidade. Quando você utiliza &lt;em&gt;CurrentControlSet&lt;/em&gt; como parte do caminho da chave que você deseja acessar, o &lt;em&gt;Object Manager&lt;/em&gt; faz a traducão pra você e todos vivem felizes para sempre.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Cadê \Registry\Machine\Software?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Já pode ter acontecido com alguns de vocês. Vocês escrevem um driver que deve ler uma certa chave dentro de &lt;em&gt;"\Registry\Machine\Software"&lt;/em&gt; quando o driver é carregado. Enquanto você faz testes com o driver, que neste momento tem seu &lt;em&gt;Start&lt;/em&gt; configurado para &lt;em&gt;Manual(3)&lt;/em&gt;, tudo funciona que é uma maravilha, mas na hora de mudar o &lt;em&gt;Start&lt;/em&gt; do driver para &lt;em&gt;Boot(0)&lt;/em&gt; ou &lt;em&gt;System(1)&lt;/em&gt;, não conseguimos abrir a chave recebendo &lt;strong&gt;STATUS_OBJECT_NAME_NOT_FOUND&lt;/strong&gt; (0xC00000034).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Como assim nome não encontrado? Estava aqui agora mesmo!&lt;/strong&gt; O que acontece é que a chave SOFTWARE é montada mais tarde. Assim, durante o boot do sistema ela ainda não existe. Para fazer acesso a essa chave, você terá que esperar um &lt;em&gt;cadim&lt;/em&gt;, talvez utilizando a tática &lt;a href="http://www.driverentry.com.br/blog/2007/06/comear-de-novo.html"&gt;deste post&lt;/a&gt;. Aí tudo funciona que é uma &lt;em&gt;beless&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Algo a dizer sobre HKEY_CURRENT_USER?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Então, veja bem. Quem é o &lt;em&gt;Current User&lt;/em&gt;? Usuário que está fazendo a chamada, certo? Assim, conforme a tradição, você começa pensando que é fácil e estando logado como Paulo, você inicia seu driver manualmente. O driver, durante a chamada à &lt;em&gt;DriverEntry&lt;/em&gt;, lê configurações referentes ao usuário logado, que de fato estão armazenadas dentro de HKEY_CURRENT_USER do Paulo. Bom, acho que está certo até aqui. Já ouviram aquela expressão &lt;em&gt;"Quem muito acha acaba se perdendo"&lt;/em&gt;? Pois é, tá tudo errado. Para Paulo, as configurações estão lá mesmo, mas a rotina &lt;em&gt;DriverEntry&lt;/em&gt; é chamada em contexto de sistema, que por sinal não é Paulo.&lt;/p&gt;&lt;p&gt;Esse é um problema enfrentado mesmo em &lt;em&gt;User Mode&lt;/em&gt;, quando serviços, que são executados em conta de sistema, tentam abrir a chave HKEY_CURRENT_USER. Uma coisa que temos que ter em mente é que "O usuário logado" não é uma informação única do sistema. Tentem imaginar um &lt;a href="http://en.wikipedia.org/wiki/Terminal_service"&gt;Terminal Service&lt;/a&gt;, podem haver vários usuários logados ao mesmo tempo. Mesmo quando há somente um usuário logado no sistema, threads podem ser executadas em contexto de outros usuários ou mesmo em contexto de sistema. Drivers não tem contexto próprio. Algumas partes do driver são executadas em contexto de sistema, outras em contexto arbitrário. Dependendo do tipo de driver e sua posiçao dentro da pilha de dispositivos, ele pode receber as chamadas em contexto do usuário. Que é o caso dos drivers de &lt;a href="http://en.wikipedia.org/wiki/File_System"&gt;File System&lt;/a&gt; por exemplo.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Ah tá! Aí sim HKEY_CURRENT_USER vai funcionar!&lt;/strong&gt; Er... Como posso dizer isso? Não, não vai funcionar. Segundo a &lt;a href="http://msdn.microsoft.com/en-us/library/aa489583.aspx"&gt;referência da Microsoft&lt;/a&gt;, não existe uma tradução simples para HKEY_CURRENT_USER, mas existem rotinas que oferecem simplificações. Depois de toda essa conversinha sobre contexto e tals, você ainda terá que acessar a chave HKEY_USERS, ou melhor dizendo, &lt;em&gt;"\Registry\User"&lt;/em&gt;, no formato já conhecido dos programadores &lt;em&gt;User Mode&lt;/em&gt; utilizado com HKEY_USERS. Um exemplo é: &lt;em&gt;"\Registry\User\S-1-5-21-73586283-1897051121-839522115-500"&lt;/em&gt;. O equivalente para uma conta de sistema é a chave &lt;em&gt;"\Registry\User\.DEFAULT"&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Tá tá tá! Exemplo agora&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Mais uma vez, estou assumindo que você já sabe compilar e instalar um driver como explicado &lt;a href="http://www.driverentry.com.br/blog/2006/09/getting-started.html"&gt;neste post&lt;/a&gt;. O código exemplo disponível para download ao final deste post também pode ser compilado pelo &lt;a href="http://en.wikipedia.org/wiki/Visual_Studio"&gt;Visual Studio&lt;/a&gt; utilizando o &lt;a href="http://www.osronline.com/article.cfm?article=43"&gt;DDKBUILD&lt;/a&gt;, como explica &lt;a href="http://www.driverentry.com.br/blog/2006/11/kernel-visual-studio-2005.html"&gt;este post&lt;/a&gt;. Neste exemplo vou mostrar a leitura de dois valores do registro que estão em uma chave como mostra a figura abaixo. Para deixar a brincadeira mais divertida, quando o driver for executado pela primeira vez, este criará a chave e os valores nela contidos quando perceber que estes ainda não existem.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/RegParams.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Os valores estarão contidos em uma sub-chave da chave que recebemos como parâmetro na rotina &lt;em&gt;DriverEntry&lt;/em&gt;. Então, nosso trabalho básico na codificação da &lt;em&gt;DriverEntry&lt;/em&gt; é justamente criar o caminho completo da chave do registro que conterá estes valores. Com esta UNICODE_STRING em mãos, a passaremos para as rotinas de leitura e escrita no registro. Segue abaixo a codificação da rotina &lt;em&gt;DriverEntry&lt;/em&gt;. Ela não mostra nada de especial com relação ao registro. Como sempre, vale a pena dar uma lida nos comentários.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="comment"&gt;/****
***     DriverEntry
**
**      Ponto de entrada do nosso driver.
**      Faca nos dentes e sangue nos olhos.
*/&lt;/span&gt;
&lt;span class="keyword"&gt;extern&lt;/span&gt; &lt;span class="literal"&gt;"C"&lt;/span&gt;
NTSTATUS
DriverEntry(__in PDRIVER_OBJECT  pDriverObject,
            __in PUNICODE_STRING pusRegistryPath)
{
    UNICODE_STRING  usParameters = RTL_CONSTANT_STRING(L&lt;span class="literal"&gt;"\\Parameters"&lt;/span&gt;);
    UNICODE_STRING  usFullRegPath = {0, 0, NULL};
    PWCHAR          pwzBuffer = NULL;
    NTSTATUS        nts = STATUS_SUCCESS;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui recebemos como parâmetro o caminho do registro até
&lt;/span&gt;    &lt;span class="comment"&gt;//      a chave do nosso driver. Nossos parâmetros estão em uma
&lt;/span&gt;    &lt;span class="comment"&gt;//      sub-chave chamada "Parameters". Vamos montar o caminho
&lt;/span&gt;    &lt;span class="comment"&gt;//      completo apendando o nome da sub-chave ao caminho do
&lt;/span&gt;    &lt;span class="comment"&gt;//      registro que recebemos como parâmetro.
&lt;/span&gt;    &lt;span class="keyword"&gt;__try&lt;/span&gt;
    {
        &lt;span class="keyword"&gt;__try&lt;/span&gt;
        {
            &lt;span class="comment"&gt;//-f--&gt; Seta a rotina de callback de descarga do driver.
&lt;/span&gt;            pDriverObject-&gt;DriverUnload = OnDriverUnload;
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Para fazer esse append, precisaremos de um buffer
&lt;/span&gt;            &lt;span class="comment"&gt;//      que seja grande o bastante para armazenar a string
&lt;/span&gt;            &lt;span class="comment"&gt;//      original mais o tamanho da sub-chave. Aqui alocamos
&lt;/span&gt;            &lt;span class="comment"&gt;//      este buffer.
&lt;/span&gt;            pwzBuffer = (PWCHAR)ExAllocatePoolWithTag(PagedPool,
                                                      pusRegistryPath-&gt;Length +
                                                      usParameters.Length,
                                                      _KRN_REG_TAG);
            &lt;span class="keyword"&gt;if&lt;/span&gt; (pwzBuffer == NULL)
                ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Agora que temos o buffer, vamos inicializar a estrutura
&lt;/span&gt;            &lt;span class="comment"&gt;//      UNICODE_STRING referente a string resultante deste append.
&lt;/span&gt;            RtlInitEmptyUnicodeString(&amp;amp;usFullRegPath,
                                      pwzBuffer,
                                      pusRegistryPath-&gt;Length +
                                      usParameters.Length);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Copia a string recebida como parâmetro
&lt;/span&gt;            RtlCopyUnicodeString(&amp;amp;usFullRegPath,
                                 pusRegistryPath);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Concatena a string "\Parameters"
&lt;/span&gt;            RtlAppendUnicodeStringToString(&amp;amp;usFullRegPath,
                                           &amp;amp;usParameters);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Utilizando o caminho completo, tenta carregar os parâmetros
&lt;/span&gt;            nts = LoadParams(&amp;amp;usFullRegPath);
            &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
            {
                &lt;span class="comment"&gt;//-f--&gt; Em caso de falha, verifica se a causa foi a falta da
&lt;/span&gt;                &lt;span class="comment"&gt;//      sub-chave no registro. Isso deve acontecer quando o
&lt;/span&gt;                &lt;span class="comment"&gt;//      driver for executado pela primeira vez. Mas se a falha
&lt;/span&gt;                &lt;span class="comment"&gt;//      for outra, então cada um com seus pobrema.
&lt;/span&gt;                &lt;span class="keyword"&gt;if&lt;/span&gt; (nts != STATUS_OBJECT_NAME_NOT_FOUND)
                    ExRaiseStatus(nts);
&amp;nbsp;
                &lt;span class="comment"&gt;//-f--&gt; Vamos criar a sub-chave e gravar os valores
&lt;/span&gt;                &lt;span class="comment"&gt;//      pré-definidos.
&lt;/span&gt;                nts = CreateParams(&amp;amp;usFullRegPath);
                &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
                    ExRaiseStatus(nts);
&amp;nbsp;
                &lt;span class="comment"&gt;//-f--&gt; Tenta ler os parâmetros novamente.
&lt;/span&gt;                &lt;span class="comment"&gt;//      Tá, eu sei que isso é burrice. Afinal, se eu
&lt;/span&gt;                &lt;span class="comment"&gt;//      acabei de criar os parâmetros, por quê eu iria
&lt;/span&gt;                &lt;span class="comment"&gt;//      lê-los novamente? Bom, pra ilustrar.
&lt;/span&gt;                nts = LoadParams(&amp;amp;usFullRegPath);
                &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
                    ExRaiseStatus(nts);
            }
        }
        &lt;span class="keyword"&gt;__finally&lt;/span&gt;
        {
            &lt;span class="comment"&gt;//-f--&gt; Limpando a bagunça.
&lt;/span&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; (pwzBuffer)
                ExFreePool(pwzBuffer);
        }
    }
    &lt;span class="keyword"&gt;__except&lt;/span&gt;(EXCEPTION_EXECUTE_HANDLER)
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops!
&lt;/span&gt;        nts = GetExceptionCode();
        ASSERT(FALSE);
    }
&amp;nbsp;
    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;Aqui é mais brincadeira de UNICODE_STRING mesmo. Vamos dar uma olhada na rotina que grava os valores no registro. Esta rotina também não oferece nada de muito diferente do que já estamos habituados a ver em &lt;em&gt;User Mode&lt;/em&gt;. Mas é como se diz por aí: &lt;em&gt;"Um exemplo vale mais que mil artigos"&lt;/em&gt;. Nossa, essa foi terrível! Eu preciso parar com isso. Vocês devem estar pensando agora: &lt;em&gt;"Caraca! Cada coisa que a gente precisa ler pra poder ter um exemplo de driver"&lt;/em&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="comment"&gt;/****
***     CreateParams
**
**      Esta rotina é chamada quando não for detectada a chave
**      que contém os parâmetros. Ela cria a chave e os parâmetros
**      com valores pré-definidos.
*/&lt;/span&gt;
NTSTATUS
CreateParams(__in    PUNICODE_STRING pusRegistryPath)
{
    HANDLE              hKey = NULL;
    NTSTATUS            nts = STATUS_SUCCESS;
    OBJECT_ATTRIBUTES   ObjAttributes;
    UNICODE_STRING      usStringParam = RTL_CONSTANT_STRING(L&lt;span class="literal"&gt;"String"&lt;/span&gt;);
    UNICODE_STRING      usDWordParam = RTL_CONSTANT_STRING(L&lt;span class="literal"&gt;"DoubleWord"&lt;/span&gt;);
    ULONG               ulParam = 0x12345678;
    WCHAR               wzParam[] = L&lt;span class="literal"&gt;"DriverEntry.com.br"&lt;/span&gt;;
&amp;nbsp;
    &lt;span class="keyword"&gt;__try&lt;/span&gt;
    {
        &lt;span class="keyword"&gt;__try&lt;/span&gt;
        {
            &lt;span class="comment"&gt;//-f--&gt; Aqui não tem segredo. É tudo pá pum.
&lt;/span&gt;            InitializeObjectAttributes(&amp;amp;ObjAttributes,
                                       pusRegistryPath,
                                       OBJ_CASE_INSENSITIVE,
                                       NULL,
                                       NULL);
&amp;nbsp;
