(virar)
Recentemente usei o DllImport e descobri que a maior parte do conteúdo é o mesmo do Baidu na Internet, e eu o coletei do MSDN. Agora vou resumir o conteúdo e compartilhar com você.
Ao estudar C # no trabalho real , você pode perguntar: Por que temos que reescrever o código de algumas funções existentes (como algumas funções no Windows, alguns métodos já escritos em C ++), existe alguma maneira de escrever C # diretamente ? você usa essas funções que já existem? A resposta é sim, você pode chamar diretamente essas funções por meio de DllImport em C #.
DllImport é uma classe de atributo no namespace System.Runtime.InteropServices, portanto, se quiser usar DllImport no ASP.NET, você deve primeiro "using System.Runtime.InteropServices;". Sua função é fornecer informações necessárias para chamar funções derivadas de DLLs não gerenciadas. O atributo DllImport é aplicado ao método e requer pelo menos o nome da dll que contém o ponto de entrada. Definição de atributo DllImport
do seguinte modo:
namespace System.Runtime.InteropServices
{
[AttributeUsage (AttributeTargets.Method)]
public class DllImportAttribute: System.Attribute
{
public DllImportAttribute (string dllName) {...} // O parâmetro de posicionamento é dllNamepublic CallingConvention CallingConvention; // Convenção de chamada do ponto de entradapublic CharSet CharSet; // Conexão de caracteres usada pelo ponto de entradapublic string EntryPoint; // Nome do ponto de entradapublic bool ExactSpelling; // Se a grafia deve ser exatamente igual ao ponto de entrada indicado, o padrão é falsopublic bool PreserveSig; // A assinatura do método é preservada ou convertidapublic bool SetLastError; // O valor de retorno do método FindLastError é armazenado aquipublic string Value {get {...}}
}
}
Descrição:
1. DllImport só pode ser colocado em declarações de método.
2. DllImport tem um único parâmetro de posicionamento: especifique o parâmetro dllName que contém o nome dll do método importado.
3. DllImport tem cinco parâmetros nomeados:
a. O parâmetro CallingConvention indica a convenção de chamada do ponto de entrada. Se CallingConvention não for especificado, o valor padrão CallingConvention.Winapi será usado.
b. O parâmetro CharSet especifica o conjunto de caracteres usado no ponto de entrada. Se CharSet não for especificado, o valor padrão CharSet.Auto será usado.
c. O parâmetro EntryPoint fornece o nome do ponto de entrada na dll. Se EntryPoint não for especificado, o nome do próprio método será usado.
d. O parâmetro ExactSpelling indica se o EntryPoint deve corresponder exatamente à grafia do ponto de entrada indicado. Se ExactSpelling não for especificado, o valor padrão false será usado.
e. O parâmetro PreserveSig indica se a assinatura do método é preservada ou convertida. Quando a assinatura é convertida, ela é convertida em uma assinatura com um valor de retorno HRESULT e um parâmetro de saída adicional denominado retval com o valor de retorno. Se PreserveSig não for especificado, o valor padrão true será usado.
f. O parâmetro SetLastError indica se o método retém o "último erro" do Win32. Se SetLastError não for especificado, o valor padrão false será usado.
4. É uma classe de atributo única.
5. O método modificado com o atributo DllImport deve ter o modificador externo.
Exemplo de uso de DllImport (um win32api usado para escrever arquivos ini):
DllImport ("kernel32")
private static extern long WritePrivateProfileString (seção string, chave string, string val, string filePath);
O tipo de dados de chamada WinAPI com este método corresponde a: DWORD = int ou uint, BOOL = bool, constante predefinida = enum, structure = struct.
Problema de caminho DllImport:
O DllImport encontrará automaticamente os lugares em ordem:
1. O diretório onde o exe está localizado
2. Diretório System32
3. Diretório de variável de ambiente
Portanto, você só precisa copiar a DLL referenciada para esses três diretórios e não precisa escrever o caminho.
Na web, mas também no aplicativo
Posteriormente, foi descoberto que usando [DllImport (@ "C: \ OJ \ Bin \ Judge.dll")] para especificar o caminho absoluto da DLL pode ser carregado normalmente.
Esse problema ocorre com mais frequência ao usar componentes DLL não gerenciados de terceiros. O meu também é o problema no momento. A solução oficial da Equipe Asp.Net é a seguinte:
Primeiro, você precisa confirmar quais componentes você faz referência, quais são gerenciados e quais não são gerenciados. Gerenciado é fácil de manusear, o uso direto precisa ser cotado e o uso indireto precisa ser copiado para o diretório bin. O processamento não gerenciado será mais problemático. Na verdade, você copiar para bin não ajudará, porque o CLR copiará os arquivos para um diretório temporário e, em seguida, executará a web lá, e o CLR copiará apenas os arquivos gerenciados, é por isso que colocamos claramente os dlls não gerenciados em Ele ainda informa que o módulo não pode ser carregado no compartimento.
A abordagem específica é a seguinte:
Em primeiro lugar, podemos encontrar um local no servidor para criar um novo diretório, se for C: \ DLL;
Então, na variável de ambiente, adicione este diretório à variável Path;
Por fim, copie todos os arquivos não gerenciados para C: \ DLL ou, mais simplesmente, coloque a DLL no diretório system32.
Para aplicações que podem ser implantadas por si mesmas, tal excepcional não é uma solução, porém, se estivermos usando espaço virtual, não podemos registrar a variável PATH ou copiar nossa própria DLL para o diretório system32. Ao mesmo tempo, não conhecemos necessariamente o caminho físico de nossa DLL.
Apenas constantes de string podem ser usadas em DllImport, e Server.MapPath (@ "~ / Bin / Judge.dll") não pode ser usado para determinar o caminho físico.
O problema da baixa velocidade de carregamento do DllImport:
No entanto, descobri que chamar essa "DLL não gerenciada" é muito lento, talvez porque meu método exija autenticação remota, mas é muito lento. Depois de muita pesquisa, finalmente encontrei uma solução perfeita.
Primeiro usamos
[DllImport ("kernel32.dll")]
private extern static IntPtr LoadLibrary (String path);
[DllImport ("kernel32.dll")]
private extern static IntPtr GetProcAddress (IntPtr lib, String funcName);
[DllImport ("kernel32.dll")]
private extern static bool FreeLibrary (IntPtr lib);
Os endereços das funções LoadLibrary e GetProcAddress são obtidos respectivamente e, em seguida, as funções em nossa DLL são obtidas por meio dessas duas funções.
Podemos primeiro usar Server.MapPath (@ "~ / Bin / Judge.dll") para obter o caminho físico de nossa DLL, então usar LoadLibrary para carregar e, finalmente, usar GetProcAddress para obter o endereço da função a ser usada.
O seguinte código de classe personalizada conclui o carregamento e a chamada de função de LoadLibrary:
public class DllInvoke
{
[DllImport ("kernel32.dll")]
private extern static IntPtr LoadLibrary (String path);
[DllImport ("kernel32.dll")]
private extern static IntPtr GetProcAddress (IntPtr lib, String funcName);
[DllImport ("kernel32.dll")]
private extern static bool FreeLibrary (IntPtr lib);
private IntPtr hLib;
public DllInvoke (String DLLPath)
{
hLib = LoadLibrary (DLLPath);
}
~ DllInvoke ()
{
FreeLibrary (hLib);
}
// Converte a função a ser executada em um delegado
Public Delegate Invoke (String APIName, Type t)
{
IntPtr api = GetProcAddress (hLib, APIName);
return (Delegate) Marshal.GetDelegateForFunctionPointer (api, t);
}
}
O código a seguir faz a chamada
delegado público int Compile (comando String, StringBuilder inf); // 编译
DllInvoke dll = new DllInvoke (Server.MapPath (@ "~ / Bin / Judge.dll"));
Compilar compilar = (Compilar) dll.Invoke ("Compilar", typeof (Compilar));
StringBuilder inf;
compile (@ "gcc ac -o a.exe", inf); // aqui é para chamar a função Compile definida em minha DLL
Exemplo de uso de DllImport:
Biblioteca usada em uma programação Win32 C # usada tipo correspondente:
1. DWORD é um número inteiro de 4 bytes, portanto, podemos usar int ou uint como o tipo correspondente em C #.
2. O tipo bool corresponde a BOOL.
Exemplo 1 : chame a API Beep () para fazer um som
Beep () é definido em kernel32.lib. Conforme definido no MSDN, Beep tem o seguinte protótipo:
BOOL Beep (DWORD dwFreq, // frequência do som
DWORD dwDuration // duração do som);
Escreva o seguinte protótipo em C #:
[DllImport ("kernel32.dll")]
public static extern bool Beep (frequência interna, duração interna);
Exemplo 2 : tipos e constantes enumerados
MessageBeep () é definido em user32.lib. Conforme definido no MSDN, MessageBeep tem o seguinte protótipo:
BOOL MessageBeep (UINT uType // tipo de som
);
Escreva o protótipo em C #:
public enum BeepType
{
SimpleBeep = -1,
IconAsterisk = 0x00000040,
IconExclamation = 0x00000030,
IconHand = 0x00000010,
IconQuestion = 0x00000020,
Ok = 0x00000000,
}
Na verdade, o parâmetro uType aceita um conjunto de constantes predefinidas. Para o parâmetro uType, faz sentido usar o tipo enum.
[DllImport ("user32.dll")]
public static extern boolMessageBeep (BeepType beepType);
Exemplo 3 : Estrutura de processamento
Às vezes, preciso determinar a condição da bateria do meu laptop. O Win32 fornece funções de gerenciamento de energia para essa finalidade. Pesquise no MSDN para localizar a função GetSystemPowerStatus ().
BOOL GetSystemPowerStatus (LPSYSTEM_POWER_STATUS lpSystemPowerStatus);
Esta função contém um ponteiro para uma estrutura, da qual ainda não tratamos. Para lidar com a estrutura, precisamos definir a estrutura em C #. Começamos com a definição de não gerenciado:
typedef struct _SYSTEM_POWER_STATUS {
BYTE ACLineStatus;
BYTE BatteryFlag;
BYTE BatteryLifePercent;
BYTE Reservado1;
DWORD BatteryLifeTime;
DWORD BatteryFullLifeTime;
} SYSTEM_POWER_STATUS, * LPSYSTEM_POWER_STATUS;
Em seguida, obtenha a versão C # substituindo os tipos C # por tipos C.
struct SystemPowerStatus
{
byte ACLineStatus;
byte batteryFlag;
byte batteryLifePercent;
byte reserved1;
int batteryLifeTime;
int batteryFullLifeTime;
}
Dessa forma, você pode facilmente escrever um protótipo C #:
[DllImport ("kernel32.dll")]
public static extern bool GetSystemPowerStatus (ref SystemPowerStatus systemPowerStatus);
Neste protótipo, usamos "ref" para indicar que o ponteiro da estrutura será passado em vez do valor da estrutura. Essa é a maneira geral de lidar com estruturas passadas por ponteiros.
Esta função funciona bem, mas é melhor definir os campos ACLineStatus e batteryFlag como enum:
enum ACLineStatus: byte
{
Offline = 0,
Online = 1,
Desconhecido = 255,
}
enum BatteryFlag: byte
{
Alto = 1,
Baixo = 2,
Crítico = 4,
Carregando = 8,
NoSystemBattery = 128,
Desconhecido = 255,
}
Observe que, como os campos da estrutura são bytes, usamos byte como o tipo básico de enum.
Chamando código C ++ em C #
tipo int
[DllImport (“MyDLL.dll")]
public static extern int mySum (int a1, int b1); // retorna um tipo int
extern “C” __declspec (dllexport) int WINAPI mySum (int a2, int b2) // Declarado na DLL
{
// a2 b2 não pode mudar a1 b1
// a2 = ..
// b2 = ...
retornar a + b;
}
// Tipo de transferência de parâmetro público estático extern int mySum (ref int a1, ref int b1); // Declara extern "C" na DLL __declspec (dllexport) int WINAPI mySum (int * a2, int * b2) {// Sim Alterar a1, b1 * a2 = ... * b2 = ... retornar a + b;}
DLL precisa passar em char * type [DllImport ("MyDLL.dll")] // Passar valor public static extern int mySum (string astr1, string bstr1); // Declare extern "C" em DLL __declspec (dllexport) int WINAPI mySum (char * astr2, char * bstr2) {// Alterar astr2bstr 2, astr1 bstr1 não será alterado return a + b;}
DLL precisa enviar char * type [DllImport ("MyDLL.dll")] // o valor enviado public static extern int mySum (StringBuilder abuf, StringBuilder bbuf); // Declare extern "C" na DLL __declspec (dllexport ) int WINAPI mySum (char * astr, char * bstr) {// outgoing char * change astr bstr -> abuf, bbuf pode ser alterado return a + b;} Função de retorno de chamada DLL BOOL EnumWindows (WNDENUMPROC lpEnumFunc, LPARAM lParam)
usingSystem; using System.Runtime.InteropServices; público delegado bool CallBack (int hwnd, int lParam); // 定义 委托 函数 类型 public class EnumReportApp { [DllImport ("user32")] public static extern int EnumWindows (CallBack x, int y); public static void Main ()
{ CallBack myCallBack = novo CallBack (EnumReportApp.Report);
EnumWindows (myCallBack, 0); } public static bool Report (int hwnd, int lParam) { Console.Write ("Window handle is"); Console.WriteLine (hwnd); return true; } }
DLL 传递 结构 BOOL PtInRect (const RECT * lprc, POINT pt); using System.Runtime.InteropServices; [StructLayout (LayoutKind.Sequential)] public struct Point
{public int x; public int y; } [StructLayout (LayoutKind.Explicit)] public struct Rect {[FieldOffset (0)] public int left; [FieldOffset (4)] public int top; [FieldOffset (8)] direito interno público; [FieldOffset (12)] public int bottom; } Classe XXXX
{[DllImport ("User32.dll")] public static extern bool PtInRect (ref Rect r, Point p); }