La programación de la estructura del complemento requiere un contenedor de complementos para controlar el funcionamiento de cada DLL y organizar cada subsistema dividido en un archivo de biblioteca DLL. Para cada programa DLL, las funciones de interfaz deben reservarse para el contenedor. Generalmente, las funciones de interfaz incluyen: funciones que comienzan a llamar a la biblioteca DLL y funciones que cierran la biblioteca DLL. A través de la función de interfaz, el contenedor de complementos puede pasar parámetros al módulo DLL para lograr el control dinámico. Explicaré los detalles de implementación específicos y daré el código de respuesta a continuación.
Es posible que primero necesite comprender la estructura de UNIT y la estructura de los proyectos en DELPHI. Este artículo no analiza en profundidad los detalles teóricos de la programación DLL, solo muestra algunos códigos prácticos que estaba estudiando en el libro "Programación en profundidad DELPHI" de Liu Yi.
También estoy en la etapa introductoria de DELPHI. Simplemente siento que hay algunas cosas que vale la pena discutir en este desarrollo de DLL, así que estoy escribiendo este artículo y espero que puedan darme sugerencias generosas sobre lo que no estoy haciendo bien.
Introducción al programa de muestra.
Para facilitar la lectura, utilizaré parte del código del programa de un sistema MIS para demostrar algunos métodos de programación de complementos. El programa de muestra es una aplicación DBMS de estructura C/S típica. Nos centraremos en las declaraciones de control del programa marco (en lo sucesivo, Hall) y el control de respuesta del programa complementario dll.
1. Estructura del programa
El contenedor de complementos Hall se crea utilizando un proyecto independiente. La ventana principal del Hall es equivalente al formulario del contenedor MDI en el programa MDI. Las funciones de interfaz en el Dll se llamarán explícitamente en el Hall.
Cada programa de complemento utiliza su propio proyecto de forma independiente. A diferencia de los proyectos normales, el proyecto DLL crea Dll Wizard y los archivos generados por la compilación correspondiente tienen el sufijo DLL.
=550) ventana.open('/upload/20080315181507424.jpg');" src="http://files.VeVB.COm/upload/20080315181507424.jpg" onload="if(this.width>'550')this.width='550';if(this.height>'1000')this.height='1000';" borde=0>
2. Diseño de interfaz
En el programa de ejemplo Narcissus nos reservamos dos funciones de interfaz:
Mostrar formulario DLL
Esta función pasa el identificador de la aplicación a la ventana secundaria de DLL y el programa DLL creará dinámicamente una instancia del formulario DLL. También puede pasar cierta lógica empresarial a la subventana DLL en forma de parámetros, como el nombre del formulario, el nombre de usuario actualmente conectado, etc. Utilice esta función para crear una instancia de formulario DLL cuando se llame por primera vez.
Formulario DLL gratuito
Esta función mostrará el lanzamiento de la instancia de la ventana DLL y llamará al método FreeDLLForm de cada formulario DLL para liberar la instancia creada al salir de la aplicación; de lo contrario, provocará un error de memoria de solo lectura. De manera similar, también puede pasar cierta lógica empresarial que debe realizarse al liberar el formulario al formulario DLL en forma de parámetros.
3. Método de depuración
El programa de formulario DLL no se puede ejecutar directamente y necesita un contenedor de complemento para llamarlo. Por lo tanto, primero debemos implementar un programa Hall básico y luego guardar Hall.exe en un directorio fijo. Realice las siguientes configuraciones para cada proyecto DLL:
1) Abra el proyecto DLL
2) Seleccione el menú Ejecutar parámetros
3) Busque nuestro contenedor Hall.exe en la ventana emergente
De esta manera, el programa Hall se llamará automáticamente al depurar el programa DLL, y el programa DLL se depurará utilizando la interfaz de llamada reservada en Hall.
Implementación básica del programa de complemento.
El método de diseño del programa DLL no es muy diferente del de WINAPP normal, excepto que todas las ventanas se guardan en la biblioteca DLL como un "recurso" especial y deben llamarse manualmente, a diferencia de WINAPP donde los proyectos se crean automáticamente. El método para declarar funciones de interfaz es muy simple.
1) Declarar la función en la sección Implementación de la Unidad
2) Agregue la marca stdcall al final de la declaración de función
3) Antes de la declaración de inicio del código del proyecto (Proyecto Ver código fuente), use la declaración de exportaciones para declarar la interfaz de función
Para que el código sea conciso, personalmente me gusta agregar una unidad de Unidad (Archivo Nuevo - Unidad) de forma independiente en el proyecto y luego definir todos los cuerpos de funciones que se generarán en esta unidad. Unidad de la forma a la que se hace referencia. Llamé a esta unidad UnitEntrance, inicialicé la ventana que se mostrará en la función ShowDLLForm y llamé al método Show para mostrarla. HALL pasará el nombre de usuario que inició sesión como parámetros. Después de obtener el nombre de usuario, se puede realizar algún control de permisos. se refleja en la inicialización de la interfaz superior.
El código es el siguiente.
unidad UnidadOficinaEntrada;
interfaz
usos
Windows, Mensajes, SysUtils, Variantes, Clases, Gráficos, Controles, Formularios;
función ShowDLLForm(AHandle: THandle; ACaption: cadena; AUserID: cadena):boolean;stdcall;
función FreeDLLForm(AHandle: THandle; ACaption: cadena; AUserID: cadena):boolean;stdcall;
implementación
utiliza UnitOfficialMainForm //Cambiar a unidad de MAINFORM;
var
DLL_Form:TFormOfficialMain //Cambiar al nombre de MAINFORM
//----------------------------------------
//Nombre: MostrarDLLForm
//Func: el complemento DLL llama a la función de entrada
//Para: AHandle identificador del programa adjunto; ACaption título de este formulario
//Rtrn: N/A
//Autentificación: CST
//Fecha: 2005-6-3
//----------------------------------------
función ShowDLLForm(AHandle: THandle; ACaption: cadena; AUserID: cadena):boolean;
comenzar
resultado:=verdadero;
intentar
Application.Handle:=AHandle; //Anclado al contenedor principal del programa
DLL_Form:=TFormOfficialMain.Create(Application); //Cambiar al NOMBRE de MAINFORM;
intentar
con DLL_Form hacer
comenzar
Título: = A Título;
StatusBar.Panels.Items[0].Texto := AUserID;
//Configurar la interfaz de usuario
Espectáculo ;
fin;
excepto
en e: excepción
comenzar
dll_form.Gratis;
fin;
fin;
excepto
resultado:=falso;
fin;
fin;
//----------------------------------------
//Nombre: FreeDLLForm
//Func: el complemento DLL llama a la función de exportación
//Para: AHandle identificador del programa adjunto
//Rtrn: verdadero/falso
//Autentificación: CST
//Fecha: 2005-6-11
//----------------------------------------
función FreeDLLForm(AHandle: THandle; ACaption: cadena; AUserID: cadena):boolean;
comenzar
Application.Handle:=AHandle; //Anclado al contenedor principal del programa
if DLL_Form.Showing then DLL_Form.Close // Si la ventana se abre y se cierra primero, activar FORM.CLOSEQUERY puede cancelar el proceso de cierre.
si no es DLL_Form.Mostrando entonces
comenzar
DLL_Form.Gratis;
resultado:=verdadero;
end //Aún abierto, indica CLOSEQUERY.CANCLOSE=FALSE
demás
comenzar
resultado:=falso;
fin;
fin;
fin.
El código del archivo del proyecto DLL es el siguiente:
funcionario de la biblioteca;
{ Nota importante sobre la administración de memoria DLL: ShareMem debe ser el
primera unidad en la cláusula USES de su biblioteca Y en la de su proyecto (seleccione
Project-View Source) cláusula USES si su DLL exporta algún procedimiento o
funciones que pasan cadenas como parámetros o resultados de funciones.
se aplica a todas las cadenas pasadas hacia y desde su DLL, incluso aquellas que
están anidados en registros y clases. ShareMem es la unidad de interfaz para.
el administrador de memoria compartida BORLNDMM.DLL, que debe implementarse junto
con su DLL Para evitar el uso de BORLNDMM.DLL, pase información de cadena.
usando parámetros PChar o ShortString }
usos
SysUtils,
clases,
UnitOfficialDetailForm en 'UnitOfficialDetailForm.pas' {FormOfficialDetail},
UnitOfficialMainForm en 'UnitOfficialMainForm.pas' {FormOfficialMain},
UnitOfficeEntrance en 'UnitOfficeEntrance.pas',
UnitOfficialClass en '../../Public/Library/UnitOfficialClass.pas',
UnitMyDataAdatper en '../../Public/Library/UnitMyDataAdatper.pas',
UnitMyHeaders en '../../Public/Library/UnitMyHeaders.pas';
{$R*.res}
exporta ShowDLLForm,FreeDLLForm //función de interfaz
comenzar
fin.
Una vez que el programa de complemento llama a la ventana DLL, la instancia de la ventana permanecerá en la parte superior de la ventana HALL, por lo que no hay necesidad de preocuparse por la oclusión.
Implementación de programas de contenedores.
1. Introducción de funciones de interfaz.
Hay dos formas de llamar a funciones en la biblioteca DLL: explícitas e implícitas. Las llamadas explícitas son más flexibles, por lo que utilizamos llamadas explícitas. En Delphi, debe declarar el tipo de función para la función de interfaz y luego crear una instancia del tipo de función. La instancia es en realidad un puntero a la función, podemos acceder a la función, pasar parámetros y obtenerla. el valor de retorno. Agregue la declaración de la clase de función en la sección Interfaz del archivo de la unidad:
tipo
//Definir el tipo de función de interfaz, la función de interfaz proviene de la interfaz DLL
TShowDLLForm = Función(AHandle:THandle; ACaption: Cadena; AUserID:cadena):Boolean;stdcall;
TFreeDLLForm = Función(AHandle:THandle; ACaption: Cadena; AUserID:cadena):boolean;stdcall;
Mostrar funciones de biblioteca de llamadas requiere los siguientes pasos:
1) Cargar el archivo de la biblioteca DLL
2) Obtener dirección de función
3) Ejecutar función
4) Liberar la biblioteca DLL
A continuación analizaremos estos pasos en detalle.
2. Cargue el archivo de la biblioteca DLL
La biblioteca DLL se puede cargar en la memoria llamando a la función API LoadLibrary. No discutiremos aquí el impacto de la DLL en la administración de la memoria. El parámetro de LoadLibrary es la ruta de la dirección del archivo DLL. Si la carga se realiza correctamente, se devolverá una variable de tipo CARDINAL como identificador de la biblioteca DLL si el archivo de destino no existe o si otras razones provocan la carga de la DLL; archivo falla, se devolverá un 0.
3. Crear instancias de funciones de interfaz
La función API para obtener el puntero de la función de la interfaz es GetProcAddress (identificador del archivo de la biblioteca, nombre de la función). Si se encuentra la función, se devolverá el puntero de la función. Si falla, se devolverá NIL.
Defina una variable de puntero de función usando el tipo de función definido anteriormente y luego use el operador @ para obtener la dirección de la función, de modo que pueda usar la variable de puntero para acceder a la función. El código principal es el siguiente:
…
var
ShowDLLForm: TShowDLLForm // instancia de función de interfaz DLL
FreeDLLForm: TFreeDLLForm;
comenzar
intentar
comenzar
APlugin.ProcAddr := LoadLibrary(PChar(sPath));
APlugin.FuncFreeAddr := GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');
APlugin.FuncAddr: = GetProcAddress (APlugin.ProcAddr, 'ShowDLLForm');
@ShowDLLForm:=APlugin.FuncAddr;
@FreeDLLForm:=APlugin.FuncFreeAddr;
si ShowDllForm(Self.Handle, APlugin.Caption, APlugin.UserID) entonces
Resultado:=Verdadero
…
4. Un método de implementación específico
Para administrar los complementos de manera estructurada y facilitar la futura expansión del sistema, podemos combinar la información DLL disponible registrada en la base de datos y luego acceder dinámicamente al programa DLL consultando los registros de la base de datos.
1) Diseño de la tabla del módulo del sistema.
Para los sistemas MIS, puede utilizar las condiciones DBS existentes para establecer una tabla de módulos del sistema para registrar archivos DLL e información relacionada asignada a los módulos del sistema.
Tipo de rol de nombre de campo
Índice de identificación automáticaINT
modAlias alias del módulo VARCHAR
modName nombre del módulo VARCHAR
modWndClass forma identificador único VARCHAR
modFile DLL ruta VARCHAR
modMemo nota TEXTO
・Los alias de módulos se utilizan para unificar las reglas de nomenclatura durante la fase de diseño de programación, especialmente para referencia de los miembros del equipo durante el desarrollo del equipo.
・El nombre del módulo se pasará como parámetro ACAPTION a la función SHOWDLLFORM como título de la ventana DLL.
・El identificador único del formulario es el CLASSNAME de la ventana principal en el submódulo DLL, que se utiliza para determinar la ventana que se controlará en tiempo de ejecución.
・La ruta DLL guarda el nombre del archivo DLL, que se convertirá en una ruta absoluta en el programa.
2) Estructura de datos de información del complemento
La definición de una interfaz de datos que registre información relacionada con los complementos puede controlar de forma centralizada los complementos DLL. Agregue el siguiente código a la sección Interfaz:
tipo
//Definir clase de información del complemento
TMyPlugins = clase
Título:Cadena; //título del formulario DLL
DllFileName:Cadena; //Ruta del archivo DLL
WndClass:String; //Identificación del formulario
ID de usuario:cadena; //Nombre de usuario
ProcAddr:THandle; //Identificador de biblioteca cargado por LOADLIBRARY
FuncAddr:Puntero; //Puntero de función SHOWDLLFORM
FuncFreeAddr:Puntero; //Puntero de función FREEDLLFORM
fin;
…
Cree una instancia de TMyPlugins para cada complemento. Los métodos de inicialización para estas instancias se analizarán a continuación.
3) Función de carga de complementos
En este ejemplo, la ventana DLL se carga y se muestra en el evento que desencadena la apertura de la ventana secundaria en HALL. Después de que se activa el evento del botón, primero determine si la DLL se ha cargado de acuerdo con la instancia de la estructura del complemento. Si se ha cargado, controle la visualización o el cierre de la ventana si no está cargado, acceda a la tabla de datos; asigne los campos a la estructura del complemento y luego ejecute la carga para que el puntero funcione.
El código parcial es el siguiente.
…
//----------------------------------------
//Nombre: OpenPlugin
//Función: Proceso de control de información del complemento: Inicialización==》Establecer permisos==》Cargar ventana DLL
//Para: APlugin-TMyPlugins; alias de sAlias;
//Rtrn: N/A
//Autentificación: CST
//Fecha: 2005-6-2
//----------------------------------------
procedimiento TFormHall.OpenPlugin(AFromActn: TAction ;APlugin:TMyPlugins; sAlias:string; sUserID:string);
var hWndPlugin:HWnd;
comenzar
//Determine si la ventana del complemento se ha cargado hWndPlugin:=FindWindow(PChar(APlugin.WndClass),nil);
si hWndPlugin <> 0 entonces //La ventana del complemento ha sido cargada
comenzar
si no es IsWindowVisible(hWndPlugin) entonces
comenzar
AFromActn.Checked := Verdadero;
ShowWindow(hWndPlugin,SW_SHOWDEFAULT); //Mostrar
fin
demás
comenzar
AFromActn.checked:= Falso;
Mostrar ventana(hWndPlugin,SW_HIDE);
fin;
Salir; //Salir del proceso de creación del complemento
fin;
//Inicializar instancia de clase de complemento
si no es InitializeMyPlugins(APlugin,sAlias) entonces
comenzar
showmessage('Error al inicializar la clase de complemento.');
salida;
fin;
//Obtener el valor del permiso actual
APlugin.ID de usuario := sUserID;
//Cargar ventana DLL
si no es LoadShowPluginForm(APlugin) entonces
comenzar
showmessage('Error al cargar el complemento del centro.');
salida;
fin;
fin;
//----------------------------------------
//Nombre: InicializarMisPlugins
//Función: Inicializar la instancia MYPLUGIN (Caption | DllFileName | IsLoaded)
//Para: APlugin-TMyPlugins
//Rtrn: N/A
//Autentificación: CST
//Fecha: 2005-6-2
//----------------------------------------
función TFormHall.InitializeMyPlugins(APlugin:TMyPlugins; sAlias:String):Boolean;
var
strSQL:cadena;
miDA:TMyDataAdapter;
comenzar
Resultado:=Falso;
myDA:=TMyDataAdapter.Create;
strSQL:='SELECT * FROM SystemModuleList DONDE modAlias='+QuotedStr(sAlias);
intentar
myDA.RetrieveData(strSQL);
excepto
en E:Excepción hacer
comenzar
resultado:=falso;
myDA.Gratis;
salida;
fin;
fin;
intentar
comenzar
con myDA.MyDataSet hacer
comenzar
si no está vacío entonces
comenzar
APlugin.Caption:= FieldByName('modName').Value;
APlugin.DllFileName := FieldByName('modFile').Value;
APlugin.WndClass := FieldByName('modWndClass').Value;
resultado:=Verdadero;
fin;
Cerca;
fin; //fin de con...hacer...
fin; //fin del intento
excepto
en E:Excepción hacer
comenzar
Resultado:=Falso;
myDA.Gratis;
Salida;
fin; //fin de la excepción
fin; //fin del intento...excepto
myDA.Gratis;
fin;
//----------------------------------------
//Nombre: LoadShowPluginForm
//Función: carga el complemento DLL y muestra la ventana
//Para: APlugin-TMyPlugins
//Rtrn: verdadero creado exitosamente
//Autentificación: CST
//Fecha: 2005-6-2
//----------------------------------------
función TFormHall.LoadShowPluginForm (const APlugin:TMyPlugins):booleano;
var
ShowDLLForm: TShowDLLForm // instancia de función de interfaz DLL
FreeDLLForm: TFreeDLLForm;
sPath:string; //Ruta completa del archivo DLL
comenzar
intentar
comenzar
sPath:=ExtractFilepath(Application.ExeName)+ 'complementos/' + APlugin.DllFileName;
APlugin.ProcAddr := LoadLibrary(PChar(sPath));
APlugin.FuncFreeAddr := GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');
APlugin.FuncAddr: = GetProcAddress (APlugin.ProcAddr, 'ShowDLLForm');
@ShowDLLForm:=APlugin.FuncAddr;
@FreeDLLForm:=APlugin.FuncFreeAddr;
si ShowDllForm(Self.Handle, APlugin.Caption, APlugin.UserID) entonces
Resultado:=Verdadero
demás
Resultado:=Falso;
fin;
excepto
en E:Excepción hacer
comenzar
Resultado:=Falso;
ShowMessage('Error al cargar el módulo de complemento, verifique si los archivos en el directorio PLUGINS están completos.');
fin;
fin;
fin;
…
4) control de ventana DLL
Como ilustra el código en 3), la apertura y el cierre de la ventana DLL se realizan solo en la capa de presentación. Cerrar la ventana en realidad no libera la ventana DLL. Simplemente llama a la función API FindWindow para obtener el identificador del formulario de acuerdo con la ventana. identificador (es decir, Form.name). Uso El parámetro nCmdShow de la función SHOWWINDOW controla la visualización/ocultación de la ventana.
De hecho, este es un punto débil en la implementación de mi programa. Si se usa el método Self.close en la ventana DLL, causará un error de memoria. No hay forma de resolverlo debido a las capacidades limitadas. el último recurso. Por tanto, el botón de cierre de la ventana principal de cada programa DLL debe estar oculto. :-PAG
5) Lanzamiento de la biblioteca DLL
Cuando se cierra el programa, las bibliotecas DLL deben liberarse una por una de acuerdo con la instancia de información del complemento. La función para liberar la biblioteca DLL es la siguiente:
procedimiento TFormHall.ClosePlugin(aPLG:TMyPlugins);
var
FreeDLLForm: TFreeDLLForm;
comenzar
si aPLG.ProcAddr = 0 entonces salga;
si aPLG.FuncFreeAddr = nil entonces salga;
@FreeDLLForm:=aPLG.FuncFreeAddr;
si no es FreeDLLForm(Application.Handle,'','') entonces
mostrarMensaje('errar');
fin;
resumen
El efecto de ejecución de este programa de ejemplo es el siguiente:
=550) ventana.open('/upload/20080315181507979.jpg');" src="http://files.VeVB.COm/upload/20080315181507979.jpg" onload="if(this.width>'550')this.width='550';if(this.height>'1000')this.height='1000';" borde=0>
Entre los métodos anteriores, debido a que hay muchos problemas sin resolver debido a la capacidad limitada, he adoptado algunos métodos de encubrimiento que no parecen razonables. Espero que todos puedan diseñar una mejor solución después de intentarlo un poco. excelente manera de aprender más.