Páginas

terça-feira, 27 de março de 2012

Criando dll JNI para Acesso a Outra dll

Nesta postagem quero compartilhar um processo de desenvolvimento de uma dll JNI para acesso a uma dll de mini-impressora da Bematech que precisei fazer nos idos de 2007. Achei o meu documento relatando a solução durante uma limpeza de arquivos obsoletos e decidi postar no blog para que fique disponível na web.

O problema

Criação de uma dll JNI que faça acesso a uma dll de controle de mini-impressora Bematech.
Ambiente: Windows XP 32 bits
Ferramentas: Eclipse 3.2.1, Dev-C++ 4.9.9.2

Solução

A criação de uma biblioteca JNI, em síntese, é simples. Basta seguir a "farta" documentação da Oracle:
- Crie a classe Java com os métodos com a devida assinatura JNI:
public Classe {
   public native void metodo();
}
- Gere o cabeçalho C em cima da nossa classe java compilada:
$> javah Classe
O cabeçalho criado terá assinaturas de métodos no seguinte padrão:
/*
* Class:     Classe
* Method:    metodo
* Signature: (V)V
*/
JNIEXPORT void JNICALL Java_Classe_Metodo (JNIEnv *, jclass);
Agora que temos o cabeçalho em mãos, é partir para o projeto C. Neste caso foi criado um projeto C++ utilizando o template DLL existente no Dev-C++.
Será necessário um arquivo .cpp, coloque o devido include do cabeçalho criado (Classe.h), e implemente o método:
JNIEXPORT void JNICALL Java_Classe_Metodo (JNIEnv *env, jclass obj) {
   printf("não faz nada de útil\n");
}
Observe a inclusão das referências passadas no método JNI.
Ótimo, já temos nossa JNI implementada, basta compilar o código C, pegar a dll gerada e colocar no diretório base da sua aplicação Java.

Mas... não acabou

Tudo seria ótimo se fôssemos parar por aqui, mas, nossa dll JNI precisa acessar outra dll. A solução encontrada deve valer apenas para o Dev-C++, mas outros compiladores devem seguir algum padrão parecido.
É necessário um arquivo de link com extensão '.a'. Se sua dll já possui um, você já pode ir mais adiante.
Se não, o próximo passo é descobrir as declarações de exportação de todas as funções necessárias. O método utilizado para isto não é muito ortodoxo, mas funciona. No nosso caso, nós já possuíamos os nomes das funções disponíveis na dll, se você não tiver a documentação da dll e nem o '.a' esta postagem não irá ajudar.
Dentro do seu código C++ faça uma chamada a uma função necessária e compile. Atenção: a chamada deve obedecer a assinatura da função na dll. O linker irá retornar um erro com o nome da função e um valor '@nm' (Exemplo: funcao@16). Esta é a declaração de exportação. Repita o processo para todas as funções da dll.
Crie um arquivo de definições (.def), não inclua ele no projeto C, coloque em um diretório junto com a dll a ser acessada e faça as declarações de exportação:
LIBRARY nomedadll.dll
EXPORTS
funcao@16
Arquivo pronto, rode a seguinte linha de comando (o programa dlltool está disponível com a distribuição do Dev-C):
dlltool --input-def arquivodefinicoes.def  --dllname nomedadll.DLL --output-lib nomedadll.a -k
Agora você possui o arquivo de link da dll. Vá nas propriedades do projeto C, na guia parâmetros, e adicione uma biblioteca localizando o arquivo '.a' recém criado.
Agora é possível implementar as funcionalidades nos métodos da JNI no arquivo .cpp, acessando os métodos mapeados da dll.
Basta compilar o projeto, e assim temos em mãos nossa dll JNI acessando uma dll externa.

Nenhum comentário:

Postar um comentário

Olá! Antes de postar seu comentário, por favor, observe que comentários técnicos, elogios e sugestões são antecipadamente agradecidos, esclarecimentos sobre os conceitos envolvidos na postagem serão respondidos da melhor forma possível, mas pedidos de ajuda técnica ou suporte individual deverão ser feitos através do formulário de contato. Grato!