Component Designer: Casting the Mold, Part 2
February 18, 2002
Welcome to the February installment of the Microsoft® Windows® XP Embedded Get Embedded column. I don't have anything really pithy to say—let's just get right down to it.
Up to Speed
If you remember from last month, I started talking about Component Designer, the first tool used when following the Windows XP Embedded Development Cycle. (For a refresher, the Development Cycle is located in the Concepts Guide.) Remember I said Component Designer is used to help replace your application's or driver's normal installation routine, and that we needed to know a few things to do that. We discussed file and registry resources last month—this month we'll tackle dependencies and other source level definition (SLD) file objects.
In order to keep the articles manageable, as well as keep the amount of information digestible, I'm electing to wait until March to cover registration of DLLs and scripting options; there's just too much information over those two subjects to cover it this month.
No Deductions for More Dependencies
When you define your component, you tell Component Designer what files and registry resources that component contains, and those resources are used by Target Designer to build your runtime. But what if you need a file or registry resource that's part of another component? For example, you've got an application that needs the Microsoft® Visual C++® Runtime Library (MSVCRT.DLL)—should you make MSVCRT.DLL a file resource in your component? It's a file that exists by default on most Microsoft® Windows® XP installations; how do you ensure it's present on your Windows XP Embedded runtime?
The answer is to create (or express) a dependency. Dependencies are simply other components (either standard components, or other components you've created) that your new component requires to function properly. In our example above, the MSVCRT.DLL file is a file resource in a component we ship, called "Microsoft Visual C++ Runtime." To ensure your component always has MSVCRT.DLL available to it, you create a dependency on the Microsoft Visual C++ Runtime component in your new component. To do this, open your component in Component Designer, and expand the tree view under it to show the Component or Group Dependency branch. Click the Component or Group Dependency branch, then right-click in the Details pane, click Add, and then click Component Dependency. You'll be shown a list of components in the current database; find the one you want, click it, and then click OK.
Sounds easy, right? It is—until you realize you have to search 9,000+ components for the one you want. And what if you don't know which one you need? What if all you know is you need to have a particular file available, and have no idea what component it's in?
One of the strengths of the new database architecture is the concept of filtering—restricting the component list based upon user-specified criteria. The filtering architecture is implemented in the Component Management Interface (CMI), meaning it's consistent across all the tools. It also means that filters created in one tool are usable across all the tools. Filters are also saved between sessions (in \Program Files\Windows Embedded\filters)—create a filter in Component Designer today, and a month from now, you can reuse that filter in Target Designer.
You can create filters in a tool called Filter Manager, by combining a set of nine filter rules to restrict the component list from which you must choose. Let's run an example, assuming you need to know that MQOA.DLL is on the system for your component to work (for reference, this is part of the MSMQ COM interface).
Note As mentioned above, you can also use filters in Target Designer and Component Database Manager. The technique for applying filters is the same for each tool, although the UI for starting Filter Manager may differ slightly.
In the Component Dependency dialog box, click the Filter icon (next to the Filters drop-down list) to open the Filter Manager dialog box. Click New to open the Filter Editor dialog box, which is used to create a new filter. Give your filter a descriptive name in the Name field, and then peruse the filter rules in the Filter Rules list box. Scroll down in that list until you see the rule titled "Component contains the following file: [filelist]." Click that rule, and then click Add Rule to add it to your filter.
In the Filter Description list, double-click the newly added rule to open the Filter Rule dialog box. Select the file to look for by entering a file name in the Enter file name field. For this example, enter MQOA.DLL. Click OK to close the Filter Rule dialog box, click OK to close the Filter Editor, and then click Apply Filter to close Filter Manager and apply the newly created filter. Component Designer will apply the filter and return the filtered component list. The time this takes depends on the complexity of the filter, the speed of your database machine, and the speed of the connection between the database and Component Designer (in other words, a minute or so). Once it's done, however, you'll notice that the Component list shows only "Message Queuing (MSMQ) COM Library."
At the bottom of the Component Dependency dialog box, you can select whether your new component depends on or conflicts with the selected component. This is important if you know you're application doesn't play well with another. By setting a conflict, you can ensure that only one component will be allowed in the configuration at any time.
Group Dependencies—Better Than They Sound
You'll also notice you can create group dependencies, which are simply dependencies expressed from your new component on a group of components. For example, let's say your new component requires that a modem is installed to function properly.
Navigate to the Component or Group Dependency branch again, right-click in the right-hand pane, and click Add, and then click Group Dependency. This will open the Group Dependency dialog box. In the list of groups shown, find and select the ClsInstall:Modem group.
At the bottom of this dialog box, you can see you have a choice of six different dependency rules. These rules give you flexibility to tailor how many components of the given group are affected by your group dependency. The first five should be self-explanatory. The last simply says to use whatever dependency rule was defined when the group was defined. You can define your own dependency groups, if you like. We'll get to that in a few paragraphs, and discuss the default dependency rule then.
One last thing to add about component and group dependencies: Having them in your database doesn’t mean they’re automatically enforced in your configurations. They are used by the dependency checking mechanism in Target Designer to help enforce the rules they define. If you skip the dependency check in Target Designer, you can circumvent the dependency rules. Do this at your own risk, however—ignoring dependency rules can lead to unbootable and unusable runtimes.
Order of the Day
There's one last type of dependency we didn't cover. It's so special, it has it's own branch under your component. Build-order dependencies are, well, dependencies on the order in which components are built. In other words, they dictate when your component will be built in relation to other specified components. They really don't mean much until you start dealing with advanced build script, where you can direct parts of the build process. In that case, however, it may be vital that your new component builds before or after some other component.
As with component and group dependencies, you can have component or group build-order dependencies. The dialog box is similar to the Component and Group Dependency dialog boxes. First you pick a component (optionally using filtering); then you select the build order by specifying the selected component to build either before or after your component.
Build-order dependencies are rare, but important—especially if you're building a set of components that interact. Unless you know that you need to define the build order, you should probably stay away from defining build-order dependencies. It is very possible to create circular build-order dependencies (Foo1 builds before Foo2, which builds before Foo3, which builds before Foo1), which results in configurations that cannot be built.
Remember we discussed group dependencies above? In order for a group dependency to work, the group has to have some member components. These groups can grow with the addition of new components. In fact, you can put your component into as many (or as few) groups as you like. To do this, click the Group Membership branch under your component, right-click in the Details pane, and click Add Group Membership.
There are three types of groups your new component can belong to—a category, a dependency group, or a package. Categories define where your component appears in the component list. Dependency groups bring similar components together to allow dependencies to be expressed on the group. Packages bring components and repositories together to better manage them. Let's go over these three different groups, starting with categories.
When you view the component list in Target Designer, the default view on installation is a tree view of Categories, with components as the leaves of the tree. These categories are highly structured, starting with the high level Hardware and Software categories, drilling down to such categories as Software : System : User Interface : Shells : Windows Shell.
You can pick any reasonable category for your component you like, although the most heavily used categories should be Software : Applications for your applications, and Hardware : Devices for your device drivers. Just remember, you've got to find it once it's there, so pick the category wisely. Components with no category appear as leaf nodes under the categories in the category list in tree view, which is like putting all your files in the root of your C: drive—easy to find for small numbers of files, but messy for lots of files. We recommend you pick a category for each component you author.
You can create your own dependency group if you wish. This is only useful if you have a collection of components with some commonality—for instance, a collection of network interface cards, or different versions of a similar application.
Creating a new dependency group is as easy as clicking the Dependency branch of the tree view in Component Designer (you'll find it one level higher than your new component branch), right-clicking in the Details pane on the right-hand side, and selecting Add Dependency. You can fill in the standard properties in the top half of the Form view that appears, just like you did for your new component and your repository. The bottom half is the interesting place.
Aside from the normal Advanced button and Platform selector, you now have a Default Dependency Type drop down list. Remember that "Uses default dependency rules in the group" selection from the Group Dependency dialog box? Here's where that default is set; you're defining the group, therefore you should be able to determine if the prospective members of the group can play with each other well or not.
Packaged and Ready to Go!
Packages are the last type of group. Like dependencies groups and categories, they allow components to be associated with each other, but unlike the other groups, they also allow repositories as members. As an example, the Windows XP Embedded QFE team recently released a set of five security fixes. Each fix contains an updated component as well as a new repository holding the updated files. Each fix also contains a new package definition—the updated component and new repository are each members of the new package.
Defining the package is quite easy: Select the Package branch, right-click in the right-hand pane, and click Add Package. Fill out the standard properties, and you're done. Now, click the Group Membership branch under your component or repository. Right-click in the right-hand pane, and click Add Group Membership. In the Available Groups box, you will see your newly created package; click it and then click OK.
Packages are used in Component Designer and Component Database Manager to provide management options. Deleting a package deletes the member components and repositories. In places where a package drop-down list is presented, it also provides a quicker alternative to filtering to locate components.
The last object we want to discuss here are repository sets. These are, oddly enough, sets of individual repositories, collected into one unit. Your components can reference any defined repository or repository set as its default repository. Sounds simple, but why use them?
Let's use the Windows XP Embedded QFEs above as an example. Almost every standard component in Windows XP Embedded lists the "Windows XP Embedded Client (x86)" repository set as its repository. That set contains the repositories that hold the Windows XP binaries. In a normal Windows XP installation, those files are located on the system drive in different directories. Normal Windows XP QFEs know where those files are, and how to replace them.
In Windows XP Embedded, however, we needed to update the files in the repository itself. One way to do this would be to physically replace the files in the physical repository on the database server. That was unacceptable, however, for a number of reasons. First, it violated our architecture rules that the repositories are static. Second, it could have caused problems with existing component scripts that expect a particular file version to exist.
A second way to do this is to create a new repository with all the files the particular component needs, replacing the updated ones before the repository is created on the database server. This was also unacceptable: One of our QFEs was to the Microsoft® Internet Explorer component, which contains around 4.2 MB in 62 file resources. If we followed this example, every Internet Explorer QFE we implemented would add another 4.2 MB repository to the database server.
The solution we went with was to create a new repository containing only the updated files. That repository was then added as a member of the "Windows XP Embedded Client (x86)" repository set. That way, the component could continue to refer to the original repository set, but when it required one of our updated files, it would find it in our new repository. This satisfied our need to have our QFE files separate from the main repositories, and still allow the updated components to find and use the updated files.
You might use repository sets to contain a set of related repositories for your related components. In that way, each component can refer to a single, defined repository set. Every new repository you create can be added to that set.
There's one caveat to using repository sets: you need to make sure each repository in the set contains files with unique file names. The build process makes no guarantees in what order it will search the repositories in a set looking for files. If two repositories in a set contain a file with the same name, it's anyone's guess which one will make it into your runtime. However, as I said last month, you can define the source and destination names of each file resource. In our QFEs, each updated file resource was renamed to include the Knowledge Base article number, and the source name for the file resource in the component was modified accordingly.
We've covered all the major tree items in Component Designer over the past two months—components, dependencies, repositories, repository sets, and packages. In addition, we've discussed the major parts of a component, including component, group, and build-order dependencies, file and registry resources, and group memberships.
Next month, we delve into the fun stuff: post-processing FBA directives to allow you to register DLLs and create shortcuts (to name a couple), configuration scripting to allow user input in your components, and build script writing to tailor the build process to meet your component's needs. There's a lot of material yet to cover; hope you stay tuned.
This article should be published by the time I reach the Embedded Systems 2002 Messe und Kongress in Nuremberg, Germany. I hope my German readers will visit me at the Microsoft booth. I look forward to meeting as many of you as possible, and to answering as many questions as I can.
As always, if you need a question answered between articles, don't hesitate to post it on our newsgroup; attach to your favorite newsreader, and subscribe to microsoft.public.windowxp.embedded. I'll be there, along with a number of the Windows XP Embedded development team members to answer questions.