USB Device View

This is an advanced example of integration between Windows Vista DDK (Driver Development Kit, part of the WDK) and Microsoft Robotics Studio (1.5) using C++/CLI programming language.

This tutorial is provided in the language. You can find the project files for this tutorial at the following location under the Microsoft Robotics Studio installation folder:

Sample location
 Samples\Technologies\Interop\UsbView

The primary focus of this tutorial will be to show how the USB View DDK sample can be turned into a Microsoft Robotics Studio DSS service. As a part of that several other items will be discussed:

  • How to write a C++/CLI DSS service that interacts with native class based C++ code and APIs
  • How to talk to device drivers using the I/O control code (IOCTL) calls
  • How to work with managed to native and native to managed calls and data
  • How to work with subscribers and notifications in a C++/ CLI DSS service
  • How to use native to managed callbacks for notifications
  • How to add an XSLT to a C++/CLI DSS service

We are going to do so in several steps:

Prerequisites

Software

This tutorial requires the Windows Vista DDK also called WDK which is available online

An introduction to WDK is outside the scope of this tutorial. Instead this tutorial uses an existing USB View sample from WDK and shows how to turn it into a Microsoft Robotics Studio DSS service.

The WDK USB View sample is an application that lists all available USB devices (including host controllers and hubs) with additional information about them. This sample is located in the WinDDK\6000\src\usb\usbview directory of your Vista WDK installation. Refer to that location on documentation about the USB View application itself.

The USB specific code parts of the tutorial assume that you are familiar with the USB view sample of the DDK. The other pieces can be easily applied to services focused on other areas.

This tutorial is designed for use with Microsoft Visual C++/CLI 2005. (Note that this is incompatible with earlier, 2003 or before, edition of C++ with managed extensions) You can use:

  • Microsoft Visual C++ 2005 Express Edition.
  • Microsoft Visual Studio 2005 Standard Edition.
  • Microsoft Visual Studio 2005 Professional Edition.
  • or Microsoft Visual Studio 2005 Team System.

You will also need Microsoft Internet Explorer or another conventional web browser.

Step 1: Code migration

This tutorial only requires some pieces of the USB View sample. These are the pieces that are actually responsible for aggregation of the USB device information. Since we are not building a windows application, but a DSS service instead, the application specific code, and the information display code is replaced by DSS specific code. The following list shows how the code files from the WDK USB view example are used in this tutorial:

File Name Role Changes
usbdesc.h Structures for audio device descriptions Unchanged, not used
usbview.h Main include file, contains declarations from other code files Unused declarations commented out, new tree item structure created
vndrlist.h Contains USB Vendor Id list Unchanged
debug.cpp Debug helper functions from the USB view sample Unchanged, not used
devnode.cpp Contains Device Description function Unchanged
dispaud.cpp Contains function to gather USB audio device information Minor changes for C++ conversion, not used
display.cpp Contains logic to display specific USB device information to the UI Minor changes for C++ conversion, not used, code partially reused in service state marshaling
enum.cpp Main code file with routines to gather USB host controller, hub, and device information Some rework, this is the main code file for enumerating available USB devices
usbview.cpp Entry point of the original application, also contains GUI functions Unchanged, not used

The settings in the tutorials project file where derived from the sources and makefile.mk files of the USB view example. The project file and the code stubs for the basic DSS service were generated by the DssNewService.exe tool, which is part of Microsoft Robotics Studio.

Step 2: Modify original logic

The USB View service does almost the same thing as the USB View application. It maintains a list of USB devices in the system. The USB View application had that list as a TreeView control of the UI, the USB service has the list as its state. Thus, the native logic that gathers this information did not have to change that much. The new structure for the tree is defined in usbview.h and looks like this:

C++
 typedef struct _USB_TREE_ITEM
{
    _USB_TREE_ITEM(){
        NextSibling = NULL;
        FirstChild = NULL;
        info = NULL;
        itemName = NULL;
    }
    _USB_TREE_ITEM* NextSibling;
    _USB_TREE_ITEM* FirstChild;
    PVOID info;
    PCHAR itemName;

} USB_TREE_ITEM, *P_USB_TREE_ITEM;

This structure holds the information, and points to the next child and the next sibling. A new helper function called AddUsbTreeChild is added to the enum.cpp file. It will be used instead of the AddLeaf function that was in the original code. Having very similar signature minimizes the required code changes. Functions that used to take an HTREEITEM parameter before were changed to take a P_USB_TREE_ITEM parameter. These functions are: EnumerateHostController, EnumerateHostControllers, EnumerateHub and EnumerateHubPorts. Another function that changed in this file is CleanupUsbTreeItem, which cleans up everything that the above 4 functions have allocated during device scan. It has changed to recursively walk the tree and clean all children and siblings.

Step 3: Create new helper class

The class myUsbView bridges between the managed service code and the native USB view code. It is a native class and is responsible for marshalling between managed and unmanaged code.The code for this class in the myUsbView.cpp file, and the declarations are in the myUsbView.h file.

3.1 Add logic to enumerate USB devices

The class is responsible for enumerating USB devices and adding them to the services state. This is done inthe EnumerateUsbDevices method, which fairly straight forward:

C++
 void myUsbViewHelper::EnumerateUsbDevices(Robotics::UsbViewSvcState^ _state)
{
    P_USB_TREE_ITEM pUSBDeviceTreeRoot = new USB_TREE_ITEM;

    EnumerateHostControllers(pUSBDeviceTreeRoot);

    if(pUSBDeviceTreeRoot->FirstChild == NULL){
        printf("!!! USB Device Tree Root is NULL!!! \n");
        return;
    }
    this->ProcessUsbItemChildren(pUSBDeviceTreeRoot,_state);

    printf("Processed USB tree, cleaning up\n");
    // clean up now that everything is moved to managed data types
    CleanupUsbTreeItem(pUSBDeviceTreeRoot);
}

It calls into the EnumerateHostControllers function that is located in the enum.cpp file. As discussed above it came from original USB View application code, and has been only slightly modified. The function returns the native tree that describes all USB devices that were found. The method ProcessUsbItemChildren processesthis tree, performs the necessary type marshalling, and populates the managed UsbViewSvcState object which is provided by the caller. This methods is mainly based on the code of the original display.cpp file, which is not directly used in this service. It is discussed in more detail later.

When finished with processing, the CleanupUsbTreeItem releases all resources that are no longer required. Like most of the USB enumeration code, this can be directly reused from the original native application code.

3.2 Add data marshaling logic

The unmanaged USB device data tree that is returned by the original enumeration logic needs to be marshaled to managed service state. The processing of the different data types stored in the tree as PVOID info; reuses logic from the UpdateEditControl function in display.cpp . This logic has been moved to the new ProcessUsbItemChildren funcion. It contains a switch statement over the possible values of the USBDEVICEINFOTYPE enumeration. Some basic processing is performed in place while specific processing is carried out in helper functions, such as DisplayHubInfo and DisplayConnectionInfo, which are also based on their originals in display.cpp. These original functions cannot be reused directly, because they preformed a significantly different task. Functions in display.cpp functions display the information out of the USB tree in the UI window practically unstructured, just as plain test, the new functions have to convert the native types to managed types, and create a structured representation of the USB device tree in the state object of the service.

Most numerical data types such as int and long do not need any explicit custom marshaling when converting from native to managed types. Strings (char *) are converted as follows:

C++
 hub->FriendlyName = Marshal::PtrToStringAnsi(
                (System::IntPtr)currentItem->itemName);

The Marshal class has several string conversion methods. The one used here is the marshals Ansi strings. Also, where needed new managed objects are created to correspond to the structure of the service state, such as:

C++
 UsbConnectInfo = gcnew UsbConnectionInfo();
hub->ConnectionInfo = UsbConnectInfo;
hub->ConnectionInfo->ParentHubPortNumber = ConnectionInfo->ConnectionIndex;

This creates a new connection for non root hubs. The ProcessUsbItemChildren method calls itself recursively to process the whole data tree. The result is a managed copy of the data populated in the service state object. The native version can be cleaned up. Since this logic populates the service state it is closely tied to it, for details on how the service state was changed see the appropriate section.

3.3 Device change listener

USB devices can be plugged and unplugged at runtime. USB device change notifications signal removal of an existing device or addition of a new device to the system. The USB View application uses them to refresh the user interface based on those notifications.

For a DSS service it is even more important to expose these notifications, since it allows other services to subscribe to them from. A window handle is required to listen to device change message. The window is just used by the service to handle messages. It must not be visible to user.

There is one important problem that has to be addressed. The window procedure is native code. But it has to call into the managed code of the service. This tutorial demonstrates two solutions.

3.3.1 Creating window and handing messages

The StartDeviceChangeListener method registers the window class, and creates the message window, blocking the call in the message loop. The window class points to the static MainWndProc procedure to process the incoming messages. To keep access to the helper class we pass a pointer to it with the CreateWindow function.

In the window procedure explicitly handles 4 message types.

On WM_CREATE it saves the instance of the helper class, and registers for notifications for USB hubs and devices.

On WM_DEVICECHANGEit uses both approaches to notify managed service class about device arrival or removal. These will be discussed in detail later.

On WM_DESTROY it cleans up the registrations and stops the message loop.

It also handles the custom message MM_STOPLISTERWINDOW to destroy the message window when during clean up. The StopDeviceChangeListener method sends that message to the window.

3.3.2 Notifying managed code

The native window procedure receives the device change notification and propagates them to the managed service code. There are several ways to do this. Two of them are presented in this tutorial: using a native callback function pointer, or using a managed CCR port. The native callback function pointer is defined as DEVICE_CHANGE_CALLBACK in myUsbView.h :

C++
 typedef void (*DEVICE_CHANGE_CALLBACK)();

The StartDeviceChangeListener method takes it in as a parameter, and saves the function pointer in a private field. On WM_DEVICECHANGE the callback function is invoked. This is all purely native, no managed code is invovled here. This means however that the managed caller has to provide a native function pointer to the managed delegate that needs to be executed.

C++
 // approach #1: call a delegate
if(thisHelper->_fncallback != NULL)
{
    thisHelper->_fncallback();
}
C++
 // native notification approach #1 - delegate callback
DeviceChangeCallback^ callback = gcnew DeviceChangeCallback(this,&UsbViewSvc::UsbDeviceChanged);
_gch = GCHandle::Alloc(callback);
_fnCallback = Marshal::GetFunctionPointerForDelegate(callback);

The second approach uses a gcroot helper object in the declaration of refreshNotifyPort class member of managed type Port<EmptyValue^>^. This does most of the necessary work for us, using the GCHandle to hold the managed code in one place, and proper allocation and release of it. All we have to do is initialize refreshNotifyPort in the constructor, and delete it in the destructor. When we want to signal the notification we just post a message to that port.

C++
 // wrapper of the managed CCR port to use with approach #2
gcroot<Port<EmptyValue^>^> refreshNotifyPort;
C++
 // approach #2 use a managed object
thisHelper->refreshNotifyPort->Post(gcnew EmptyValue());

This makes it very simple for the managed code to receive this notification. All it has to do is activate a receiver on that port. We will take a look at the receiver side of things when we look at the DSS service code next.

Step 4: USB View DSS service code

The initial code for this service was generated using DssNewService.exe tool, which is part of the Microsoft Robotics Studio. The service’s state has been changed to represent the USB devices that are present in the system. The service also handles operations such as Get, receives USB device change notifications and supports subscriptions.

4.1 USB View service state

The structure of the state resembles the tree in the original USB view application. Its single root is the PC. The immediate children are the USB host controller. Their children are USB hubs which have either more hubs or regular devices as children.

The service state is defined in the UsbViewSvcTypes.h file. The actual state class UsbViewSvcState only contains a list of UsbHostController. This class has some information about the host controller and also a single instance of the UsbHubDevice as root hub. The UsbHubDevice class is a representation of a hub device, independent of whether it’s a root hub or an external hub. It contains some information about the hub, and also a list of hubs that are connected directly to it, a list of devices connected to it, as well as a list of empty ports.

This approach is used, instead of just a list of abstract ports from which the hub and device classes are inherited, because the serialization cannot correctly deal with derived types in place of their base types. A list of ports will go through just a list of ports, and not the actual classes that inherited from it. The UsbEmptyPort and UsbDevice are leaf nodes in the tree. Both UsbDevice and UsbHubDevice classes contain an instance of UsbConnectionInfo which has details about the connection of the device. The root hub however will not have this information, and this member will just be null.

4.2 USB View service operations

The service supports subscriptions for notifications from other services to the USB device plug/unplug event. Thus, the services operation port contains a subscribe operation.

C++
 public ref class Subscribe : Microsoft::Dss::ServiceModel::Dssp::Subscribe<SubscribeRequestType^, PortSet<SubscribeResponseType^, Fault^>^>
{
};

public ref class UsbViewSvcOperations : PortSet<DsspDefaultLookup^, DsspDefaultDrop^, Get^, HttpGet^, Replace^,Subscribe^>
{
};

4.3 Supporting Get operations

The simplest interaction between the service and helper class is to get the full list of USB devices. The list in the services state is refreshed on each Get or HttpGet operation so that the information is always up to date. Both handlers call a helper function, which in turn calls the helper class:

C++
 [ServiceHandler(ServiceHandlerBehavior::Exclusive)]
void UsbViewSvc::GetHandler(Get^ get)
{
    get->ResponsePort->Post(RefreshUSBDevices());
}

[ServiceHandler(ServiceHandlerBehavior::Exclusive)]
void UsbViewSvc::HttpGetHandler(HttpGet^ httpGet)
{
    // similar to regular Get, but also provides the trasform xslt file
    httpGet->ResponsePort->Post(gcnew HttpResponseType(HttpStatusCode::OK,RefreshUSBDevices(),_transform));
}
C++
 Robotics::UsbViewSvcState^ UsbViewSvc::RefreshUSBDevices()
{
    // call into native wrapper class to get the usb device list
    Robotics::UsbViewSvcState^ state = gcnew UsbViewSvcState();
    ((myUsbViewHelper*)pUsbViewHelper.ToPointer())->EnumerateUsbDevices(state);
    _state = state;
    return state;
}

4.4 Supporting device notifications

We have talked about the 2 approaches to communicate device change notifications from the native window procedure to the managed service code in section 3.3.2 . There we looked at the native side, now let go through how the managed side works.

First of all we need to create an instance of our native service class and save it as a class member for other methods to use. A service class cannot have a native instance as one of its members, so we have to use a managed wrapper for the native instance called IntPtr. It stores inside the native pointer, that we can access using the ToPointer() method. This member of our class is called pUsbViewHelper and it’s initialized in the constructor using the following code:

C++
 // create the instance of the helper class
myUsbViewHelper* pHelper = new myUsbViewHelper();
pUsbViewHelper = IntPtr(pHelper);

The first approach that we discussed involved passing in a native function pointer to the StartDeviceChangeListener method. To do that we need to declare a delegate with the same signature as the expected function, which is done in UsbViewSvc.h file.

C++
 delegate void DeviceChangeCallback();

The next step is to create an instance of that delegate, use the GC helper GCHandle class to make sure that this instance is not moved or destroyed by the garbage collector, and finally create a native function pointer for our delegate. We do this in the constructor of our service class and it ends up looking like this:

C++
 // native notification approach #1 - delegate callback
DeviceChangeCallback^ callback = gcnew DeviceChangeCallback(this,&UsbViewSvc::UsbDeviceChanged);
_gch = GCHandle::Alloc(callback);
_fnCallback = Marshal::GetFunctionPointerForDelegate(callback);

Now we are ready to pass that function pointer to the native method.

The second approach consists of using a managed CCR port, to which the native function posts messages. We have already looked at what that requires from the native side, let’s see what we need to do for managed side. Since we have an instance of a managed CCR port, all we have to do is a regular activate on that port:

C++
 // native notification approach #2 - use a managed CCR port
Activate(
    Arbiter::Receive<EmptyValue^>(
        true, 
        pHelper->refreshNotifyPort, 
        gcnew Handler<EmptyValue^>(this, &UsbViewSvc::DeviceChangeNotification)
        )
);

This is also done in the constructor of our class.

As you see the difference between the 2 approaches is where the work for dealing with the native to managed transition has to be done, on managed or on native side. One approach makes the native side quite regular, the other makes the managed side easy. Depending on circumstances one or the other will make sense.

Now let’s finally start the device listener. As we saw above the StartDeviceChangeListener method will block the call to it to deal with the message loop. That means that we need to create a new thread on which we will call it. This is done in the start method of our service and looks like this:

C++
 void UsbViewSvc::DeviceListenStart()
{
    // approach #1 delegate callback 
    myUsbViewHelper* myHelper = (myUsbViewHelper*)pUsbViewHelper.ToPointer();
    DEVICE_CHANGE_CALLBACK dc_callback = (DEVICE_CHANGE_CALLBACK)_fnCallback.ToPointer();
    // start native listener and provide a native function pointer to our callback delegate
    myHelper->StartDeviceChangeListener(dc_callback);
}
C++
 void UsbViewSvc::Start()
{
    __super::Start();
    // initialize device plug/unplug listener
    Thread^ ListenerThread = gcnew Thread(gcnew ThreadStart(this,&UsbViewSvc::DeviceListenStart));
    ListenerThread->Start();

As you see in DeviceListenStart method we get a native pointer to our helper class as well as a native pointer to the callback function, and make the call to start the listener blocking that thread. From the 2 approaches we used the native notification will end up in both of these methods, one after the other:

C++
 void UsbViewSvc::UsbDeviceChanged()
{
    // callback from approach #1
    LogInfo("Usb Device Change function callback");
}
void UsbViewSvc::DeviceChangeNotification(EmptyValue^ empty)
{
    // processing message from approach #2
    LogInfo("Usb Device Change message posted");
    // refresh state and send notification to subscribers
    RefreshUSBDevices();
    SendNotification<Replace^>(_submgrPort,_state);
}

Only one of them actually does some logic to deal with this. The second method, which will be called on the CCR thread, and not the native UI thread, refreshes the USB device list in the state and sends notification to subscribers. To refresh the USB device list it calls the same helper function that the Get and HttpGet handlers use. For notification it uses the subscription manager port. We will look at subscriptions slightly later, now let’s look at what we need to do to clean up when necessary.

4.5 Cleanup and service drop

The destructor has to clean up whatever was created in the constructor. This is a GC helper GCHandle instance, which holds a pointer to the delegate, and an instance of the helper class:

C++
 UsbViewSvc::~UsbViewSvc()
{
    // cleanup what we allocated in the constructor
    _gch.Free();
    delete pUsbViewHelper;
}

It is equally important to stop the device listener when the service is dropped. Thus, the custom Drop handler calls into the helper class, which in turn destroys the message window and cleans up the device notifications:

C++
 // specify that this is a custom tear down handler
[ServiceHandler(ServiceHandlerBehavior::Teardown)]
void UsbViewSvc::DropHandler(DsspDefaultDrop^ drop)
{
    // cleanup native resources on drop
    ((myUsbViewHelper*)pUsbViewHelper.ToPointer())->StopDeviceChangeListener();
    __super::DefaultDropHandler(drop);
}

The base implementation of the Drop handler needs to be called to correctly drop the service.

4.6 Supporting subscribers

The service uses the Subscription Manager service to support subscription from other services to device change notifications. The subscription manager is declared as a Partner in the service declaration in UsbViewSvc.h:

C++
 // subscription manager partner for notifications
[Partner("SubMgr", Contract = submgr::Contract::Identifier, CreationPolicy = PartnerCreationPolicy::CreateAlways)]
submgr::SubscriptionManagerPort^ _submgrPort;

Its instance is created by the DSS infrastructure on service startup. Incoming subscriptions requests are handled in SubscribeHandler:

C++
 [ServiceHandler(ServiceHandlerBehavior::Concurrent)]
void UsbViewSvc::SubscribeHandler(Subscribe^ subscribe)
{
    // process subscriber and send current state using helper class
    UsbViewSvcSubscribeHelper^ helper = gcnew UsbViewSvcSubscribeHelper();
    helper->parentSvc = this;
    helper->request = subscribe->Body;;
    Activate(
        Arbiter::Choice(
            SubscribeHelper(_submgrPort, helper->request, subscribe->ResponsePort),
            gcnew Handler<SuccessResult^>(helper, &UsbViewSvc::UsbViewSvcSubscribeHelper::SubscribeSuccess),
            gcnew Handler<Exception^>(helper, &UsbViewSvc::UsbViewSvcSubscribeHelper::SubscribeFailure)
            )
    );
}

This uses a small helper class to deal if the subscription succeeds or fails. The helper class preserves the instance of the subscribe request to post an appropriate reply once the outcome of the request to the subscription manager is know. The class has handlers for the success and fault case:

C++
 void UsbViewSvc::UsbViewSvcSubscribeHelper::SubscribeSuccess(SuccessResult^ success)
{
    this->parentSvc->SendNotification<Replace^>(this->parentSvc->_submgrPort, this->request->Subscriber, this->parentSvc->_state);
}
void UsbViewSvc::UsbViewSvcSubscribeHelper::SubscribeFailure(Exception^ e)
{
    this->parentSvc->LogError(nullptr, "Subscribe failed", e);
}

If the subscription failed, it just log an error. If it succeeded, a notification to the with the current state is sent to the subscriber to bring him up to date. As seen before, similar update notifications are sent to all subscribers when a native device change listener signals that a device was plugged/unplugged.

4.7 Adding an XSLT for HttpGet

The standard display of a service’s state in a web browser is a bare XML view. By adding an XSLT that transforms the XML into HTML a nicer user experience can be provided. The XSLT needs to be resource in the service’s project. That can be done through the project properties in Visual Studia: Configuration Properties->Linker->Input->Embed Managed Resource File. To use that XSLT with a service it needs to be declared as one of the members of the service class:

C++
 // subscription manager partner for notifications
[Partner("SubMgr", Contract = submgr::Contract::Identifier, CreationPolicy = PartnerCreationPolicy::CreateAlways)]
submgr::SubscriptionManagerPort^ _submgrPort;

This string will be populated when the service is started, and will point to the location where the embedded resources have been mounted to. Generally it should be: /resources/<your service name>/<your resource name>. It can now be used to specify the transform in the HttpGet handler:

C++
 [ServiceHandler(ServiceHandlerBehavior::Exclusive)]
void UsbViewSvc::HttpGetHandler(HttpGet^ httpGet)
{
    // similar to regular Get, but also provides the trasform xslt file
    httpGet->ResponsePort->Post(gcnew HttpResponseType(HttpStatusCode::OK,RefreshUSBDevices(),_transform));
}

The XSLT file is included in the project. It is fairly straight forward. It creates a <table> and <div> HTML tree for the USB devices. In order to be able to sort children of a USB hub according to their port number, they have to be grouped:

XML
 <xsl:for-each select="$UsbHubItem/usbview:Hubs/usbview:UsbHubDevice | $UsbHubItem/usbview:Devices/usbview:UsbDevice | $UsbHubItem/usbview:EmptyPorts/usbview:UsbEmptyPort">
<xsl:sort select="./usbview:ParentHubPortNumber | ./usbview:ConnectionInfo/usbview:ParentHubPortNumber " data-type="number" order="ascending"/>

</xsl:for-each>

Now when you access the service instance from the browser you should see something like this:

Figure 1

Figure 1 - RoboticsTutorial4 project files

The icons are reused from the original application, and connected devices are highlighted in bold for easier viewing.

Step 5: Compile and run

You can run the included service binaries by just copying them into your bin directory in Microsoft Robotics Studio 1.5 installation location. To start your node you can use the manifest that is part of this tutorial’s code, or you can just create the service using the DSS node control panel.

Console
 DssHost.exe /p:50000 /t:50001 /m:"UsbViewCode\UsbViewSvc.manifest.xml"

You can view the service state with your browser to see the list of USB devices in the system.

Console
 http://localhost:50000/usbviewsvc

If you want to compile the included code you might have to change the following project settings to point to the correct Vista DDK installation directory for your machine:

  • Under Configuration Properties -> C\C++ -> General -> Additional Include Directories change to point to your WinDDK\6000\inc\api directory
  • Under Configuration Properties ->Linker-> General -> Additional Library Directories change to point to your WinDDK\6000\lib\wnet\i386 directory
  • Under Common Properties->References->Additional reference search paths change to point to the bin directory in your Microsoft Robotics Studio installation location
  • Under Configuration Properties -> General-> Output Directory change to point to the bin directory in your Microsoft Robotics Studio installation location.

You also might have to change the post build settings that generate the proxy assembly, if you have not placed the example code under the Microsoft Robotics Studio installation directory.

If you want to try out the notifications from this service, you can see an example VPL diagram that does that under UsbViewVPLtest directory of this tutorial.

Summary

This tutorial walked through the code of Vista DDK USB View sample migrated to be a Microsoft Robotics Studio DSS Service. The key things that are demonstrated are:

  • How to talk to device drivers using the I/O control code (IOCTL) calls (see the Usbioctl.h system include file) such as :
    • IOCTL_GET_HCD_DRIVERKEY_NAME
    • IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION
    • IOCTL_USB_GET_NODE_CONNECTION_INFORMATION
    • IOCTL_USB_GET_NODE_INFORMATION
    etc.., from a DSS service.
  • How to write a C++ /CLI DSS service that interacts with native C++ code.
  • How to work with managed to native and native to managed calls and data
  • How to work with subscribers and notifications in a C++/ CLI DSS service
  • How to add an XSLT to a C++ /CLI DSS service

You can run the included service binaries with your Microsoft Robotics Studio 1.5 installation, or compile your own.

Windows Driver Kit (WDK): OverviewDownload

Page view tracker