How does IntelliSense work in smart device projects?
Would you like to know how Intellisense in Managed Smart Device Project is different from Intellisense in Managed Desktop projects? What is the base architecture that is used and how extensible is it? If so, then you may want to continue reading.
Intellisense is a feature provided in the Visual Studio IDE which aids a developer in coding via auto completion of variable names, functions, class members etc. It helps in reducing the development time as well as reduces the number of compile time errors in the code. In Visual Studio .NET intellisense is implemented as a VSIP package. What this means is that developers can write their own add-ins to Visual studio and use the existing intellisense VSIP package within their add-in. Hence each add-in need not implement intellisense as a sub feature. Each VSIP package is implemented as a VSPackage and is registered with Visual Studio in the “Packages” key under Visual Studio registry key.
First I would like to give you a basic idea of how Intellisense works for managed desktop projects. Every project maintains a list of assemblies that it references. The basic funda behind intellisense is that whenever it is required, load the metadata corresponding to each assembly referenced by the project and cache all the metadata in an Intellisense filter. This filter is then used to identify what all members are visible for any given namespace/assembly.
Intellisense for device projects has similar requirements as for Desktop projects, except for the following:
1) Desktop project contains references to .NET System assemblies (present in the GAC) while device project contains references to .NETCF System assemblies installed in the Visual Studio install directory
2) Device project needs an additional level of filtering, i.e. filtering based on the Target Platform for the project. For eg. “Button” control should not be visible under WinForm controls for Smartphone project (as buttons are not supported in Smartphone) but should be visible for PocketPC project.
Essentially what is needed is a Platform specific intellisense filter. Given the fact that the services required by Smart Device projects have similar requirements and behavior to those required by Desktop projects (except the fact that they depend upon the platform), the obvious design technique chosen was to reuse the existing intellisense filtering done by the Visual Studio shell (used for desktop projects) but add another device specific intellisense filter on top of it. This is where AsmMeta assemblies come into picture. For each .NETCF system assembly (such as System.dll, System.Data.dll, etc.) Visual studio installs an AsmMeta assembly (such as System.PocketPC.asmmeta.dll, System.Smartphone.assmeta.dll, System.Data.PocketPC.asmmeta.dll, System.Data.Smatphone.asmmeta.dll etc.). This assmeta dll will contain information as to whether a given member in the corresponding .NETCF assembly is visible for this platform, whether it is browsable from code editor etc.
So esentially the logic for intellisense in smart device project is as follows:
1) For a smart device project, the VS shell will load the metadata for all assemblies referenced by the project (which include the .NETCF assemblies implicitly referenced when a smart device project is created)
2) Smart devices package implements a Contextual filter. This is a platform specific filter, which will load the metadata from the asmmeta dlls only.
3) Whenever the shell has to show the intellisense drop down it first queries the first filter (one which stores the metadata from referenced assemblies). Then for each of the members that are visible through first filter, it applies the second filter (platform specific filter). All the members visible through the second filter are then shown in the intellisense dropdown.
In this session I will be discussing only the second filter, i.e. Smart device contextual filter. To start with, I will give you a basic idea of individual components that are necessary for Intellisense in Smart Device projects and then will show how they fit into the end to end user scenario.
Considering the requirements of Smart device package, in order to make the entire design fully extensible, Visual Studio has implemented a service called as Platform Services. The main task of this service is that given a Platform ID and the Service ID for the service required, it scans the registry and loads the Package in which the required service is implemented and initializes the service.
Hence, Smart Device Contextual Intellisense filter is implemented as a VSIP service, Given the current platform’s ID and the Contextual filter service ‘s guid the Smart device Contextual Intellisense filter service will be loaded by Platform Services. You will get a clearer picture once I take the example of intellisense filter and explain it.
Intellisense for smart device projects is implemented in a separate VSPackage called Device Configuration Package (DevCfg). DevCfg is a Visual Studio package that mainly deals with accessing and modifying Device configuration settings from the Visual Studio IDE. Intellisense is one of the features provided in DevCfg package. DevCfg implements the contextual Intellisense filter as well as an Intellisense filter provider.
The intellisense filter provider is instantiated whenever intellisense is required. As the name itself suggests it is a provider class for different types of intellisense filters. The filter provider maintains a table of all intellisense filters (platform specific) that have been loaded (for caching purposes). If the intelisense filter for a particular platform has not been loaded, then it creates a new one and adds it to the table.
As told earlier the intellisense filter implemented in DevCfg is platform specific, i.e. it is a Contextual Intellisense Filter which depends upon the context in which intellisense is required. This context is got from the Project that the developer is currently working on.
So how does everything fit in the big picture? The following sequence of steps might make it clearer:
1) User creates a Smart Device project (let us consider a Managed VB Pocket PC 2003 project): Assuming this is our first smart device project created in this instance of devenv, Visual Studio loads the Device Configuration Package into memory. The project is initialized by the Smart Device Project System. Its platform is set to Pocket PC 2003.
2) User opens the code editor and types in the following code: “Dim vas as “. At this instance the visual studio shell identifies that intellisense package is needed. The Visual studio shell loads its intellisense filter and loads the metadata from all assemblies referenced by the current project into it. Then it checks that this being a smart device project it needs to do second level of filtering. It then passes the control to smart device project system.
3) The project system gets the current context (essentially a handle to the current project). The project system then creates a Device specific service provider. This service provider is responsible for providing all the services specific to the device (this provider is created only for the first time any service required for a specific platform has to be loaded. For any further services for that platform, it is reused). This service provider in turn communicates with Platform services.
4) Device specific service provider passes on the Platform ID and the Service ID for Intellisense Filter to Platform services. Platform services searches the registry for the Platform ID registry key under “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Platforms” key. Then it searches for the Service ID key under the “Services” key for that platform. This key contains the Package GUID in which the service is implemented (for this scenario it is GUID for DevCfg) as well as the service name (for this scenario service name is: “IVsContextualIntellisenseFilterProvider”) values. It then loads the corresponding package if needed (in our case DevCfg is already loaded) and then instantiates the service provider (IVsContextualIntellisenseFilterProvider). This service provider is then returned to the Device Service provider.
5) The device service provider sites the filter provider with the current project context to retrieve the ContextualIntellisenseFilter (it is also added to the table of filters maintained by the service provider).
6) The Contextual Intellisense Filter is then initialized. During initialization, the filter goes through the list of all project references, then uses the AsmMetaBinding service to map each reference to the corresponding platform specific AsmMeta dll (if one exists, otherwise it skips that assembly), and then uses Metadata Import service to import all the metadata for that dll (During this phase, AsmMetaBinding service and the MetaData services are also loaded through platform services). This metadata is cached by the filter and used to identify which all variables, functions, classes etc. and also their corresponding properties, methods and events are visible in the given context.
7) The IDE then creates a dropdown and populates it with the metadata from the Intellisense filter .
One of the main advantages of this design is that it is fully extensible. Tomorrow if a totally new device type comes out in the market, to add intellisense support for that we only require to create AsmMeta assemblies corresponding to that device and add the corresponding registry entries for Platform services, as simple as that J.
- Manish Vasani