i have an issue where, when i go to add more "names" to the if statements. Its hard for me to see if there all ready on there. Thus is there a more clean way to write this Where i can easly see what names are all read there?
function TfDB.GetW(name: string) :integer;
begin
result := 0;
if (name = 'Destacker') or (name='Router') or (name = 'Turn Table') Then
result := 57;
if (name = 'Laser Marker') then
result := 66;
if (name = 'SP28')OR(name='Chamber') OR (name = 'CM402') OR (name = 'SP60') then
result := 65;
if (name = 'Conveyor') OR (name = 'Slide Gate') OR (name = 'Washer') then
result := 51;
if (name = 'BTU') OR (name = 'Furukawa') OR (name = 'Radial') OR (name = 'HDP') or (name = 'MSR') OR (name = 'Koki') Or (name = 'MSF') then
result := 98;
if (name = 'Buffer')OR (name = 'Reclaimer') OR (name = 'ECO Roller') then
result := 49;
if (name = 'Inverter') or (name = 'CNC') then
result := 42;
if (name = '3-D Check Station') or (name='Screw Machine') or (name='VT-Win') or(name='Component Viewer') then
result := 58;
if (name = 'AOI Panel') or (name='AirBlow') then
result := 42;
if (name='Mag Loader') or (name='Soltec') then
result := 73;
if (name='Tester') then
result := 33;
if (name='LoadBox') then
result := 17;
if (name = 'DeltaWave') then
result := 89;
if (name = 'ScrewFeeder') then
result := 25;
if (name='Pump') then
result := 33;
//if result is 0 show message error.
end;
You could create a dictionary, TDictionary<string, Integer>, and store it in a global variable. Load it up with the name to width mapping at initialization time. And then your function becomes a one-liner.
var
WidthMapping: TDictionary<string, Integer>;
....
function TfDB.GetW(name: string) :integer;
begin
if not WidthMapping.TryGetValue(name, Result) then
... handle error condition
end;
....
initialization
WidthMapping := TDictionary<string, Integer>.Create;
WidthMapping.Add('Destacker', 57);
WidthMapping.Add('Router', 57);
... etc.
....
finalization
WidthMapping.Free;
Yes, don't use an if statement but an array and a loop:
const
NAME_RESULT: array [1..2] of record
Name: string;
Value: Integer;
end = (
(Name: 'whatever'; Value: 57)
, (Name: 'Something else'; Value: 57)
);
var
i: Integer;
begin
Result := 0; // or whatever you need your default to be
for i := Low(NAME_RESULT) to High(NAME_RESULT) do
if SameText(name, NAME_RESULT[i].Name) then
begin
Result := NAME_RESULT[i].Value;
Break;
end;
end;
Additional advantage: you don't need to keep the names which return the same values together but can sort the list alphabetically.
Create an array (or dynamic array if you want to further add more names without any concerns of the array dimensions) of strings that contains all the names you want to test for (here I assumed fixed size string array):
var test = array [1..50] of string;
a[1]:='Destacker';
a[2]:='Router';
etc.
In your test routine you may use the case keyword like this:
function TfDB.GetW(index: integer) :integer
begin
result:=0;
case index of
1,2,3: result:=57;
4: result:=66
end
end;
I think it's easier this way
Related
I am trying to acquire file properties such as Title, Subject, Author, Copyright, Comments and the various file dates. Other properties would be nice, but not required. My searches all seem to lead to variations of the code I found here as well as here. Copying and pasting the code, I had to do a bit of tweaking to make it work at all. I was finally able to get it to partially work, but only on .doc files. Other files produced "EOleSysError with message '%1 could not be found'". I thought this related to compound files, but the same error occurred with .docx and .xls files.
I have tried STGFMT_STORAGE, STGFMT_FILE, STGFMT_ANY, and STGFMT_DOCFILE, but the only one that works is STGFMT_ANY. Again, that only works on .doc files. Even on .doc files, it doesn't return dates, although I have only treated them as string values. Here's my code, and the results.
Output in a TListView
unit u_fSummary;
interface
uses Windows, ComObj, ActiveX, Variants, Sysutils, dialogs;
function GetFileSummaryInfo(const FileName: WideString): String;
function IsNTFS(AFileName : string) : boolean;
implementation
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}';
IID_IPropertySetStorage : TGUID = '{0000013A-0000-0000-C000-000000000046}';
STGFMT_STORAGE = 0; {Indicates that the file must be a compound file}
STGFMT_FILE = 3; {Indicates that the file must not be a compound file.
This element is only valid when using the StgCreateStorageEx
or StgOpenStorageEx functions to access the NTFS file system
implementation of the IPropertySetStorage interface.
Therefore, these functions return an error if the riid
parameter does not specify the IPropertySetStorage interface,
or if the specified file is not located on an NTFS file system volume.}
STGFMT_ANY = 4; {Indicates that the system will determine the file type and
use the appropriate structured storage or property set
implementation.
This value cannot be used with the StgCreateStorageEx function.}
STGFMT_DOCFILE = 5; {Indicates that the file must be a compound file, and is
similar to the STGFMT_STORAGE flag, but indicates that the
compound-file form of the compound-file implementation must
be used. For more information}
// Summary Information
PID_TITLE = 2;
PID_SUBJECT = 3;
PID_AUTHOR = 4;
PID_KEYWORDS = 5;
PID_COMMENTS = 6;
PID_TEMPLATE = 7;
PID_LASTAUTHOR = 8;
PID_REVNUMBER = 9;
PID_EDITTIME = 10;
PID_LASTPRINTED = 11;
PID_CREATE_DTM = 12;
PID_LASTSAVE_DTM = 13;
PID_PAGECOUNT = 14;
PID_WORDCOUNT = 15;
PID_CHARCOUNT = 16;
PID_THUMBNAIL = 17;
PID_APPNAME = 18;
PID_SECURITY = 19;
// Document Summary Information
PID_CATEGORY = 2;
PID_PRESFORMAT = 3;
PID_BYTECOUNT = 4;
PID_LINECOUNT = 5;
PID_PARCOUNT = 6;
PID_SLIDECOUNT = 7;
PID_NOTECOUNT = 8;
PID_HIDDENCOUNT = 9;
PID_MMCLIPCOUNT = 10;
PID_SCALE = 11;
PID_HEADINGPAIR = 12;
PID_DOCPARTS = 13;
PID_MANAGER = 14;
PID_COMPANY = 15;
PID_LINKSDIRTY = 16;
PID_CHARCOUNT2 = 17;
function IsNTFS(AFileName : string) : boolean;
var
fso, drv : OleVariant;
begin
IsNTFS := False;
fso := CreateOleObject('Scripting.FileSystemObject');
drv := fso.GetDrive(fso.GetDriveName(AFileName));
if drv.FileSystem = 'NTFS' then
IsNTFS := True;
end;
function StgOpenStorageEx (
const pwcsName : POleStr; //Pointer to the path of the
//file containing storage object
grfMode : LongInt; //Specifies the access mode for the object
stgfmt : DWORD; //Specifies the storage file format
grfAttrs : DWORD; //Reserved; must be zero
pStgOptions : Pointer; //Address of STGOPTIONS pointer
reserved2 : Pointer; //Reserved; must be zero
riid : PGUID; //Specifies the GUID of the interface pointer
out stgOpen : //Address of an interface pointer
IStorage ) : HResult; stdcall; external 'ole32.dll';
function GetFileSummaryInfo(const FileName: WideString): String;
var
I: Integer;
PropSetStg: IPropertySetStorage;
PropSpec: array of TPropSpec;
PropStg: IPropertyStorage;
PropVariant: array of TPropVariant;
Rslt: HResult;
S: String;
Stg: IStorage;
PropEnum: IEnumSTATPROPSTG;
HR : HResult;
PropStat: STATPROPSTG;
k : integer;
function _PropertyPIDToCaption(const ePID: Cardinal): string;
begin
case ePID of
PID_TITLE:
Result := 'Title';
PID_SUBJECT:
Result := 'Subject';
PID_AUTHOR:
Result := 'Author';
PID_KEYWORDS:
Result := 'Keywords';
PID_COMMENTS:
Result := 'Comments';
PID_TEMPLATE:
Result := 'Template';
PID_LASTAUTHOR:
Result := 'Last Saved By';
PID_REVNUMBER:
Result := 'Revision Number';
PID_EDITTIME:
Result := 'Total Editing Time';
PID_LASTPRINTED:
Result := 'Last Printed';
PID_CREATE_DTM:
Result := 'Create Time/Date';
PID_LASTSAVE_DTM:
Result := 'Last Saved Time/Date';
PID_PAGECOUNT:
Result := 'Number of Pages';
PID_WORDCOUNT:
Result := 'Number of Words';
PID_CHARCOUNT:
Result := 'Number of Characters';
PID_THUMBNAIL:
Result := 'Thumbnail';
PID_APPNAME:
Result := 'Creating Application';
PID_SECURITY:
Result := 'Security';
else
Result := '$' + IntToHex(ePID, 8);
end
end;
begin
Result := '';
try
OleCheck(StgOpenStorageEx(StringToOleStr(FileName), STGM_READ or STGM_SHARE_DENY_WRITE,
STGFMT_ANY, 0, nil, nil, #IID_IPropertySetStorage, stg));
PropSetStg := Stg as IPropertySetStorage;
OleCheck(PropSetStg.Open(FmtID_SummaryInformation,
STGM_READ or STGM_SHARE_EXCLUSIVE, PropStg));
OleCheck(PropStg.Enum(PropEnum));
i := 0;
hr := PropEnum.Next(1, PropStat, nil);
while hr = S_OK do
begin
inc(I);
SetLength(PropSpec,I);
PropSpec[i-1].ulKind := PRSPEC_PROPID;
PropSpec[i-1].propid := PropStat.propid;
hr := PropEnum.Next(1, PropStat, nil);
end;
SetLength(PropVariant,i);
Rslt := PropStg.ReadMultiple(i, #PropSpec[0], #PropVariant[0]);
if Rslt <> S_FALSE then;
begin
for k := 0 to i -1 do
begin
S := '';
if PropVariant[k].vt = VT_LPSTR then
if Assigned(PropVariant[k].pszVal) then
S := PropVariant[k].pszVal;
S := Trim(Format(_PropertyPIDToCaption(PropSpec[k].Propid)+ ': %s',[s]));
if S <> '' then Result := Result + S + #176;
end;
end;
finally
end;
end;
end.
I have several hardcoded validations like these:
const
cLstAct = 1;
cLstOrg = 4;
cLstClockAct = 11;
const
FUNCT_1 = 224;
FUNCT_2 = 127;
FUNCT_3 = 3;
if lFuncID in [FUNCT_1,FUNCT_2,FUNCT_3] then ...
if not (lListType in [cLstAct..cLstOrg,cLstClockAct]) then ...
if not (lPurpose in [0..2]) then ...
that I want to replace with a common method like
function ValidateInSet(AIntValue: integer; AIntSet: ###): Boolean;
begin
Result := (AIntValue in AIntSet);
if not Result then ...
end;
but what type to choose for AIntSet?
Currently the values to be tested throughout the code go up to a const value 232 (so I can e.g. use a TByteSet = Set of Byte), but I can foresee that we will bump into the E1012 Constant expression violates subrange bounds when the constant values exceed 255.
My Google-fu fails me here...
(Currently on Delphi Seattle Update 1)
Use a dictionary, TDictionary<Integer, Integer>. The value is irrelevant and you only care about the key. If the dictionary contains a specific key then that key is a member of the set. Use AddOrSetValue to add a member, Remove to delete a member and ContainsKey to test membership.
The point of using a dictionary is that it gives you O(1) lookup.
You don't want to use this type directly as a set. You should wrap it in a class that just exposes set like capabilities. An example of that can be found here: https://stackoverflow.com/a/33530037/505088
You can use an array of Integer:
function ValidateInSet(AIntValue: integer; AIntSet: array of Integer): Boolean;
var
I: Integer;
begin
Result := False;
for I := Low(AIntSet) to High(AIntSet) do
begin
if AIntSet[I] = AIntValue then
begin
Result := True;
Break;
end;
end;
if not Result then ...
end;
const
cLstAct = 1;
cLstOrg = 4;
cLstClockAct = 11;
const
FUNCT_1 = 224;
FUNCT_2 = 127;
FUNCT_3 = 3;
if ValidateInSet(lFuncID, [FUNCT_1, FUNCT_2, FUNCT_3]) then ...
if not ValidateInSet(lListType, [cLstAct, 2, 3, cLstOrg, cLstClockAct]) then ...
if not ValidateInSet(lPurpose, [0, 1, 2]) then ...
If you are on a recent Delphi version, you can use TArray<Integer>.
function ValidateInSet(AIntValue: integer; const AIntSet: TArray<Integer>): Boolean;
var
N: Integer;
begin
{ option1 : if AIntSet is always sorted }
result := TArray.BinarySearch(AIntSet, AIntValue, N);
{ option 2: works for any array }
result := false;
for N in AIntSet do begin
if AIntValue = N then begin
result := true;
Break;
end;
end;
if not Result then begin
// ...
end;
end;
Calling is merely the same as with a set (except for ranges):
if ValidateInSet(lFuncID, [FUNCT_1,FUNCT_2,FUNCT_3]) then begin
end;
The direct answer would be TBits class
http://docwiki.embarcadero.com/Libraries/Seattle/en/System.Classes.TBits.Bits
Note: This can only be used starting with Delphi XE4 though - http://qc.embarcadero.com/wc/qcmain.aspx?d=108829
However for your "Set of integers" it in most inflated case would take 2^31 / 8 bytes of memory (because negative values of integer would not be even considered), and that would be a lot...
So I hope you would never really want to have a set of the whole integer. Or you should invest into Sparse Arrays instead.
function ValidateInSet(const AIntValue: integer; const AIntSet: TBits): Boolean;
begin
Result := (AIntValue >= 0) and (AIntValue < AIntSet.Size);
if Result then
Result := AIntSet.Bits[AIntValue];
if not Result then ...
v-a-l-i-d-a-t-e
end;
or rather
function ValidateInSet(const AIntValue: integer; const AIntSet: TBits): Boolean;
begin
Result := false;
if AIntValue < 0 then exit; // Validation criterion #1
if AIntValue >= AIntSet.Size then exit; // Validation criterion #2
if not AIntSet.Bits[AIntValue] then exit; // Validation criterion #3
if .... then exit; // Validation criterion #4
if .... then exit; // Validation criterion #5
if .... then exit; // Validation criterion #6
Result := true;
end;
or perhaps
TSetTestCriterion = TFunc<Integer, Boolean>;
TSetTestCriteria = TArray<TFunc<Integer, Boolean>>;
function ValidateInSet(const AIntValue: integer;
const AIntSet: TBits; const Tests: TSetTestCriteria = nil): Boolean;
var ExtraTest: TSetTestCriterion;
begin
Result := false;
if AIntValue < 0 then exit; // Validation criterion #1
if AIntValue >= AIntSet.Size then exit; // Validation criterion #2
if not AIntSet.Bits[AIntValue] then exit; // Validation criterion #3
if Tests <> nil then // Validation criteria #4, #5, #6, ...
for ExtraTest in Tests do
if not ExtraTest(AIntValue) then exit;
Result := true;
end;
http://docwiki.embarcadero.com/Libraries/Seattle/en/System.SysUtils.TFunc
Now - just for demo, in real app you would create those set and array once and cache for long (forever, or at least unless the configuration change would demand rebuilding them).
Type FuncIDs = ( FUNCT_3 = 3, FUNCT_2 = 127, FUNCT_1 = 224);
var MysticGlobalFlag: Boolean;
function ValidateFuncID( const lFuncID: FuncIDs): Boolean;
var map: TBits;
begin
map := TBits.Create;
try
map.Size := High(lFuncID) + 1;
map.Bits[ Ord(Func_1) ] := True;
map.Bits[ Ord(Func_2) ] := True;
map.Bits[ Ord(Func_3) ] := True;
Result := ValidateInSet( Ord(lFuncID), map,
TSetTestCriteria.Create(
function( lFuncID: integer) : Boolean
begin
Result := MysticGlobalFlag or (lFuncID <> Ord(FuncIDs.FUNC_2))
end
,
function( lFuncID: integer) : Boolean
begin
Result := (lFuncID <> Ord(FuncIDs.FUNC_3)) or (DayOfTheWeek(Now()) = 4)
end
)
);
finally
map.Destroy;
end;
if not Result then // from the original question code
... // seems like a placeholder for error handling or object creation and registration
end;
All, I know it's years since people answered this, but here is a new solution using Delphi generics: -
interface
uses
System.Generics.Defaults;
type
TUtilityArray<T> = class
public
class function Contains(const x : T; const an_array : array of T) : boolean;
end;
implementation
class function TUtilityArray<T>.Contains(const x: T; const an_array: array of T): boolean;
var
y : T;
l_comparer : IEqualityComparer<T>;
begin
Result := false;
l_comparer := TEqualityComparer<T>.Default;
for y in an_array do
begin
if l_comparer.Equals(x, y) then
begin
Result := true;
break;
end;
end;
end;
end.
To use include the class, then write if(TUtilityArray<integer>.Contains(some integer value, [value1, value2 etc.])) then .... An added benefit of this method is that it works for other primitives as well.
I have this:
type
PList = ^TSome;
TSome = record
next :PList;
...
var
tmp:PList;
...
begin
tmp := list;
while tmp^.next <> nil do
tmp := tmp^.next;
end
Is there any way to get an item on the second index?
Like array[2], but since this is not an array it's not possible.
This is how it's usually done:
var
tmp: PList;
index: Integer;
begin
index := 0;
tmp := list;
repeat
tmp := tmp^.next;
Inc(Index);
until (tmp = nil) or (index = 2);
end;
But this would be sloooow! much slower than arrays.
TSome = record
next :PList;
...
function GetNext(skip: cardinal): TSome;
property ArrayLike[index: cardinal]: TSome read GetNext; default;
end;
....
{$T+}
function TSome.GetNext(skip: cardinal): TSome;
type PSome = PList;
var candidate: PSome;
begin
candidate := #Self;
while skip > 0 do
candidate := candidate.Next;
if candidate = nil then raise EBoundsError.Create('out of index');
Dec(skip);
end;
Result := candidate^;
end;
...
var x: TSome;
x[0] = x.ArrayLike[0] = x;
WARNING: since you work with records, not classes - you get a COPY of record not the record itself;
Like in var x,y: TSome; y := x; - you do a NEW DATA COPY, not a second pointer to the same data.
This as well as the very issue of list structure would make this kind of access slower, much slower than array.
And
var x,y,z: TSome; i: integer;
x.SomeValue := 1;
y.SomeValue := 2;
x.Next := #y;
i := x[1].SomeValue;
// i == 2 ( making copy of y, then taking a SomeValue from it)
x[1].SomeValue := 10;
z := x[1];
z.SomeValue := 20;
i := x[1].SomeValue;
// still i == 2 - we were NOT changing the value in y itself, we were making DATA COPIES of y and changing COPIES
I would like to set properties as shown in Windows Explorer File Properties->Summary Tab (Authors, Title, Subject, etc). (In Windows 7 is the Details tab)
I already know how to GET them using StgCreateStorageEx,
The target file extensions are xls, xlsx, csv, txt and jpg files.
OS: Windows 2003/2008/XP/Windows 7.
Notice that this code will go a web service application and the server does not have Excel installed.
Note:It seems to be that there is no information/sample code about how to SET them.
To write summary information use IPropertyStorage:WriteMultiple method. See MSDN sample WriteRead. Not delphi but is easy to convert.
function GetFileSummaryInfo(const FileName: WideString): String;
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}';
IID_IPropertySetStorage : TGUID = '{0000013A-0000-0000-C000-000000000046}';
const
STGFMT_FILE = 3; //Indicates that the file must not be a compound file.
//This element is only valid when using the StgCreateStorageEx
//or StgOpenStorageEx functions to access the NTFS file system
//implementation of the IPropertySetStorage interface.
//Therefore, these functions return an error if the riid
//parameter does not specify the IPropertySetStorage interface,
//or if the specified file is not located on an NTFS file system
//volume.
STGFMT_ANY = 4; //Indicates that the system will determine the file type and
//use the appropriate structured storage or property set
//implementation.
//This value cannot be used with the StgCreateStorageEx function.
// Summary Information
PID_TITLE = 2;
PID_SUBJECT = 3;
PID_AUTHOR = 4;
PID_KEYWORDS = 5;
PID_COMMENTS = 6;
PID_TEMPLATE = 7;
PID_LASTAUTHOR = 8;
PID_REVNUMBER = 9;
PID_EDITTIME = 10;
PID_LASTPRINTED = 11;
PID_CREATE_DTM = 12;
PID_LASTSAVE_DTM = 13;
PID_PAGECOUNT = 14;
PID_WORDCOUNT = 15;
PID_CHARCOUNT = 16;
PID_THUMBNAIL = 17;
PID_APPNAME = 18;
PID_SECURITY = 19;
// Document Summary Information
PID_CATEGORY = 2;
PID_PRESFORMAT = 3;
PID_BYTECOUNT = 4;
PID_LINECOUNT = 5;
PID_PARCOUNT = 6;
PID_SLIDECOUNT = 7;
PID_NOTECOUNT = 8;
PID_HIDDENCOUNT = 9;
PID_MMCLIPCOUNT = 10;
PID_SCALE = 11;
PID_HEADINGPAIR = 12;
PID_DOCPARTS = 13;
PID_MANAGER = 14;
PID_COMPANY = 15;
PID_LINKSDIRTY = 16;
PID_CHARCOUNT2 = 17;
var
I: Integer;
PropSetStg: IPropertySetStorage;
PropSpec: array of TPropSpec;
PropStg: IPropertyStorage;
PropVariant: array of TPropVariant;
Rslt: HResult;
S: String;
Stg: IStorage;
PropEnum: IEnumSTATPROPSTG;
HR : HResult;
PropStat: STATPROPSTG;
k : integer;
function PropertyPIDToCaption(const ePID: Cardinal): string;
begin
case ePID of
PID_TITLE:
Result := 'Title';
PID_SUBJECT:
Result := 'Subject';
PID_AUTHOR:
Result := 'Author';
PID_KEYWORDS:
Result := 'Keywords';
PID_COMMENTS:
Result := 'Comments';
PID_TEMPLATE:
Result := 'Template';
PID_LASTAUTHOR:
Result := 'Last Saved By';
PID_REVNUMBER:
Result := 'Revision Number';
PID_EDITTIME:
Result := 'Total Editing Time';
PID_LASTPRINTED:
Result := 'Last Printed';
PID_CREATE_DTM:
Result := 'Create Time/Date';
PID_LASTSAVE_DTM:
Result := 'Last Saved Time/Date';
PID_PAGECOUNT:
Result := 'Number of Pages';
PID_WORDCOUNT:
Result := 'Number of Words';
PID_CHARCOUNT:
Result := 'Number of Characters';
PID_THUMBNAIL:
Result := 'Thumbnail';
PID_APPNAME:
Result := 'Creating Application';
PID_SECURITY:
Result := 'Security';
else
Result := '$' + IntToHex(ePID, 8);
end
end;
begin
Result := '';
try
OleCheck(StgOpenStorageEx(PWideChar(FileName),
STGM_READ or STGM_SHARE_DENY_WRITE,
STGFMT_FILE,
0, nil, nil, #IID_IPropertySetStorage, stg));
PropSetStg := Stg as IPropertySetStorage;
OleCheck(PropSetStg.Open(FmtID_SummaryInformation,
STGM_READ or STGM_SHARE_EXCLUSIVE, PropStg));
OleCheck(PropStg.Enum(PropEnum));
I := 0;
hr := PropEnum.Next(1, PropStat, nil);
while hr = S_OK do
begin
inc(I);
SetLength(PropSpec,I);
PropSpec[i-1].ulKind := PRSPEC_PROPID;
PropSpec[i-1].propid := PropStat.propid;
hr := PropEnum.Next(1, PropStat, nil);
end;
SetLength(PropVariant,i);
Rslt := PropStg.ReadMultiple(i, #PropSpec[0], #PropVariant[0]);
if Rslt = S_FALSE then Exit;
for k := 0 to i -1 do
begin
S := '';
if PropVariant[k].vt = VT_LPSTR then
if Assigned(PropVariant[k].pszVal) then
S := PropVariant[k].pszVal;
S := Format(PropertyPIDToCaption(PropSpec[k].Propid)+ ' %s',[s]);
if S <> '' then Result := Result + S + #13;
end;
finally
end;
for more http://www.delphi-central.com/tutorials/File_Summary_Info.aspx. Also this : http://www.swissdelphicenter.ch/torry/showcode.php?id=1614 show you how to manipulate [IPropertyStorage]
Partial answer: The set property Delphi code can be found here.
or if you have the latest JCL library - use TJclFilePropertySet at jclNtfs.pas
Warning: Notice that this code works for xls files but it does not seem to work for txt/cvs and jpg files in Windows 7 Pro/Enterprise or 2008 (64 bits).
It seems that M$ has changed the way properties work in these OS: "You can't add or change the file properties of some types of files. For example, you can't add any properties to TXT or RTF file". Sadly for me, going back to XP mode is not an option.
How should I convert this while loop into a for loop?
const
TheRightWord = 'hello';
MaximumTries = 3;
var
NTries : integer;
AWord : string;
begin
NTries := 1;
AWord := ' ';
while (AWord <> TheRightWord) and (NTries <= MaximumTries) do
I thought this would be the answer:
for (AWord <> TheRightWord) and (NTries <= MaximumTries) do
Must I just put a for inplace of the while? Or is it for i := 1 to 3 do?
for numtries := 1 to maxnumtries do begin
if AWord = TheRightWord then
break;
...
How to determine success or failure when exiting the loop (or, for that matter, how to iterate through different values of "AWord") is an exercise for the student :-)