Não é mais uma simples chamada de função, mas uma invocação de um processo.
Então, para ler o retorno, por exemplo, você teria que ou gravar um arquivo, ou ler a saída padrão, o que é muito mais complexo do que simplesmente ler o retorno da função. Além disso, se você for realizar várias tarefas no C++ (tipo, com várias funções na dll), você teria que disparar um processo para cada função, ou deixar um processo rodando em segundo plano que receba algum tipo de entrada. Muito mais complicado.
Por isso eu disse que a alternativa de disparar com o Runtime só é viável para sistemas legados, sem fonte, ou para quando o sistema faz uma tarefa muito simples.
Claro, se vc se empenhar, pode fazer tudo o que o JNI faz só com comunicação entre processos via std::in e std::out. Mas daí o trabalho será tão grande que compensa fazer a dll do JNI de uma vez.