Je développe une vaste DLL en Delphi 6 Peronal Eidition, avec des centaines de fonctions de support pour un langage externe.
Dans le cadre de ce développement, je rencontre depuis longtemps des problèmes aléatoires (en appparence !) avec le presse-papier. Une même opération peut se dérouler 10, 20, 30 fois sans erreur, puis, subitement, il y a une erreur NON CAPTABLE par TRY..EXCEPT disant que l'accès au presse-papier est impossible.
Récemment, j'ai pu cerner ce problème d'un peu plus près. Dans le cadre d'une fonction complexe conçue pour charger une imabe de n'importe quel format dans le presse-papier, j'ai ce problème à un endroit bien précis que j'ai pu identifier. Voici le priincipe:
- je commence, par sécurité, à "vider" le presse-papier. Cela marche bien.
- selon le format du fichier à charger, je le charge dans un objet Delphi approprié (TBitMap, TPngObject, TPicture etc). Sans problème.
- puis, j'affecte la bitmap de cet objet au presse-papier via la méthode ASSIGN. Sans problème. Je précise ici que j'ai conscience que seul un pointeur est copié à ce moment. La copie physique aura lieu lorsque l'objet d'origine est modifié ou supprimé.
- pour procoquer la copie réelle le modifie un des attributs de l'objet d'origine (HEIGHT pour un objet TBitMap, par exemple). Passe sans problème. Je sais que maintenant, la copie physique est initiée, mais elle aura lieu malheureusement de façon asynchrone.
- je libère l'objet intermédiaire (TBitMap, ...) par la méthode FREE. Sans problème.
Jusque là, on a l'impression que tout se passe bien. Pas d'anomalie de compilation, pas d'erreur en exécution.
Mais ça plante juste après, lorsque le programme appelant ma fonction veut interroger le presse-papier pour connaître les dimentions de la bitmap qu'il contient. A ce moment, il y a souvent un plantage lorsqu'il s'agit d'images de dimensions importantes (photos, ...). Accès refusé au presse-papier. Toute cette séquence pour se passer très bien dix fois ou plus, puis à la n-ième fois, il y a ce plantage.
Or, j'ai réussi à éliminer ce plantage en insérant une attente de 500 ms, juste avant l'interrogation du presse-papier concernant les dimensions qu'il contient.
Visiblement, la copie de la bitmap intermédiaire n'est pas terminée puisqu'à l"évidence, elle se fait de façon asynchrone. Et donc, ma question est: comment peut-on savoir si cette copie est terminée ? Le moment idéal (à mn point de vue) serait juste après avoir changé une des propriétés de l'objet intermédiaire et avant la libération de l'objet intermédiaire par FREE. Je pourrais alors retarder l'emploi de FREE jusqu'à la la fin de la copie physique et il n'y aura plus de problème.
Voici un extrait des étapes décrites ci-dessus, pour un fichier JPG, par exemple:
Les fonctions retournant les dimensions de la bitmap dans le presse-papier (parfaitement opérationnelles):
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 var BitmapRaw : TBitmap; nom: string; ... // la variable nom est chargée par le chemin et le nom du fichier contenant l'image BitmapRaw := LoadGraphicsFile(nom); // <======== fonction interne de la DLL trouvée sur le net (voir ci-dessous). Cette fonction marche parfaitement. clipboard.Assign(BitmapRaw); // ici, seul un pointeur est copié BitmapRaw.Height := 1; // ici, je provoque la copie physique // <============ ici, attendre la fin du transfert, ou lancer une procédure call-back lors de la fin ? BitmapRaw.Free; // libération de la ressource exit; ...
Fonction interne:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 function GetClipboardPictureWidth:integer; stdcall; export; var b:TBitmap; begin result := 0; if Clipboard.HasFormat(CF_BITMAP) then begin b:=TBitmap.Create; try b.Assign(Clipboard); result := b.Width; finally b.Free; end; end; end; exports GetClipboardPictureWidth; function GetClipboardPictureHeight:integer; stdcall; export; var b:TBitmap; begin result := 0; if Clipboard.HasFormat(CF_BITMAP) then begin b:=TBitmap.Create; try b.Assign(Clipboard); result := b.Height; finally b.Free; end; end; end; exports GetClipboardPictureHeight;
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133 // Create TBitmap from BMP, JPG, WMF, EMF or GIF or TIF disk file. // Could be easily extended to other image types. FUNCTION LoadGraphicsFile(CONST Filename: STRING): TBitmap; VAR Extension: STRING; {$IFDEF GIF} GIFImage : TGIFImage; {$ENDIF} Icon : TIcon; JPEGImage: TJPEGImage; Metafile : TMetafile; pict: TPicture; img: TImage; BEGIN RESULT := NIL; // In case anything goes wrong try IF FileExists(Filename) THEN BEGIN Extension := UpperCase( COPY(Filename, LENGTH(Filename)-2, 3) ); // Quick and dirty check that file type is OK ASSERT( (Extension = 'BMP') OR (Extension = 'EMF') OR {$IFDEF GIF} (Extension = 'GIF') OR {$ENDIF} (Extension = 'ICO') OR (Extension = 'JPG') OR (Extension = 'TIF') OR (Extension = 'WMF') ); RESULT := TBitmap.Create; // BMP File -- no additional work to get TBitmap IF Extension = 'BMP' THEN BEGIN RESULT.LoadFromFile(Filename); END; {$IFDEF GIF} // GIF File IF Extension = 'GIF' THEN BEGIN GIFImage := TGIFImage.Create; TRY GIFImage.LoadFromFile(Filename); RESULT.Height := GIFImage.Height; RESULT.Width := GIFImage.Width; RESULT.PixelFormat := pf24bit; RESULT.Canvas.Draw(0,0, GIFImage) FINALLY GIFImage.Free END END; {$ENDIF} // ICO File IF Extension = 'ICO' THEN BEGIN if KGF_debug_mode=3 then begin // pict := TPicture.Create; img := GetBitmapFromIcon(FileName); //showmessage('x1: '+inttostr(icon.Width)); RESULT.Height := img.Height; RESULT.Width := img.Width; RESULT.PixelFormat := pf24bit; //showmessage('retour 1'); RESULT.Canvas.Draw(0,0, img.Picture.bitmap); //showmessage('retour 2'); end else begin Icon := TIcon.Create; TRY TRY Icon.LoadFromFile(Filename); //showmessage('x2: '+inttostr(icon.Width)); if IconTransparentColor<>-1 then RESULT.TransparentColor := TColor(IconTransparentColor); RESULT.Height := Icon.Height; RESULT.Width := Icon.Width; RESULT.PixelFormat := pf24bit; // avoid palette problems RESULT.Canvas.Draw(0,0, Icon) EXCEPT // Ignore problem icons, e.g., Stream read errors END; FINALLY Icon.Free END end; END; // JPG File IF Extension = 'JPG' THEN BEGIN JPEGImage := TJPEGImage.Create; TRY JPEGImage.LoadFromFile(Filename); RESULT.Height := JPEGImage.Height; RESULT.Width := JPEGImage.Width; RESULT.PixelFormat := pf24bit; RESULT.Canvas.Draw(0,0, JPEGImage) FINALLY JPEGImage.Free END END; // TIF File IF Extension = 'TIF' THEN BEGIN pict := TPicture.Create; pict.LoadFromFile(Filename); RESULT.Assign(pict); { RESULT.Height := pict.graphic.Height; RESULT.Width := pict.graphic.Width; RESULT.PixelFormat := pf24bit; RESULT.Canvas.Draw(0,0, pict.graphic); } pict.free; END; // Windows Metafiles, WMF or EMF IF (Extension = 'WMF') OR (Extension = 'EMF') THEN BEGIN Metafile := TMetafile.Create; TRY Metafile.LoadFromFile(Filename); RESULT.Height := Metafile.Height; RESULT.Width := Metafile.Width; RESULT.PixelFormat := pf24bit; // avoid palette problems RESULT.Canvas.Draw(0,0, Metafile) FINALLY Metafile.Free END END; END; finally end; END {LoadGraphicsFile};
Partager