2017-03-14 2 views
0

J'ai un problème, et j'ai essayé de chercher une solution mais ne peut pas réaliser ce que je veux. Désolé si c'est en fait simple, s'il vous plaît pointez-moi juste sur la façon correcte de le faire.VCL/LCL - une forme dans la DLL - aucune fenêtre de la barre des tâches d'application, ne peut pas minimiser la forme principale

Alors! J'ai un programme C qui est un chargeur. Il doit appeler ma DLL écrite en Delphi ou Lazarus (Free Pascal). La DLL est en fait une application GUI autonome: lors du débogage, je la compile sous forme d'EXE et fonctionne.

Mon script de compilation le compile en tant que DLL avec un point d'entrée qui doit l'exécuter tout comme il fonctionne de manière autonome. Je m'attends exactement au même comportement, mais je peux faire des choses différentes (en particulier en réglant l'icône de l'application) si nécessaire.

Loader est un programme de style console mais compilé sans console - pas de fenêtres, rien. Il charge juste DLL et appelle une fonction.

Le problème est que quand je construis même projet par défaut vide avec un formulaire comme un EXE - il aura réellement "maître" Application (.Handle <> 0) fenêtre dans la barre des tâches. Je peux donc définir son titre indépendamment de la légende du formulaire principal.

Mais quand la même chose est à l'intérieur d'une DLL - il n'y a pas de fenêtre d'application (.Handle = 0), le titre sera la forme sous-titres, mais le bug le plus important: une forme ne peuvent pas être réduits au minimum!

Dans Delphi 7, il passe en arrière-plan sous d'autres fenêtres (mais la barre des tâches reste!); dans Lazarus il minimise juste à nulle part (caché, aucun moyen de restaurer plus); les deux sans aucune animation de minimisation.

Autre que cela, ma demande semble se comporter normalement. C'est seulement le problème que j'ai.

OK, je sais que les formes dans les bibliothèques est une mauvaise chose à faire, mais:

  1. Je vais bien instancier « autre » VCL complètement indépendant de l'instance de l'hôte, peut-être même dans différents fil.

  2. Il n'y a pas de VCL dans mon application hôte particulière! Pour moi, il doit travailler exactement comme il le fera dans EXE seul ...

Je cherchai quelque chose Application.Handle dans la DLL, et je comprends maintenant que je dois passer une poignée pour accueillir objet Application, donc DLL sera joint avec d'autres formes d'hôtes, mais je n'en ai aucun! Il est même pas Delphi ... (et application: = TApplication.Create (néant), n'a pas aidé non plus)

Tout de suite me aidera probablement:

  • A) Comment créer instruisent VCL un objet d'application normal pour moi? Comment ça marche quand dans EXE, peut-être que je peux copier ce code? B) Comment créer une fenêtre maître appropriée à partir de C (styles appropriés, etc.) pour passer son handle à la DLL? Aussi, je crois, dans Free Pascal il n'y a pas d'accès direct à la valeur du handle TApplication, donc je ne pourrais probablement pas l'assigner. C) Comment vivre sans une fenêtre de la barre des tâches, mais avoir ma forme (bonne nouvelle: mon programme a seulement une forme!) Pour minimiser correctement (ou juste en quelque sorte ...)?

Je vous maintenant tous voir un peu de code, donc la voici:

// default empty project code, produces valid working EXE: 
program Project1; 

uses Forms, Unit1 in 'Unit1.pas' {Form1}; 
{$R *.res} 

begin 
    Application.Initialize; 
    Application.CreateForm(TForm1, Form1); 
    Application.Run; 
end. 

+

// that's how I tried to put it in a DLL: 
library Project1; 

uses Forms, Unit1 in 'Unit1.pas' {Form1}; 
{$R *.res} 

function entry(a, b, c, d: Integer): Integer; stdcall; 
begin 
    Application.Initialize; 
    Application.CreateForm(TForm1, Form1); 
    Application.Run; 
    Result := 0; 
end; 

exports 
    entry; 

begin 
end. 

I fonction d'entrée spécialement conçue() pour être appelable avec rundll32, juste pour tester.

Aussi, j'ai essayé de mettre le corps directement à "begin end." section d'initialisation - même comportement faux.

// To call a DLL, this can be used: 
program Project1; 

function entry(a, b, c, d: Integer): Integer; stdcall; external 'Project1.dll'; 

begin 
    entry(0, 0, 0, 0); 
end. 

également, CMD-commande "rundll32 project1.dll entry" fonctionnera instantanément. (Ouais, de cette façon je pourrais obtenir un handle que Rundll me donne, mais ce n'est pas ce que je veux de toute façon.)

Dernières notes: (a) la DLL doit être compilée dans Lazarus; en fait la première chose que je pensais que c'est un bug dans LCL, mais maintenant quand testé dans Delphi7 je vois la même chose; et puisque le cas de Delphi est plus simple et robuste, j'ai décidé de mettre ici cela; (B) mon chargeur C n'appelle pas LoadLibrary, il utilise TFakeDLL hack (ce fichier OBJ a été modifié pour fonctionner sans wrapper Delphi) et charge ma DLL de la mémoire (donc je n'ai pas un handle à DLL lui-même), mais sinon leur comportement est le même.

+2

J'ai utilisé des formulaires dans dlls dans mon temps D3 pour les utilitaires communs, pouvant également fonctionner indépendamment d'un exécutable contenant une seule ligne, très similaire à votre configuration. N'a pas joué avec l'application ou quoi que ce soit, juste utilisé ShowModal pour lancer le formulaire, qui exécute la boucle de message nécessaire. Bien sûr, il n'y a pas de modalité efficace puisqu'il n'y a pas d'autres formes. Je ne me souviens pas de complications avec les formulaires, ils ont simplement agi normalement. –

+0

@David Heffernan, bonjour! (Se souvenir de moi? ^^). Pourquoi supprimer d'autres tags? Je pense au moins que "delphi" est nécessaire, puisque ce problème est aussi simple dans Delphi DLL. Puis-je ajouter [delphi] de retour? – aleksusklim

+0

@Sertac Akyuz, merci! J'ai ajouté ma propre réponse en fonction de votre solution. – aleksusklim

Répondre

1

D'accord, merci à @Sertac Akyuz, j'ai essayé avec .ShowModal:

// working Delphi solution: 
library Project1; 

uses Forms, Dialogs, SysUtils, Unit1 in 'Unit1.pas' {Form1}; 
{$R *.res} 

function entry(a, b, c, d: Integer): Integer; stdcall; 
begin 
    Result := 0; 
    Application.Initialize; 
    Form1 := TForm1.Create(nil); 
    try 
    Form1.ShowModal; 
    except 
    on e: Exception do 
     ShowMessage(e.message); 
    end; 
    Form1.Free; 
end; 

exports 
    entry; 

begin 
end. 

Il n'y a toujours pas de fenêtre d'application (titre barre des tâches égale pour former la légende), mais maintenant ma forme peut être réduite au minimum avec succès (avec le système animation). Notez que pour la compilation EXE, je dois aller par défaut avec Application, parce que quand j'ai essayé de créer le formulaire comme ceci - il a commencé à réduire au bureau (iconify) au lieu de la barre des tâches.

Fonctionne parfaitement dans le projet par défaut de Lazarus. Mais quand j'ai essayé de l'implémenter dans mon programme de production, cela m'a donné l'exception "Disk Full" à .ShowModal! Cette chose me frustrait un peu plus tôt (et c'est pourquoi je me suis débarrassé complètement de la modalité, je ne l'ai plus essayé), mais maintenant j'étais assez déterminé pour avoir le fond du problème.

Et j'ai trouvé le problème! Mon script de génération ne transmet pas l'option de compilation "-WG" ("Spécifier l'application de type graphique"). On dirait que quelque chose dans LCL utilisait le mode console-like, et la boucle de modalité a échoué pour une raison quelconque.

Ensuite, j'ai eu un autre problème très confus que je veux partager. Mon formulaire OnCreate était plutôt gros et complexe (même en commençant d'autres threads), et certaines fonctions internes me donnent une violation d'accès quand on essaie de faire quelque chose avec un des contrôles sur le formulaire. Il semblait que le contrôle ne se construit pas encore, ou la forme elle-même ...

Active que l'appel réel Form1:=TForm1.Create(nil); évidemment quittera la variable globale « Form1 » non affecté pendant l'événement FormCreate. La solution était simple: ajouter Form1:=Self; au début de TForm1.FormCreate(Sender: TObject);

Maintenant tout fonctionne sans aucun problème.Je peux même utiliser d'autres formes avec un Form2.Show(); normal si je les ajoute d'abord à ma fonction d'entrée(), comme Form2:=TForm2.Create(Form1);

(édition: note mineure, si vous utiliseriez Lazarus et essayiez d'exécuter la fonction d'entrée() de n'importe quel différent thread que celui qui a chargé DLL bibliothèque elle-même - alors vous devriez mettre MainThreadID:=GetCurrentThreadId(); juste au-dessus de Application.Initialize;)

Yay, cette question est résolue!