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 ...}
)
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++.
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).
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.
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.
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.
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.
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.
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 :)
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
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