-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathWpdSample.dpr
2892 lines (2781 loc) · 143 KB
/
WpdSample.dpr
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
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(* Delphi 10 program
Delphi transcription of WPD API Sample
======================================
Demonstrates the following using the WPD API:
- Enumerate portable devices
- Enumerate content on a portable device
- Query the capabilities of a portable device
- Read/Write properties for content on a portable device
- Transfer content on/off a portable device
- Register/Unregister for portable device events
provided by Microsoft on GiTHub:
https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/multimedia/wpd/wpdapisample/cpp
Delphi adaption
© Dr. J. Rathlev, D-24222 Schwentinental (kontakt(a)rathlev-home.de)
The contents of this file may be used under the terms of the
Mozilla Public License ("MPL") or
GNU Lesser General Public License Version 2 or later (the "LGPL")
Software distributed under this License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.
Vers. 1 - May 2022
- November 2023 added: WPD_OBJECT_ORIGINAL_FILE_NAME
last modified: November 2023
*)
program WpdSample;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Winapi.ActiveX,
Winapi.Windows,
Winapi.CommDlg,
System.StrUtils,
System.Win.ComObj,
IStreamApi,
PortableDeviceDefs,
PortableDeviceApi;
const
CLIENT_NAME = 'Delphi WPD Sample Application';
CLIENT_MAJOR_VER = 1;
CLIENT_MINOR_VER = 0;
CLIENT_REVISION = 0;
// This number controls how many object identifiers are requested during each call
// to IEnumPortableDeviceObjectIDs::Next()
NUM_OBJECTS_TO_REQUEST = 10;
ImgFilter = 'JPEG (*.JPG)|*.JPG|JPEG (*.JPEG)|*.JPEG|JPG (*.JPE)|*.JPE|JPG (*.JFIF)|*.JFIF';
MusicFilter ='MP3 (*.MP3)|*.MP3';
ContactFilter = 'VCARD (*.VCF)|*.VCF';
JpgExt = 'jpg';
Mp3Ext = 'mp3';
VcfExt = 'vcf';
type
TCharArray = array of PWideChar;
var
BulkPropertyOperationEvent : THandle;
eventCookie : string;
function ErrorMsg (const s : string; hr : HResult) : string;
begin
Result:='* '+s+Format('- Returned HRESULT = $%.8x: %s',[hr,SysErrorMessage(hr)]);
end;
procedure WriteErrMsg (const s : string; hr : HResult); overload;
begin
writeln('==> ',ErrorMsg(s,hr));
end;
procedure WriteErrMsg (const s : string); overload;
begin
writeln('==> * ',s);
end;
//===================================================================
// from "DeviceCapabilities.cpp"
// Get a friendly name for a passed in functional
// category. If the category is not known by this function
// the GUID will be displayed in string form.
function FunctionalCategoryAsString (const functionalCategory : TGuid) : string;
begin
if functionalCategory=WPD_FUNCTIONAL_CATEGORY_STORAGE then Result:='WPD_FUNCTIONAL_CATEGORY_STORAGE'
else if functionalCategory=WPD_FUNCTIONAL_CATEGORY_STILL_IMAGE_CAPTURE then Result:='WPD_FUNCTIONAL_CATEGORY_STILL_IMAGE_CAPTURE'
else if functionalCategory=WPD_FUNCTIONAL_CATEGORY_AUDIO_CAPTURE then Result:='WPD_FUNCTIONAL_CATEGORY_AUDIO_CAPTURE'
else if functionalCategory=WPD_FUNCTIONAL_CATEGORY_SMS then Result:='WPD_FUNCTIONAL_CATEGORY_SMS'
else if functionalCategory=WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION then Result:='WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION'
else Result:=GuidToString(functionalCategory);
end;
// Displays a friendly name for a passed in event
// If the event is not known by this function
// the GUID will be displayed in string form.
function EventAsString (const event : TGuid) : string;
begin
if event=WPD_EVENT_OBJECT_ADDED then Result:='WPD_EVENT_OBJECT_ADDED'
else if event=WPD_EVENT_OBJECT_REMOVED then Result:='WPD_EVENT_OBJECT_REMOVED'
else if event=WPD_EVENT_OBJECT_UPDATED then Result:='WPD_EVENT_OBJECT_UPDATED'
else if event=WPD_EVENT_DEVICE_RESET then Result:='WPD_EVENT_DEVICE_RESET'
else if event=WPD_EVENT_DEVICE_CAPABILITIES_UPDATED then Result:='WPD_EVENT_DEVICE_CAPABILITIES_UPDATED'
else if event=WPD_EVENT_STORAGE_FORMAT then Result:='WPD_EVENT_STORAGE_FORMAT'
else Result:=GuidToString(event);
end;
// Get a friendly name for a passed in content type
// If the content type is not known by this function
// the GUID will be displayed in string form.
function ContenTypeAsString (const contentType : TGuid) : string;
begin
if contentType=WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT then Result:='WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT'
else if contentType=WPD_CONTENT_TYPE_FOLDER then Result:='WPD_CONTENT_TYPE_FOLDER'
else if contentType=WPD_CONTENT_TYPE_IMAGE then Result:='WPD_CONTENT_TYPE_IMAGE'
else if contentType=WPD_CONTENT_TYPE_DOCUMENT then Result:='WPD_CONTENT_TYPE_DOCUMENT'
else if contentType=WPD_CONTENT_TYPE_CONTACT then Result:='WPD_CONTENT_TYPE_CONTACT'
else if contentType=WPD_CONTENT_TYPE_AUDIO then Result:='WPD_CONTENT_TYPE_AUDIO'
else if contentType=WPD_CONTENT_TYPE_PLAYLIST then Result:='WPD_CONTENT_TYPE_PLAYLIST'
else if contentType=WPD_CONTENT_TYPE_VIDEO then Result:='WPD_CONTENT_TYPE_VIDEO'
else if contentType=WPD_CONTENT_TYPE_TASK then Result:='WPD_CONTENT_TYPE_TASK'
else if contentType=WPD_CONTENT_TYPE_APPOINTMENT then Result:='WPD_CONTENT_TYPE_APPOINTMENT'
else if contentType=WPD_CONTENT_TYPE_EMAIL then Result:='WPD_CONTENT_TYPE_EMAIL'
else if contentType=WPD_CONTENT_TYPE_MEMO then Result:='WPD_CONTENT_TYPE_MEMO'
else if contentType=WPD_CONTENT_TYPE_UNSPECIFIED then Result:='WPD_CONTENT_TYPE_UNSPECIFIED'
else Result:=GuidToString(contentType);
end;
// Display the basic event options for the passed in event.
function EventOptionsAsString (capabilities : IPortableDeviceCapabilities; const event : TGuid) : string;
var
hr : HResult;
isBroadcastEvent : bool;
eventOptions : IPortableDeviceValues;
begin
hr:=capabilities.GetEventOptions(event,eventOptions);
if succeeded(hr) then begin
Result:='Event Options: ';
hr:=eventOptions.GetBoolValue(WPD_EVENT_OPTION_IS_BROADCAST_EVENT,isBroadcastEvent);
if succeeded(hr) then Result:=Result+Format('WPD_EVENT_OPTION_IS_BROADCAST_EVENT = %s',[BoolToStr(isBroadcastEvent,true)])
else Result:=Result+ErrorMsg('Failed to get WPD_EVENT_OPTION_IS_BROADCAST_EVENT (assuming FALSE)',hr);
end
else Result:=ErrorMsg('Failed to get event options',hr);
end;
// Display all content types contained in an IPortableDevicePropVariantCollection
// NOTE: These values are assumed to be in VT_CLSID VarType format.
procedure DisplayContentTypes (contentTypes : IPortableDevicePropVariantCollection);
var
hr : HResult;
nc,i : cardinal;
contentType : TPropVariant;
begin
if contentTypes=nil then begin
WriteErrMsg ('A nullptr IPortableDevicePropVariantCollection interface pointer was received');
Exit;
end;
// Get the total number of content types in the collection.
hr:=contentTypes.GetCount(nc);
if succeeded(hr) then begin
// Loop through the collection and displaying each content type found.
// This loop prints a comma-separated list of the content types.
if nc>0 then for i:=0 to nc-1 do begin
hr:=contentTypes.GetAt(i,contentType);
if succeeded(hr) then begin
// We have a content type. It is assumed that
// content types are returned as VT_CLSID varTypes.
if (contentType.vt=VT_CLSID) and (contentType.puuid<>nil) then begin
// Display the content types separated by commas
write(ContenTypeAsString(contentType.puuid^));
if i+1<nc then write(',');
end
else WriteErrMsg ('Invalid functional object identifier found');
end;
PropVariantClear(contentType);
end
else write('<no functional objects found>');
end;
end;
// Display all functional object identifiers contained in an IPortableDevicePropVariantCollection
// NOTE: These values are assumed to be in VT_LPWSTR VarType format.
procedure DisplayFunctionalObjectIDs (functionalObjectIDs : IPortableDevicePropVariantCollection);
var
hr : HResult;
nc,i : cardinal;
objectID : TPropVariant;
begin
// Get the total number of object identifiers in the collection.
hr:=functionalObjectIDs.GetCount(nc);
if succeeded(hr) then begin
// Loop through the collection and displaying each object identifier found.
// This loop prints a comma-separated list of the object identifiers.
if nc>0 then for i:=0 to nc-1 do begin
hr:=functionalObjectIDs.GetAt(i,objectID);
if succeeded(hr) then begin
// We have a functional object identifier. It is assumed that
// object identifiers are returned as VT_LPWSTR varTypes.
if (objectID.vt=VT_LPWSTR) and (objectID.pwszVal<>nil) then begin
// Display the object identifiers separated by commas
write(Format('%s',[objectID.pwszVal]));
if i+1<nc then write(',');
end
else WriteErrMsg ('Invalid functional object identifier found');
end;
PropVariantClear(objectID);
end
else write('<no functional objects found>');
end;
end;
// List supported content types the device supports
procedure ListSupportedContentTypes (device : IPortableDevice);
var
hr : HResult;
nc,i : cardinal;
pv : TPropVariant;
capabilities : IPortableDeviceCapabilities;
functionalCategories,
contentTypes : IPortableDevicePropVariantCollection;
begin
// Get an IPortableDeviceCapabilities interface from the IPortableDevice interface to
// access the device capabilities-specific methods.
hr:=device.Capabilities(capabilities);
if failed(hr) then WriteErrMsg('Failed to get IPortableDeviceCapabilities from IPortableDevice',hr)
else begin
// Get all functional categories supported by the device.
// We will use these categories to enumerate functional objects
// that fall within them.
hr:=capabilities.GetFunctionalCategories(functionalCategories);
if failed(hr) then WriteErrMsg ('Failed to get functional categories from the device',hr)
else begin
// Get the number of functional categories found on the device.
hr:=functionalCategories.GetCount(nc);
if failed(hr) then WriteErrMsg ('Failed to get number of functional categories',hr)
else begin
writeln(Format('%u Functional Categories found on the device',[nc]));
// Loop through each functional category and display its name and supported content types.
if nc>0 then for i:=0 to nc-1 do begin
hr:=functionalCategories.GetAt(i,pv);
if succeeded(hr) then begin
// We have a functional category. It is assumed that
// functional categories are returned as VT_CLSID varTypes.
if (pv.vt=VT_CLSID) and (pv.puuid<>nil) then begin
// Display the functional category name
writeln('Functional Category: ',FunctionalCategoryAsString(pv.puuid^));
// Display the content types supported for this category
hr:=capabilities.GetSupportedContentTypes(pv.puuid^,contentTypes);
if succeeded(hr) then begin
write('Supported Content Types: ');
DisplayContentTypes(contentTypes);
writeln;
end
else WriteErrMsg ('Failed to get functional objects',hr);
end
else WriteErrMsg ('Invalid functional category found');
end;
PropVariantClear(pv);
end;
end;
end;
end;
end;
// List all functional objects on the device
procedure ListFunctionalObjects (device : IPortableDevice);
var
hr : HResult;
nc : cardinal;
i : integer;
pv : TPropVariant;
capabilities : IPortableDeviceCapabilities;
functionalCategories,
functionalObjectIDs : IPortableDevicePropVariantCollection;
begin
// Get an IPortableDeviceCapabilities interface from the IPortableDevice interface to
// access the device capabilities-specific methods.
hr:=device.Capabilities(capabilities);
if failed(hr) then WriteErrMsg('Failed to get IPortableDeviceCapabilities from IPortableDevice',hr)
else begin
// Get all functional categories supported by the device.
// We will use these categories to enumerate functional objects
// that fall within them.
hr:=capabilities.GetFunctionalCategories(functionalCategories);
if failed(hr) then WriteErrMsg ('Failed to get functional categories from the device',hr)
else begin
// Get the number of functional categories found on the device.
hr:=functionalCategories.GetCount(nc);
if failed(hr) then WriteErrMsg ('Failed to get number of functional categories',hr)
else begin
writeln(Format('%u Functional Categories found on the device',[nc]));
// Loop through each functional category and get the list of
// functional object identifiers associated with a particular
// category.
for i:=0 to nc-1 do begin
hr:=functionalCategories.GetAt(i,pv);
if succeeded(hr) then begin
// We have a functional category. It is assumed that
// functional categories are returned as VT_CLSID varTypes.
if (pv.vt=VT_CLSID) and (pv.puuid<>nil) then begin
// Display the functional category name
writeln('Functional Category: ',FunctionalCategoryAsString(pv.puuid^));
// Display the object identifiers for all
// functional objects within this category
hr:=capabilities.GetFunctionalObjects(pv.puuid^,functionalObjectIDs);
if succeeded(hr) then begin
write('Functional Objects: ');
DisplayFunctionalObjectIDs(functionalObjectIDs);
writeln;
end
else WriteErrMsg ('Failed to get functional objects',hr);
end
else WriteErrMsg ('Invalid functional category found');
end;
PropVariantClear(pv);
end;
end;
end;
end;
end;
// List all functional categories on the device
procedure ListFunctionalCategories (device : IPortableDevice);
var
hr : HResult;
nc,i : cardinal;
pv : TPropVariant;
capabilities : IPortableDeviceCapabilities;
functionalCategories : IPortableDevicePropVariantCollection;
begin
// Get an IPortableDeviceCapabilities interface from the IPortableDevice interface to
// access the device capabilities-specific methods.
hr:=device.Capabilities(capabilities);
if failed(hr) then WriteErrMsg('Failed to get IPortableDeviceCapabilities from IPortableDevice',hr)
else begin
// Get all functional categories supported by the device.
hr:=capabilities.GetFunctionalCategories(functionalCategories);
if failed(hr) then WriteErrMsg ('Failed to get functional categories from the device',hr)
else begin
// Get the number of functional categories found on the device.
hr:=functionalCategories.GetCount(nc);
if failed(hr) then WriteErrMsg ('Failed to get number of functional categories',hr)
else begin
writeln(Format('%u Functional Categories found on the device',[nc]));
// Loop through each functional category and display its name
if nc>0 then for i:=0 to nc-1 do begin
hr:=functionalCategories.GetAt(i,pv);
if succeeded(hr) then begin
// We have a functional category. It is assumed that
// functional categories are returned as VT_CLSID varTypes.
if (pv.vt=VT_CLSID) and (pv.puuid<>nil) then
// Display the functional category name
writeln(' ',FunctionalCategoryAsString(pv.puuid^));
end;
PropVariantClear(pv);
end;
end;
end;
end;
end;
// Determines if a device supports a particular functional category.
function SupportsFunctionalCategory (device : IPortableDevice; const functionalCategory : TGuid) : boolean;
var
hr : HResult;
nc,i : cardinal;
pv : TPropVariant;
capabilities : IPortableDeviceCapabilities;
functionalCategories : IPortableDevicePropVariantCollection;
begin
Result:=false;
// Get an IPortableDeviceCapabilities interface from the IPortableDevice interface to
// access the device capabilities-specific methods.
hr:=device.Capabilities(capabilities);
if failed(hr) then WriteErrMsg('Failed to get IPortableDeviceCapabilities from IPortableDevice',hr)
else begin
// Get all functional categories supported by the device.
// We will use these categories to search for a particular functional category.
// There is typically only 1 of these types of functional categories.
hr:=capabilities.GetFunctionalCategories(functionalCategories);
if failed(hr) then WriteErrMsg('Failed to get functional categories from the device',hr)
else begin
// Get the number of functional categories found on the device.
hr:=functionalCategories.GetCount(nc);
if failed(hr) then WriteErrMsg('Failed to get umber of functional categories',hr)
else begin
// Loop through each functional category and find the passed in category
if nc>0 then for i:=0 to nc-1 do begin
hr:=functionalCategories.GetAt(i,pv);
if succeeded(hr) then begin
// We have a functional category. It is assumed that
// functional categories are returned as VT_CLSID varTypes.
if (pv.vt=VT_CLSID) and (pv.puuid<>nil) then Result:=functionalCategory=pv.puuid^
else WriteErrMsg('Invalid functional category found');
end;
PropVariantClear(pv);
// If the device supports the category, exit the for loop.
// NOTE: We are exiting after calling PropVariantClear to make
// sure we free any allocated data in the PROPVARIANT returned
// from the GetAt() method call.
if Result then Break;
end;
end;
end;
end;
end;
// Determines if a device supports a particular command.
function SupportsCommand (device : IPortableDevice; command : TPropertyKey) : boolean;
var
hr : HResult;
nc,i : cardinal;
key : TPropertyKey;
capabilities : IPortableDeviceCapabilities;
commands : IPortableDeviceKeyCollection;
begin
Result:=false;
// Get an IPortableDeviceCapabilities interface from the IPortableDevice interface to
// access the device capabilities-specific methods.
hr:=device.Capabilities(capabilities);
if failed(hr) then WriteErrMsg('Failed to get IPortableDeviceCapabilities from IPortableDevice',hr)
else begin
// Get all commands supported by the device.
// We will use these commands to search for a particular functional category.
hr:=capabilities.GetSupportedCommands(commands);
if failed(hr) then WriteErrMsg('Failed to get supported commands from the device',hr)
else begin
// Get the number of supported commands found on the device.
hr:=commands.GetCount(nc);
if failed(hr) then WriteErrMsg('Failed to get number of supported commands',hr)
else begin
// Loop through each functional category and find the passed in category
if nc>0 then for i:=0 to nc-1 do begin
hr:=commands.GetAt(i,key);
if succeeded(hr) then begin
Result:=command=key;
if Result then Break;
end;
end;
end;
end;
end;
end;
// Reads the WPD_RENDERING_INFORMATION_PROFILES properties on the device.
function ReadProfileInformationProperties (device : IPortableDevice; const functionalObjectID : string;
var renderingInfoProfiles : IPortableDeviceValuesCollection) : HResult;
var
thr : HResult;
renderingInfoProfilesTemp : IPortableDeviceValuesCollection;
content : IPortableDeviceContent;
properties : IPortableDeviceProperties;
propertiesToRead : IPortableDeviceKeyCollection;
objectProperties : IPortableDeviceValues;
begin
// Get an IPortableDeviceContent interface from the IPortableDevice interface to
// access the content-specific methods.
Result:=device.Content(content);
if failed(Result) then WriteErrMsg('Failed to get IPortableDeviceContent from IPortableDevice',Result)
else begin
// Get an IPortableDeviceProperties interface from the IPortableDeviceContent interface
// to access the property-specific methods.
Result:=Content.Properties(properties);
if failed(Result) then WriteErrMsg ('Failed to get IPortableDeviceProperties from IPortableDevice',Result)
else begin
// CoCreate an IPortableDeviceKeyCollection interface to hold the the property keys
// we wish to read WPD_RENDERING_INFORMATION_PROFILES)
Result:=CoCreateInstance(CLSID_PortableDeviceKeyCollection,nil,CLSCTX_INPROC_SERVER,
IPortableDeviceKeyCollection,propertiesToRead);
if succeeded(Result) then begin
// Populate the IPortableDeviceKeyCollection with the keys we wish to read.
// NOTE: We are not handling any special error cases here so we can proceed with
// adding as many of the target properties as we can.
thr:=propertiesToRead.Add(WPD_RENDERING_INFORMATION_PROFILES);
if failed(thr) then WriteErrMsg ('Failed to add WPD_RENDERING_INFORMATION_PROFILES to IPortableDeviceKeyCollection',thr)
end;
// Call GetValues() passing the collection of specified PROPERTYKEYs.
if succeeded(Result) then begin
Result:=properties.GetValues(PChar(functionalObjectID), // The object whose properties we are reading
propertiesToRead, // The properties we want to read
objectProperties); // Driver supplied property values for the specified object
if failed(Result) then WriteErrMsg (Format('Failed to get all properties for object "%s"',[functionalObjectID]),Result);
end;
// Read the WPD_RENDERING_INFORMATION_PROFILES
if succeeded(Result) then begin
Result:=objectProperties.GetIPortableDeviceValuesCollectionValue(WPD_RENDERING_INFORMATION_PROFILES,renderingInfoProfilesTemp);
if failed(Result) then WriteErrMsg ('Failed to get WPD_RENDERING_INFORMATION_PROFILES from rendering information',Result);
end;
// QueryInterface the interface into the out-going parameters.
if succeeded(Result) then renderingInfoProfiles:=renderingInfoProfilesTemp;
end;
end;
end;
procedure DisplayExpectedValues (expectedValues : IPortableDeviceValues);
var
hr : HResult;
formAttribute,
rangeMin,rangeMax,
rangeStep : cardinal;
begin
formAttribute:=WPD_PROPERTY_ATTRIBUTE_FORM_UNSPECIFIED;
// 1) Determine what type of valid values should be displayed by reading the
// WPD_PROPERTY_ATTRIBUTE_FORM property.
hr:=expectedValues.GetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM,formAttribute);
if failed(hr) then WriteErrMsg ('Failed to get WPD_PROPERTY_ATTRIBUTE_FORM from expected value set',hr)
else begin
rangeMin:=0; rangeMax:=0; rangeStep:=0;
case formAttribute of
WPD_PROPERTY_ATTRIBUTE_FORM_RANGE : begin
hr:=expectedValues.GetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_RANGE_MIN,rangeMin);
if failed(hr) then WriteErrMsg ('Failed to get WPD_PROPERTY_ATTRIBUTE_RANGE_MIN from expected values collection',hr);
hr:=expectedValues.GetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_RANGE_MAX,rangeMax);
if failed(hr) then WriteErrMsg ('Failed to get WPD_PROPERTY_ATTRIBUTE_RANGE_MAX from expected values collection',hr);
hr:=expectedValues.GetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_RANGE_STEP,rangeStep);
if failed(hr) then WriteErrMsg ('Failed to get WPD_PROPERTY_ATTRIBUTE_RANGE_STEP from expected values collection',hr);
writeln(Format('MIN: %u, MAX: %u, STEP: %u',[rangeMin,rangeMax,rangeStep]));
end;
else writeln(Format('DisplayExpectedValues helper function did not display attributes for form %u',[formAttribute]));
end;
end;
end;
// Displays a rendering profile.
procedure DisplayRenderingProfile (profile : IPortableDeviceValues);
var
hr : HResult;
totalBitrate,
channelCount,
audioFormatCode : cardinal;
expectedValues : IPortableDeviceValues;
objectFormat : TGuid;
begin
// Display WPD_MEDIA_TOTAL_BITRATE
hr:=profile.GetUnsignedIntegerValue(WPD_MEDIA_TOTAL_BITRATE,totalBitrate);
if succeeded(hr) then writeln(Format('Total Bitrate: %u',[totalBitrate]))
else if hr=DISP_E_TYPEMISMATCH then begin
hr:=profile.GetIPortableDeviceValuesValue(WPD_MEDIA_TOTAL_BITRATE,expectedValues);
if succeeded(hr) then begin
write('Total Bitrate: ');
DisplayExpectedValues(expectedValues);
end
// If we are still a failure here, report the error
else WriteErrMsg ('Failed to get WPD_MEDIA_TOTAL_BITRATE from rendering profile',hr);
end;
// Display WPD_AUDIO_CHANNEL_COUNT
hr:=profile.GetUnsignedIntegerValue(WPD_AUDIO_CHANNEL_COUNT,channelCount);
if succeeded(hr) then writeln(Format('Channel Count: %u',[channelCount]))
else WriteErrMsg ('Failed to get WPD_AUDIO_CHANNEL_COUNT from rendering profile',hr);
// Display WPD_AUDIO_FORMAT_CODE
hr:=profile.GetUnsignedIntegerValue(WPD_AUDIO_FORMAT_CODE,audioFormatCode);
if succeeded(hr) then writeln(Format('Audio Format Code: %u',[audioFormatCode]))
else WriteErrMsg ('Failed to get WPD_AUDIO_FORMAT_CODE from rendering profile',hr);
// Display WPD_OBJECT_FORMAT
objectFormat:=WPD_OBJECT_FORMAT_UNSPECIFIED;
hr:=profile.GetGuidValue(WPD_OBJECT_FORMAT,objectFormat);
if succeeded(hr) then writeln(Format('Object Format: %s',[GuidToString(objectFormat)]))
else WriteErrMsg ('Failed to get WPD_OBJECT_FORMAT from rendering profile',hr);
end;
// List rendering capabilities the device supports
procedure ListRenderingCapabilityInformation (device : IPortableDevice);
var
hr : HResult;
nc,i : cardinal;
pv : TPropVariant;
capabilities : IPortableDeviceCapabilities;
renderingInfoObjects : IPortableDevicePropVariantCollection;
renderingInfoProfiles : IPortableDeviceValuesCollection;
profiles : IPortableDeviceValues;
begin
if not SupportsFunctionalCategory(device,WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION) then
writeln('This device does not support device rendering information to display')
else begin
// Get an IPortableDeviceCapabilities interface from the IPortableDevice interface to
// access the device capabilities-specific methods.
hr:=device.Capabilities(capabilities);
if failed(hr) then WriteErrMsg('Failed to get IPortableDeviceCapabilities from IPortableDevice',hr)
else begin
// Get the functional object identifier for the rendering information object
hr:=capabilities.GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION,renderingInfoObjects);
if failed(hr) then WriteErrMsg ('Failed to get functional objects',hr)
else begin
// Assume the device only has one rendering information object for this example.
// We are going to request the first Object Identifier found in the collection.
hr:=renderingInfoObjects.GetAt(0,pv);
if succeeded(hr) and (pv.vt=VT_LPWSTR) and (pv.pwszVal<>nil) then begin
hr:=ReadProfileInformationProperties(device,pv.pwszVal,renderingInfoProfiles);
// Error output statements are performed by the helper function, so they
// are omitted here.
// Display all rendering profiles
if succeeded(hr) then begin
// Get the number of profiles supported by the device
hr:=renderingInfoProfiles.GetCount(nc);
if failed(hr) then WriteErrMsg ('Failed to get number of profiles supported by the device',hr);
writeln(Format('%u Rendering Profiles are supported by this device',[nc]));
if succeeded(hr) then begin
if nc>0 then for i:=0 to nc-1 do begin
hr:=renderingInfoProfiles.GetAt(i,profiles);
if succeeded(hr) then begin
writeln(Format('Profile #%u:',[i]));
DisplayRenderingProfile(profiles);
writeln;
end
else WriteErrMsg (Format('Failed to get rendering profile at index "%u"',[i]),hr)
end;
end;
end;
end
else WriteErrMsg ('Failed to get first rendering object''s identifier',hr);
PropVariantClear(pv);
end;
end;
end;
end;
// List all supported events on the device
procedure ListSupportedEvents (device : IPortableDevice);
var
hr : HResult;
nc,i : cardinal;
pv : TPropVariant;
capabilities : IPortableDeviceCapabilities;
events : IPortableDevicePropVariantCollection;
begin
// Get an IPortableDeviceCapabilities interface from the IPortableDevice interface to
// access the device capabilities-specific methods.
hr:=device.Capabilities(capabilities);
if failed(hr) then WriteErrMsg('Failed to get IPortableDeviceCapabilities from IPortableDevice',hr)
else begin
// Get all events supported by the device.
hr:=capabilities.GetSupportedEvents(events);
if failed(hr) then WriteErrMsg ('Failed to get supported events from the device',hr)
else begin
// Get the number of supported events found on the device.
hr:=events.GetCount(nc);
if failed(hr) then WriteErrMsg ('Failed to get number of supported events',hr);
end;
writeln(Format('%u Supported Events found on the device',[nc]));
// Loop through each event and display its name
if succeeded(hr) then begin
if nc>0 then for i:=0 to nc-1 do begin
hr:=events.GetAt(i,pv);
if succeeded(hr) then begin
// We have an event. It is assumed that
// events are returned as VT_CLSID varTypes.
if (pv.vt=VT_CLSID) and (pv.puuid<>nil) then begin
writeln(EventAsString(pv.puuid^));
writeln(EventOptionsAsString(capabilities,pv.puuid^));
end
end;
PropVariantClear(pv);
end;
end;
end;
end;
//===================================================================
// from "ContentEnumeration.cpp"
// Recursively called function which enumerates using the specified
// object identifier as the parent and populates the returned object
// identifiers into an IPortableDevicePropVariantCollection object.
procedure RecursiveEnumerateAndCopyToCollection(const objectID : string;
content : IPortableDeviceContent; objectIDs : IPortableDevicePropVariantCollection);
var
hr : HResult;
numFetched,i : cardinal;
pv : TPropVariant;
enumObjectIDs : IEnumPortableDeviceObjectIDs;
objectIDArray : TCharArray;
begin
// Allocated a new string in a PROPVARIANT so we can add it to our
// collection object.
hr:=InitPropVariantFromString(PChar(objectID),pv);
if failed(hr) then begin
WriteErrMsg (Format('Failed to copy object identifier "%s"',[objectID]),hr); Exit;
end;
// Add the object identifier...
hr:=objectIDs.Add(pv);
// Free the allocated string in the PROPVARIANT
PropVariantClear(pv);
// If we failed to add the object identifier, return immediately.
if failed(hr) then begin
WriteErrMsg (Format('Failed to add object identifier "%s" to the IPortableDevicePropVariantCollection',[objectID]),hr); Exit;
end;
// Get an IEnumPortableDeviceObjectIDs interface by calling EnumObjects with the
// specified parent object identifier.
hr:=content.EnumObjects(0,PChar(objectID),nil,enumObjectIDs);
if failed(hr) then WriteErrMsg ('Failed to get IEnumPortableDeviceObjectIDs from IPortableDeviceContent',hr);
// Loop calling Next() while S_OK is being returned.
while hr=S_OK do begin
SetLength(objectIDArray,NUM_OBJECTS_TO_REQUEST);
hr:=enumObjectIDs.Next (NUM_OBJECTS_TO_REQUEST, // Number of objects to request on each Next() call
objectIDArray[0], // PWSTR array which will be populated on each Next() call
numFetched); // Number of objects written to the PWSTR array
if succeeded(hr) then begin
// Traverse the results of the Next() operation and recursively enumerate
// Remember to free all returned object identifiers using CoTaskMemFree()
if numFetched>0 then for i:=0 to numFetched-1 do if objectIDArray[i]<>nil then begin
RecursiveEnumerateAndCopyToCollection(objectIDArray[i],Content,objectIDs);
CoTaskMemFree(objectIDArray[i]); objectIDArray[i]:=nil;
end;
end;
objectIDArray:=nil;
end;
end;
// Enumerate all content on the device starting with the
// "DEVICE" object and populates the returned object identifiers
// into an IPortableDevicePropVariantCollection
function CreateIPortableDevicePropVariantCollectionWithAllObjectIDs(content : IPortableDeviceContent;
out objectIDs : IPortableDevicePropVariantCollection) : HResult;
begin
// CoCreate an IPortableDevicePropVariantCollection interface to hold the the object identifiers
Result:=CoCreateInstance(CLSID_PortableDevicePropVariantCollection,nil,CLSCTX_INPROC_SERVER,
IID_IPortableDevicePropVariantCollection,objectIDs);
if succeeded(Result) then begin
RecursiveEnumerateAndCopyToCollection(WPD_DEVICE_OBJECT_ID,content,objectIDs);
end;
end;
//===================================================================
// from "ContentProperties.cpp"
// Get a property assumed to be in string form - refer to "DisplayStringProperty"
function StringPropertyAsString (properties : IPortableDeviceValues; key : TPropertyKey; const keyname : string) : string;
var
hr : HResult;
value : PWideChar;
begin
hr:=properties.GetStringValue(key,value);
if succeeded(hr) then begin
// Get the length of the string value so we
// can output <empty string value> if one is encountered.
if length(keyname)=0 then begin
if length(value)>0 then Result:=value
else Result:='<empty string value>';
end
else begin
if length(value)>0 then Result:=Format('%s: %s',[keyname,value])
else Result:=Format('%s: <empty string value>',[keyname]);
end;
// Free the allocated string returned from the
// GetStringValue method
end
else if length(keyname)=0 then Result:='<Not Found>'
else Result:=Format('%s: <Not Found>',[keyname]);
CoTaskMemFree(value); value:=nil;
end;
function GetProperty (properties : IPortableDeviceValues; key : TPropertyKey; var PropStr : string) : boolean;
var
hr : HResult;
value : PWideChar;
begin
Result:=false;
hr:=properties.GetStringValue(key,value);
if succeeded(hr) then begin
// Get the length of the string value so we
// can output <empty string value> if one is encountered.
if length(value)>0 then PropStr:=value
else PropStr:='<empty string value>';
Result:=true;
end
else PropStr:='';
// Free the allocated string returned from the
// GetStringValue method
CoTaskMemFree(value); value:=nil;
end;
// Get a property assumed to be in GUID form - refer to "DisplayGuidProperty"
function GuidPropertyAsString (properties : IPortableDeviceValues; key : TPropertyKey; const keyname : string) : string;
var
hr : HResult;
value : TGuid;
begin
hr:=properties.GetGuidValue(key,value);
if succeeded(hr) then Result:=Format('%s: %s',[keyname,value.ToString])
else Result:=Format('%s: <Not Found>',[keyname]);
end;
//-------------------------------------------------------------------
// IPortableDevicePropertiesBulkCallback implementation for use with
// IPortableDevicePropertiesBulk operations.
type
TGetBulkValuesCallback = class (TInterfacedObject, IPortableDevicePropertiesBulkCallback)
public
function OnStart(const pContext: TGuid): HResult; stdcall;
function OnProgress(const pContext: TGuid; pResults: IPortableDeviceValuesCollection): HResult; stdcall;
function OnEnd(const pContext: TGuid; hrStatus: HResult): HResult; stdcall;
end;
function TGetBulkValuesCallback.OnStart(const pContext: TGuid): HResult; stdcall;
begin
writeln(Format('*** BULK Property operation starting, context = %s',[pContext.ToString]));
Result:=S_OK;
end;
function TGetBulkValuesCallback.OnProgress(const pContext: TGuid; pResults: IPortableDeviceValuesCollection): HResult; stdcall;
var
i,n : cardinal;
objectProperties : IPortableDeviceValues;
begin
Result:=pResults.GetCount(n);
// Display the returned properties to the user.
// NOTE: We are reading for expected properties, which were setup in the
// QueueGetXXXXXX bulk operation call.
if succeeded(Result) then begin
writeln(Format('Received next batch of %u object value elements..., context = %s',[n,pContext.ToString]));
if n>0 then for i:=0 to n-1 do begin
Result:=pResults.GetAt(i,objectProperties);
if succeeded(Result) then begin
writeln(StringPropertyAsString(objectProperties,WPD_OBJECT_PARENT_ID,'WPD_OBJECT_PARENT_ID'));
writeln(StringPropertyAsString(objectProperties,WPD_OBJECT_ID,'WPD_OBJECT_ID'));
writeln(StringPropertyAsString(objectProperties,WPD_OBJECT_NAME,'WPD_OBJECT_NAME'));
writeln(StringPropertyAsString(objectProperties,WPD_OBJECT_ORIGINAL_FILE_NAME,'WPD_OBJECT_ORIGINAL_FILE_NAME'));
writeln(StringPropertyAsString(objectProperties,WPD_OBJECT_PERSISTENT_UNIQUE_ID,'WPD_OBJECT_PERSISTENT_UNIQUE_ID'));
writeln(GuidPropertyAsString (objectProperties,WPD_OBJECT_CONTENT_TYPE,'WPD_OBJECT_CONTENT_TYPE'));
writeln(GuidPropertyAsString (objectProperties,WPD_OBJECT_FORMAT,'WPD_OBJECT_FORMAT'));
writeln;
end
else WriteErrMsg(Format('Failed to get IPortableDeviceValues from IPortableDeviceValuesCollection at index "%u"',[i]),Result);
end;
end;
end;
function TGetBulkValuesCallback.OnEnd(const pContext: TGuid; hrStatus: HResult): HResult; stdcall;
begin
writeln(Format('*** BULK Property operation ending, context = %s',[pContext.ToString]));
writeln(Format(' status = $%.8x: ',[hrStatus,SysErrorMessage(hrStatus)]));
// This assumes that we are only performing a single operation
// at a time, so no check is needed on the context when setting
// the operation complete event.
if BulkPropertyOperationEvent<>0 then SetEvent(BulkPropertyOperationEvent);
Result:=S_OK;
end;
//-------------------------------------------------------------------
// Reads a set of properties for all objects.
procedure ReadContentPropertiesBulk (device : IPortableDevice);
var
hr,thr : HResult;
callback : TGetBulkValuesCallback;
Content : IPortableDeviceContent;
Properties : IPortableDeviceProperties;
PropertiesBulk : IPortableDevicePropertiesBulk;
PropertiesToRead : IPortableDeviceKeyCollection;
objectIDs : IPortableDevicePropVariantCollection;
context : TGuid;
begin
// 1) Get an IPortableDeviceContent interface from the IPortableDevice interface to
// access the content-specific methods.
hr:=device.Content(content);
if failed(hr) then WriteErrMsg ('Failed to get IPortableDeviceContent from IPortableDevice',hr)
else begin
// 2) Get an IPortableDeviceProperties interface from the IPortableDeviceContent interface
// to access the property-specific methods.
hr:=Content.Properties(Properties);
if failed(hr) then WriteErrMsg ('Failed to get IPortableDeviceProperties from IPortableDevice',hr)
else begin
// 3) Check to see if the driver supports BULK property operations by call QueryInterface
// on the IPortableDeviceProperties interface for IPortableDevicePropertiesBulk
hr:=Properties.QueryInterface(IID_IPortableDevicePropertiesBulk,PropertiesBulk);
if failed(hr) then WriteErrMsg ('This driver does not support BULK property operations.',hr);
end;
// 4) CoCreate an IPortableDeviceKeyCollection interface to hold the the property keys
// we wish to read.
if succeeded(hr) then begin
hr:=CoCreateInstance(CLSID_PortableDeviceKeyCollection,nil,
CLSCTX_INPROC_SERVER,IPortableDeviceKeyCollection,PropertiesToRead);
if failed(hr) then WriteErrMsg ('Failed to CoCreate IPortableDeviceKeyCollection to hold the property keys to read',hr);
end;
if succeeded(hr) then begin
// 5) Populate the IPortableDeviceKeyCollection with the keys we wish to read.
// NOTE: We are not handling any special error cases here so we can proceed with
// adding as many of the target properties as we can.
thr:=PropertiesToRead.Add(WPD_OBJECT_PARENT_ID);
if failed(thr) then WriteErrMsg ('Failed to add WPD_OBJECT_PARENT_ID to IPortableDeviceKeyCollection',thr);
thr:=PropertiesToRead.Add(WPD_OBJECT_ID);
if failed(thr) then WriteErrMsg ('Failed to add WPD_OBJECT_ID to IPortableDeviceKeyCollection',thr);
thr:=PropertiesToRead.Add(WPD_OBJECT_NAME);
if failed(thr) then WriteErrMsg ('Failed to add WPD_OBJECT_NAME to IPortableDeviceKeyCollection',thr);
thr:=propertiesToRead.Add(WPD_OBJECT_ORIGINAL_FILE_NAME);
if failed(thr) then WriteErrMsg ('Failed to add WPD_OBJECT_ORIGINAL_FILE_NAME to IPortableDeviceKeyCollection',thr);
thr:=PropertiesToRead.Add(WPD_OBJECT_PERSISTENT_UNIQUE_ID);
if failed(thr) then WriteErrMsg ('Failed to add WPD_OBJECT_PERSISTENT_UNIQUE_ID to IPortableDeviceKeyCollection',thr);
thr:=PropertiesToRead.Add(WPD_OBJECT_FORMAT);
if failed(thr) then WriteErrMsg ('Failed to add WPD_OBJECT_FORMAT to IPortableDeviceKeyCollection',thr);
thr:=PropertiesToRead.Add(WPD_OBJECT_CONTENT_TYPE);
if failed(thr) then WriteErrMsg ('Failed to add WPD_OBJECT_CONTENT_TYPE to IPortableDeviceKeyCollection',thr);
end;
// 6) Create an instance of the IPortableDevicePropertiesBulkCallback object.
if succeeded(hr) then begin
callback:=TGetBulkValuesCallback.Create;
if callback=nil then begin
hr:=E_OUTOFMEMORY;
WriteErrMsg ('Failed to allocate memory for TGetBulkValuesCallback object',hr);
end;
end;
// 7) Call our helper function CreateIPortableDevicePropVariantCollectionWithAllObjectIDs
// to enumerate and create an IPortableDevicePropVariantCollection with the object
// identifiers needed to perform the bulk operation on.
if succeeded(hr) then hr:=CreateIPortableDevicePropVariantCollectionWithAllObjectIDs(content,objectIDs);
// 8) Call QueueGetValuesByObjectList to initialize the Asynchronous
// property operation.
if succeeded(hr) then begin
hr:=PropertiesBulk.QueueGetValuesByObjectList(objectIDs,PropertiesToRead,callback,context);
// 9) Call Start() to actually begin the property operation
if succeeded(hr) then begin
// Cleanup any previously created global event handles.
if BulkPropertyOperationEvent<>0 then begin
CloseHandle(BulkPropertyOperationEvent); BulkPropertyOperationEvent:=0;
end;
// In order to create a simpler to follow example we create and wait infinitly
// for the bulk property operation to complete and ignore any errors.
// Production code should be written in a more robust manner.
// Create the global event handle to wait on for the bulk operation
// to complete.
BulkPropertyOperationEvent:=CreateEvent(nil,false,false,nil);
if BulkPropertyOperationEvent<>0 then begin
// Call Start() to actually begin the Asynchronous bulk operation.
hr:=PropertiesBulk.Start(context);
if failed(hr) then WriteErrMsg ('Failed to start property operation',hr)
end
else WriteErrMsg ('Failed to create the global event handle to wait on for the bulk operation. Aborting operation',hr);
end
else WriteErrMsg ('QueueGetValuesByObjectList Failed',hr);
end;
// In order to create a simpler to follow example we will wait infinitly for the operation
// to complete and ignore any errors. Production code should be written in a more
// robust manner.
if succeeded(hr) then begin
if BulkPropertyOperationEvent<>0 then WaitForSingleObject(BulkPropertyOperationEvent,INFINITE);
end;
// Cleanup any created global event handles before exiting..
if BulkPropertyOperationEvent<>0 then begin
CloseHandle(BulkPropertyOperationEvent); BulkPropertyOperationEvent:=0;
end;
end;
end;
// Writes a set of properties for all objects.
procedure WriteContentPropertiesBulk (device : IPortableDevice);
var
hr : HResult;
nc,i : cardinal;
nn : string;
context : TGuid;
objectID : TPropVariant;
callback : TGetBulkValuesCallback;
Content : IPortableDeviceContent;
Properties : IPortableDeviceProperties;
PropertiesBulk : IPortableDevicePropertiesBulk;
PropertiesToWrite : IPortableDeviceValuesCollection;
objectIDs : IPortableDevicePropVariantCollection;
newvalues : IPortableDeviceValues;
begin
// 1) Get an IPortableDeviceContent interface from the IPortableDevice interface to
// access the content-specific methods.
hr:=device.Content(content);
if failed(hr) then WriteErrMsg ('Failed to get IPortableDeviceContent from IPortableDevice',hr)
else begin
// 2) Get an IPortableDeviceProperties interface from the IPortableDeviceContent interface
// to access the property-specific methods.
hr:=Content.Properties(Properties);
if failed(hr) then WriteErrMsg ('Failed to get IPortableDeviceProperties from IPortableDevice',hr)
else begin
// 3) Check to see if the driver supports BULK property operations by call QueryInterface
// on the IPortableDeviceProperties interface for IPortableDevicePropertiesBulk
hr:=Properties.QueryInterface(IID_IPortableDevicePropertiesBulk,PropertiesBulk);
if failed(hr) then WriteErrMsg ('This driver does not support BULK property operations.',hr);
end;
// 4) CoCreate an IPortableDeviceValuesCollection interface to hold the the properties
// we wish to write.
if succeeded(hr) then begin
hr:=CoCreateInstance(CLSID_PortableDeviceKeyCollection,nil,
CLSCTX_INPROC_SERVER,IPortableDeviceKeyCollection,PropertiesToWrite);
if failed(hr) then WriteErrMsg ('Failed to CoCreate IPortableDeviceKeyCollection for bulk property values',hr);
end;
// 5) Create an instance of the IPortableDevicePropertiesBulkCallback object.
if succeeded(hr) then begin
callback:=TGetBulkValuesCallback.Create;
if callback=nil then begin
hr:=E_OUTOFMEMORY;
WriteErrMsg ('Failed to allocate TGetBulkValuesCallback',hr);
end;
end;
// 6) Call our helper function CreateIPortableDevicePropVariantCollectionWithAllObjectIDs
// to enumerate and create an IPortableDevicePropVariantCollection with the object