I am building a TFileSaveDialog descendent component. The descendent has a PushButton who’s event is handled by:
function TFileDialogEvent.OnButtonClicked(const pfdc: IFileDialogCustomize;
dwIDCtl: DWORD): HResult; stdcall;
var
iImageEnIO: TImageEnIO;
iFilename: string;
iName: PChar;
pfd: IFileDialog;
begin
if dwIDCtl = dwVisualGroup8ID then
begin
iImageEnIO := TImageEnIO.Create(nil);
try
FileDialog.QueryInterface(
StringToGUID('{8016B7B3-3D49-4504-A0AA-2A37494E606F}'),
pfd);
// How to get correct valid handle to IFileDialog?
pfd.GetFileName(iName);
iFilename := string(iName);
if FileExists(iFilename) then
begin
The component also displays image information in various control labels correctly. The component sucessfully returns the selected filename and allows changing folders, but the getting the filename from pfd.GetFileName(iName) in the OnButtonClicked event is returning an invalid filename. I think the problem is caused by not getting the correct handle for pfd: IFileDialog.
UPDATE:
I solved this by defining
FileDialog: IFileDialog as a var at the component level then I called
function TFileDialogEvent.OnButtonClicked(const pfdc: IFileDialogCustomize;
dwIDCtl: DWORD): HResult; stdcall;
var
iImageEnIO: TImageEnIO;
iFilename: string;
pFolder: PWideChar;
iFolder: string;
iName: PChar;
pfd: IFileDialog;
hr: HRESULT;
aShellItem: IShellItem;
begin
if dwIDCtl = dwVisualGroup8ID then
begin
iImageEnIO := TImageEnIO.Create(nil);
try
FileDialog.QueryInterface(IFileDialog, pfd);
pfd.GetFileName(iName);
// Get the ShellItem
hr := SHCreateItemFromParsingName(iName, nil,
StringToGUID(SID_IShellItem), aShellItem);
// Get the folder
pfd.GetFolder(aShellItem);
// Get the folder displayname
aShellItem.GetDisplayName(SIGDN_FILESYSPATH, pFolder);
iFolder := string(pFolder);
if DirectoryExists( iFolder) then
iFilename := IncludeTrailingPathDelimiter( iFolder) + string(iName);
if FileExists(iFilename) then
begin
Thank-you all… Thank-you Rob… your post was helpful.
You’re querying the object for an interface matching the GUID {8016B7B3-3D49-4504-A0AA-2A37494E606F}, and storing the result in an
IFileDialogreference. The problem is that {8016B7B3-3D49-4504-A0AA-2A37494E606F} is the GUID for theIFileDialogCustomizeinterface, notIFileDialog. You attempt to callGetFileName, which is the sixth method of theIFileDialoginterface, but since the variable actually holds anIFileDialogCustomizeinterface, control ends up being transferred to the sixth function of that interface instead. The compiler cannot catch the type mismatch for you, partly because you’re constructing the GUID dynamically at run time (so it doesn’t know the value at compile time), and partly because the second parameter toQueryInterfaceis untyped (so it can’t know that the type of the variable is supposed to match the first parameter).There’s an easier way than computing the GUID at run time. Interface types are automatically useable as their associated GUIDs (when they have GUIDs). To request the
IFileDialoginterface, just pass that identifier directly toQueryInterface:You don’t even have to call
QueryInterfaceif you use theasoperator:When you call
QueryInterfacedirectly, you need to make sure you check the result for error codes. If you use theasoperator, an unsupported interface will raise an exception. If you just want pass-fail without too much error checking, use theSupportsfunction instead: