Na verdade, eu tenho uma DLL C ++ (funcionando) que quero importar para o meu projeto C # para chamar suas funções.
Ele funciona quando eu especifico o caminho completo para a DLL, assim:
string str = "C:\\Users\\userName\\AppData\\Local\\myLibFolder\\myDLL.dll";
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);
O problema é que ele será um projeto instalável, portanto a pasta do usuário não será a mesma (por exemplo: pierre, paul, jack, mãe, pai, ...) dependendo do computador / sessão em que seria executado.
Então, eu gostaria que meu código fosse um pouco mais genérico, assim:
/*
goes right to the temp folder of the user
"C:\\Users\\userName\\AppData\\Local\\temp"
then go to parent folder
"C:\\Users\\userName\\AppData\\Local"
and finally go to the DLL's folder
"C:\\Users\\userName\\AppData\\Local\\temp\\myLibFolder"
*/
string str = Path.GetTempPath() + "..\\myLibFolder\\myDLL.dll";
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);
O grande problema é que "DllImport" deseja um parâmetro "const string" para o diretório da DLL.
Então, minha pergunta é: O que poderia ser feito nesse caso?
Respostas:
Ao contrário das sugestões de algumas das outras respostas, o uso do
DllImport
atributo ainda é a abordagem correta.Sinceramente, não entendo por que você não pode fazer como todos os outros no mundo e especificar um caminho relativo para sua DLL. Sim, o caminho em que seu aplicativo será instalado difere nos computadores de outras pessoas, mas isso é basicamente uma regra universal quando se trata de implantação. o
DllImport
mecanismo é projetado com isso em mente.De fato, nem isso é o
DllImport
que lida com isso. São as regras nativas de carregamento da DLL do Win32 que governam as coisas, independentemente de você estar usando os úteis wrappers gerenciados (o empacotador P / Invoke apenas chamaLoadLibrary
). Essas regras são enumeradas em grandes detalhes aqui , mas as importantes são extraídas aqui:Portanto, a menos que você esteja nomeando sua DLL da mesma forma que uma DLL do sistema (o que você obviamente nunca deveria fazer, em nenhuma circunstância), a ordem de pesquisa padrão começará a procurar no diretório em que seu aplicativo foi carregado. Se você colocar a DLL lá durante a instalação, ela será encontrada. Todos os problemas complicados desaparecem se você apenas usar caminhos relativos.
Apenas escreva:
Mas se isso não faz o trabalho por qualquer motivo, e você precisa forçar o aplicativo a olhar em um diretório diferente para o DLL, você pode modificar o caminho de busca padrão usando a
SetDllDirectory
função .Observe que, conforme a documentação:
Portanto, desde que você chame essa função antes de chamar a função importada da DLL pela primeira vez, poderá modificar o caminho de pesquisa padrão usado para localizar DLLs. O benefício, é claro, é que você pode passar um valor dinâmico para essa função que é calculada em tempo de execução. Isso não é possível com o
DllImport
atributo; portanto, você ainda usará um caminho relativo (apenas o nome da DLL) e contará com a nova ordem de pesquisa para encontrá-lo.Você terá que P / Invocar esta função. A declaração é assim:
fonte
.dll
e outros sistemas adicionam a extensão apropriada em Mono (por exemplo,.so
no Linux). Isso pode ajudar se a portabilidade for uma preocupação.SetDllDirectory
. Você também pode mudarEnvironment.CurrentDirectory
e todos os caminhos relativos serão avaliados a partir desse caminho!AddDllDirectory
por outro lado ...DllImport
é mais do que apenas um invólucroLoadLibrary
. Ele também considera o diretório do assembly no qual oextern
método está definido . OsDllImport
caminhos de pesquisa podem ser adicionalmente limitados usandoDefaultDllImportSearchPath
.Ainda melhor do que a sugestão de Ran de usar
GetProcAddress
, basta fazer a chamadaLoadLibrary
antes de qualquer chamada para asDllImport
funções (com apenas um nome de arquivo sem um caminho) e elas usarão o módulo carregado automaticamente.Usei esse método para escolher, em tempo de execução, se é necessário carregar uma DLL nativa de 32 ou 64 bits sem precisar modificar várias funções de P / Invoke-d. Cole o código de carregamento em um construtor estático para o tipo que possui as funções importadas e tudo funcionará bem.
fonte
FunctionLoader
código.Se você precisar de um arquivo .dll que não esteja no caminho ou no local do aplicativo, não acho que você possa fazer exatamente isso, porque
DllImport
é um atributo, e os atributos são apenas metadados definidos em tipos, membros e outros elementos de linguagem.Uma alternativa que pode ajudá-lo a realizar o que acho que está tentando é usar o nativo
LoadLibrary
por meio do P / Invoke, para carregar uma .dll do caminho que você precisa e, em seguida, usarGetProcAddress
para obter uma referência à função que você precisa a partir desse .dll. Em seguida, use-os para criar um delegado que você pode chamar.Para facilitar o uso, você pode definir esse delegado como um campo da sua classe, para que pareça chamar um método de membro.
EDITAR
Aqui está um trecho de código que funciona e mostra o que eu quis dizer.
Nota: Eu não me incomodei em usar
FreeLibrary
, portanto este código não está completo. Em um aplicativo real, você deve ter o cuidado de liberar os módulos carregados para evitar um vazamento de memória.fonte
Desde que você saiba o diretório em que suas bibliotecas C ++ podem ser encontradas em tempo de execução, isso deve ser simples. Eu posso ver claramente que esse é o caso no seu código. Você
myDll.dll
estaria presente dentro domyLibFolder
diretório dentro da pasta temporária do usuário atual.Agora você pode continuar usando a instrução DllImport usando uma string const, como mostrado abaixo:
Apenas em tempo de execução, antes de chamar a
DLLFunction
função (presente na biblioteca C ++), adicione esta linha de código no código C #:Isso simplesmente instrui o CLR a procurar as bibliotecas C ++ não gerenciadas no caminho do diretório que você obteve no tempo de execução do seu programa.
Directory.SetCurrentDirectory
chamada define o diretório de trabalho atual do aplicativo para o diretório especificado. Se vocêmyDLL.dll
estiver presente no caminho representado peloassemblyProbeDirectory
caminho, ele será carregado e a função desejada será chamada através de p / invoke.fonte
defina o caminho da DLL no arquivo de configuração
antes de chamar a dll no seu aplicativo, faça o seguinte
então chame a dll e você pode usar como abaixo
fonte
DllImport funcionará bem sem o caminho completo especificado, desde que a dll esteja localizada em algum lugar no caminho do sistema. Você pode adicionar temporariamente a pasta do usuário ao caminho.
fonte
Se tudo falhar, basta colocar a DLL na
windows\system32
pasta. O compilador irá encontrá-lo. Especifique a DLL para carregar de:,DllImport("user32.dll"...
definaEntryPoint = "my_unmanaged_function"
para importar a função não gerenciada desejada para o aplicativo C #:Fonte e ainda mais
DllImport
exemplos: http://msdn.microsoft.com/en-us/library/aa288468(v=vs.71).aspxfonte