| Scripting your Windows XP Embedded components |
by Hans de Vetten (Dec. 3, 2003)
Microsoft has come a long way with their embedded operating systems. Every release results in a more mature toolset and more opportunities to tailor the operating system to the user's need. However, less attention is paid to developing the application efficiently and the way this application can be componentized for use in the runtime. The only solution most articles will give is a way to determine the dependencies.
When we develop both runtime and application, we can perhaps get away with removing or adding of components by hand or running setup scripts during first boot. But what if we develop an application to be used by a customer who integrates our application in its own runtime? If we don't want our own helpdesk to overload, we have to offer a more user friendly solution. The toolset for Windows XP Embedded allows us to do just that if you know where to look.
This article will describe some steps that will lead to a user-friendlier component. It also will tread the way for more sophisticated solutions which will be described in a follow-up article.
In this article we will convert a fictional application for an information terminal. The terminal is meant to give customers of pharmacies a way to get information about the products they are using. The terminal is sold in two varieties: with and without printer. The software is made up out of three files:
- PharmacieInfo.exe – The main application
- PharmaciePrintUI.dll – The user interface for the printer part
- PharmaciePrint.dll – The printer functionality
PharmacieInfo.exe will load PharmaciePrintUI.dll on startup when it can be found.
With current flash memory prices still being what they are, it can be very interesting to keep the runtime and therefore the ROM image as small as possible. This means that every overhead must be removed. Even when we should adopt our application so it won't automatically load the printer user interface when found, it is better to not to have the files at all.
Component Designer
At first sight, the case is not very exciting. It is divided into two parts: a fixed part (the .exe file) and a optional part (the printer DLL's). When we create a component for the optional part, we can simply add this component when we want printer functionality. This is a solution that will work for this case, but this article is not meant to find a solution that is easy on the component designer, but is meant to find a solution that is easy on the user of the component (our customer remember?) The manual adding of the component is the part that must be remembered by the customer and this must be avoided if possible. There is a way that allows us to automatically add a component we want and that's using dependencies. The problem is that dependencies only work for components that are always depending on other components or that exclude one another. So this is no solution either. Luckily, Component Designer offers another solution: scripting. When creating a component, it is possible to attach a script file to this component. The documentation states that this script is used when building the runtime, but that is all that is said about it. No documentation whatsoever can be found, so a dead end once again?
Environment
To determine whether scripting can be a solution to our problem and to circumvent the missing documentation, we first have to take a look at the environment we have available when the embedded tools are installed. The base of the environment is made out of a SQL Server database. This database contains all components, repositories, packages and other stuff we need to build our own runtimes. On top of this database we have Component Designer and Target Designer. These tools allow us to build our own components and make them available in the database and to build our own runtime. Between these tools and the database is the Component Management Interface (CMI). This is a COM based layer that offers a generic interface to the database for all tools.
The entire structure of the elements in the database is object oriented. Components, Resources, Repositories are all examples of classes. The element in the database is the class, the element in the configuration is an instance (object) of that class.
The following table shows a couple of interfaces that are available within the CMI together with the user interface elements they represent (if any):
| IResource | The resources belonging to a component (instance). Resources are FBA commands, but also all files that are part of a component. | | IDependency | Dependency of a component. | | IInstance | Instance of a component. | | IConfiguration | The configuration which is build out of components . | | IExtProperty | All elements can contain (extended) properties. These are accessed through this interface. |
When it is possible to use these interfaces in our script, it should be possible to do what we want. We still have a couple of questions to answer:- What hooks are called in my script?
- How do I use the interfaces mentioned above when in a script?
Due to the fact that nothing is documented, it is kind of tricky to find out what hooks are called. Below are two routines mentioned that are most important to our solution.
| OnBeginBuild | When this subroutine is defined in the script, it will be called before the build is started. | | OnEndBuild | When this subroutine is defined in the script, it will be called after the build is done. |
A predefined variable called cmiThis is the instance of our own component (IInstance interface).
This concludes all we need to build a solution. Let's do it.
The solution
Let's go back to the original problem: A couple of files need to be disabled (not installed) when we are building a runtime without printer.
To start we need a method for the user to say whether he wants to use a printer or not. This can easily be accomplished by using an advanced property, which we add to our component in Component Designer:

It is common to give properties a three-letter prefix. The prefix 'cmi' is used by Microsoft, so choose your own one here. I went for the generic 'OEM'.
This advanced property can be set to FALSE in Target Designer when no printer is used. We 'only' have to make sure that something is done when the user sets or resets this property. We want to disable files (so they won't get installed) when the property is set to FALSE. This means that we have to do something before the build starts, meaning we have to write a cmiBeginBuild subroutine.
Sub cmiBeginBuild(dwFlags) If cmiThis.Properties("oemUsePrinter").Value Then REM do the enabling of the files. Else REM do the disabling of the files End If End Sub
As you can see in the code, cmiThis.Properties allows us to get to the properties of the component. We use it to get to the one with the name 'oemUsePrinter' and check the Value. cmiThis.Resources gives us a collection of resources (IResource). We need this to find the correct files. We simply find the files by using their names and when found enable or disable them as needed.
Sub cmiBeginBuild(dwFlags) Dim myResource Dim myFiles myFiles = "PharmaciePrint.dll;PharmaciePrintUI.dll" For Each myResource in cmiThis.Resources If instr(1,myFiles,myResource.DisplayName) Then myResource.Disabled = not cmiThis.Properties("oemUsePrinter").Value End If Next End Sub
We have to put the code above in a separate sourcefile (i.e. Pharmacie.vbs) so we can add it to the component.

When this is done, the component can now be saved and imported in the database. The component is now ready to be used.
Usage
Create a new project in Target Designer and only add the Pharmacy component. Set the advanced property for the printer use to FALSE en choose build (ignore the dependency check; this will only slow you down in this case). Check the status for the files when the build is done. Both files that are related to printing must be striked (disabled). When we check the image on the hard drive, it should only show the .exe file.
You can try it again with the property on TRUE to see that all three files are installed on the runtime.
 Ready?
Did we help our component user with the solution we created? Well, yes and no. He doesn't have to remember to disable or delete components or add components, but he still needs to set a property. We stated at the beginning of this article that we wanted to find a solution that was really user friendly. Suppose the image will include a printer. Isn't it true that one of the components in the configuration will be a printer driver? Wouldn't it be nice if we could detect the presence of such driver and adjust our files accordingly? We can get to all components in the configuration through the IConfiguration interface. Luckily cmiThis (IInstance) has a method Configuration to get to that configuration.
As you can see in Component Designer it is possible to add a component to one or more groups. All printer drivers are added to the group Hardware\Devices\Printers. The IInstance interface gives us a function called GroupVSGUIDS which returns a collection of BSTR's containing the VSGuids for all groups that component belongs to. Knowing this, we can now write the following code :
Dim myFoundDriver myFoundDriver = False Set myInstances = cmiThis.Configuration.Instances For Each myInstance In myInstances myGroupVsGuids = myInstance.GroupVSGUIDS For Each myVsGuid In myGroupVsGuids If myVsGuid = "{DE57767C-9566-11D4-8E84-00B0D03D27C6}" Then myFoundDriver = True End If Next Next
The outer loop iterates over all instances in the configuration. The inner loop checks the GUIDS. The string {DE57767C-9566-11D4-8E84-00B0D03D27C6} is the GUID for the group Hardware\Devices\Printers. The variable myFoundDriver is True when a printerdriver is found, so instead of checking the advanced property, check this variable instead (see complete code at the end of the article).
Conclusion
We have only seen a small part of all posibilities we have when manipulating the CMI by ourselves. Think of adjusting dependencies automatically (how do you think the selector prototype works?) or dynamically adding of registry values to the runtime. Another point that has not received any thought is the DHTML interface we can offer to our advanced properties. This allows for user friendly adjustment of the properties. Enough for now, but as you can easily guess, to be continued . . .
Option Explicit Sub cmiOnBeginBuild(dwFlags) Dim myVsGuid Dim myGroupVsGuids Dim myInstances Dim myInstance Dim myFoundIt Dim myResource Dim myFiles myFoundIt = False
Set myInstances = cmiThis.Configuration.Instances For Each myInstance In myInstances myGroupVsGuids = myInstance.GroupVSGUIDS For Each myVsGuid In myGroupVsGuids If myVsGuid = "{DE57767C-9566-11D4-8E84-00B0D03D27C6}" Then myFoundIt = True End If Next Next cmiThis.Properties("ptsUsePrinter").Value = myFoundIt myFiles = "PharmaciePrintUI.dll;PharmaciePrint.dll" For each myResource in cmiThis.Resources if instr(1,myFiles,myResource.DisplayName) then myResource.Disabled = not myFoundIt end if Next End Sub
About the author: Hans de Vetten is a consultant working for PTS Software, the Netherlands. He has more than eight years of experience in developing object oriented software solutions for Microsoft Windows platforms. In 2003 he added Windows XP Embedded to his field of expertise.
(Click here for further information)
|
|
|
|
|
|
|