
The Device Emulator Manager API
The Device Emulator Manager API is implemented as a Component Object Model (COM) In-Process Library. The .NET assembly file Microsoft.DeviceEmulatorManager.Interop.9.0.dll provides access to the Device Emulator Manager API from managed code. The classes and interfaces of the Device Emulator Manager API appear to .NET applications just as they are implemented and do not provide any abstraction over their COM characteristics. For this reason, you’ll find that you sometimes deal directly with COM error codes, also known as HRESULTs, and COMExceptions when working with the API.
Note: |
|---|
|
This paper offers one possible .NET abstraction for the Device Emulator Manager API. This abstraction is discussed in the second part of this paper.
|
To use the Device Emulator Manager API from .NET, you need to include a reference to the Microsoft.DeviceEmulatorManager.Interop.9.0 assembly. The assembly does not appear in the Visual Studio 2008 Add Reference dialog box list of .NET assemblies; you’ll need to browse to the Microsoft.DeviceEmulatorManager.Interop.9.0.dll assembly file located in the %Program Files%\Microsoft Device Emulator\1.0 folder. All of the types in the Device Emulator Manager API are in the "Microsoft.DeviceEmulatorManager.Interop" namespace. When working with the Device Emulator Manager API, you'll also want to include a using statement (C#) or project Import (Visual Basic .NET) for the "System.Runtime.InteropServices" namespace so that you handle any COMExceptions that API may throw.
Note: |
|---|
|
The Device Emulator Manager and Device Emulator are always upgraded in place and therefore overwrite the previous version. For this reason, all versions of the Device Emulator Manager and Device Emulator are located in the 1.0 folder under the %Program Files%\Microsoft Device Emulator folder irrespective of their actual version.
|
Controlling the Device Emulator
The most common Device Emulator API interface you use is the IDeviceEmulatorManagerVMID interface. The IDeviceEmulatorManagerVMID interface represents an individual Device Emulator image such as Windows Mobile 5.0 Pocket PC, Pocket PC 2003 SE, or Windows Mobile 6 Standard. Using the IDeviceEmulatorManagerVMID interface, you can programmatically perform the actions exposed by the Device Emulator Manager user interface. Table 1 shows the IDeviceEmulatorManagerVMID interface methods and corresponding descriptions.
Table 1. IDeviceEmulatorManagerVMID Interface Methods
|
Method
|
Description
|
|---|
|
BringToFront
|
Moves the Device Emulator to the foreground of the desktop display and makes the Device Emulator the active application.
|
|
ClearSaveState
|
Clears the Device Emulator's saved state file (.dess). The next time you start the Device Emulator, the Device Emulator will have no existing state and will start from the ROM image. The Device Emulator will therefore appear as a new device. Any software you have installed or setting changes you have made are lost.
|
|
Connect
|
Starts the Device Emulator.
|
|
Cradle
|
Puts the Device Emulator in the virtual cradle and causes the Device Emulator to connect to the desktop using Microsoft ActiveSync® (Windows XP) or Windows Mobile Device Center (Windows Vista®).
|
|
get_Name
|
Returns the display name of the emulator such as Windows Mobile 6 Professional Emulator or Windows Mobile 5.0 Smartphone. The display name is the same value that appears in the Device Emulator Manager user interface's list of Device Emulators.
|
|
get_State
|
Returns the EMULATOR_STATE enumeration value corresponding to the Device Emulator's current state. Possible values are EMU_NOT_RUNNING, EMU_RUNNING, and EMU_CRADLED. A Device Emulator is in only one state at a time.
|
|
get_VMID
|
Returns the Device Emulator's Virtual Machine Identifier (VMID), which is a globally unique identifier (GUID) unique to each emulator image.
|
|
GetConfiguration
|
Returns an XML string describing the Device Emulator's current configuration.
|
|
Reset
|
Performs a soft-reset or hard-reset of the Device Emulator as specified by the passed integer parameter. A non-zero parameter value specifies a soft-reset.
|
|
SetConfiguration
|
Sets the Device Emulator to the configuration described in the provided XML string parameter.
|
|
Shutdown
|
Shuts down the Device Emulator, optionally saving the Device Emulator's state as specified by the passed integer parameter. A non-zero parameter value specifies that the state should be saved.
|
|
UnCradle
|
Removes the Device Emulator from the virtual cradle and causes the Device Emulator to disconnect from ActiveSync (Windows XP) or Windows Mobile Device Center (Windows Vista).
|
Note: |
|---|
|
The GetConfiguration and SetConfiguration methods provide a wide variety of capabilities; a separate paper will discuss the capabilities of these functions in detail.
|
The following code sample demonstrates how to cradle an emulator using the IDeviceEmulatorManagerVMID interface.
void CradleEmulator(IDeviceEmulatorManagerVMID theEmulator)
{
if (theEmulator != null)
throw new ArgumentException("Must provide a valid reference");
EMULATOR_STATE state = theEmulator.get_State();
if (state == EMULATOR_STATE.EMU_NOT_RUNNING)
theEmulator.Connect(); // start emulator
if (state != EMULATOR_STATE.EMU_CRADLED)
theEmulator.Cradle(); // cradle to start ActiveSync
else
theEmulator.BringToFront();
}
To cradle the emulator, the emulator must already be running; therefore, the preceding sample code gets the emulator state by calling the get_State method and then checks to see if the current state indicates that the emulator is not running. If this is the case, the program starts the emulator by calling the Connect method. The program then checks the emulator state to see if the emulator is already cradled. If necessary, the program then cradles the emulator by calling the Cradle method; cradling the emulator initiates the emulator's connection to ActiveSync (Windows XP) or Windows Mobile Device Center (Windows Vista). If the emulator is already cradled, the program gives the emulator focus by calling the BringToFront method.
Understanding the Device Emulator Manager Hierarchy
The beauty of the Device Emulator Manager API is that interacting with and controlling a Device Emulator is quite easy. To work with any of the Device Emulators you must first navigate through the Device Emulator Manager hierarchy.
The Device Emulator Manager hierarchy is divided into three layers. At the highest level are the categories. Categories are used to group Windows Mobile Software Development Kits (SDKs). Each SDK contains a list of Device Emulators. To locate a specific Device Emulator, you need to traverse the layers of the hierarchy above it.
Note: |
|---|
|
The next several sections discuss the details of programmatically working with the COM-style features of the Device Emulator Manager API hierarchy. You are encouraged to read these sections to get a better understanding of the API. However, if you have only limited time available you may want to jump ahead to the A Device Emulator Manager Wrapper section, which discusses a simplified Device Emulator Manager API programming model.
|
To get started with the Device Emulator Manager API, you must first create an instance of the Device Emulator Manager. You do this by creating an instance of the DeviceEmulatorManagerClass class. This class encapsulates the COM component that represents the Device Emulator Manager. When working with the Device Emulator Manager, you can use the DeviceEmulatorManagerClass class directly or use the IDeviceEmulatorManager interface. Both the class and the interface provide all of the same methods. For consistency with the interface-based nature of COM, this paper uses the interface as shown in the following code sample.
IDeviceEmulatorManager _emulatorManager;
_emulatorManager = new DeviceEmulatorManagerClass();
Working with the Category List
Once you have a reference to the Device Emulator Manager, you can start working with the first level of the hierarchy, the categories. Two points that you should be aware of when iterating through the list of categories are the multifaceted nature of the IDeviceEmulatorManager interface and the way the IDeviceEmulatorManager interface indicates the end of the list. First, the multifaceted nature of the IDeviceEmulatorManager interface. The IDeviceEmulatorManager interface plays three distinct roles: the Device Emulator Manager, the enumerator over the list of categories, and the current category in the list. When working with the individual interface methods you'll want to be certain that you understand to which of these three identities the specific method applies. The other point is the way that the IDeviceEmulatorManager.MoveNext method indicates that there are no more categories left, which is to throw a COMException. The following sample demonstrates aspects of both points.
void ListDEMCategoryMembers(IDeviceEmulatorManager emulatorManager)
{
const int END_OF_DATA = -2147024637;
string categoryName;
emulatorManager.Reset(); // Ensure that at beginning of categories
try
{
while (true)
{
categoryName = emulatorManager.get_Name();
Console.WriteLine(categoryName);
ListDEMSdkMembers(emulatorManager); // SDKs for this category
_mulatorManager.MoveNext();
}
}
catch (COMException ex)
{
if (ex.ErrorCode != END_OF_DATA)
throw ex;
}
}
As mentioned previously, the IDeviceEmulatorManager interface represents multiple roles. You can see in the preceding sample code that the IDeviceEmulatorManager reference, emulatorManager, exposes the methods Reset, MoveNext, and get_Name. The first two methods allow you to work with the category enumerator. The Reset method positions the emulatorManager reference at the beginning of the category list; the MoveNext method advances the emulatorManager reference to the next category in the list. The get_Name method returns the name of category at the emulatorManager's current position in the category list thereby representing the current category. This code sample does not show any of the IDeviceEmulatorManager methods that represent the Device Emulator Manager role, however upcoming code samples will. Similar multi-role behavior is repeated in the IEnumManagerSDKs interface discussed in the next section.
With regard to the way that the MoveNext method indicates that no categories remain, notice that the loop iterating over the category list uses a true value as the exit condition for the while loop. Using a true value within the loop condition often indicates an endless loop, but not in this case. As you can see, the while loop is contained within the try portion of a try/catch block. The way this code is written, the only way to exit the while loop is for one of the Device Emulator Manager API methods to throw an exception. In fact, that is how the MoveNext method exits the loop.
Being written as a COM component, the Device Emulator Manager API uses numeric codes known as HRESULTs to indicate the success or failure of each method call; in most cases a zero-value HRESULT indicates success. In addition to success or failure, HRESULTs can also indicate a state change such as reaching the end of a list. In the case of the MoveNext method, this is exactly what happens.
When the call to the MoveNext method causes the emulatorManager to advance beyond the end of the category list, the MoveNext method returns a specific HRESULT value (-2147024637) to the COM layer to indicate that no further data exists in the category list. The .NET Interoperability layer interprets the non-zero HRESULT value as an error and translates the HRESULT into a COMException. With this being the case, the .NET Interoperability layer will always raise a COMException when reaching the end of the category list; the preceding code catches the exception outside of the while loop and checks the COMException HRESULT. When the HRESULT is the expected end-of-data value the program continues normally, otherwise the COMException is re-thrown. The Device Emulator Manager SDK enumerator and Device Emulator enumerator indicate the end-of-data in this same way.
Working with the SDK List
Working with the SDK list is very similar to working with the category list. Each category has its own list of SDKs; therefore, each category has a separate SDK enumerator. The Device Emulator Manager API exposes each SDK enumerator through the IEnumManagerSDKs interface. You access the SDK enumerator for a specific category through the IDeviceEmulatorManager.EnumerateSDKs method for that category. Similar to the IDeviceEmulatorManager interface, the IEnumManagerSDKs interface plays multiple roles acting as both the SDK list enumerator and the current SDK in the list.
The following code sample demonstrates how to iterate over the list of SDKs for a particular category. The method in this sample code is called once for each iteration of the loop within the ListDEMCategoryMembers method shown in the previous sample code.
void ListDEMSdkMembers(IDeviceEmulatorManager emulatorManager)
{
const int END_OF_DATA = -2147024637;
string sdkName;
IEnumManagerSDKs sdkEnum = emulatorManager.EnumerateSDKs();
sdkEnum.Reset();
try
{
while (true)
{
sdkName = sdkEnum.get_Name();
Console.WriteLine("\t"+sdkName);
ListDEMEmulators(sdkEnum);
sdkEnum.MoveNext();
}
}
catch (COMException ex)
{
if (ex.ErrorCode != END_OF_DATA &&
ex.ErrorCode != DeviceEmulatorManagerErrorCodes.E_ENUMSDK_NOT_LOADED)
throw ex;
}
}
Just as was the case in the earlier sample code for iterating over the category list, the Device Emulator Manager API indicates the end of the SDK list through a COMException with an HRESULT value of -2147024637. In addition to the catch block handling the end of the SDK list HRESULT, this code sample includes a check for an additional HRESULT. This additional HRESULT check handles the case of a category that contains no SDKs.
The Device Emulator Manager API is implemented such that the Reset method positions the enumerator at the first item in the list and you cannot call the MoveNext method until you no longer need the first list item, because the call to the MoveNext method immediately advances the enumerator to the next member in the list. What this means is that there is no method call you can make that will indicate that the list is empty (in other words a category with no SDKs). The only way to know that the SDK list is empty is to call one of the methods that represent the current list item, such as the get_Name method. When you call the get_Name method when no SDK is available, the method returns an HRESULT value that corresponds to the constant DeviceEmulatorManagerErrorCodes.E_ENUMSDK_NOT_LOADED, which the .NET Interoperability layer translates into a COMException containing this same HRESULT. Like when reaching the end of the list, the program catches the COMException and checks that the HRESULT has the expected value, if not, the COMException is re-thrown.
Working with the Emulator List
The IEnumVMIDs interface represents the list enumerator for the Device Emulators within an SDK. Working with the emulator list is very much like working with the SDK list. The following code sample demonstrates how to iterate over the list of Device Emulators. The method shown in this code sample, ListDEMEmulators, is called once for each iteration of the loop within the ListDEMSdkMembers method shown in the previous sample code.
void ListDEMEmulators(IEnumManagerSDKs sdkEnum)
{
string emulatorName;
IDeviceEmulatorManagerVMID theEmulator;
IEnumVMIDs vmidEnum = sdkEnum.EnumerateVMIDs();
vmidEnum.Reset();
try
{
while (true)
{
theEmulator = vmidEnum.GetVMID();
emulatorName = theEmulator.get_Name();
Console.WriteLine(emulatorName);
vmidEnum.MoveNext();
}
}
catch (COMException ex)
{
if (ex.ErrorCode != END_OF_DATA &&
ex.ErrorCode != DeviceEmulatorManagerErrorCodes.E_ENUMVMID_INVALID_VMID &&
ex.ErrorCode != DeviceEmulatorManagerErrorCodes.E_ENUMVMID_NOT_LOADED)
throw ex;
}
}
One minor difference in looping through the Device Emulator list as compared to the SDK list is the error codes you must handle in the case of an empty list. When working with the SDK list, the enumerator always returns DeviceEmulatorManagerErrorCodes.E_ENUMSDK_NOT_LOADED, whereas the Device Emulator enumerator indicates an empty list by returning DeviceEmulatorManagerErrorCodes.E_ENUMVMID_INVALID_VMID in some cases and DeviceEmulatorManagerErrorCodes.E_ENUMVMID_NOT_LOADED in others. For this reason, the catch block must check for both of these HRESULTs in addition to the end-of-data HRESULT.
Unlike the IDeviceEmulatorManager and IEnumManagerSDKs interfaces, which each represent both their list enumerator and the current item in their list, the IEnumVMIDs interface represents only the Device Emulator list enumerator. To access an individual Device Emulator enumerator, you use the IDeviceEmulatorManagerVMID interface. Through this interface you can access not only the emulator name but gain full control of the Device Emulator including all of the capabilities shown earlier in the Controlling the Device Emulator section of this paper.
Accessing a Device Emulator
The Device Emulator Manager API does not provide any facility for directly accessing a Device Emulator; you loop through the layers of the hierarchy to find a particular Device Emulator. For this reason, one of the most important facilities to have for the Device Emulator Manager API is a Find method. To create a Find method you combine the category, SDK, and Device Emulator iteration code from the previous three code samples adding a condition in the Device Emulator iteration code to locate the Device Emulator of interest by matching its name.
A complete implementation of a Device Emulator Manager API Find method is available at Windows Mobile Device Emulator Programmability - Just Needs a Find-Emulator Function.
Monitoring for Changes
The information in the Device Emulator Manager hierarchy can change for a variety reasons: a user installs or uninstalls a SDK, a user creates or deletes an emulator configuration, an application creates or deletes an emulator configuration, and so on. The Device Emulator Manager API provides notification of such changes through a Win32® synchronization event object.
You should monitor for changes in the Device Emulator Manager hierarchy to be aware of any new items but more importantly to avoid any unexpected issues that may occur as a result of the removal of one of the Device Emulator Manager objects.
To monitor for changes in the Device Emulator Manager hierarchy, you must create a named Win32 synchronization event object and pass the name of event object to the IDeviceEmulatorManager.RegisterRefreshEvent method. You must then launch a thread that blocks on the event. If any changes in Device Emulator Manager hierarchy occur, the Device Emulator Manager will signal the synchronization event. The following code sample demonstrates how to monitor for a change in the Device Emulator Manager hierarchy.
const string refreshEventName = "MyApplication.Refresh";
EventWaitHandle _refreshEvent;
Thread _refreshEventThread;
volatile bool _shutDownRequested = false;
private void SetupEventListener(IDeviceEmulatorManager emulatorMgr)
{
// Create the event
_refreshEvent =
new EventWaitHandle(false, EventResetMode.AutoReset, refreshEventName);
// Start thread to monitor for changes
_shutDownRequested = false;
_refreshEventThread = new Thread(RefreshEventMonitor);
_refreshEventThread.Start();
// Notify Device Emulator Manager API of event name
emulatorMgr.RegisterRefreshEvent(refreshEventName);
}
void RefreshEventMonitor()
{
while (!_shutDownRequested)
{
bool signaled = _refreshEvent.WaitOne();
if (signaled && !_shutDownRequested)
// hierarchy has changed
}
}
private void ShutdownEventListener(IDeviceEmulatorManager emulatorMgr)
{
// Tell Device Emulator Manager that we're done listening
emulatorMgr.UnRegisterRefreshEvent();
// Bring down thread monitoring the refresh event
_shutDownRequested = true;
_refreshEvent.Set();
_refreshEventThread.Join();
// Release event
_refreshEvent.Close();
}
The code that actually monitors for changes to the Device Emulator Manager hierarchy is in the RefreshEventMonitor method. This method runs on a thread separate from the main application. Anytime the synchronization event signals, RefreshEventMonitor performs the appropriate action to deal with the Device Emulator Manager hierarchy changes. Each instance of the DeviceEmulatorManagerClass and the related Device Emulator Manager classes cache the list of objects in the Device Emulator Manager hierarchy; therefore, most applications should call the IDeviceEmulatorManager.Refresh method to update this cached list. Other actions may include re-reading the contents of the Device Emulator Manager hierarchy, updating any cached references your program holds to one of the Device Emulator Manager objects, and so on. When these actions are complete, the RefreshEventMonitor method then goes back to monitoring for changes.
Note: |
|---|
|
Because the function monitoring the refresh synchronization event runs on a thread separate from the main application thread, use caution if you call the IDeviceEmulatorManager.Refresh method from this thread, as the update to the Device Emulator Manager hierarchy may cause problems with code using the hierarchy on the main application thread. For information on synchronizing activities between threads, C# developers should see Thread Synchronization from C# Programming Guide; Visual Basic .NET developers should see Thread Synchronization from Visual Basic Language concepts.
|
The RefreshEventMonitor continues monitoring for changes until the class-level field, _shutDownRequested, is set to true. Notice that the _shutDownRequested field is declared with the volatile keyword; this is necessary because the thread that modifies the field value is separate from the thread that reads the field value. Without the volatile modifier, the thread monitoring for refresh events may cache the value of the _shutDownRequested field, possibly causing the thread to miss the main application thread's change to the field value.
The code to start the process of monitoring for changes to the Device Emulator Manager hierarchy is contained in the SetupEventListener method. This method starts by creating a named Win32 synchronization AutoReset event object. As an AutoReset synchronization event, the synchronization event guarantees that the RefreshEventMonitor method will carry out exactly one iteration of the while loop; the next time the RefreshEventFunction calls the _refreshEvent.WaitOne method, the thread is guaranteed to block again. The event name can be any value you like but should be a value that is not likely to collide with event names that other programs may use.
Once the program creates the synchronization event, the program then goes on to start the thread on which the RefreshEventMonitor method runs. Finally, the program informs the Device Emulator Manager API of the name of the event to signal if the Device Emulator Manager hierarchy changes.
When the program no longer needs to monitor the Device Emulator Manager API for changes, most likely just before the application exits, the ShutdownEventListener method takes care of the details. The ShutdownEventListener method first calls the IDeviceEmulatorManager.UnRegisterRefreshEvent method to inform the Device Emulator Manager API that the program no longer needs to be notified of changes. The method then shuts down the RefreshEventMontor thread and releases the synchronization event.