Strings no Kernel
Tem coisa mais besta que manipular strings? Creio que a resposta correta seria "Depende". 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 Templates, Smart Pointers, STL 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: "Mas onde está o buffer mesmo?". Hoje vamos apenas dar uma passadinha de leve nas estruturas UNICODE_STRING, ANSI_STRING 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.
E no prézinho...
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.
CHAR szExemplo[] = "Tava ruim lá na Bahia, profissão de bóia-fria\n""Trabalhando noite e dia, num era isso que eu queria\n""Eu vim-me embora pra \"Sum Paulo\",\n""Eu vim no lombo dum jumento com pouco conhecimento\n""Enfrentando chuva e vento e dando uns peido fedorento (vish)\n""Até minha bunda fez um calo\n""Chegando na capital, uns puta predião legal\n""As mina pagando um pau, mas meu jumento tava mal\n""Precisando reformar\n""Fiz a pintura, importei quatro ferradura\n""Troquei até dentadura e pra completar a belezura\n""Eu instalei um Road-Star!";
Jumento Celestino / Mamonas Assassinas
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 gancho para o Lesma explicar essas coisas aos meninos e meninas interessados.
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 HiPhone que você comprou no China.
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.

Quatro tipos de strings
Destes temos duas strings que já estamos acostumados a ver em user-Mode, ambas terminadas com caractere NULL.
CHAR szString[] = "Uma string de CHAR";WCHAR wzString[] = L"Uma string de WCHAR";
As outras duas strings são as que normalmente vemos em kernel-mode.
typedef struct _UNICODE_STRING {USHORT Length;USHORT MaximumLength;PWSTR Buffer;} UNICODE_STRING, *PUNICODE_STRING;typedef struct _STRING {USHORT Length;USHORT MaximumLength;PCHAR Buffer;} ANSI_STRING, *PANSI_STRING;
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.
Buffer: E um ponteiro para a região de memória onde os caracteres são armazenados.
Length: Indica a quantidade de bytes válidos da string. Este tamanho é sempre expresso em bytes, mesmo que esta seja uma string de WCHAR.
MaximumLength: Indica o tamanho máximo que esta string pode ter.
Para entender de como estes membros são interpretados, dê uma olhada no exemplo abaixo:
void OsTresMembros(void){WCHAR wsUmArray[200];UNICODE_STRING usString;//--f-> Indico onde os caracteres desta string// serão armazenados.usString.Buffer = wsUmArray;//-f--> Ainda não escrevemos nada no buffer,// por isso, nada do que esteja no buffer// é válido.usString.Length = 0;//-f--> Embora o buffer não esteja inicializado// ele ainda está lá e pode conter uma string// de no máximo seu tamanho em bytes.usString.MaximumLength = sizeof(wsUmArray);}
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 leak de memória será causado com o termino desta rotina.
Inicializando Strings
Podemos ainda utilizar algumas macros que fazem a inicialização destas estruturas.
void InitString(void){UNICODE_STRING usOutraConstante;UNICODE_STRING usVazia;WCHAR wzBuffer[30] = L"Isso não será considerado.";//-f--> Inicaliza uma string na sua criação.UNICODE_STRING usConstante = RTL_CONSTANT_STRING(L"Uma string.");//-f--> Inicializa uma string constante.RtlInitUnicodeString(&usOutraConstante,L"Uma outra string constante que não muda.");//-f--> Inicializa uma string vazia para uso posteriorRtlInitEmptyUnicodeString(&usVazia,wzBuffer,sizeof(wzBuffer));}
Reparem nos valores destas estruturas.
kd> ?? usConstantestruct _UNICODE_STRING"Uma string."+0x000 Length : 0x16+0x002 MaximumLength : 0x18+0x004 Buffer : 0xf8cd8600 "Uma string."kd> db 0xf8cd8600 L0x18f8cd8600 55 00 6d 00 61 00 20 00-73 00 74 00 72 00 69 00 U.m.a. .s.t.r.i.f8cd8610 6e 00 67 00 2e 00 00 00 n.g.....
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.
A macro RTL_CONSTANT_STRING() 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 RtlInitUnicodeString().
kd> ?? usVaziastruct _UNICODE_STRING"Isso não será considerado."+0x000 Length : 0+0x002 MaximumLength : 0x3c+0x004 Buffer : 0xf8ae5c34 "Isso não será considerado."
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.
kd> db 0xf8ae5c34 L0x3cf8ae5c34 49 00 73 00 73 00 6f 00-20 00 6e 00 e3 00 6f 00 I.s.s.o. .n...o.f8ae5c44 20 00 73 00 65 00 72 00-e1 00 20 00 63 00 6f 00 .s.e.r... .c.o.f8ae5c54 6e 00 73 00 69 00 64 00-65 00 72 00 61 00 64 00 n.s.i.d.e.r.a.d.f8ae5c64 6f 00 2e 00 00 00 00 00-00 00 00 00 o...........
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.
A extensão !ustr mostra apenas os dados válidos de uma string unicode.
kd> !ustr usConstanteString(22,24) at f899fc6c: Uma string.kd> !ustr usVaziaString(0,60) at f8ae5c1c:
O terminador é necessário?

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.
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 Buffer como parâmetro para uma chamada à rotina wcslen() por exemplo. Além do risco de obter a informação incorreta, ainda tem um plus de poder gerar uma tela azul.
"Mas Fernando, eu já testei isso em vários sistemas operacionais e sempre funcionou."
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.
Manipulando Strings
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.
void CopyString(void){UNICODE_STRING usSource;UNICODE_STRING usTarget;WCHAR wzTarget[10];//-f--> Aqui inicializamos nossa string de origem.RtlInitUnicodeString(&usSource,L"12345678901234567890");//-f--> Aqui inicializamos nossa string de destino.RtlInitEmptyUnicodeString(&usTarget,wzTarget,sizeof(wzTarget));//-f--> Realiza a cópiaRtlCopyUnicodeString(&usTarget,&usSource);}
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.
kd> !ustr usTargetString(20,20) at f89a3c6c: 1234567890kd> ?? usTargetstruct _UNICODE_STRING"1234567890"+0x000 Length : 0x14+0x002 MaximumLength : 0x14+0x004 Buffer : 0xf89a3c54 "1234567890"kd> db 0xf89a3c54 L0x20f89a3c54 31 00 32 00 33 00 34 00-35 00 36 00 37 00 38 00 1.2.3.4.5.6.7.8.f89a3c64 39 00 30 00 98 5d 5f 00-14 00 14 00 54 3c 9a f8 9.0..]_.....T<..
Diferentes da rotina RtlCopyUnicodeString(), algumas outras rotinas nos retornam STATUS_BUFFER_TOO_SMALL quando o buffer é insuficiente.
VOIDRtlCopyUnicodeString(IN OUT PUNICODE_STRING DestinationString,IN PCUNICODE_STRING SourceString);NTSTATUSRtlAppendUnicodeStringToString(IN OUT PUNICODE_STRING Destination,IN PUNICODE_STRING Source);
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 strrchr() por exemplo. Quando necessário teremos que construir uma versão que manipule estruturas UNICODE_STRING da mesma maneira. Aqui está uma lista básica de rotinas de string que o WDK suporta. Outras funções são listadas aqui, mas falaremos delas mais tarde.
Mas onde está o buffer mesmo?
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 RtlUnicodeStringToAnsiString() e RtlAnsiStringToUnicodeString().
NTSTATUSRtlAnsiStringToUnicodeString(IN OUT PUNICODE_STRING DestinationString,IN PANSI_STRING SourceString,IN BOOLEAN AllocateDestinationString);NTSTATUSRtlUnicodeStringToAnsiString(IN OUT PANSI_STRING DestinationString,IN PUNICODE_STRING SourceString,IN BOOLEAN AllocateDestinationString);
O exemplo abaixo faz uma conversão de ANSI para UNICODE com alocação do resultado, e em seguida, libera o buffer recebido.
void ConvertString(void){ANSI_STRING asString;UNICODE_STRING usString;//-f--> Aqui inicializamos nossa string de origem.RtlInitAnsiString(&asString,"Um exemplo simples.");//-f--> Neste caso não precisaremos inicializar a// string de destino. A rotina fará isso por nós.RtlAnsiStringToUnicodeString(&usString,&asString,TRUE);//-f--> Imprime a string resultanteDbgPrint("String convertida: %wZ\n",&usString);//-f--> Liberamos o buffer alocado na conversão.RtlFreeUnicodeString(&usString);}
Você mesmo pode escrever uma rotina que gere UNICODE_STRINGs alocando o buffer dinamicamente. O buffer pode ser alocado dinamicamente utilizando ExAllocatePoolWithTag() 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 ExFreePoolWithTag(). Não saia utilizando RtlFreeUnicodeString() a torto e a direito. Aprecie com moderação. Apenas utilize essa rotina para liberar strings que foram obtidas por funções como RtlAnsiStringToUnicodeString(), cuja documentação indica o uso de RtlFreeUnicodeString().
"..., the caller must deallocate the buffer by calling RtlFreeUnicodeString."
Safe Strings em Kernel
Uma parte das rotinas de manipulação de strings da biblioteca padrão do C/C++, tais como strcpy() e sprintf(), 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 este link.
Mais um driver de exemplo
Este outro post traz o exemplo de um driver que guarda uma lista de strings em memória. Já neste outro post, 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.
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.
/******* OnWrite**** A aplicação está enviando uma string.*/NTSTATUSOnWrite(IN PDEVICE_OBJECT pDeviceObj,IN PIRP pIrp){PIO_STACK_LOCATION pStack;ANSI_STRING asString;PSTRING_LIST pStringList;PSTRING_REG pStringReg;ULONG ulBytes;KIRQL kIrql;NTSTATUS nts;//-f--> Obtemos a Stack Location corrente.pStack = IoGetCurrentIrpStackLocation(pIrp);//-f--> Obtém a ponta da lista.pStringList = (PSTRING_LIST)pStack->FileObject->FsContext;//-f--> O que temos no buffer de sistema aqui é um array// de bytes terminados com NULL. Vamos inicializar// uma ANSI_STRING com este buffer.RtlInitAnsiString(&asString,(PCSZ)pIrp->AssociatedIrp.SystemBuffer);//-f--> Aqui alocamos o nó que vai ser colocado na listapStringReg = (PSTRING_REG) ExAllocatePoolWithTag(NonPagedPool,sizeof(STRING_REG),STR_LST_TAG);//-f--> Para fazer a conversão para UNICODE_STRING, vamos// solicitar que a rotina faça a alocação do bufer// resultante. Por essa razão, não precisaremos inicializar// a string de saída.nts = RtlAnsiStringToUnicodeString(&pStringReg->usString,&asString,TRUE);if (!NT_SUCCESS(nts)){//-f--> Ops! Provavelmente não tivemos memória para isso.// Vamos sinalizar a falha e informar ao IoManager// que zero bytes foram copiados.ExFreePoolWithTag(pStringReg, STR_LST_TAG);pIrp->IoStatus.Information = 0;}else{//-f--> Vamos segurar o spinlock para evitar concorrência// no acesso à lista.KeAcquireSpinLock(&pStringList->SpinLock,&kIrql);//-f--> Insere o nó na lista.InsertTailList(&pStringList->ListHead,&pStringReg->Entry);//-f--> Libera o spinlock.KeReleaseSpinLock(&pStringList->SpinLock,kIrql);//-f--> Aqui informamos ao IoManager que todos os bytes// enviados pela aplicação foram recebidos com// sucesso pelo driver.pIrp->IoStatus.Information = pStack->Parameters.Write.Length;}//-f--> O campo de Information já foi preenchido, vamos apenas// copiar o status da operação e completar a IRP.pIrp->IoStatus.Status = nts;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return nts;}
Agora vejamos a recuperação das strings na leitura.
/******* OnRead**** A aplicação está querendo receber as strings eviadas** por ela.*/NTSTATUSOnRead(IN PDEVICE_OBJECT pDeviceObj,IN PIRP pIrp){ANSI_STRING asString;PIO_STACK_LOCATION pStack;PSTRING_LIST pStringList;PSTRING_REG pStringReg;PLIST_ENTRY pEntry;KIRQL kIrql;NTSTATUS nts;//-f--> Vamos deixar este campo com zero até que tenhamos// certeza de que a cópia foi feita paro o buffer da// aplicaçãopIrp->IoStatus.Information = 0;//-f--> Obtemos a Stack Location corrente.pStack = IoGetCurrentIrpStackLocation(pIrp);//-f--> Obtém a ponta da lista.pStringList = (PSTRING_LIST)pStack->FileObject->FsContext;//-f--> Vamos retornar para aplicação apenas um array de CHAR// com terminador NULL. As strings estão armexadas como// UNICODE_STRING. Vamos convertê-las para ANSI_STRING.// Aqui vamos inicializar a ANSI_STRING que receberá// o resultado da conversão de UNICODE_STRING.//-f--> Vamos oferecer Lengh-1 para reservar um byte para// o terminador null após conversão.RtlInitEmptyAnsiString(&asString,(PCHAR)pIrp->AssociatedIrp.SystemBuffer,(USHORT)pStack->Parameters.Read.Length - 1);//-f--> Aqui vamos adquirir o spinlock para evitar concorrência// no acesso à lista.KeAcquireSpinLock(&pStringList->SpinLock,&kIrql);//-f--> Verifica se a lista está vazia.if (IsListEmpty(&pStringList->ListHead)){//-f--> Sinaliza erro na leitura e já libera o spinlock.nts = STATUS_NO_MORE_ENTRIES;KeReleaseSpinLock(&pStringList->SpinLock,kIrql);}else{//-f--> Remove o registro da lista e já libera o spinlock.pEntry = RemoveHeadList(&pStringList->ListHead);KeReleaseSpinLock(&pStringList->SpinLock,kIrql);pStringReg = CONTAINING_RECORD(pEntry,STRING_REG,Entry);//-f--> Aqui convertemos a string. Reparem que não solicitamos// a alocação do buffer. Estamos utilizando o buffer de// sistema para receber o resultado da conversão. Neste// caso a string de destino deve estar inicializada.nts = RtlUnicodeStringToAnsiString(&asString,&pStringReg->usString,FALSE);if (NT_SUCCESS(nts)){//-f--> Aqui usamos aquele byte que reservamos e assim// o terminador null também é copiado pelo IoManager// do SystemBuffer para o buffer da aplicaçãoasString.Buffer[asString.Length] = 0;//-f--> Precisamos informar ao IoManager a quantidade de// bytes que serão copiados para o buffer da aplicação.// Esse tamanho é o tamanho da string convertida mais// um byte ocupado pelo terminador NULL que colocamos.pIrp->IoStatus.Information = asString.Length+1;}//-f--> Neste exemplo, não estamos lidando com o caso de erro// na conversão, isso pode acontecer se a aplicação mandar// um buffer pequeno para a string. Caso algum erro ocorra// durante a conversão, vamos simplesmente descartar a string//-f--> Libera o buffer da string e em seguida o registro que ela// ocupava na lista.RtlFreeUnicodeString(&pStringReg->usString);ExFreePoolWithTag(pStringReg, STR_LST_TAG);}//-f--> O campo de Information já foi preenchido, vamos apenas// copiar o status da operação e completar a IRP.pIrp->IoStatus.Status = nts;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return nts;}
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.
Enfim, mais uma vêz espero ter ajudado. E se alguma dúvida surgir é só me mandar um e-mail.
Have fun!
Download StringList.zip



9 Comments:
Parabens pelo seu blog e pelas materias. Muito bom.
Aproveitando sua materia sobre strings, eu gostaria de saber, como posso apresentar um texto em uma janela em kernel mode.
Exemplo. Gostaria de apresentar a string "Teste" em uma janela amarela no canto da tela, perto do relogio, apenas um aviso, mas como é somente uma string, nao queria desenvolver algo em user mode, quero em kernel mode mesmo, ja que isto seria apresentado em uma situacao especifica.
Tem como ?
Obrigado
Julio Cesar
Boa pergunta Júlio. Como apresentar uma strings na tela em kernel mode ?
Ops! Essa ficou sem responder.
Vamos lá Júlio,
Para exibir janelas mesmo, no verdadeiro sentido da palavra, você precisará de uma ajuda de algum programa feito em user mode, que possa usar os serviços da GDI para exibir as janelas com as strings obtidas do kernel.
Mas se você quer exibir mensagens diretamente do driver, então você terá que respirar fundo e se enfiar nos serviços providos pela placa de vídeo. O grande problema é que não existem meios documentados de se fazer isso com janelas, afinal, janela é coisa de user-mode. Você terá que lidar com os frames e áreas do monitor que são completamente independendes do que está acontecendo na user-modelandia.
Então... Impossível não é, mas vai ser uma trabalheira danada. Considere ter um programinha que rode em user-mode.
Have fun!
Obrigado Fernando...
Você tem algum exemplo simples, que possa me mostrar esta comunicação de um kernel-mode com um user-mode ? Exemplo... numa determinada situação, o DRIVER enviará uma mensagem pro user-mode e o user mode irá apresentar uma MessageBox por exemplo... entendeu? Não é do user-mode para o driver... é do driver para o user-mode.
Obrigado
Julio Cesar
Júlio,
O kernel do Windows funciona num modelo cliente/servidor. O kernel, no papel de servidor, presta serviços para os processos, no papel de clientes. O kernel sempre responde às requisições de clientes. Ele não toma uma açao por si. Dessa forma sempre teremos clientes solicitando serviços ao servidor.
O que normalmente é feito é ter uma requisição que apenas se complete quando determinado evento ocorra. Por exemplo uma rotina MyGetDriverMessage() que só vai retornar quando o driver resolver enviar a mensagem à aplicação.
Outro exemplo típico é enviar um handle de um evento ao driver. Quando o driver tiver uma mensagem a enviar à aplicação, este seta o evento e assim a aplicação pode chamar a tal rotina MyGetDriverMessage(), que se completará imediatamente com a mensagem do driver.
Este é um exemplo de comunicação entre aplicação e driver onde (como sempre) a aplicação solicita um serviço ao driver.
Espero que tenha ajudado.
De qualquer forma, o exemplo do evento me parece ótimo para um post. Posso usar sua dúvida e seu nome nele?
[]s,
Ola Fernando.
Com certeza, pode sim, eu que agradeço.
Obrigado.
Julio César.
Julio,
Pode por favor me mandar um e-mail informado sua cidade?
[]s.
Ola Fernando... Sou do Rio de Janeiro - Capital.
Abraços
Opa, eu estou tentando entrar nesse mundo novo sobre drivers, cara eut enho uma duvida gostaria que se algum pude-se me ajudar se teria como, eu fiz um driver usando o visual studio, o win ddk tbm, porem gostaria de saber se existe alguma forma de estar criptando todo o conteudo do meu .sys, como no delphi vc pode cript algumas funçoes ou ate mesmo todas, eu fiz esse driver em c nativo 16 bits. muito obrigado, se poder me ajuda!
Post a Comment
<< Home