Bonjour, je cherche depuis quelques jours comment modifier les métadonnées d'une image JPEG (données EXIF et/ou XMP). En particulier le titre de l'image.

J'ai essayé plusieurs solutions comme cette unité qui modifie l'en-tête du fichier JPEG :
Code pascal : 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
{*******************************************************}
{                                                       }
{         KAZ JPEG Comment Unit 1.0                     }
{                                                       }
{         Copyright (c) 2001 by KAZ                     }
{                                                       }
{*******************************************************}
 
{
This is an easy to use JPEG comment unit (for JPG files).
Use this unit for comment a JPG picture. This code is free, but if
you like this, please drop me a mail.
 
Copyright: Kovacs Attila Zoltan
Hungary
 
kaz@freemail.hu
szarvasp@freeweb.hu
}
 
unit uJPEGComment;
 
interface
 
uses SysUtils;
 
function ReadComment(const FileName: string): string;
function ReadCommentAndSize(const FileName: string; var Width, Height: Integer): string;
procedure WriteComment(const FileName: string; Comment: string);
 
type
  EInvalidJPGFormat = class(Exception)
  end;
 
resourcestring
  SInvalidJPGFile = '%s Format d''image non non valide';
 
implementation
 
uses Windows, Classes;
 
{ TMyFileStream }
 
type
  TMyFileStream = class(TFileStream)
  private
    function GetEof: Boolean;
  public
    function ReadByte: Byte;
    function ReadWord: Word;
    function ReadString(const StrSize: Integer): string;
    property Eof: Boolean read GetEof;
  end;
 
function TMyFileStream.GetEof: Boolean;
begin
  Result:=Position>=Size;
end;
 
function TMyFileStream.ReadByte: Byte;
begin
  ReadBuffer(Result, sizeof(Result));
end;
 
function TMyFileStream.ReadWord: Word;
begin
  Result:=ReadByte shl 8 + ReadByte;
end;
 
function TMyFileStream.ReadString(const StrSize: Integer): string;
begin
  SetLength(Result, StrSize);
  ReadBuffer(PChar(Result)^, Length(Result));
end;
 
 
{ TMyMemoryStream }
 
type
  TMyMemoryStream = class(TMemoryStream)
  public
    function WriteByte(const Value: Byte): Byte;
    function WriteWord(const Value: Word): Word;
    procedure WriteString(const Str: string);
    procedure CopyFromStream(S: TStream; Size: Integer);
  end;
 
function TMyMemoryStream.WriteByte(const Value: Byte): Byte;
begin
  WriteBuffer(Value, sizeof(Value));
  Result:=Value;
end;
 
function TMyMemoryStream.WriteWord(const Value: Word): Word;
begin
  WriteByte((Value shr 8) and $FF);
  WriteByte(Value and $FF);
  Result:=Value;
end;
 
procedure TMyMemoryStream.WriteString(const Str: string);
begin
  WriteBuffer(PChar(Str)^, Length(Str));
end;
 
procedure TMyMemoryStream.CopyFromStream(S: TStream; Size: Integer);
begin
  if Self.Size<Position+Size then
    SetSize(Position+Size);
  S.ReadBuffer(PChar(Memory)[Position], Size);
  Position:=Position+Size;
end;
 
 
function ReadComment(const FileName: string): string;
var
  Width, Height: Integer;
begin
  Result:=ReadCommentAndSize(FileName, Width, Height);
end;
 
function ReadCommentAndSize(const FileName: string; var Width, Height: Integer): string;
var
  FS: TMyFileStream;
  SegmentType: Byte;
  SegmentLen: Word;
  NewPos: Integer;
begin
  Result:='';
  Width:=0; Height:=0;
  FS:=TMyFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  try
    FS.Position:=2; // skip FF D8
    repeat
      if FS.ReadByte<>$FF then
        raise EInvalidJPGFormat.CreateFmt(SInvalidJPGFile, [FileName]);
      SegmentType:=FS.ReadByte;
      SegmentLen:=FS.ReadWord;
      if SegmentType=$FE then begin // comment
        Result:=FS.ReadString(SegmentLen-2);
      end
      else if (SegmentType=$C0) and (SegmentLen>8) then begin // size
        NewPos:=FS.Position+SegmentLen-2;
        FS.Seek(1, soFromCurrent); // skip data precision
        Height:=FS.ReadWord;
        Width:=FS.ReadWord;
        FS.Position:=NewPos;
      end
      else
        FS.Seek(SegmentLen-2, soFromCurrent); // skip segment
    until FS.Eof or (SegmentType=$DA); // it shoud work
  finally
    FS.Free;
  end;
end;
 
procedure WriteComment(const FileName: string; Comment: string);
var
  FS: TMyFileStream;
  SegmentType: Byte;
  SegmentLen: Word;
  MemStream: TMyMemoryStream;
  Written: Boolean;
  CT, LAT, LWT: TFileTime;
begin
  if Length(Comment)>65530 then
    SetLength(Comment, 65530);
  Written:=False;
  FS:=TMyFileStream.Create(FileName,fmOpenReadWrite	or fmShareDenyWrite);
  MemStream:=TMyMemoryStream.Create;
  try
    Win32Check(GetFileTime(FS.Handle, @CT, @LAT, @LWT));
    MemStream.Size:=FS.Size; // allocate memory
    MemStream.CopyFromStream(FS, 2); // skip FF D8
    repeat
      if MemStream.WriteByte(FS.ReadByte)<>$FF then
        raise EInvalidJPGFormat.CreateFmt(SInvalidJPGFile, [FileName]);
      SegmentType:=FS.ReadByte;
      SegmentLen:=FS.ReadWord;
      if ((SegmentType=$FE) or ((SegmentType and $F0)=$C0)) and not Written and (Comment<>'') then begin
        MemStream.WriteByte($FE);
        MemStream.WriteWord(Length(Comment)+2);
        MemStream.WriteString(Comment);
        MemStream.WriteByte($FF);
        Written:=True;
      end;
      if SegmentType=$FE then begin // Comment
        FS.Seek(SegmentLen-2, soFromCurrent); // skip
        MemStream.Position:=MemStream.Position-1; // go back
      end
      else begin
        MemStream.WriteByte(SegmentType);
        MemStream.WriteWord(SegmentLen);
        MemStream.CopyFromStream(FS, SegmentLen-2);
      end;
    until FS.Eof or (SegmentType=$DA);
    MemStream.CopyFromStream(FS, FS.Size-FS.Position);
    MemStream.SetSize(MemStream.Position); // truncate
    FS.SetSize(MemStream.Size);
    FS.Position:=0;
    MemStream.Position:=0;
    MemStream.SaveToStream(FS);
    Win32Check(SetFileTime(FS.Handle, @CT, @LAT, @LWT));
  finally
    MemStream.Free;
    FS.Free;
  end;
end;
 
end.
J'arrive bien à écrire et lire un titre dans mon image JPEG, ce titre n'est pas récupèré dans la fenêtre Propriétés de Windows.

J'ai essayé également cette unité qui utilise l'API de Windows :
Code pascal : 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
unit storage;
 
interface  
 
uses  
  Windows, ComObj, ActiveX, SysUtils;
 
const  
  FMTID_SummaryInformation          : TGUID =  
    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9}';  
  FMTID_DocSummaryInformation       : TGUID =  
    '{D5CDD502-2E9C-101B-9397-08002B2CF9AE}';  
  FMTID_UserDefinedProperties       : TGUID =  
    '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}';  
  FMTID_AudioSummaryInformation     : TGuid =  
    '{64440490-4C8B-11D1-8B70-080036B11A03}';  
  FMTID_VideoSummaryInformation     : TGUID =  
    '{64440491-4C8B-11D1-8B70-080036B11A03}';  
  FMTID_ImageSummaryInformation     : TGUID =  
    '{6444048f-4c8b-11d1-8b70-080036b11a03}';  
  FMTID_MediaFileSummaryInformation : TGUID =  
    '{64440492-4c8b-11d1-8b70-080036b11a03}';  
 
  STGFMT_STORAGE     = 0;  
  STGFMT_FILE        = 3;  
  STGFMT_ANY         = 4;
  STGFMT_DOCFILE     = 5;
 
  // Summary Information  
  PIDSI_TITLE        = $00000002;     // VT_LPSTR  
  PIDSI_SUBJECT      = $00000003;  
  PIDSI_AUTHOR       = $00000004;  
  PIDSI_KEYWORDS     = $00000005;  
  PIDSI_COMMENTS     = $00000006;  
  PIDSI_TEMPLATE     = $00000007;  
  PIDSI_LASTAUTHOR   = $00000008;  
  PIDSI_REVNUMBER    = $00000009;  
  PIDSI_EDITTIME     = $0000000A;     // VT_FILETIME (UTC)  
  PIDSI_LASTPRINTED  = $0000000B;  
  PIDSI_CREATE_DTM   = $0000000C;  
  PIDSI_LASTSAVE_DTM = $0000000D;  
  PIDSI_PAGECOUNT    = $0000000E;     // VT_I4  
  PIDSI_WORDCOUNT    = $0000000F;  
  PIDSI_CHARCOUNT    = $00000010;  
  PIDSI_THUMBNAIL    = $00000011;     // VT_CF  
  PIDSI_APPNAME      = $00000012;     // VT_LPSTR  
  PIDSI_DOC_SECURITY = $00000013;     // VT_I4  
 
  // Document Summary Information  
  PIDDSI_CATEGORY    = $00000002;     // VT_LPSTR  
  PIDDSI_PRESFORMAT  = $00000003;  
  PIDDSI_BYTECOUNT   = $00000004;     // VT_I4  
  PIDDSI_LINECOUNT   = $00000005;  
  PIDDSI_PARCOUNT    = $00000006;  
  PIDDSI_SLIDECOUNT  = $00000007;  
  PIDDSI_NOTECOUNT   = $00000008;  
  PIDDSI_HIDDENCOUNT = $00000009;  
  PIDDSI_MMCLIPCOUNT = $0000000A;  
  PIDDSI_SCALE       = $0000000B;     // VT_BOOL  
  PIDDSI_HEADINGPAIR = $0000000C;     // VT_VARIANT | VT_VECTOR  
  PIDDSI_DOCPARTS    = $0000000D;     // VT_LPSTR | VT_VECTOR  
  PIDDSI_MANAGER     = $0000000E;     // VT_LPSTR  
  PIDDSI_COMPANY     = $0000000F;  
  PIDDSI_LINKSDIRTY  = $00000010;     // VT_BOOL  
 
  // FMTID_MediaFileSummaryInfo  
  PIDMSI_EDITOR      = $00000002;     // VT_LPWSTR  
  PIDMSI_SUPPLIER    = $00000003;  
  PIDMSI_SOURCE      = $00000004;  
  PIDMSI_SEQUENCE_NO = $00000005;  
  PIDMSI_PROJECT     = $00000006;  
  PIDMSI_STATUS      = $00000007;     // VT_UI4  
  PIDMSI_OWNER       = $00000008;     // VT_LPWSTR  
  PIDMSI_RATING      = $00000009;  
  PIDMSI_PRODUCTION  = $0000000A;     // VT_FILETIME (UTC)  
  PIDMSI_COPYRIGHT   = $0000000B;     // VT_LPWSTR  
 
  // FMTID_AudioSummaryInformation  
  PIDASI_FORMAT        = $00000002;   // VT_BSTR  
  PIDASI_TIMELENGTH    = $00000003;   // VT_UI4, milliseconds  
  PIDASI_AVG_DATA_RATE = $00000004;   // VT_UI4,  Hz  
  PIDASI_SAMPLE_RATE   = $00000005;   // VT_UI4,  bits  
  PIDASI_SAMPLE_SIZE   = $00000006;   // VT_UI4,  bits  
  PIDASI_CHANNEL_COUNT = $00000007;   // VT_UI4  
  PIDASI_STREAM_NUMBER = $00000008;   // VT_UI2  
  PIDASI_STREAM_NAME   = $00000009;   // VT_LPWSTR  
  PIDASI_COMPRESSION   = $0000000A;   // VT_LPWSTR  
 
  // FMTID_VideoSummaryInformation  
  PIDVSI_STREAM_NAME   = $00000002;   // "StreamName", VT_LPWSTR  
  PIDVSI_FRAME_WIDTH   = $00000003;   // "FrameWidth", VT_UI4  
  PIDVSI_FRAME_HEIGHT  = $00000004;   // "FrameHeight", VT_UI4  
  PIDVSI_TIMELENGTH    = $00000007;   // "TimeLength", VT_UI4, milliseconds  
  PIDVSI_FRAME_COUNT   = $00000005;   // "FrameCount". VT_UI4  
  PIDVSI_FRAME_RATE    = $00000006;   // "FrameRate", VT_UI4, frames/millisecond  
  PIDVSI_DATA_RATE     = $00000008;   // "DataRate", VT_UI4, bytes/second  
  PIDVSI_SAMPLE_SIZE   = $00000009;   // "SampleSize", VT_UI4  
  PIDVSI_COMPRESSION   = $0000000A;   // "Compression", VT_LPWSTR  
  PIDVSI_STREAM_NUMBER = $0000000B;   // "StreamNumber", VT_UI2  
 
type  
  TMultipleArray     = record  
    pidInfoType : cardinal;  
    pidInfoStr  : pchar;  
  end;  
  TMultipleArrayList = array of TMultipleArray;  
 
function GetFileSummaryInfo(const FileName: WideString;  
  GUID_SummaryType: TGUID; PID_InfoType: cardinal): string;  
function SetFileSummaryInfo(const FileName: WideString; GUID_InfoType: TGUID;  
  PID_InfoType: cardinal; InfoStr: string): boolean;  
function SetMultipleFileSummaryInfo(const FileName: WideString;  
  GUID_InfoType: TGUID; MultipleArrayList: TMultipleArrayList): boolean;  
function IsNTFS(AFileName : string) : boolean;
 
implementation
 
const
  szOleDll                = 'ole32.dll';
  IID_IPropertySetStorage : TGUID = '{0000013A-0000-0000-C000-000000000046}';
type
  TStgOpenStorageEx       = function(const pwcsName: PWideChar;
    grfMode: DWORD; stgfmt: DWORD; grfAttrs: DWORD; pStgOptions: pointer;
    reserved2: pointer; riid: PGUID; out ppObjectOpen: IStorage): HRESULT;  
    stdcall;  
var  
  StgOpenStorageEx        : TStgOpenStorageEx = nil;
  dll                     : dword = 0;  
  Win2k                   : boolean = false;  
 
function GetHardDiskPartitionType(const DriveLetter: Char): string;
var
  NotUsed: DWORD;
  VolumeFlags: DWORD;
  VolumeSerialNumber: DWORD;
  PartitionType: array[0..32] of Char;
begin
  GetVolumeInformation(PChar(DriveLetter + ':\'),
    nil, MAX_PATH+1, @VolumeSerialNumber, NotUsed,
    VolumeFlags, PartitionType, 32);
  Result := PartitionType; // "FAT" ou "NTFS"
end;
 
function IsNTFS(AFileName : string) : boolean;
begin
     IsNTFS := False;
     if GetHardDiskPartitionType(ExtractFileDrive(AFileName)[1])='NTFS' then IsNTFS := True;
end;
 
function GetFileSummaryInfo(const FileName: WideString;  
  GUID_SummaryType: TGUID; PID_InfoType: cardinal): string;  
var  
  hr          : HRESULT;  
  Stg         : IStorage;  
  PropSetStg  : IPropertySetStorage;  
  PropStg     : IPropertyStorage;  
  PropSpec    : TPropSpec;  
  PropVariant : TPropVariant;  
begin  
  Result      := '';  
  hr          := S_FALSE;  
 
  // for 9x and NT4 users  
  if(not Win2k) or (@StgOpenStorageEx = nil) then  
    hr := StgOpenStorage(pwidechar(FileName),nil,STGM_READ or  
      STGM_SHARE_DENY_WRITE,nil,0,Stg)  
  // for 2000, XP and higher  
  else if(@StgOpenStorageEx <> nil) then  
    hr := StgOpenStorageEx(pwidechar(FileName),STGM_READ or
      STGM_SHARE_DENY_WRITE,STGFMT_ANY,0,nil,nil,  
      @IID_IPropertySetStorage,Stg);  
 
 
  if(hr = S_OK) then  
  begin  
    PropSetStg := Stg as IPropertySetStorage;  
 
 
    if(PropSetStg.Open(GUID_SummaryType,STGM_READ or  
      STGM_SHARE_EXCLUSIVE,PropStg) = S_OK) then  
    begin  
      PropSpec.ulKind := PRSPEC_PROPID;  
      PropSpec.propid := PID_InfoType;  
 
 
      if(PropStg.ReadMultiple(1,@PropSpec,@PropVariant) = S_OK) and  
        (PropVariant.vt = VT_LPSTR) and  
        (PropVariant.pszVal <> nil) then  
      Result := PropVariant.pszVal;  
    end;  
  end;  
end;  
 
 
function SetFileSummaryInfo(const FileName: WideString; GUID_InfoType: TGUID;  
  PID_InfoType: cardinal; InfoStr: string): boolean;  
var  
  hr          : HRESULT;  
  Stg         : IStorage;  
  PropSetStg  : IPropertySetStorage;  
  PropStg     : IPropertyStorage;  
  PropSpec    : TPropSpec;  
  PropVariant : TPropVariant;  
begin  
  Result      := false;  
  hr          := S_FALSE;
 
  // 9x and NT4  
  if(not Win2k) or (@StgOpenStorageEx = nil) then  
    hr := StgOpenStorage(pwidechar(FileName),nil,STGM_SHARE_EXCLUSIVE or  
      STGM_READWRITE,nil,0,Stg)  
  // 2000, XP and higher  
  else if(@StgOpenStorageEx <> nil) then  
    hr := StgOpenStorageEx(pwidechar(FileName),STGM_SHARE_EXCLUSIVE or  
    STGM_READWRITE,STGFMT_ANY,0,nil,nil,@IID_IPropertySetStorage,Stg);
 
  if(hr = S_OK) then  
  begin  
    PropSetStg := Stg as IPropertySetStorage;  
 
 
    // try to open an existing Property set  
    hr         := PropSetStg.Open(GUID_InfoType,STGM_READWRITE or
      STGM_SHARE_EXCLUSIVE,PropStg);
 
 
    // create a new set  
    if(hr <> S_OK) then  
      hr       := PropSetStg.Create(GUID_InfoType,GUID_InfoType,
        PROPSETFLAG_DEFAULT,STGM_CREATE or STGM_READWRITE or
        STGM_SHARE_EXCLUSIVE,PropStg);
 
 
    if(hr = S_OK) then  
    begin  
      PropSpec.ulKind    := PRSPEC_PROPID;  
      PropSpec.propid    := PID_InfoType;  
      PropVariant.vt     := VT_LPSTR;  
      PropVariant.pszVal := pchar(InfoStr);  
 
 
      if(PropStg.WriteMultiple(1,@PropSpec,@PropVariant,2) = S_OK) then  
      begin  
        Result := (PropStg.Commit(STGC_DEFAULT) = S_OK);  
      end;  
    end;  
  end;  
end;  
 
 
function SetMultipleFileSummaryInfo(const FileName: WideString;  
  GUID_InfoType: TGUID; MultipleArrayList: TMultipleArrayList): boolean;  
var  
  Stg         : IStorage;  
  PropSetStg  : IPropertySetStorage;  
  hr          : HRESULT;  
  PropStg     : IPropertyStorage;  
  PropSpec    : array of TPropSpec;  
  PropVariant : array of TPropVariant;  
  i           : integer;  
begin  
  Result      := false;  
  hr          := S_FALSE;  
  if(length(MultipleArrayList) = 0) then exit;  
 
 
  // 9x, NT4  
  if(not Win2k) or (@StgOpenStorageEx = nil) then  
    hr := StgOpenStorage(pwidechar(FileName),nil,STGM_SHARE_EXCLUSIVE or  
      STGM_READWRITE,nil,0,Stg)  
  // 2000, XP and higher  
  else if(@StgOpenStorageEx <> nil) then  
    hr := StgOpenStorageEx(pwidechar(FileName),STGM_SHARE_EXCLUSIVE or  
    STGM_READWRITE,STGFMT_ANY,0,nil,nil,@IID_IPropertySetStorage,Stg);  
 
 
  if(hr = S_OK) then  
  begin  
    PropSetStg := Stg as IPropertySetStorage;  
 
 
    // try to open an existing Property set  
    hr         := PropSetStg.Open(GUID_InfoType,STGM_READWRITE or  
      STGM_SHARE_EXCLUSIVE,PropStg);  
 
 
    // create a new set  
    if(hr <> S_OK) then  
      hr       := PropSetStg.Create(GUID_InfoType,GUID_InfoType,  
        PROPSETFLAG_DEFAULT,STGM_CREATE or STGM_READWRITE or  
        STGM_SHARE_EXCLUSIVE,PropStg);  
 
 
    if(hr = S_OK) then  
    begin  
      // set default length  
      SetLength(PropSpec,0);  
      SetLength(PropVariant,0);  
 
 
      // fill  
      for i := 0 to length(MultipleArrayList) - 1 do  
        if(MultipleArrayList[i].pidInfoType <> 0) and  
          (MultipleArrayList[i].pidInfoStr <> nil) then  
        begin  
          SetLength(PropSpec,length(PropSpec) + 1);  
          PropSpec[length(PropSpec)-1].ulKind       := PRSPEC_PROPID;  
          PropSpec[length(PropSpec)-1].propid       :=  
            MultipleArrayList[i].pidInfoType;  
 
 
          SetLength(PropVariant,length(PropVariant) + 1);  
          PropVariant[length(PropVariant)-1].vt     := VT_LPSTR;  
          PropVariant[length(PropVariant)-1].pszVal :=  
            MultipleArrayList[i].pidInfoStr;  
        end;  
 
 
      // write  
      if(length(PropSpec) > 0) and  
        (length(PropVariant) > 0) and  
        (length(PropSpec) = length(PropVariant)) then  
      begin  
        if(PropStg.WriteMultiple(length(PropSpec),@PropSpec[0],  
          @PropVariant[0],2) = S_OK) then  
        begin  
          Result := (PropStg.Commit(STGC_DEFAULT) = S_OK);  
        end;  
 
 
        SetLength(PropSpec,0);  
        SetLength(PropVariant,0);  
      end;  
    end;  
  end;  
end;  
 
 
 
 
//  
// Main  
//  
var  
  os : TOSVersionInfo;  
 
 
initialization  
  // get OS  
  ZeroMemory(@os,sizeof(os));  
  os.dwOSVersionInfoSize := sizeof(os);  
 
 
  // is it Windows 2000 or higher?  
  Win2k                  := (GetVersionEx(os)) and  
    (os.dwMajorVersion >= 5) and  
    (os.dwPlatformId = VER_PLATFORM_WIN32_NT);  
 
 
  CoInitialize(nil);  
 
 
  dll := LoadLibrary(szOleDll);  
  if(dll <> 0) then  
  begin  
    StgOpenStorageEx := GetProcAddress(dll,'StgOpenStorageEx');  
    if(@StgOpenStorageEx = nil) then  
    begin  
      FreeLibrary(dll);  
      dll := 0;  
    end;  
  end;  
finalization  
  if(dll <> 0) then FreeLibrary(dll);  
  CoUninitialize;  
end.
Les données sont bien écrites et lus sur le NTFS, mais n'apparaissent pas non plus dans la fenêtre Propriétés de Windows.

Quelqu'un connaîtrait-il l'API de Windows à utiliser pour modifier les propriétés d'une image ?