Custom Dialog Filters, Part 1

A new feature in Windows Embedded Standard 7 called Windows Embedded Dialog Filter allows devices, such as Kiosks and Digital Signs, to reroute system dialogs away from the public. Using search criteria including the window’s title, class name, process name, and buttons, the window can be moved off screen or onto another screen. Also, actions can be performed such as clicking a button or closing the window.

For information on how to use the Dialog Filter, see this blog article.

There is one catch: it cannot do .NET WinForms and WPF Windows. The reason is that one of the filter criteria, the Window Class, contains instance information which changes frequently. This isn’t important for Window’s dialogs because they use consistent names, but it can be important if a 3rd party application written in .NET shows an unwanted dialog box.

Custom Filters, a feature in the Dialog Filter component, can be created to handle this.

Basic Design

Custom Dialog Filters are in-process COM DLLs that implement a published interface provided by the Dialog Filter. The default filter which uses XML to define filters is itself a Custom Filter which means it can be overridden or replaced fairly easy. Being COM driven, Custom Dialog Filters can be written in Native and Managed code. The example below is in C#.

Step 1: .NET and COM

For more detailed information on .NET and COM, see the MSDN documentation at:

https://msdn.microsoft.com/en-us/library/zsfww439.aspx.

To get started, a TLB file is provided on the Windows Embedded Standard 2011 installed to C:\Program Files\Windows Embedded Standard 2011\EmbeddedSDK\lib\DialogFilterXmlFilter.tlb. If you don’t have this file, it can be installed from the Windows Embedded Standard 2011 DVD. This file is not registered so you’ll have to do this yourself using the command:

TLBIMP <path to DialogFilterXmlFilter.tlb> /out:<path to DialogFilterXmlFilter.import.dll>

MSDN has more information about this and other commands at https://msdn.microsoft.com/en-us/library/xwzy44e4(VS.71).aspx.

Create a new C# Class Library Project. I used the name DialogFilterDotNet but you should name it whatever is appropriate for your project. Rename the class to something more appropriate. In the example below, I used MainFilter.

With the new project’s properties, change the following settings:

  1. Under the Application tab, click Assembly Information and check “Make assembly COM-Visible”.
  2. Under the Signing tab, check “Sign the Assembly”. Under “Strong Name Key File”, select <New> and uncheck “Protect my key file with a password. If your organization has software digital certificates, use this instead.

Add the References. This includes the import DLL created from the TLB which can be done using the Browse option. UIAutomationClient and UIAutomationTypes are also needed.

Visual Studio 2008 and 2010 make it really easy to implement the interface from DialogFilterXmlFilter library. To do this, add the interface IObjectFilter. Visual Studio will provide an option to implement the interface by highlighting a small underscore below this name. If you do this, Visual Studio will provide the functions you’ll need to implement automatically.

Step 2: The Functions

The IObjectFilter interface has only 4 functions, 2 of which are filters, 1 for logging, and a factory.

CreateObjectInfo

This factory function is used to create an object to store information about a window which is then passed around within the Dialog Filter service. In this example, we can create the default ObjectInfoClass, a COM object implemented by the Dialog Filter’s built-in filter. You may also create your own by implementing IObjectInfo, but example will use the default.

The implementation of CreateObjectInfo is as follows:

string ModifiedClassName = WindowClass;

Match RegexMatch = _FormRegex.Match(WindowClass);

if (RegexMatch.Success)

{

ModifiedClassName = RegexMatch.Result("${type}");

}

ObjectInfo r = new ObjectInfoClass();

r.SetWindowInfo(ProcessId, ProcessName, Handle, ModifiedClassName, WindowTitle, XPos, YPos);

return r;

When a WinForm or WPF class name (WindowClass) is read, it’s going to be something like:

WinForm: WindowsForms10.Window.8.app.0.33c0d9d

WPF: HwndWrapper[WpfApplicationClassCheck.exe;;69c35e29-70fe-4bb4-af05-70880670403c]

This example looks at the class name (WindowClass) and if it follows the format of a WinForm or WPF class, a new class name replaces it with one from a Regular Expression. This Regular Expression is formatted as:

^((?<type>HwndWrapper)\[[\w.]+;;([\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12})\])|(^(?<type>WindowsForms).(\d).Window.(\d).app.(\d).([a-f\d]{6,}))$

Regular Expressions never readable, but if there’s a match format wise, ${type} will equal HwndWrapper or WindowsForms.

CheckWindow

This function is a kind of a pre-filter. Its job is to look at the basics of the window, such as the process name, title, and class name, and return to the service whether we want further processing and if we want it visible. For our example, we’re only interested in the class name and will obscure any WinForm or WPF window until we know for sure what it is.

If the window is neither a WinForm or WPF window, default processing is handled. Default processing is done by using the existing XML configured filter that ships with Dialog Filter. Doing this is easy. A single line object is added to the class:

private IObjectFilter _defaultFilter = new ObjectFilterClass();

This allows us to continue to use the XML file to list windows and actions which greatly simplifies what we need to do. The implementation to CheckWindow is as follows:

switch (pObjectInfo.WindowClass)

{

case "HwndWrapper": // HwndWrapper will stay as-is

case "WindowsForms": //WindowsForms will change to the actual class name

return CheckWindowResult.cwHideGetDetails;

default:

return _defaultFilter.CheckWindow(pObjectInfo);

}

One note about CheckWindow is that it is multithreaded and will block the window from showing itself until it returns. This is to improve system performance and eliminate flicker on the screen. Implementations should be aware that they should not spend much time doing filtering since this will negatively affect how long an application loads and performs. It also cannot call UIAutomation, however CheckWindowContents can.

CheckWindowContents

This function gives us a chance to dive into the content of a window. Unlike CheckWindow, calls to CheckWindowContents are queued and managed from a single thread. This gives us the ability to look for specific information within a window, and in our case, an AutomationId that WinForms provides.

This example looks for WindowsForms as the class name. If this is the case, the class name is reset to the AutomationId which is really the class name as written in C#. After this, the default filter is used.

if (pObjectInfo.WindowClass == "WindowsForms")

{

AutomationElement winForm = AutomationElement.FromHandle(pObjectInfo.Handle);

if (winForm != null)

{

pObjectInfo.SetWindowInfo(pObjectInfo.ProcessId,

pObjectInfo.ProcessName,

pObjectInfo.Handle,

winForm.GetCurrentPropertyValue(

AutomationElement.AutomationIdProperty) as string,

pObjectInfo.WindowTitle,

pObjectInfo.XPos,

pObjectInfo.YPos);

}

}

_defaultFilter.CheckWindowContents(pObjectInfo);

Again, the window is forwarded to the default XML filter. From there, XML configuration will dictate whether we show the dialog or not and whether we want to perform an action on it.

LogMessage

How we log is completely up to the implementation however it’s easiest just to forward it on to the default. The default will create log entries in the Event Viewer under Applications and Services, Microsoft, Windows, Dialog Filter. On top of that, it will provide suggestions on XML that can be added to block a window that pops up.

Implementation is a single line:

_defaultFilter.LogMessage(type, pObjectInfo, szData);

XML

The ConfigurationList.xml (usually located in c:\ProgramData\Microsoft\DialogFilter but is customizable) will vary from system to system. For my example, I created two simple programs, one WinForm and one WPF, both with a couple buttons placed in the window. Once created and run, the DialogFilterEditor is able to create filters that can be saved in the XML. The only change is that the class name must be updated to match what we’ve changed them to.

For the WinForms filter, the class name becomes “Form1”. For the WPF filter, the class name becomes “HwndWrapper”. The Close action is applied to both and is saved.

Step 3: More COM: Registering

Like in C++, .NET libraries must be registered by the administrator, however the process is completely different. This is how to do this:

  1. Open a Visual Studio command prompt as administrator on the test system. Note that the default command prompt won’t work.
  2. Change to the directory where the binary is built.
  3. Run “RegAsm /tlb:<path to EmbeddedSDK\lib\DialogFilterXmlFilter.tlb> <path to custom filter.dll>”. This only needs to be done once.
  4. Cache the custom filter dll by running “GacUtil /I <path the custom filter.dll>”. This must be done every time there’s a new build.
  5. Run Regedit, browse to HKEY_CLASSES_ROOT\<your namespace>.<your filter name>\CLSID. For my example, it will be DialogFitlerDotNet.MainFilter.
  6. Copy to the Clipboard the GUID value of CLSID.
  7. Browse again to HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\DialogFilter\Parameters.
    1. Create a string (REG_SZ) called “FilterDll” and set it to the value of the GUID in step 6.
  8. Start/Restart the service. If all goes well, the DialogFilter.exe process should appear in the Task Manager and there should be no errors or warnings in the Event Log.

Step 4: Debugging

You’re going to find out that debugging isn’t as easy as it usually is, but the easiest way is to install the Visual Studio Remote debugger on the Test system and use remote debugging. This is because breakpoints in code may block other windows from running, Visual Studio included. This often causes race conditions where Visual Studio and Dialog Filter wait for each other. There’s no nice way of getting out of this other when this happens, so avoid debugging the Dialog Filter locally.

For more information on remote debugging see

https://msdn.microsoft.com/en-us/library/bt727f1t(VS.71).aspx (using Visual Studio)

https://msdn.microsoft.com/en-us/library/cc266321.aspx (WinDbg)

- Brendan

Technorati Tags: XPe,Embedded Standard,CTP