utilisé dll c++ dans delphi :plus vite

delphino
utilisé dll c++ dans delphi :plus vite

de delphino :
Salut...,
j'utilise une dll ecrite en C++,dans delphi,

//le code de l'application :
//**-------------------**
//note:
//****
//je utilisé la l'apele denamique de DLL.

type
charp=^char;
var
Form1: TForm;
const
NomDLL = 'DefragDll.dll';

//1° - declaration static:--------------------
Function RunDefrag(MountPoint:pchar;Mode:integer):integer;cdecl; external 'DefragDll.dll' name 'RunDefrag';

implementation
{$R *.dfm}

function LierFonction(DLL: String; var HandleDLL: THandle; NomFct: String; IndexFct: Integer = -1): Pointer;
begin
Result := nil;
HandleDLL := 0;
HandleDLL := LoadLibrary(pAnsiChar(DLL));
If HandleDLL = 0 then
Exit;
If IndexFct < 0 then
Result := GetProcAddress(HandleDLL, pAnsiChar(NomFct))
else
Result := GetProcAddress(HandleDLL, pAnsiChar(IndexFct));
end;

//2° -declaration denamique.
//-------°°°°°°°°°°°°°°°°°°---

procedure TForm1.Button1Click(Sender: TObject);
var HandleDLL: THandle;
RunDefrag : function(MountPoint:charp;Mode:integer):integer;cdecl ; //Notre fonction, sous forme de variable.
begin
RunDefrag := LierFonction(NomDLL, HandleDLL, 'RunDefrag');
If assigned(RunDefrag) then
begin
RunDefrag(nil,1);
FreeLibrary(HandleDLL);
end
else
ShowMessage('Erreur de chargement de la fonction "rundefrag"');
end;
//----------------------

////Static procedure .....

procedure TForm1.Button2Click(Sender: TObject);
begin
RunDefrag(nil,1);
end;
mais toujour rien erreur de compilation ;

//le code c++ de DLL.-----------------
//***----------------------***

#define _WIN32_WINNT 0x0500
#include
#include
#include
#include

#include "Common1.c" /* Includes and variables. */

/* Dll specifics. */
#define DEFRAG_EXPORTS
#include "DefragDll.h"
static PDefragShowMessageCallback ShowMessageCallback;
static PDefragShowMoveCallback ShowMoveCallback;
static PDefragShowAnalyzeCallback ShowAnalyzeCallback;
static PDefragShowDebugCallback ShowDebugCallback;
static PDefragDrawClusterCallback DrawClusterCallback;
static PDefragClearScreenCallback ClearScreenCallback;
static char **DebugMsg;

/* Show a general progress message. */
void ShowMessage(int Message) {
ShowMessageCallback(Message);
}

/* Show progress message during defrag/optimization, about moving a file. */
void ShowMove(char *FileName, DWORD Clusters, ULONG64 FromLcn, ULONG64 ToLcn) {
ShowMoveCallback(FileName,Clusters,FromLcn,ToLcn);
}

/* Show progress message during analyzing. */
void ShowAnalyze(
struct FileListStruct *File,
ULONG64 CountDirectories,
ULONG64 CountAllFiles, ULONG64 CountFragmentedFiles,
ULONG64 CountAllBytes, ULONG64 CountFragmentedBytes,
ULONG64 CountAllClusters, ULONG64 CountFragmentedClusters) {
if (File != NULL) {
ShowAnalyzeCallback(File->FileName,
CountDirectories,
CountAllFiles,CountFragmentedFiles,
CountAllBytes,CountFragmentedBytes,
CountAllClusters,CountFragmentedClusters);
} else {
ShowAnalyzeCallback(NULL,
CountDirectories,
CountAllFiles,CountFragmentedFiles,
CountAllBytes,CountFragmentedBytes,
CountAllClusters,CountFragmentedClusters);
}
}

/* Show a debug message. */

void ShowDebug(int Level, struct FileListStruct *File, char *Message) {
if (File != NULL) {
ShowDebugCallback(Level,File->FileName,Message);
} else {
ShowDebugCallback(Level,NULL,Message);
}
}

/* Paint a cluster on the screen in the color. */
void DrawCluster(ULONG64 ClusterStart, ULONG64 ClusterEnd, int Color) {
DrawClusterCallback(ClusterStart,ClusterEnd,MaxLcn,Color);
}

/* Clear the screen and show the name of the volume. */
void ClearScreen(char *VolumeDescription) {
ClearScreenCallback(VolumeDescription);
}

/* Include the subroutines that are common (equal) in all versions. */
#include "Common2.c"

/* Run the defragger. Execution can be stopped by calling StopDefrag().
If a MountPoint is specified (for example "C:\") then only defrag
that disk, otherwise defrag all fixed disks.
Mode:
0 Analyze only
1 Analyze, Defragment
2 Analyze, Defragment, Fast Optimize
3 Analyze, Defragment, Full Optimize

*/ rundefrag

DEFRAG_API int RunDefrag(char *MountPoint, int Mode) {
if ((MountPoint == NULL) || (*MountPoint == '\0')) {
DefragAllDisks(Mode);
} else {
DefragOneDisk(MountPoint,Mode);
}
return(0);
}

/* Stop RunDefrag(). */

DEFRAG_API int StopDefrag(int WaitForIt) {
StopProcessing(WaitForIt);
return(0);
}

/* Stop the defragger. This function can be called from another thread to
stop the defragger or the analyzer. */

DEFRAG_API int DefragInitialize(

PDefragShowMessageCallback ShowMessageFunction,
PDefragShowMoveCallback ShowMoveFunction,
PDefragShowAnalyzeCallback ShowAnalyzeFunction,
PDefragShowDebugCallback ShowDebugFunction,
PDefragDrawClusterCallback DrawClusterFunction,
PDefragClearScreenCallback ClearScreenFunction,
char **DebugMessagesArray) {
ShowMessageCallback = ShowMessageFunction;
ShowMoveCallback = ShowMoveFunction;
ShowAnalyzeCallback = ShowAnalyzeFunction;
ShowDebugCallback = ShowDebugFunction;
DrawClusterCallback = DrawClusterFunction;
ClearScreenCallback = ClearScreenFunction;
DebugMsg = DebugMessagesArray;
return(0);
}

//-------------------------------

{et en fin comment declarer function "DefragInitialize".
merci inf
a tous ...}
)

fredericmazue

Quote:

j'utilise une dll ecrite en C++,dans delphi,
mais toujour rien erreur de compilation ;

Normal. Tu as deux problèmes. L'un à la compilation, l'autre à l'exécution.

1) A la compilation. Ca ne peut pas aller parce que les compilateurs C++ décorent les noms de fonctions en y ajoutant des lettres et des nombres.
Pour éviter ça lorsqu'on écrit une librairie C++ on fait précéder les déclarations des noms de fonctions de -- extern "C" --. afin que le nom reste intègre.
Je ne vois pas d'extern "C" dans le code C++, donc les noms des fonctions sont décorés, donc Pascal/Delphi ne peut pas les trouver. Il faudrait modifier et recompiler le code C++, et en faisant aussi bien attention aux options de compilations C++.

2) A l'exécution. Sauf si j'ai mal vu, ton code Delphi ne signale nulle part que les fonctions C++ doivent être appelées avec le protocole d'appel C++. Elles seront donc appelées avec le protocole Pascal(Delphi), il y aurait une erreur de pile au premier appel et ça va planter.
Consulte ta doc Delphi. Tu y verras comment spécifier dans le code source Delphi un protocole spécifique pour une appel de fonction C/C++.

CeDreams

fredericmazue wrote:
Consulte ta doc Delphi. Tu y verras comment spécifier dans le code source Delphi un protocole spécifique pour une appel de fonction C/C++.

Je suis très intéressé pour avoir plus d'information à ce sujet puisque je ne trouve rien dans l'aide Delphi sur ce protocole spécifique !
D'autant plus que dans mon cas, j'ai une procédure en paramètre à envoyer qui elle-même contient 2 paramètres (un PChar et un integer).
fredericmazue

Quote:

Je suis très intéressé pour avoir plus d'information à ce sujet puisque je ne trouve rien dans l'aide Delphi sur ce protocole spécifique !

C'est dans la doc sans aucun doute. Je ne sais plus où car j'ai désintallé mes Delphi depuis bien longtemps. Mais en cherchant des mots-clés comme pascal, cdecl ou stdcall tu vas forcément trouver.

De mémoire je dirais qu'il y a 5 conventions d'appels possibles en Delphi. register, pascal, cdecl stdcall et safecall

Par défaut Delphi utilise register il me semble. Et obligatoirement pour les propriétés publiques des objets. Mais mieux vaut oublier register qui risque de ne pas faire bon ménage avec d'autres langages. On oublie aussi le très particulier safecall qui concerne les appels COM Windows

Soit une fonction: fonction(arg1, arg2)

pascal:
pousse les paramètres sur la pile de gauche à droite. arg1, puis arg2. C'est le code de la fonction qui réajuste le pointeur de pile.

cdecl:
pousse les paramètres sur la pile de gauche à droite. arg2, puis arg1. C'est le code appelant la fonction qui réajuste le pointeur de pile.
cdecl est la convention d'appel normal en C et C++ sauf si ça a été compilé autrement...

stdcall
pousse les paramètres sur la pile de gauche à droite. arg2, puis arg1. C'est le code de la fonction qui réajuste le pointeur de pile.
stdcall est la convention des API Windows

Donc s'il s'agit d'appeler du code C ou C++ la fonction (protoype) à appeler doit être déclarée cdecl (cf doc) dans le code Delphi.
Du C pas de problème. Du C++, si la fonction à appeler a été déclarée extern "C" pas de problème. Sinon le nom de la fonction C++ est décoré et l'éditeur de lien de Delphi ne va pas la trouver. Le mieux dans ce cas est d'écrire un stub C++ avec le même compilateur qui a été utilisé pour compiler la librairie (toujours le problème de la décoration, il faut cette fois que ce soit l'éditeur de lien C++ qui puisse le trouver, ce qui ne posera pas de pb si le compilateur est le même)

S'il s'agit d'appeler une API Windows il faut déclarer stdcall le prototype de la fonction côté Delphi.

Quote:

D'autant plus que dans mon cas, j'ai une procédure en paramètre à envoyer qui elle-même contient 2 paramètres (un PChar et un integer).

Dans tous les cas y compris celui-ci, il faut s'assurer que les types font la même taille dans les deux langages. Il ne s'agit donc pas de passer un PChar ou un integer mais de passer quelque chose qui à la même taille que ce à quoi s'attend le code C ou C++. Même remarque pour une éventuelle valeur de retour.

Dans le cas particulier de la procédure à passer il peut y avoir des difficultés supplémentaires

- qu'est-ce qui est attendu côté C ? S'il s'agit de passer une fonction de rappel c'est peut être la convention stdcall qui est attendue. Le cas est fréquent.

- les procédures (beurk) n'ont pas de valeur de retour. Donc le code va à priori modifier des paramètres reçus par référence. Si c'est le cas ça doit aller. Si le code travaille avec des variables globales, des mauvaises surprises ne sont pas exclues.

CeDreams

Tout d'abord, je vous remercie pour votre disponibilité, encore désolé pour le MP.

Ok, j'avais bien connaissance de ces conventions d'appel mais je pensais avoir compris que le protocole dont vous parliez concernait d'autres points que j'ignorai.

Je n'ai pas encore été explicite sur ce que je recherche alors je vais tenter de l'expliquer.

J'appelle à partir de Delphi une fonction de la DLL écrite en C++ par stdcall.
Cette fonction demande en paramètre une procédure et elle retourne immédiatement lors d'un appel un type DWORD en C++ donc un LongInt chez Delphi. Jusque-là pas de soucis particulier.

La procédure en paramètre sera appelée dans la DLL lors d'un passage d'un badge magnétique pour une identification de personne. Donc il y a un Thread qui tourne pour gérer l'attente du passage du badge.

Cette procédure en C++ attend deux paramètres, le 1er est un char *, le 2nd un int. Donc du côté Delphi, un PChar et un Integer et déclarée avec cdecl.

Lors du déclenchement de la procédure dans la DLL, cela remonte dans mon .exe Delphi qui affecte les deux paramètres à des variables locales de types string et Integer. Pour l'entier, ça ne doit pas poser de problème. Par contre, pour la chaîne de caractère, il me semble que je peux affecter un PChar à une string sans problème.

D'après mes dernières manipulations, c'est à l'accès des paramètres dans Delphi que je rencontre des soucis. L'entier est accessible mais me renvoie une valeur incohérente. Mais pire, l'accès à la chaîne me renvoie une erreur du type 'L'instruction à ... emploie l'adresse mémoire ... . La mémoire ne peut pas être "read"'. J'en conclus que j'ai un problème de compatibilité de mes variables/paramètres.

Voici un extrait de mon prog. de test avec Delphi.

type
  TMaFonction = procedure(szCodeIdent: PChar; nStatus: Integer); cdecl;

procedure MaFonction(szCodeIdent: PChar; nStatus: Integer);
begin
  Etat:= nStatus;
  if Etat = 0 then
    strCodePerso:= szCodeIdent;
end;

procedure TForm1.Button1Click(Sender: TObject);
Var
  HND: THandle;
  DLL_Start: function (fct: TMaFonction): LongInt; stdcall;
begin
  ResultStart:= 10;
  HND := LoadLibrary('XXX.dll');
  try
    If (HND >= 32) Then
    Begin
      @DLL_Start:= GetProcAddress(HND,'_Start@4');
      if @DLL_Start<> nil then
        ResultStart:= DLL_Start(@MaFonction)
      else
        MessageBox(0,'ERROR','NIL',MB_ICONERROR);
      End
    Else MessageBox(0,'ERROR','ERROR',MB_ICONERROR);
    Sleep(4000); // 4 sec. pour passer le badge
  finally
    FreeLibrary(HND);
  end;
end;
fredericmazue

Quote:

J'en conclus que j'ai un problème de compatibilité de mes variables/paramètres.

Je n'ai pas regardé le code en détail faute de temps mais il me semble que la conclusion n'est pas bonne du moins pas entièrement.

En ce qui concerne la compatibilité de type:

D'abord il faudrait être sûr que cdecl est ce qu'il faut. Une librairie C++entièrement compilée en stdcall ça s'est déjà vu.
Mais admettons que ça soit ok.

Quote:

DWORD en C++ donc un LongInt chez Delphi. Jusque-là pas de soucis particulier.

Si...
Un LongInt Delphi est un 32 bits signé tandis d'un DWORD Windows est un 32 bits non signé.
Il faut mettre un LongWord Delphi.

Quote:

Par contre, pour la chaîne de caractère, il me semble que je peux affecter un PChar à une string sans problème.

Là non, jamais.
Un PChar c'est comme un char* en C c'est à dire un pointeur sur une chaîne avec zéro terminal. Tandis qu'une string en Pascal c'est un tableau dont le premier élément est la longueur de la chaîne et les éléments suivants la chaîne elle même (dont la longueur est limitée à 255 donc). Et là il n'y a pas de zéro terminal. Les deux types ne sont pas compatibles. Il faudrait construire une AnsiString à partir du PChar.

Maintenant je flaire qu'il y a un autre problème. Rien ne dit (plaisanterie classique du C) que le pointeur char* alias PChar est toujours valable au moment de son utilisation dans Delphi, ainsi que le message d'erreur peut le laisser penser, et si le problème n'apparaît pas déjà à cause de l'incompatibilité entre les types chaînes. Raison de plus pour recopier les caractères pointés et non affecter le pointeur dans un type Delphi.

Voilà, en espérant t'avoir dépanné un peu :)

CeDreams

Encore merci pour vos réponses très complètes.
Mon problème semble enfin se débloquer.
En effet, le fait de rajouter simplement stdcall dans l'implémentation de MaFonction (pourtant le type de ma procédure possède déjà cette convention) me récupère enfin un status cohérent et surtout le bon code d'identification. :shock:
Ouf de soulagement ! :D

procedure MaFonction(szCodeIdent: PChar; nStatus: Integer); stdcall;
begin
  Etat:= nStatus;
  if (Etat = 0) and Assigned(szCodeIdent) then
    strCodePerso:= szCodeIdent;
end;
fredericmazue

Quote:

En effet, le fait de rajouter simplement stdcall dans l'implémentation de MaFonction (pourtant le type de ma procédure possède déjà cette convention)

Un léger manque de rigueur de la part du compilateur Delphi...

Je suis heureux que votre problème se débloque. Faites néanmoins bien attention aux concordances de types. Si l'entier est une petite valeur positive, LongInt peut aller, mais c'est un bug potentiel, juste le genre de ceux qui se manisfesn en démonstration chez le client ;-). LongWord est mieux. AMHA