How to show a hyperlink in Inno Setup? - hyperlink

I'm making a validation in my Inno Setup installer to check whether or not a Microsoft update is installed on the machine, if not, I'm showing a simple message box telling the user that the update is required, this is the message code:
MsgBox(
'Your system requires an update supplied by Microsoft. ' +
'Please follow this link to install it: ' +
'http://www.microsoft.com/downloads/details.aspx?FamilyID=1B0BFB35-C252-43CC-8A2A-6A64D6AC4670&displaylang=en',
mbInformation, MB_OK);
I want to make the URL an hyperlink to the web page, but I haven't been able to figure it out how, it is possible in Inno Setup to add text as an hyperlink?
Thanks.

The MsgBox() function in Inno Setup is a wrapper for the standard Windows MessageBox() function, which AFAIK doesn't support embedded links, so it's not possible to simply show the link there.
What you could do however is to notify the user that the update is required, and ask them whether to open the link in the default browser. Something like:
function InitializeSetup(): Boolean;
var
ErrCode: integer;
begin
if MsgBox('Your system requires an update supplied by Microsoft. Would you like to visit the download page now?', mbConfirmation, MB_YESNO) = IDYES
then begin
ShellExec('open', 'http://www.microsoft.com/downloads/details.aspx?FamilyID=1B0BFB35-C252-43CC-8A2A-6A64D6AC4670&displaylang=en',
'', '', SW_SHOW, ewNoWait, ErrCode);
end;
Result := False;
end;
This code will abort the installation, but you could create a custom page instead which checks whether the update has been installed, and otherwise prevents navigation to the next page. This would only work if the update can be installed without a system restart, though.

Related

Allow insecure networks for Delphi Edge Browser component

I am using Edge browser component of Delphi 11.1 in order to automate a navigation process. The problem is that at the start of the navigation process I get the Message "Your connection isn't private" & the whole process stops until I press the button "Advanced" & the button "Continue to 1xx.xx.xx.xx (unsafe)"!
I have to mention that when I navigate to this intranet site via Windows Edge Browser I do not get that Message since I have uploaded the certificate of this site & I have also added it to the exceptions of Edge browser.
I think that I have to set it somehow the "InsecurePrivateNetworkRequestsAllowed" property to true but I cannot find the way or to upload somehow the certificate to Delphi's Edge browser component.
Any Ideas?
Thank you.
Insecure connection warning can be bypassed by clicking on the Proceed link.
Below there is a code sample:
procedure TForm1.EdgeBrowserNavigationCompleted(Sender: TCustomEdgeBrowser;
IsSuccess: Boolean; WebErrorStatus: TOleEnum);
resourcestring
scProceed =
'function run() { ' +
' var e = document.getElementById("proceed-link"); ' +
' if (e) e.click(); ' +
'} run();';
begin
if not IsSuccess and (WebErrorStatus = COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_IS_INVALID) then
EdgeBrowser.ExecuteScript(scProceed);
end;
Another way is to control the WebView2 behaviour using CoreWebView2EnvironmentOptions.AdditionalBrowserArguments. Some of arguments are documented here.
Currently, Delphi 11.1 does not allow you to manage them using some component/interface, but such options could be set using WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS environement varialble before WebView initialization:
procedure TForm1.FormCreate(Sender: TObject);
begin
SetEnvironmentVariable('WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS',
'--ignore-certificate-errors');
EdgeBrowser.CreateWebView;
end;
Note, this option could be removed in future because of security concerns and sporadic debates on it.

TWebBrowser and URL

I'm working on a software to verify the problem of porting it from D5 to XE5. In D5, TWebBrowser.BeforeNavigate2 was call each time the user click on the submit button of a displayed form. In XE5, it's not the case. I figured out it is because the URL for the submit contain http:/aDirectory/ExecToBeCall.exe. If I add an extra / after: the event is fire.
Under D5 the URL is change for:
http ://localhost/aDirectory/ExecToBeCall.exe (space added to break the link in the post)
That behavior of TWebBrowser under D5 to fire anyway and change the URL is important for the software and I cannot change the HTML (about 2000 files) to contain 2. It allowed us to know if the submit was made inside Delphi or from a outside Browser. I tried other and newer events of TWebBrowser and none are fire.
How can I be informed of a problematic URL, check it and change it into a localhost URL? A small and clean method would be preferable.
Thanks for your help and suggestions
TWebBrowser is just a thin wrapper around Internet Explorer's ActiveX object, so it is IE itself, not TWebBrowser, that is behaving differently.
http:/aDirectory/ExecToBeCall.exe is actually a valid URL. Since the : is not followed by //, there is no authority portion in the URL, and thus no explicit hostname. localhost is used as an implicit hostname, and the path is /aDirectory/ExecToBeCall.exe. That is what the URL is being changed to in D5, which is correct behavior. Changing the URL to http://aDirectory/ExecToBeCall.exe is incorrect, as that creates an authority portion of the URL and thus the hostname is explicitly set to aDirectory and the path is set to /ExecToBeCall.exe, which is not what you want.
Why the URL is not changing in XE5, I have no clue. Sounds like a bug in whatever version of IE is being used in that version of TWebBrowser.
In any case, it is IE that is triggering the event, so if it is not triggering for a URL it does not like, there is nothing you can do about that, short of using the browser's DOM interfaces to handle the onsubmit event of the HTML webform directly.
If you want to redirect unexpected url instead of navigating to it, you can start with TEmbeddedWB project or you can DIY by extending TWebBrowser class with IDocHostUIHandler, which has an interesting method TranslateURL.
function TAdvWebBrowser.TranslateURL(const dwTranslate: DWORD; const pchURLIn: POLESTR; var ppchURLOut: POLESTR): HRESULT;
var
Url: string;
BufferSize: Integer;
begin
Url := PChar(pchURLIn);
if GetSafeUrlFor(Url) then
begin
ppchURLOut := CoTaskMemAlloc(BufferSize);
CopyMemory(ppchURLOut, PChar(Url), BufferSize);
// redirects to new location
Result := S_OK;
end
else
// no redirection
Result := S_FALSE;
end;
// You can change the function to add more complex redirection rules
function GetSafeUrlFor(var Url: string): Boolean;
begin
Result := Url.EndsWithText('.exe');
if Result then
Url := 'http://localhost/';
end;

twebbrowser download in popupwindow

Looking to get my delphi app to log into a website, navigate to a page, and automatically download certain files, the solution at How do I keep an embedded browser from prompting where to save a downloaded file?, helped a great deal with the file download.
The final problem is the last step on navigating opens in a popup window, there are plenty of solutions out there to capture popup windows by implementing TWebBrowser.NewWindow2 but none of these events seem to work with the above code, something to do with how twebbrowser.invokeevent in the above code works maybe?
If I use invokeveent and the dispID of 273(newwindow3) to call a function I can twebbwowser.navigate() a second webbrowser to the url of the popupwindow.
My problem is the popup window has basicly one line of javascript "document.print(parent.parent.opener.thefunction())" the second twebbrowser has no reference to its parent so this fails.
I can see two possible solutions, get the TWebBrowser.NewWindow2 or 3 to trigger, fix the code sample bellow, LVarArray[0] {const IDispatch}, is null for some reason.
procedure TWebBrowser.InvokeEvent(ADispID: TDispID; var AParams: TDispParams);
// DispID 250 is the BeforeNavigate2 dispinterface and to the FFileSource here
// is stored the URL parameter (for cases, when the IDownloadManager::Download
// won't redirect the URL and pass empty string to the pszRedir)
//showmessage('test');
var
ArgCount : Integer;
LVarArray : Array of OleVariant;
LIndex : Integer;
begin
inherited;
ArgCount := AParams.cArgs;
SetLength(LVarArray, ArgCount);
for LIndex := Low(LVarArray) to High(LVarArray) do
LVarArray[High(LVarArray)-LIndex] := OleVariant(TDispParams(AParams).rgvarg^[LIndex]);
case ADispID of
250: FFileSource := OleVariant(AParams.rgvarg^[5]);
273: DoNewWindow3(Self,
LVarArray[0] {const IDispatch},
WordBool((TVarData(LVarArray[1]).VPointer)^) {var WordBool},
LVarArray[2] {const OleVariant},
LVarArray[3] {const OleVariant},
LVarArray[4] {const OleVariant});
end;
end;
I'm not going to answer your question directly because I think you've asked the wrong question. You are trying to download files over the internet without any GUI being shown to the user. As such, an embedded browser is simply the wrong solution.
Rather than trying to suppress popup dialogs, use a tool that never shows popup dialogs. What I believe you should be doing is downloading the files using direct HTTP download. There are many different ways to achieve that. For example, an extremely convenient method, available out of the box with Delphi, is to use Indy. I believe that the component you need is TIdHttp.

How do I get a list of control-panel items and then execute one?

1. How can I get a list of control panels, including their names and icons, so I can create a menu like the one the Start menu shows?
2. When I click an entry, how do I execute the corresponding control panel?
By the way, what controls are used to do this kind of PopupMenu? But it has right click event.
update :
I use PItemIDList to get a Folder:
var:
PIDL, TempPIDL: PItemIDList;
Path: array[0..MAX_PATH] of Char;
FI: SHFILEINFOW;
begin
SHGetSpecialFolderLocation(0, CSIDL_FAVORITES, PIDL);
SHGetPathFromIDList(PIDL , Path);
Memo1.Lines.Add(Path);
SHGetFileInfo(LPCTSTR(PIDL), 0, FI, SizeOf(FI), SHGFI_PIDL or SHGFI_DISPLAYNAME or SHGFI_ICON);
Memo1.Lines.Add(FI.szDisplayName);
Image1.Picture.Icon.Handle := FI.hIcon;
it display normal , but when I change CSIDL_FAVORITE to CSIDL_CONTROLS , I always get error .
this is a wrong way to get controls panel items ?
I also use another method by use CPL
copy from here
But it can not display complete Items.
You can check the registry for all registered applets.
This describes how to register them: http://msdn.microsoft.com/en-us/library/windows/desktop/bb757044.aspx
Similarly you may scan registry to check already registered applets and their run methods.
However on 64-bit windows there would be 64-bit applets that your 32-bit application would not be able to load, so extracting icon might be a pain. I don't know if you can call LoadLibraryEx with AsResourceLibrary flag over 64-bit DLLs for mere icon extraction though.
Another approach would be using Windows explorer namespaces. Get some Shell component suite that provides opening virtual paths like My Computer and My Documents rather than c:\ and such. Control Panel has a special GUID (that i do not remember right of. But Microsoft TweakUI tool can create Control Panel in any folder using that GUID). You can probably use some Shell UI to open Control Panel special virtual folder into kind of ListView , then get then enumerate items and extract correspondent pictures and re-arrange them as menu. Then executing would be probably done as double-click over item in that shell listview.
Control panel applets are CPL files that are located in your system folder
EG : C:\Windows\system32
My suggestion is to list those files and then extract icons and get their file name
If you have trouble with the code post it here so that we can help
CPL files are just DLL files they can contain multiple applets
After a google search I found this tutorial :
http://delphi.about.com/od/kbwinshell/l/aa062403a.htm
In your help, I solved the problem! Special thanks to David Heffernan
1.Get control panel items I use Windows Shell to get control panel items , use CPL files not get complete items .
Code :
var
psfDeskTop: IShellFolder;
psfControl: IShellFolder;
pidControl: PITEMIDLIST;
pidChild: PITEMIDLIST;
pidAbsolute: PItemIdList;
pEnumList: IEnumIDList;
celtFetched: ULONG;
FileInfo: SHFILEINFOW;
begin
OleCheck(SHGetDesktopFolder(psfDeskTop));
OleCheck(SHGetSpecialFolderLocation(0, CSIDL_CONTROLS, pidControl));
OleCheck(psfDeskTop.BindToObject(pidControl, nil, IID_IShellFolder, psfControl));
OleCheck(psfControl.EnumObjects(0, SHCONTF_NONFOLDERS or SHCONTF_INCLUDEHIDDEN or SHCONTF_FOLDERS, pEnumList));
while pEnumList.Next(1, pidChild, celtFetched) = 0 do
begin
pidAbsolute := ILCombine(pidControl, pidChild);
SHGetFileInfo(LPCTSTR(pidAbsolute), 0, FileInfo, SizeOf(FileInfo), SHGFI_PIDL
or SHGFI_DISPLAYNAME);
// SHGetFileInfo can get name and icon
//Do something to save item name and icon
end;
2. Execute must have to use ShellExecuteEx to execute a PIDL item.
var
ShExeInfo : SHELLEXECUTEINFO;
begin
ZeroMemory(#ShExeInfo, SizeOf(ShExeInfo));
ShExeInfo.cbSize := SizeOf(ShExeInfo);
ShExeInfo.lpVerb := 'Open';
// control panel item's PIDL
ShExeInfo.lpIDList := pidAbsolute;
ShExeInfo.nShow := SW_SHOWNORMAL;
ShExeInfo.fMask := SEE_MASK_IDLIST;
end
and use
ShellExecuteEx(#ShExeInfo);
Finally thanks to David Heffernan again. help me a lot.
Following the suggestion of Arioch 'The
Reference: http://www.geoffchappell.com/studies/windows/shell/shell32/classes/controlpanel.htm
The other "two or three methods" I was thinking of are detailed there:
the [MMCPL] section of the CONTROL.INI file, nowadays mapped to the
registry key HKEY_CURRENT_USER\Control Panel\MMCPL;
the registry key
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Control Panel\CPLs;
Also, detailed there as well are the don't load lists:
A candidate CPL module is rejected if its filename appears as a value
in either of the following registry keys:
HKEY_CURRENT_USER\Control Panel\don't load
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Control Panel\don't load
Like was said, the challenge of this project is sweeping all the proper locations and getting the data in the right way to be able to act on it. This is because of how a few of the "newer" design control panel items (and shell folders that appear there, I'm not really sure I discovered how to access those yet) are presented. I don't have the data handy, but I can copy an example or two in if it would further the discussion.

INNO Setup [CODE] block, set status?

I have an installer that needs to install some visual c++ redistributables before anything else gets installed. This is because an ActiveX COM object wont register without it being installed first.
I have the code that properly installs the visual c++, but the program seems to hang for a minute (to the user) while it is installing since they have no clue what is happening. Is there any way to inform the user what i am doing without popping up an actual messagebox? Like how you can set the status in the [Run] block.
// This function will be called after the last "Next" button is pressed, but before any files get installed
procedure DoPreInstall();
var
ErrorCode: Integer;
begin
Exec(ExpandConstant('vcredist_x86_2008.exe'), '/q', '', SW_SHOW, ewWaitUntilTerminated, ErrorCode)
end;
end;
I haven't tried it but I found this: http://news.jrsoftware.org/news/innosetup.code/msg21747.html.
It might help. Another is to run the vcredist in a non-silent mode.

Resources