I have a weird problem and am not able to solve it. I have a lot of images and I want to create thumbnails of them. I point my application to a directory and it creates thumbnails (64 * 64) of each of them. The trouble is that the previous bitmap persists in a new bitmap which I don't understand. Here is the code of the procedure causing the error:
procedure TMain.import_image_resize (source, destination: string);
var
Input_Bitmap: TBitmap;
begin
Input_Bitmap := TBitmap.CreateFromFile (source);
Input_Bitmap.ReSize (iSize, iSize); // iSize = 64
Input_Bitmap.SaveToFile (destination);
Input_Bitmap.Free;
end; // import_image_resize //
It is called as an argument by import_process_images, below.
procedure TMain.import_process_images (sub: string; process: TConverter);
var
i, n: int32;
dir_input: string;
dir_new: string;
temp: string;
file_path: string;
file_name: string;
file_ext: string;
new_file: string;
source_dirs: TStringDynArray;
destination_dirs: TStringDynArray;
files: TStringDynArray;
begin
// get list of directories from selected directory
source_dirs := TDirectory.GetDirectories (Dir_Selected);
SetLength (destination_dirs, Length (source_dirs));
// create these directories in the destination directory
n := 0;
for dir_input in source_dirs do
begin
i := LastDelimiter ('\', dir_input) - 1;
temp := dir_input.Substring (i + 1);
dir_new := TPath.Combine (Project_Root, Project_Selected);
dir_new := TPath.Combine (dir_new, sub);
dir_new := TPath.Combine (dir_new, temp);
TDirectory.CreateDirectory (dir_new);
destination_dirs [n] := dir_new;
n := n + 1;
end; // for
// for each directory in the selected directory
// - get each image
// - convert it
// - and copy it to the destination directory
n := 0;
Stop_Conversion := False;
for dir_new in source_dirs do
begin
files := TDirectory.GetFiles (dir_new);
for file_path in files do
begin
file_name := TPath.GetFileName (file_path);
file_ext := LowerCase (TPath.GetExtension (file_name));
if (file_ext = '.bmp') or (file_ext = '.jpg') or
(file_ext = '.png') or (file_ext = '.jpeg') then
begin
new_file := TPath.Combine (destination_dirs [n], file_name);
process (file_path, new_file);
Label_Progress.Text := new_file;
Application.ProcessMessages;
if Stop_Conversion then Exit;
end; // if
end; // for
n := n + 1;
end; // for
Label_Progress.Text := 'Ready';
end; (*** import_process_images ***)
Both functions are called from the event handler as follows:
procedure TMain.Button_SelectClick (Sender: TObject);
var
tree_item: TTreeViewItem;
begin
iSize := StrToInt (edit_XSize.Text);
tree_item := Directory_Tree.Selected;
Dir_Selected := tree_item.Text;
import_process_images ('rs', import_image_resize);
end; // Button_SelectClick //
One would expect that the new Input_Bitmap should be only filled with the bitmap loaded from file. However, the resized bitmap shows all images of previous bitmaps (loaded by previous calls from import_image_resize) overlayed with the current one. I don't understand this behavior, anybody got an explanation and, preferrably, a workaround?
Thanks you for your time.
Edit 1
I'll show an example of two photo's successively converted: the first is a landscape photo, the second in portrait. You see the first photo at the edges of the second photo. The second photo just overlayed the first one (the third overlayes the combination of the first two, etc.)
Edit 2
There was a suggestion that some code not shown might have impact on the procedure import_image_resize. As for completeness I added this code but I cannot see my self what exactly I am doing wrong.
Related
Scenario
I'm trying to duplicate the standard way to fill a Treeview with directories/folders from a folder structure, starting at the root, but using IdFTP to get the structure from a remote server instead of my local hard drive. I'd like the result to look similar to clients like Filezilla.
I used this reasonably standard code from the Swiss Delphi Centre (which works to display my hard drive's structure) and then modified it to use IdFTP.ChangeDir(Directory) and IdFTP.List instead of FindFirst() and FindNext().
Problem
I seem to have got myself in a muddle as it is not correctly 'unwinding' the recursion so that once it traverses down the /cpanel/cashes/config directories on the remote server it doesn't return and traverse all the other directories hanging off the root but exits the procedure without displaying anything else. Also it doesn't seem to show all the top level folders but this could be simply due to the order that IdFTP.List returns them in
Can anyone tell me what I have done wrong here?
If you can also tell me how I should get the root (/) shown as well that would be very helpful
(I've commented out displaying non directories as I only want folders at this stage)
What I expected to see Copied from Filezilla
What I did see Using a Ttreeview in Delphi
My Code
procedure TForm2.Button1Click(Sender: TObject);
var StartingDir : string;
begin
TreeView1.Items.BeginUpdate;
try
StartingDir := '/';
Screen.Cursor := crHourGlass;
TreeView1.Items.Clear;
FTPconnect; //procedure to connect to remote server
GetDirectories(TreeView1, StartingDir, nil, True);
FTPDisconnect; //procedure to disconnect from remote server
finally
TreeView1.Items.EndUpdate;
Screen.Cursor := crDefault;
end;
end;
procedure TForm2.GetDirectories(Tree: TTreeView; Directory: string; Item: TTreeNode; IncludeFiles: Boolean);
var
ItemTemp: TTreeNode;
DirItemType : TIdDirItemType ;
Filename , NewStartingDirectory: string;
i : Integer;
begin
Tree.Items.BeginUpdate;
IdFTP.ChangeDir(Directory);
IdFTP.List; //get directory of remote folder
i:=0;
repeat
DirItemType := IdFTP.DirectoryListing[I].ItemType;
Filename := IdFTP.DirectoryListing[I].FileName;
If (DirItemType = ditDirectory) and (Filename <> '.') and (Filename <> '..')then
begin
if DirItemType = ditDirectory then
Item := Tree.Items.AddChild(Item, Filename);
ItemTemp := Item.Parent;
if Directory = '/' then
NewStartingDirectory := Directory + Filename
else
NewStartingDirectory := Directory + '/' +Filename;
GetDirectories(Tree, NewStartingDirectory, Item, IncludeFiles);
Item := ItemTemp;
end
else
if IncludeFiles then
begin //this bit commented out as we only want to see directories
// if (Filename <> '.') and (Filename <> '..') then
// Tree.Items.AddChild(Item, Filename);
end;
inc(i);
until i = IdFTP.DirectoryListing.Count;
Tree.Items.EndUpdate;
end;
Swiss Delhpi Centre's code (for comparison)
procedure TForm1.Button1Click(Sender: TObject);
var
Node: TTreeNode;
Path: string;
Dir: string;
begin
Dir := 'c:\temp';
Screen.Cursor := crHourGlass;
TreeView1.Items.BeginUpdate;
try
TreeView1.Items.Clear;
GetDirectories(TreeView1, Dir, nil, True);
finally
Screen.Cursor := crDefault;
TreeView1.Items.EndUpdate;
end;
end;
procedure TForm1.GetDirectories(Tree: TTreeView; Directory: string; Item: TTreeNode; IncludeFiles: Boolean);
var
SearchRec: TSearchRec;
ItemTemp: TTreeNode;
begin
Tree.Items.BeginUpdate;
if Directory[Length(Directory)] <> '\' then Directory := Directory + '\';
if FindFirst(Directory + '*.*', faDirectory, SearchRec) = 0 then
begin
repeat
if (SearchRec.Attr and faDirectory = faDirectory) and (SearchRec.Name[1] <> '.') then
begin
if (SearchRec.Attr and faDirectory > 0) then
Item := Tree.Items.AddChild(Item, SearchRec.Name);
ItemTemp := Item.Parent;
GetDirectories(Tree, Directory + SearchRec.Name, Item, IncludeFiles);
Item := ItemTemp;
end
else if IncludeFiles then
if SearchRec.Name[1] <> '.' then
Tree.Items.AddChild(Item, SearchRec.Name);
until FindNext(SearchRec) <> 0;
FindClose(SearchRec);
end;
Tree.Items.EndUpdate;
end;
I've looked on SO here - too complicated and wrong language and here - similar to the Swiss Delphi Centre and here - wrong language and not sure what its doing.
if it's better to use a TlistView, can you please show me the equivalent code to use that instead?
Untested:
I made the TIdFTP variable a parameter, since TTreeView was also one and it should be done consistently, not archaic.
Using for loops instead of repeat until.
Eliminating IncludeFiles when it wasn't used anyway.
Eliminating weird logic to always get the new TreeNode's parent.
Not locking the TreeView anymore - do this once before calling this method and unlock it after calling - otherwise you do that dozens of times in vain.
Basic logic is as I wrote in the comments:
Store all folder strings into your own list and avoid recursion at this point.
Fix the path to be concatenated once, not with every iteration of a loop.
Go through that list to do the recursion - at this point the state of FTP is irrelevant and you won't mess up listings at different levels.
Of course, release the created instance of the StringList.
procedure TForm2.GetFolders
( Ftp: TIdFTP // The source, from which we read the content
; Tree: TTreeView // The destination, which we want to fill
; ParentNode: TTreeNode // Node under which all new child nodes should be created
; Path: String // Starting directory
);
var
NewNode: TTreeNode; // New child in the tree
Filename: String; // Check against unwanted folder entries
i: Integer; // Looping over both lists
sl: TStringList; // Collect folders only
begin
FTP.ChangeDir( Path );
FTP.List; // Entire remote listing
sl:= TStringList.Create; // Collect all entries we're interested in
try
for i:= 0 to FTP.DirectoryListing.Count- 1 do begin // For each entry
Filename:= FTP.DirectoryListing[i].FileName;
if (FTP.DirectoryListing[i].ItemType= ditDirectory) // Only folders
and (Filename<> '.')
and (Filename<> '..') then begin
sl.Add( Filename ); // Only the name, not the full path
end;
end;
// Do this only once
if Path<> '/' then Path:= '/'+ Path+ '/';
for i:= 0 to sl.Count- 1 do begin // All collected folders
NewNode:= Tree.Items.AddChild( ParentNode, sl[i] ); // Populate tree
GetFolders( Ftp, Tree, NewNode, Path+ sl[i] ); // Recursion of folder name + current path
end;
finally
sl.Free;
end;
end;
Untested, but should compile.
I am trying to drag and drop from VirtualTreeView to create a file in shell (drag and drop from VirtualTreeView to a folder in File Explorer or desktop folder).
I only found example of doing the opposite (shell to VirtualTreeView), but I cannot find any example for doing that. Help?
Doing any drag-drop operations in Windows involves creating an IDataObject, and giving that object to Windows.
The Virtual Treeview handles a lot of that grunt-work for you, creating an object that implements IDataObject for you. The tree then raises events when you need to help populate it.
When passing "file-like" things through a copy-paste or a drag-drop, you are require to add two clipboard formats to the IDataObject:
CF_FILEDESCRIPTOR, and
CF_FILECONTENTS
In addition to support for formats that the virtualtree itself will add, you can choose to indicate support for more clipboard format.
OnGetUserClipboardFormats Event
This is the event where you are given a chance to add additional clipboard formats to the IDataObject that the tree will be creating:
procedure TForm1.lvAttachmentsGetUserClipboardFormats(Sender: TBaseVirtualTree;
var Formats: TFormatEtcArray);
var
i: Integer;
begin
//Add formats for CF_FILEDESCRIPTOR and CF_FILECONTENTS
i := Length(Formats);
SetLength(Formats, i + 1);
Formats[i].cfFormat := CF_FILEDESCRIPTOR;
Formats[i].ptd := nil;
Formats[i].dwAspect := DVASPECT_CONTENT;
Formats[i].lindex := -1;
Formats[i].tymed := TYMED_HGLOBAL;
i := Length(Formats);
SetLength(Formats, i + 1);
Formats[i].cfFormat := CF_FILECONTENTS;
Formats[i].ptd := nil;
Formats[i].dwAspect := DVASPECT_CONTENT;
Formats[i].lindex := 0;
Formats[i].tymed := TYMED_ISTREAM;
end;
The tree will then given the IDataObject to the shell as part of the drag-drop operation.
Later, an application that the user dropped items onto will enumerate all formats in the IDataObject, e.g.:
CF_HTML ("HTML Format")
CFSTR_FILEDESCRIPTOR ("FileGroupDescriptorW")
CFSTR_FILECONTENTS ("FileContents")
CF_ENHMETAFILE
And it will see that the IDataObject contains FileDescriptor and FileContents.
The receiving application will then ask the IDataObject to actually cough up data. (This "delayed-rendering" is a good thing, it means your source application doesn't actually have to read any content unless it actually gets requested).
OnRenderOleData Event
This is the event where the virtual tree realizes its IDataObject has been asked to render something, and it needs you to finally render that actual content.
The general idea with these two clipboard formats is:
CF_FILEDESCRIPTOR lets you return a record that describes the file-like thing (e.g. filename, file size, created date, last modified date, last accessed date)
CF_FILECONTENTS lets you return an IStream that contains the actual file contents
procedure TForm1.lvAttachmentsRenderOLEData(Sender: TBaseVirtualTree; const FormatEtcIn: tagFORMATETC;
out Medium: tagSTGMEDIUM; ForClipboard: Boolean; var Result: HRESULT);
var
global: HGLOBAL;
stm: IStream;
begin
if FormatEtcIn.cfFormat = CF_FILEDESCRIPTOR then
begin
global := GetAttachmentFileDescriptorsFromListView(lvAttachments, ForClipboard);
if global = 0 then
Exit;
ZeroMemory(#Medium, SizeOf(Medium));
Medium.tymed := TYMED_HGLOBAL;
Medium.hGlobal := global;
Result := S_OK;
end
else if FormatEtcIn.cfFormat = CF_FILECONTENTS then
begin
ZeroMemory(#Medium, SizeOf(Medium));
Medium.tymed := TYMED_ISTREAM;
Result := GetAttachmentStreamFromListView(lvAttachments, ForClipboard, FormatEtcIn.lindex, stm);
if Failed(Result) then
Exit;
Medium.stm := Pointer(stm);
IUnknown(Medium.stm)._AddRef;
Result := S_OK;
end;
end;
The first helper function creates an array of FILE_DESCRIPTOR objects, and copies them to a HGLOBAL allocated memory:
function GetAttachmentFileDescriptorsFromListView(Source: TVirtualStringTree; ForClipboard: Boolean): HGLOBAL;
var
i: Integer;
nCount: Integer;
nodes: TNodeArray;
descriptors: TFileDescriptorDynArray;
data: TAttachment;
begin
Result := 0;
if ForClipboard then
nodes := Source.GetSortedCutCopySet(False)
else
nodes := Source.GetSortedSelection(False);
if Length(nodes) = 0 then
Exit;
nCount := 0;
for i := 0 to Length(nodes) - 1 do
begin
//Get the file thing from this node
data := GetNodeDataFromNode(nodes[i]);
if not Assigned(data) then
Continue;
//Increase the size of our descriptors array by one
Inc(nCount);
SetLength(Descriptors, nCount);
//Fill in the next descriptor
descriptors[nCount-1] := data.ToWindowsFileDescriptor;
end;
Result := FileDescriptorsToHGLOBAL(descriptors);
end;
The second helper copies your file-like thing's binary contents to an IStream:
function GetAttachmentStreamFromListView(Source: TVirtualStringTree; ForClipboard: Boolean; lindex: Integer; var stm: IStream): HResult;
var
nodes: TNodeArray;
data: TAttachment;
begin
Result := E_FAIL;
if ForClipboard then
nodes := Source.GetSortedCutCopySet(False)
else
nodes := Source.GetSortedSelection(False);
if Length(nodes) = 0 then
Exit;
if (lIndex < Low(Nodes)) or (lIndex > High(Nodes)) then
begin
Result := DV_E_LINDEX;
Exit;
end;
//Get the file thing from this node
data := GetNodeDataFromNode(nodes[i]);
if not Assigned(data) then
Continue;
//Fetch the content into a IStream wrapped memory stream
stm := data.GetStream(nil);
Result := S_OK;
end;
Your attachment object, whatever it is has to know:
how to represent itself as a TFileDescriptor
how to return the contents as an IStream
I have a function that returns a pointer to the memory where the image is stored as Bitmap32.Bits:
function FileToMemoryAsBitmap32Bits: pointer;
var
bmp32: TBitmap32;
wic: TWICImage;
begin
Result := nil;
bmp32 := TBitmap32.Create();
try
wic := TWICImage.Create;
try
wic.LoadFromFile('256x256.jpg');
bmp32.Assign(wic);
GetMem(Result, 256*256*4);
Move(bmp32.Bits^, Result^, 256*256*4);
finally
FreeAndNil(wic);
end;
finally
FreeAndNil(bmp32);
end;
end;
Somewhere further in the code I need to create a new Bitmap32 from this Bits in memory. How to do it correctly?
I tried to do the following:
var
p: Pointer;
NewBitmap32: TBitmap32;
// ...
p := FileToMemoryAsBitmap32Bits;
// ... do something with Bits in memory
NewBitmap32 := TBitmap32.Create(256, 256);
NewBitmap32.Bits := p;
but I get an error:
E2129 Cannot assign to a read-only property
ADDED for #RudyVelthuis:
procedure RenderMemoryToBitmap32(Output: TBitmap32; p: pointer; const x, y: integer);
var
d, i,j: integer;
OutputRowRGBA, RowRGBA: PColor32Array;
begin
RowRGBA := PColor32Array(p);
for j := 0 to 255 do begin
OutputRowRGBA := Output.Scanline[y+j]; // row in large bitmap
for i := 0 to 255 do begin
d := i + x; // offset
TColor32Entry(OutputRowRGBA[d]).B := TColor32Entry(RowRGBA[i]).B;
TColor32Entry(OutputRowRGBA[d]).G := TColor32Entry(RowRGBA[i]).G;
TColor32Entry(OutputRowRGBA[d]).R := TColor32Entry(RowRGBA[i]).R;
TColor32Entry(OutputRowRGBA[d]).A := TColor32Entry(RowRGBA[i]).A;
end;
inc(longword(RowRGBA), 256*4); // next row
end;
end;
You cannot change address of data using this way, memory is already allocated and bitmap32 doesn't allow to replace its address.
But you can move stored data in this location like (but in reverse direction) you already did for storing, but in reverse direction:
Move(p^, NewBitmap32.Bits^, 256*256*4);
While I don't want to question your needs in regards of storing the bitmap as bitstream,
it should also be possible to clone the original bitmap like this:
NewBitmap32 := TBitmap32.Create;
NewBitmap32.Assign(OriginalBitmap);
This will clone the bitmap perfectly in terms of preserving the width and height of the bitmap (which otherwise gets lost). The overhead of having a TBitmap32 in memory instead of a pure bitstream is minimal.
I have a procedure that saves a chess board into a text file. I am trying to read the board back in to the program once saved. When I call this procedure I get this error.
Code which I have for loading in board.
Procedure LoadBoard(Var Board : Tboard);
var
fptr:text;
i,j,x:integer;
line:string;
load:char;
begin
Write('Do you want a load a game? (Enter Y for yes)');
Readln(load);
If (Ord(load) >= 97) and (Ord(load) <= 122)
Then load := Chr(Ord(load) - 32);
if load='Y' then
begin
assignfile(fptr,'SBoard.txt');
reset(fptr);
i:=1;
repeat
readln(fptr,line);
j:=1;
x:=1;
repeat
begin
if (line[x]<>',') and (line[x+1]<>',')
then
begin
Board[i,j][1]:=line[x];
Board[i,j][2]:=line[x+1];
end;
if line[x]=','
then
j:=j+1;
x:=x+1;
end;
until j=9;
i:=i+1;
until i=9;
close(fptr);
end;
end;
You get the Access Violation exception because the string members in your Board array are empty (length is zero) and therefore have no accessible character positions.
To fix your present code, you should use SetLength() on each string member before you assign content to the character positions. You have not shown what the strings contain, so only you know what the set length should be.
On the other hand, in previous answer to your questions you have been adviced several other methods to save your chess board. You should review those and possibly choose one of them. It would also be polite to respond to those answers and maybe tell why you did not select them. Maybe we were not able to explain the benefits.
You are over complicating things by using a Text file and saving your board line by line.
I think you should use a TStringList for saving and loading :
Const
BoardDimension = 8;
BoardFileName = 'SBoard.txt';
Type
TBoard = Array [1 .. BoardDimension, 1 .. BoardDimension] Of String;
procedure SaveBoard(Board: TBoard);
var
i, j: Integer;
Line, BoardFile: TStringList;
begin
BoardFile := TStringList.Create;
Line := TStringList.Create;
for i := 1 to BoardDimension do
begin
Line.Clear;
for j := 1 to BoardDimension do
Line.Add(Board[i, j]);
BoardFile.Add(Line.CommaText);
end;
Line.Free;
BoardFile.SaveToFile(BoardFileName);
BoardFile.Free;
end;
procedure LoadBoard(Board: TBoard);
var
i, j: Integer;
Line, BoardFile: TStringList;
begin
if not FileExists(BoardFileName) then
exit; // Show error message
BoardFile := TStringList.Create;
BoardFile.LoadFromFile(BoardFileName);
Line := TStringList.Create;
for i := 1 to BoardDimension do
begin
Line.CommaText := BoardFile[i];
for j := 1 to BoardDimension do
Board[i, j] := Line[j];
end;
Line.Free;
BoardFile.Free;
end;
And if you want to test the load an save proceudre you could do it like this :
procedure Test;
var
Board: TBoard;
BoardA: TBoard;
i, j: Integer;
begin
randomize;
for i := 1 to BoardDimension do
for j := 1 to BoardDimension do
Board[i, j] := Random(500).ToString;
SaveBoard(Board); //Save Board
LoadBoard(BoardA); //Load the file into a NEW board
for i := 1 to BoardDimension do //Comapre the two boards
for j := 1 to BoardDimension do
if Board[i,j] <> BoardA[i,j] then
raise Exception.Create('Wrong file format');
end;
I'm having problem with SHGetFileInfoW function I'm using.
It's a quite slow and first read on startup (Initialization) consumes 100ms.
In MSDN stays that it should be read from thread, not the main thread because it can stuck process.
I want to use some other function, if there is any, in order to read Icons.
Another thing. How is possible to read big icons, currently I can read up to 32x32 (SHGFI_LARGEICON)
Thanks!
Actual code:
procedure TForm1.LoadIcons;
var
Info: TShFileInfo;
Icon: TIcon;
Flags: UINT;
FileName: PAnsiChar;
begin
FileName := '.txt';
Flags := SHGFI_USEFILEATTRIBUTES or SHGFI_ICON or SHGFI_LARGEICON;
Icon := TIcon.Create;
try
SHGetFileInfo(FileName, FILE_ATTRIBUTE_NORMAL, Info,
SizeOf(Info), Flags);
Icon.Handle := Info.hIcon;
Image1.Picture.Assign(Icon);
Image1.Refresh;
finally
DestroyIcon(Info.hIcon);
Icon.Free;
end;
end;
You could find the DefaultIcon for a given file extension from the Registry and use ExtractIconEx. Here is an example
I don't know if it's faster than SHGetFileInfo
EDIT:
I have extracted (from the sample) the part which gets the ICON from the Extension.
It actually works very fast. could be optimized more.
(I modified the code a bit):
// find the icon for a certain file extension in the registry
function TForm1.RegistryIconExtraction(Extension : string): integer;
var
RegKey : TRegistry;
IconPos : integer;
AssocAppInfo : string;
ExtractPath, FileName : string;
IconHandle, PLargeIcon, PSmallIcon : HICON;
AnIcon : TIcon;
begin
Result := 0; // default icon
if Extension[1] <> '.' then Extension := '.' + Extension;
RegKey := TRegistry.Create(KEY_READ);
try
// KEY_QUERY_VALUE grants permission to query subkey data.
RegKey.RootKey := HKEY_CLASSES_ROOT; // set folder for icon info lookup
if RegKey.OpenKeyReadOnly(Extension) then // extension key exists?
try
AssocAppInfo := RegKey.ReadString(''); // read app key
RegKey.CloseKey;
except
Exit;
end;
if ((AssocAppInfo <> '') and // app key and icon info exists?
(RegKey.OpenKeyReadOnly(AssocAppInfo + '\DefaultIcon'))) then
try
ExtractPath := RegKey.ReadString(''); // icon path
RegKey.CloseKey;
except
Exit;
end;
finally
RegKey.Free;
end;
// IconPos after comma in key ie: C:\Program Files\Winzip\Winzip.Exe,0
// did we get a key for icon, does IconPos exist after comma seperator?
If ((ExtractPath <> '') and (pos(',', ExtractPath) <> 0)) then
begin
// Filename in registry key is before the comma seperator
FileName := Copy(ExtractPath, 1, Pos(',', ExtractPath) - 1);
// extract the icon Index from after the comma in the ExtractPath string
try
IconPos := StrToInt(copy(ExtractPath, Pos(',', ExtractPath) + 1,
Length(ExtractPath) - Pos(',', ExtractPath) + 1));
except
Exit;
end;
IconHandle := ExtractIconEx(PChar(FileName), IconPos, PLargeIcon, PSmallIcon, 1);
If (PLargeIcon <> 0) then
begin
AnIcon := TIcon.Create;
AnIcon.Handle := PLargeIcon;
Image1.Picture.Assign(AnIcon);
Image1.Refresh;
AnIcon.Free;
end;
DestroyIcon(PLargeIcon);
DestroyIcon(PSmallIcon);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
t1, t2: DWORD;
begin
t1 := GetTickCount;
RegistryIconExtraction('.txt');
t2 := GetTickCount;
Memo1.Lines.Add(IntToStr(t2-t1));
end;
EDIT2: The sample code is now Vista/Win7 UAC compliant.