Manipulação de Structs Aninhadas com Vetores de Ponteiros em JNA

Em JNA (Java Native Access), é comum lidar com estruturas C que contêm vetores de ponteiros para outras structs. O gerenciamento correto de memória é essencial para evitar falhas. Vejamos um exemplo complexo.

Exemplo com Vetor de Ponteiros para Struct

Código C originla (adaptado):

struct EmpresaStruct {
    long id;
    wchar_t* nome;
    UsuarioStruct* usuarios[100];
    int quantidade;
};

void mostrarEmpresa(EmpresaStruct* pEmpresa);

Mapeamento em JNA:

public static class EmpresaStruct extends Structure {
    public static class ByReference extends EmpresaStruct implements Structure.ByReference {}

    public NativeLong id;
    public WString nome;
    public UsuarioStruct.ByReference[] usuarios = new UsuarioStruct.ByReference[100];
    public int quantidade;
}

public void mostrarEmpresa(EmpresaStruct.ByReference pEmpresa);

Código de teste em Java:

EmpresaStruct.ByReference empresa = new EmpresaStruct.ByReference();
empresa.id = new NativeLong(2);
empresa.nome = new WString("Yahoo");
empresa.quantidade = 10;

UsuarioStruct.ByReference usuario = new UsuarioStruct.ByReference();
usuario.id = new NativeLong(90);
usuario.idade = 99;
usuario.nome = new WString("João");

usuario.write(); // Importante: fixa a memória do objeto Java

for (int i = 0; i < empresa.quantidade; i++) {
    empresa.usuarios[i] = usuario;
}

MinhaBiblioteca.INSTANCE.mostrarEmpresa(empresa);

Explicação sobre o "Pin" (Fixação) de Memória

O Java possui gerenciamento automático de memória (GC). Objetos podem ser movidos, o que quebra a comunicação com código nativo que usa ponteiros. JNA "fixa" (pinned) os objetos durante chamadas nativas. No exemplo acima, a instância empresa é fixada automaticamente ao chamar mostrarEmpresa(), mas os ponteiros internos (usuario) não são fixados. Por isso, é necessário chamar usuario.write() antes, que fixa a memória daquele objeto. Sem isso, o C acessaria um endereço inválido.

A classe Structure oferece métodos como write() e writeField(String nome) para forçar a escrita dos campos na memória nativa e fixar o objeto.

Alternativas: Pointer vs ByReference

Em JNA, Pointer e ByReference podem ser usados para passar endereços. Pointer exige alocação/liberação manual de memória (ex.: new Memory(50)), enquanto ByReference delega ao GC. Para ponteiros multinível, Pointer pode ser mais flexível.

Pointer buffer = new Memory(50);
buffer.setString(0, "Exemplo");
Native.free(Pointer.nativeValue(buffer));
Pointer.nativeValue(buffer, 0); // evita double-free

Criando Vetores de Structs com Memória Contígua

Quando um struct C espera um vetor de structs (não ponteiros), a memória deve ser conntígua. O uso de new MinhaStruct[3] não garante isso. A solução é usar o método toArray() da classe Structure.

InfoStruct[] arr = (InfoStruct[]) new InfoStruct().toArray(3);

Mapeamento de Tipos Comuns

C Java
bool int
void* Pointer
char* String ou Pointer
int* IntByReference

Alinhamento de Bytes Personalizado

Por padrão, JNA determina o alinhamento baseado na plataforma. Para sobrescrever, imlpemente o método getNativeAlignment na subclasse de Structure.

@Override
protected int getNativeAlignment(Class<?> type, Object value, boolean isFirstElement) {
    int alinhamentoPadrao = super.getNativeAlignment(type, value, isFirstElement);
    return Math.min(alinhamentoPadrao, 4); // alinhamento máximo de 4 bytes
}

Tags: JNA java struct ponteiro Array

Publicado em 8-3 11:29