Exposing Events in the Visual Studio SDK
Visual Studio allows you to source events through automation. It is advised that you source events for projects and project items, but you can add any events that are applicable to your VSPackage.
Events are retrieved by automation consumers from the Events object or GetObject ("EventObjectName"). The environment calls IDispatch::Invoke using the DISPATCH_METHOD or DISPATCH_PROPERTYGET flags in order to return an event.
The following steps explain the process for returning VSPackage-specific events.
The environment starts.
It reads from the registry all value names under the Automation, AutomationEvents and AutomationProperties keys of all VSPackages and stores those names in a table.
An automation consumer calls, in this example, DTE.Events.AutomationProjectsEvents or DTE.Events.AutomationProjectItemsEvents.
The environment finds the string parameter in the table and loads the corresponding VSPackage.
The environment calls GetAutomationObject method with the name passed in the call — AutomationProjectsEvents or AutomationProjectItemsEvents.
The VSPackage creates a root object that has the methods like get_AutomationProjectsEvents and get_AutomationProjectItemEvents and then returns an IDispatch pointer to the object.
The environment calls the appropriate method based on the name passed into the automation call.
The get_ method creates another IDispatch-based event object that implements both IConnectionPointContainer and IConnectionPoint interfaces and returns an IDispatch pointer to it.
To expose an event through automation, you must respond to GetAutomationObject and watch for the strings that you add to the registry. In the Basic Project sample, the strings are "BscProjectsEvents" and "BscProjectItemsEvents".
Registry Entries from the Basic Project Sample
The following shows where to add automation event values to the registry.
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Packages\<PkgGUID>\AutomationEvents]
"AutomationProjectEvents"="Returns the AutomationProjectEvents Object"
"AutomationProjectItemEvents"="Returns the AutomationProjectItemsEvents Object"
Name |
Type |
Range |
Description |
---|---|---|---|
Default (@) |
REG_SZ |
Unused |
Unused. You can use the data field for documentation. |
AutomationProjectsEvents |
REG_SZ |
Name of your event object. |
Only the key name is relevant. You can use the data field for documentation. This example comes from the Basic Project sample. |
AutomationProjectItemEvents |
REG_SZ |
Name of your event object |
Only the key name is relevant. You can use the data field for documentation. This example comes from the Basic Project sample. |
When any of your event objects are requested by an automation consumer, create a root object with methods for any event that your VSPackage supports. The environment calls the appropriate get_ method on this object. If, for example, DTE.Events.AutomationProjectsEvents is called, the get_AutomationProjectsEvents method on the root object is invoked.
Automation model for events
The class CProjectEventsContainer represents the source object for BscProjectsEvents, while CProjectItemsEventsContainer represents the source object for BscProjectItemsEvents.
In most cases, you need to return a new object for each event request as most event objects take a filter object. When firing your event, check this filter to verify that the event handler is being called.
AutomationEvents.h and AutomationEvents.cpp contain declarations and implementations of the following classes.
Class |
Description |
---|---|
CAutomationEvents |
Implements an event root object, retrieved from the DTE.Events object. |
CProjectsEventsContainer and CProjectItemsEventsContainer |
Implements the event source objects firing the corresponding events. |
The following Visual C++ code example shows how to respond to a request for an event object.
STDMETHODIMP CVsPackage::GetAutomationObject(
/* [in] */ LPCOLESTR pszPropName,
/* [out] */ IDispatch ** ppIDispatch)
{
ExpectedPtrRet(ppIDispatch);
*ppIDispatch = NULL;
if (_wcsicmp(pszPropName, g_wszAutomationProjects) == 0)
//Is the requested name our Projects object?
{
return GetAutomationProjects(ppIDispatch);
// Gets our Projects object.
}
else if (_wcsicmp(pszPropName, g_wszAutomationProjectsEvents) == 0)
//Is the requested name our ProjectsEvents object?
{
return CAutomationEvents::GetAutomationEvents(ppIDispatch);
// Gets our ProjectEvents object.
}
else if (_wcsicmp(pszPropName, g_wszAutomationProjectItemsEvents) == 0) //Is the requested name our ProjectsItemsEvents object?
{
return CAutomationEvents::GetAutomationEvents(ppIDispatch);
// Gets our ProjectItemsEvents object.
}
return E_INVALIDARG;
}
In the code above, g_wszAutomationProjects is the name of your project collection ("FigProjects"), g_wszAutomationProjectsEvents ("FigProjectsEvents") and g_wszAutomationProjectItemsEvents ("FigProjectItemEvents") are the names of project and project items events that are sourced from your VSPackage implementation.
Event objects are all retrieved from the same central location, from the DTE.Events object. This way, all event objects are grouped together so that an end user does not need to browse the entire object model to find a specific event. This also gives you the means to provide your specific VSPackage objects, rather than requiring you to implement your own code for system-wide events. However, for the end user browsing is a bit more difficult. If a user must find an event for your ProjectItem interface, it is not immediately clear from where that event object is retrieved.