I want to know if there's a way to insert, for example, a FilePath in a DialogBox opened by Windows itself, but inserted from a Delphi variable to the Windows Dialog? Something like, click on an Upload button in any website that does open the File Explorer Dialog, and in Delphi send the value of the Path to the File Explorer of Website.
PS : I already have the code to get his HWND handle.
I don't know if this is possible, or is there's a way to do it.
Edit : A Select File from the site, what I want is simply to input the FilePath of this site by an app variable in Delphi.
Variants
If you know that dialog window's handle already then there are at least 2 variants:
finding the control by going through the children hierarchy, using FindWindowEx()
relying on the dialog template's IDs, using GetDlgItem()
Both may work out great, but are at the same time fragile, since the dialog window's structure (as per its controls and IDs) can change even by Service Packs already, let alone entire Windows versions. My experience here is from Win2000 up to Win7 and on those systems it worked.
Dialog versions
What you posted as screenshot is the "Vista" version of the dialog; the older "Win95" version is identical in terms of accessing the "filename" ComboBox.
The old one is typical for having the buttons ("open" and "cancel") in one row. Older software might still use these, but your web browser most likely not:
The one since Vista has its buttons in one line and is notable of featuring a full folder pane:
But both pictures show that the control layout is the same: a ComboBoxEx32 is a direct child of the window, having its own child(s). So we can use the same code for both versions.
Code
var
hDialog, hCbx32, hCbx, hEdit, hFilename: HWND;
sText: String;
begin
hDialog:= 9568854; // Dialog window, class "32770"
sText:= 'M:\my\filename.ext'; // What should be set
// Variant #1: finding the control by parents and childs.
// Luckily both the old dialog up to XP and the new dialog
// since Vista do not differ as per the filename ComboBox.
hCbx32:= FindWindowEx( hDialog, 0, 'ComboBoxEx32', nil ); // Most likely the 3rd child control
hCbx:= FindWindowEx( hCbx32, 0, 'ComboBox', nil ); // Actual ComboBox inside that
hEdit:= FindWindowEx( hCbx, 0, 'Edit', nil ); // Edit control inside ComboBox
SendMessage( hEdit, WM_SETTEXT, 0, LPARAM(PChar(sText)) );
// Variant #2: using dialog template IDs, which haven't
// changed since XP with one of its Service Packs. However,
// tested with Win7 only.
hFilename:= GetDlgItem( hDialog, $47C ); // "cmb13", found at least in XP SP3
if hFilename= 0 then hFilename:= GetDlgItem( hDialog, $480 ); // "edt1" = Maybe prior to XP without any SP
SendMessage( hFilename, WM_SETTEXT, 0, LPARAM(PChar(sText)) );
end;
One of both variants should do already. Successfully tested on Win7x64
using Paint's "Open" command for the Vista dialog version, and
one of my older program's "Open" command for the Win95 dialog version:
the text in the filename's ComboBox was set as expected.
Related
With Vcl.Themes.TStyleManager.StyleNames, I have access to just the style names enabled in the project options (Application/Appearance).
How can I list all of the style names registered in the system, and make it active in Delphi 11?
This is how I fill a TComboBox with the available styles:
procedure FillComboboxWithStyles;
var
stylename: string;
begin
for stylename in TStyleManager.StyleNames do
CBVclStyles.Items.Add(stylename);
end;
To activate a certain style you fill TStyleManager.SetStyle(const Name: string) with the stylename. For example the selected one from the TComboBox.
if (CBVclStyles.ItemIndex >= 0) then
TStyleManager.SetStyle(CBVclStyles.Items[CBVclStyles.ItemIndex])
else
;// handle a non-selected style
Don't forget Vcl.Themes in your uses.
You can include the style files (*.vsf from C:\Program Files (x86)\Embarcadero\Studio\22.0\Redist\styles\vcl with 22.0 marking your RAD Studio version) along with (externally from) your .EXE and then iterate over them when you fill your ComboBox. Then manually load the style from the file with TStyleManager.LoadFromFile when selected.
This will also allow your user to add his own style files to the setup.
This, I believe, is what the IDE does (except that it reads the list directly from the folder, since there's no "installation" of a style other than making the file available in a recognized location).
There is a desktop window, which has more tabs, and some tabs include a text editor.
The editor buttons have the following mapping on the first tab (simplified here):
Window.PageControl.Tab1.Editor.Panel.Button1
Window.PageControl.Tab1.Editor.Panel.Button2 and so on.
The editor buttons have the following mapping on the second tab (simplified here):
Window.PageControl.Tab2.Editor.Panel.Button1
Window.PageControl.Tab2.Editor.Panel.Button2 and so on.
(Sometimes the Editor and other objects between Editor and Panel are cached and their mapping is not stable.)
I wrote a test which checks the functions of the text editor in the following way:
Window.PageControl.TabIndex := 1;
editor_test;
Window.PageControl.TabIndex := 2;
editor_test;
The editor_test looks like:
Window.PageControl.Refresh;
lprops := ['FullName','WndClass'];
lvals := ['*Panel', 'TWPToolPanel'];
ltarget := Aliases.(application name).Find(lprops,lvals,20,true);
ltarget.Button1.Click;
ltarget.Button2.Click...
editor_test properly works on any single tab of the window. Although, when I try to run the editor_test more times in one test, the test fails after changing tab, because it searches the buttons on the previous tab.
I tried Refresh and RefreshMappingInfo methods on the common parent object of the tabs (see above), but they doesn't help.
(The names and number of tabs can change in the window depending on the conditions.)
Is there any other way to clear cached mapping tree during test run? Is there any mistake in the concept?
Thank you in advance for any suggestion!
The solution was to complement the editor test in the following way:
lprops := ['FullName','WndClass', 'VisibleOnScreen'];
lvals := ['*Panel', 'TWPToolPanel', true];
Using this, TestComplete does not recognize editor buttons on inactive tabs of the window, which happened in the original scenario.
This question already has answers here:
How to associate a program with a file type, but only for the current user?
(2 answers)
Closed 8 years ago.
How can i auto-open a custom extension in my Delphi app ? I'm trying to make application
that loads text files but with a different extension.
Example : Text.DZ | all i want is when i click the file with .DZ Extension it opens my app automatically and loads the text inside into the memo1.lines.text.
I know that i have to register my new custom extension but i have no idea how to do it and
load the text into my app.
Also if you can include a source-code example that would be awesome.
Any help will be much appreciated ! and
Sorry for my newbiness and bad English explanation but i hope you guys understand me (^-^)/.
There's a way to do it programmatically, although I don't recall how off the cuff.
That said, if you're just wanting to do it for your own personal use, not for a piece of software you'll be distributing to others, then the easiest way is to use Windows Explorer: click the data file you want, then right-click and select Open With -> ... you'll probably need to select the Other... option at the bottom. That will bring up a dialog box that lets you choose your app. There should also be a checkbox somewhere that says something like "Make Default" or "Always Open With This" or something along those lines. Make sure that box is checked. Then click OK a couple of times and you're off to the races.
But you'll also need to set up your app to read the filename from the command line. You use ParamStr and ParamCount for this.
for i := 0 to ParamCount do
ShowMessage('Parameter '+IntToStr(i)+' = '+ParamStr(i));
When you double-click on the data file, it will open your app and pass this filename as ParamStr(1). So when you get it, simply do something like this:
memo1.lines.LoadFromFile( ParamStr(1) );
Use this as a STARTING POINT (it won't compile if you just copy-and-paste!):
procedure TMyForm234:FormCreate( blah blah )
begin
if ParamCount > 0 then
theMemo.Lines.LoadFromFile( ParamStr(1) );
end;
I am able to open a CHM file by passing a ShortInteger and casting it as a Word for the dwData parameter. I.E.
Unit Help; //this is where the Id's are set with their description
Interface
Const
Address_File = 35; //delphi identifies Address_File as a shortint
etc..
Call get help pass my ID
GetHelp(Address_File); //call get help pass my ID to open to the Address_File topic
GetHelp procedure
procedure GetHelp(HelpID : Word);
begin
Application.HelpFile := ProgramPath + 'help.chm';
HtmlHelpW(0, PWideChar(Application.HelpFile),HH_HELP_CONTEXT , HelpID);
end;
HtmlHelpW function
function HtmlHelpW(hwndCaller : HWND; pszFile: PWideChar; uCommand : Integer;
dwData : DWORD) : HWND; stdcall; external 'hhctrl.ocx' name 'HtmlHelpW';
As I pass different ShortIntegers I am able to initialize the help file at different sections.
However I can't figure out how the values are mapped. There are some sections in the chm file that I want to be able to map to but the short Integer or context ID associated with them is not documented in the program or is not mapped.
Free Pascal comes with a chmls.exe util that has a command that tries to recover the alias (context) data:
chmls, a CHM utility. (c) 2010 Free Pascal core.
Usage: chmls [switches] [command] [command specific parameters]
Switches :
-h, --help : this screen
-p, --no-page : do not page list output
-n,--name-only : only show "name" column in list output
Where command is one of the following or if omitted, equal to LIST.
list <filename> [section number]
Shows contents of the archive's directory
extract <chm filename> <filename to extract> [saveasname]
Extracts file "filename to get" from archive "filename",
and, if specified, saves it to [saveasname]
extractall <chm filename> [directory]
Extracts all files from archive "filename" to directory
"directory"
unblockchm <filespec1> [filespec2] ..
Mass unblocks (XPsp2+) the relevant CHMs. Multiple files
and wildcards allowed
extractalias <chmfilename> [basefilename] [symbolprefix]
Extracts context info from file "chmfilename"
to a "basefilename".h and "basefilename".ali,
using symbols "symbolprefix"contextnr
extracttoc <chmfilename> [filename]
Extracts the toc (mainly to check binary TOC)
extractindex <chmfilename> [filename]
Extracts the index (mainly to check binary index)
This might be a start, since at least you'll know which pages are exported using an ID, and maybe the URL names will give some information.
The util is in recent releases (make sure you get 2.6.0) and also available in Free Pascal source, which should be convertable to Delphi with relatively minor effort.
Basically the chmls tool was created out of various test codebases. The testprograms decompiled and printed contents of different CHM sections and were used while creating the helpfile compiler, chmcmd, which is also part of FPC.
In Delphi, calling a help file is rather easy. In any VCL Forms application, you can set the HelpContext property of almost any control to a unique Context ID, which corresponds to a particular topic in the Help File. The Help File was compiled with these mappings, but when you decompile it, these mappings are no longer there. You must have access to the original help file project in order to know these ID's.
Set HelpContext of controls to the corresponding Context ID in the Help File
Set HelpType of controls to htContext to use the HelpContext ID
Assign Application.HelpFile to the appropriate location of the CHM file
When pressing F1 anywhere in your application, the help file will open based on the Help Context ID on the control, or its parent control
If you don't have the original project, and you don't want to re-create it, then you would have a long task of iterating through the Context ID's of your help file. Try to call the help file starting from 0 through 1,000 or possibly 50,000, depending on the size of it.
A practice I implement is a set of constants in a designated unit called HelpConstants.pas which is shared across our common application base. Each constant name uniquely and briefly describes the topic which it represents. Upon starting the application, I dynamically assign these Context ID's to their corresponding controls (usually forms) and VCL takes care of the rest.
I got the utility Marco suggested from
https://github.com/alrieckert/freepascal_arm/blob/master/packages/chm/bin/i386-win32/chmls.exe
(download by selecting View Raw).
I was able to extract all the context tags from the .chm help file and add the one I was interested in to my C++ Builder program by calling Application->HelpJump().
HTH
I've been trying to use tomazy's FutureWindows infrastructure (see his answer at Delphi GUI Testing and Modal Forms or the home of the tool at https://github.com/tomazy/DelphiUtils), but would like to know if and how can it be used with standard Windows file open dialogs? They don't seem to be inheriting from TControl, which the FutureWindows infra seems to assume (unless I've misunderstood it).
What I'd like to do is basically to just select a file in an OpenFileDialog which is opened modally by a command within my testing, but haven't yet been able to figure out how to do this.
Use a tool like Spy++ to find out what the window class name is. For example, on my Windows 7 machine, the window class name for a system file open dialog is #32770 (Dialog).
My current solution is below:
TFutureWindows.Expect(MESSAGE_BOX_WINDOW_CLASS)
.ExecProc(
procedure (const AWindow: IWindow)
var
DlgHandle: HWND;
FileName: string;
begin
FileName := ExpandFileName('myFileToUse.txt');
DlgHandle := AWindow.GetHandle;
Windows.SetDlgItemText(DlgHandle, 1148, PChar(FileName));
end
)
.ExecSendKey(VK_RETURN);
So basically sending a message using Windows API. The ideas (and the ID 1148) were found from here: http://social.msdn.microsoft.com/forums/en-US/winforms/thread/62d5db14-5497-4ceb-8af0-d7f81732e937/
Possible better solutions are welcome, but this seems fine enough for me at least for now.
Thanks for the comments so far!