Passando referência ao vetor STL sobre o limite da DLL
Eu tenho uma boa biblioteca para gerenciar arquivos que precisa retornar listas específicas de strings. Como o único código com o qual eu vou usá-lo será C ++ (e Java, mas que está usando C ++ através de JNI), decidi usar o vetor das bibliotecas padrão. As funções da biblioteca se parecem um pouco com isso (onde FILE_MANAGER_EXPORT é um requisito de exportação definido pela plataforma):
extern "C" FILE_MANAGER_EXPORT void get_all_files(vector<string> &files)
{
files.clear();
for (vector<file_struct>::iterator i = file_structs.begin(); i != file_structs.end(); ++i)
{
files.push_back(i->full_path);
}
}
O motivo pelo qual eu usei o vetor como referência, em vez do valor de retorno, é uma tentativa de manter as alocações de memória sãs e porque o Windows estava realmente infeliz por eu ter um "C" externo em torno de um tipo de retorno c ++ (quem sabe por quê, meu entendimento é que todos são externos " C "faz é impedir que o nome seja alterado no compilador). De qualquer forma, o código para usá-lo com outros c ++ é geralmente o seguinte:
#if defined _WIN32
#include <Windows.h>
#define GET_METHOD GetProcAddress
#define OPEN_LIBRARY(X) LoadLibrary((LPCSTR)X)
#define LIBRARY_POINTER_TYPE HMODULE
#define CLOSE_LIBRARY FreeLibrary
#else
#include <dlfcn.h>
#define GET_METHOD dlsym
#define OPEN_LIBRARY(X) dlopen(X, RTLD_NOW)
#define LIBRARY_POINTER_TYPE void*
#define CLOSE_LIBRARY dlclose
#endif
typedef void (*GetAllFilesType)(vector<string> &files);
int main(int argc, char **argv)
{
LIBRARY_POINTER_TYPE manager = LOAD_LIBRARY("library.dll"); //Just an example, actual name is platform-defined too
GetAllFilesType get_all_files_pointer = (GetAllFilesType) GET_METHOD(manager, "get_all_files");
vector<string> files;
(*get_all_files_pointer)(files);
// ... Do something with files ...
return 0;
}
A biblioteca é compilada através do cmake usando add_library (file_manager SHARED file_manager.cpp). O programa é compilado em um projeto cmake separado usando add_executable (file_manager_command_wrapper command_wrapper.cpp). Também não há sinalizadores de compilação especificados, apenas esses comandos.
Agora, o programa funciona perfeitamente bem no mac e no linux. O problema é o Windows. Quando executado, recebo este erro:
Falha na declaração de depuração!
...
Expressão: _pFirstBlock == _pHead
Eu descobri e meio que entendo que isso se deve a pilhas de memória separadas entre executáveis e dlls carregadas. Eu acredito que isso ocorre quando a memória é alocada em um heap e desalocada no outro. O problema é que, para a minha vida, não consigo entender o que está errado. A memória é alocada no executável e passada como referência à função dll, os valores são adicionados por meio da referência e, em seguida, são processados e finalmente desalocados no executável.
Eu revelaria mais código, se pudesse, mas a propriedade intelectual da minha empresa afirma que não, então todo o código acima é apenas exemplos.
Alguém com mais conhecimento do assunto capaz de me ajudar a entender esse erro e me indicar a direção certa para depurar e corrigi-lo? Infelizmente, não sou capaz de usar uma máquina Windows para depuração, pois desenvolvo no Linux, depois cometo quaisquer alterações em um servidor gerrit que aciona compilações e testes através do jenkins. Eu tenho acesso ao console de saída ao compilar e testar.
Eu considerei usar tipos não-stl, copiando o vetor em c ++ para um caractere **, mas a alocação de memória era um pesadelo e eu estava lutando para fazê-lo funcionar muito bem no linux, sem falar nas janelas e é horrível várias pilhas.
EDIT: Definitivamente trava assim que o vetor de arquivos sai do escopo. Meu pensamento atual é que as seqüências de caracteres inseridas no vetor são alocadas no heap dll e desalocadas no heap executável. Se for esse o caso, alguém pode me esclarecer sobre uma solução melhor?