29 August 2012

For a a long time now, my CodeSnip program's propensity to make little clicking noises when some pages are displayed or some internal links are clicked has been irritating me to death.

Of course, this is because CodeSnip uses Delphi's TWebBrowser control to display the main part of its UI: and TWebBrowser wraps the Internet Explorer display engine, and that's where the well known clicks come from.

Many people say that the click is part of the expected user experience, but while I agree for apps that present a noticeably web inspired interface, I don't think it's helpful in apps like CodeSnip that simply use the browser control as a way of diplaying a richly formatted user interface: users aren't supposed to even know there's a browser control there.

Any road up, I've finally found how to turn off the dreaded click using a proper API rather than a kludge that hacks about the registry. I've seen this solution discussed for VB and C#, but not for Delphi, so here's the solution I'm using.

My app needs to also support Win 2K, so I can't import the function statically - we need a dynamic approach. This is what I came up with:

First declare a type for the imported function and a global variable to reference the function:

type

TCoInternetSetFeatureEnabled=function(

FeatureEntry:DWORD;

Flags:DWORD;

Enable:BOOL

):HResult;stdcall;

var

CoInternetSetFeatureEnabled:TCoInternetSetFeatureEnabled;

Now declare a function to use in case we can't import CoInternetSetFeatureEnabled from UrlMon.dll:

functionCoInternetSetFeatureEnabledFallback(FeatureEntry:DWORD;

Flags:DWORD;Enable:BOOL):HResult;stdcall;

begin

Result:=E_NOTIMPL;

end;

This function does nothing except return E_NOTIMPL to show that it is not implemented.

Next comes a procedure that is to be called from the unit's initialization section to load CoInternetSetFeatureEnabled if possible and if not to use CoInternetSetFeatureEnabledFallback:

procedureInit;

begin

LibHandle:=SafeLoadLibrary('urlmon.dll');

CoInternetSetFeatureEnabled:=nil;

ifLibHandle<>0then

CoInternetSetFeatureEnabled:=GetProcAddress(

LibHandle,'CoInternetSetFeatureEnabled'

);

ifnotAssigned(CoInternetSetFeatureEnabled)then

CoInternetSetFeatureEnabled:=CoInternetSetFeatureEnabledFallback;

end;

At the end of the unit we need the following initialisation and finalisation code:

initialization

Init;

finalization

FreeLibrary(LibHandle);

end.

Now we have an implementation of CoInternetSetFeatureEnabled that we can call safely, we can switch off those horrid little clicks by calling the routine with the necessary parameters:

procedureSwitchOffBrowserSounds;

const

FEATURE_DISABLE_NAVIGATION_SOUNDS=21;

SET_FEATURE_ON_PROCESS=$00000002;

begin

CoInternetSetFeatureEnabled(

FEATURE_DISABLE_NAVIGATION_SOUNDS,SET_FEATURE_ON_PROCESS,True

);

end;

The FEATURE_DISABLE_NAVIGATION_SOUNDS is the important value here: it switches off the IE ticks and is one of a number of "feature controls" that can be passed to CoInternetSetFeatureEnabled. You can see the full list at http://msdn.microsoft.com/en-us/library/ms537169.

SET_FEATURE_ON_PROCESS makes the change only for the current process so we don't interfere with the settings that apply in other applications. Again, there are flags for different options: see http://msdn.microsoft.com/en-us/library/ms537168.

Of course this code has no effect if UrlMon.dll does not export CoInternetSetFeatureEnabled, but at least it fails gracefully.

At last peace reigns over the CodeSnip UI! (Or rather, it will when v4.0 beta 3 is available).