For Users
Download Plug-ins
Desktop Search Forum
For Developers
Plug-in Development
Download SDK
Developer Guide
Index API
Query API
Display API
Script API
Communication API
Plug-in Design Guidelines
Plug-in Tutorials
Using Wizard
Using Helper Framework
Using ActiveX
Action API
Event API
Plug-in Installer
Submit Software
Developer Forum
Desktop Blog
|
Contents
GoogleDesktopDisplayAPI.idl
Introduction
This is a tutorial on how to create a DLL Google
Desktop Sidebar plug-in using the helper framework. This framework
simplifies plug-in development by providing functions for doing common
Sidebar plug-in tasks. It also ensures a consistent user interface
across Sidebar plug-ins. For simple tasks, such as displaying text,
developing with the helper framework is much easier than creating an
ActiveX control. Note that this is not an API reference, but
rather step-by-step instructions for creating an helper library based
plug-in. Before reading this, you should be familiar with the Google
Desktop SDK/Display API concepts and interfaces described here.
In particular, this tutorial shows how to write a
basic plug-in that creates a Sidebar tile that notifies the user when
removable media is inserted or removed from their computer. The overall
name of our example is MyDiskMonitor, and we indicate example code that
you will need to change for your own plug-ins by coloring it red. A Sidebar including the MyDiskMonitor tile is
shown to the right.
|
|
Creating the Project in Visual Studio
To start with, you need to create your project
in Visual Studio, name it, and set a couple of its
properties. This results in a basic project template that you'll
register with Google Desktop and fill in with code in the following
tutorial sections.
- Start Visual Studio .NET 2003.
- Go to the File menu and select New,
Project...
- Find ATL Project on the
project list (in Visual C++ Projects and then ATL). Now, enter
a name for your plug-in in the textbox. For our example, we use
the name MyDiskMonitor. Click OK.
- The ATL Project Wizard will ask you for settings. On the dialog's
left side, click on the Application Settings tab.
- Uncheck the Attributed checkbox. Then click the
Finish button.
- Visual Studio now creates your project. The Solution Explorer will
display all of the new project files. The wizard will have created an
unneeded second project, called
<YourProjectName>PS, in the case of
our example project MyDiskMonitorPS. You can delete
the second project by clicking on it in the Solution Explorer and
hitting the DEL key.
- Right click on the main project (MyDiskMonitor) in the
Solution Explorer and select Properties.
- At the top, beside Configuration, select All
Configurations.
- On the dialog's left side, select General. Beside
Character Set, select Unicode. You should
use Unicode in your applications in order to be compatible with
non-English languages.
- Also in the Properties dialog, go in the Build
Events folder and select Post-Build Event.
- Clear the value specified for the Command Line
parameter. This
text automatically registers your plug-in when a successful
build is done. Unfortunately, when updating your plug-in you
also need to unregister it and this value does not handle
unregistration. Later on, we'll see how to set up your plug-in
to do both registration and unregistration.
- Click the OK button to close
this dialog.
Importing Google Desktop Libraries
You must import Google Desktop's constants and
helper functions into your application for it to interact with
GD. When you unzipped the SDK, its
GD_SDK\api\samples\common
directory included GoogleDesktopComponentCommon.vcproj,
which contains the constants and helper functions.
-
Import GoogleDesktopComponentCommon into your application
by right clicking on your solution (for our example, MyDiskMonitor) and then selecting Add,
Existing Project....
- Find the GoogleDesktopComponentCommon.vcproj file in
the SDK's /api/samples/common area and import it by
clicking Open.
- Since your project depends on the
GoogleDesktopComponentCommon project, you need to specify
the dependency. To do so, right click on your project
(MyDiskMonitor in our example) and select Project
Dependencies.
- Put a checkmark in the box that says
GoogleDesktopComponentCommon and then click OK.
Creating the Sidebar Display Object
Now that we've got the project set up and the
necessary Desktop libraries imported, it's time to create the object
that will be displayed in the Sidebar.
- In the Solution Explorer, click on the Class View
tab.
- Right click on your project (MyDiskMonitor for the
example) and select Add, Add Class....
- Select ATL Simple Object and click Open.
- Under Short name, give the control a name. Our
example control could be called MyDiskMonitorObj;
note that since MyDiskMonitor is already taken by the project
namespace, you can't give your control the exact same name as
your project.
- From the Options tab select IObjectWithSite. Click
the Finish button. This creates
your display object definition and inserts two new files into your
application for the new object (for our example, the files are
MyDiskMonitorObj.h and MyDiskMonitorObj.cpp).
- Open the plug-in object's Header file (For our example, MyDiskMonitorObj.h).
- To use the Google Desktop helper functions, you must add an include at the top of the file. Make sure this points to the correct location of the .h file:
#include "../common/GoogleDesktopDisplayAPI.h"
- Next, we have to define the Helper variable (which allows us to access the helper functions) in this file
before closing the class' scope.
public:
CComPtr<IUnknown> helper_;
};
- In order to simplify plug-in development by giving the
helper_ object access to
helper functions, add the following code in the file's COM Map:
BEGIN_COM_MAP(CMyDiskMonitorObj)
COM_INTERFACE_ENTRY(IMyDiskMonitorObj)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND(helper_.p,
CLSID_GoogleDesktopDisplayPluginHelper)
END_COM_MAP()
DECLARE_GET_CONTROLLING_UNKNOWN()
DECLARE_PROTECT_FINAL_CONSTRUCT()
Registering the Plug-In with Google Desktop
In order for Google Desktop to know about and
be able to interact with a plug-in, the plug-in must register itself
with GD. The best time to do this is to have it register with GD
when Windows registers this DLL and unregister with GD when
Windows unregisters this DLL.
- Open the main CPP file,
<ProjectName>.cpp (for our example,
MyDiskMonitor.cpp. Note the difference between
MyDiskMonitor.cpp and .h and MyDiskMonitorObj.cpp and .h. MyDiskMonitor.*
is for the library itself and takes care of registering the
components with Google Desktop. MyDiskMonitorObj.* is the object
which actually displays data and interacts with the
user).).
- Put an include command for the Google Desktop helper
functions at the top of this file. Make sure it points to the
.h file's correct location, which is based on where you
previously imported the
GoogleDesktopComponentCommon.vcproj. The command
will look like the following, changed to be the actual installed
location of the file:
#include "../common/GoogleDesktopComponentRegistration.h"
- Modify DllRegisterServer(void) and
DllUnregisterServer(void) to notify Google Desktop when
Windows registers or unregisters this DLL. The following code shows the
modifications to our MyDiskMonitor sample code. Red text indicates code that should be changed
to values correct for your project instead of for
MyDiskMonitor.
RegisterDisplayComponentHelper() registers
the plug-in with Google Desktop. Its first parameter is your object's GUID,
which identifies your plug-in to Google Desktop. Remember to replace
MyDiskMonitorObj with the your project's object name.
The second and third parameters are the title and
description the Google Desktop UI displays for your plug-in.
The fourth parameter is a path to the plug-in's
icon. This isn't actually used for a Sidebar plug-in, so we pass an
empty string. The final parameter should be set to true if your
plug-in sends notifications to be displayed in the GD notifier
area. In the sample code, we use the variable
plugin_shows_notifications as the value of this final
parameter. Set its value accordingly in its definition. For
MyDiskMonitor,
plugin_shows_notifications is set to false.
// DllRegisterServer - Adds entries to the system registry
STDAPI DllRegisterServer(void) {
// Register object, typelib and all interfaces in typelib
HRESULT hr = _AtlModule.DllRegisterServer();
if (SUCCEEDED(hr)) {
// Set the plug-in's default settings
bool plugin_shows_notifications = false;
CComBSTR plugin_title(_T("My Disk Monitor"));
CComBSTR plugin_description(_T("Monitors removable drives and alerts you when a storage device is
inserted or removed"));
// Register the plug-in with Google desktop
hr = RegisterDisplayComponentHelper(__uuidof(MyDiskMonitorObj), plugin_title,
plugin_description, _T(""), plugin_shows_notifications);
}
return hr;
}
// DllUnregisterServer - Removes entries from the system registry
STDAPI DllUnregisterServer(void) {
// Unregister plug-in from Google Desktop
UnregisterComponentHelper(__uuidof(MyDiskMonitorObj));
// Unregister plug-in from Windows
HRESULT hr = _AtlModule.DllUnregisterServer();
return hr;
}
Building and Testing the Plug-In
Now that the plug-in is registered and has
access to the Google Desktop libraries, it's time to build it
and do basic tests before customizing it.
- Create the DLL by going to the Build menu and
selecting Build Solution.
- Open the Windows command prompt and run the following command:
regsvr32 "<PathToBuiltDLL>"
PathToBuiltDLL is the path to the DLL created in the
previous step. It should look something like:
regsvr32 "C:\Documents and
Settings\Larry\Desktop\MyDiskMonitor\Debug\MyDiskMonitor.dll"
This registers the plug-in with Windows COM, which immediately
calls Google Desktop to register the plug-in there as well.
- Google Desktop will prompt you to register the plug-in. Click
OK to do so. You now have an active plug-in.
- You should test your plug-in to be sure that it is runable
and appearing in the Sidebar.
- When you're done testing the plug-in, rerun the regsvr32 command, but with a /u
flag to unregister the plug-in instead of registering it.
For example:
regsvr32 /u "C:\Documents and Settings\Larry\Desktop\MyDiskMonitor\Debug\MyDiskMonitor.dll"
Setting the Sidebar Tile Title and Icon
Now that we have defined and built the plug-in
object, we can start customizing it. First, we'll add code to let
it display a tile-specific title and icon when it appears in the
Sidebar.
- Open your plug-in object's Header file
(MyDiskMonitorObj.h in our
example).
- Define the
SetClientSite method
by inserting the following code at the end of the class, but before
ending scope. SetSite
is a method from the implemented
ObjectWithSite. It is
commonly used by ActiveX objects to set their title and icon.
public:
STDMETHOD(SetSite) (IUnknown* site);
- Open the plug-in object's CPP file (MyDiskMonitorObj.cpp
in our example).
- Insert a BITMAP resource into the project's resource file containing the 16x16 icon you'd like to use
for the tile's titlebar. You can either import and existing one or create a new one with the tools
provided in the Visual Studio interface. For our example, we'll name
the BITMAP resource
IDB_MYDISKMONITOROBJ.
- Insert the following code at the end of the file (This code is
written to work with our MyDiskMonitor sample code. Red text indicates code that should be changed
to values correct for your project instead of for
MyDiskMonitor).
We are using IObjectWithSite
method SetSite() to set
the tile's title and icon (OLE Control) when displayed in the Sidebar.
Make sure to change the tile_title
variable value to what you want as the tile's title. Also change the
IDB_MYDISKMONITOROBJ argument
in the LoadBitmap call to
an existing Bitmap resource you want to be the title bar displayed
icon. The code following the
LoadBitmap call converts
the returned Bitmap handle
(HBITMAP) to an OLE
Picture Object (IPicture).
HRESULT CMyDiskMonitorObj::SetSite(IUnknown* site) {
CComQIPtr<IGoogleDesktopDisplaySite> sidebar_site(site);
if (sidebar_site) {
// Set the tile's title on the GD interface
CComBSTR tile_title(_T("My Disk Monitor"));
sidebar_site->put_title(tile_title);
// Load the icon bitmap resource
CComPtr<IPicture> tile_icon;
HBITMAP bitmap = LoadBitmap(_AtlBaseModule.GetResourceInstance(),
MAKEINTRESOURCE(IDB_MYDISKMONITOROBJ));
if (bitmap == NULL)
return E_FAIL;
// Do the conversion from a HBITMAP to IPicture
PICTDESC picture_desc;
picture_desc.cbSizeofstruct = sizeof(picture_desc);
picture_desc.picType = PICTYPE_BITMAP;
picture_desc.bmp.hbitmap = bitmap;
picture_desc.bmp.hpal = NULL;
HRESULT hr = OleCreatePictureIndirect(&picture_desc, IID_IPicture, TRUE,
(void**)&tile_icon);
if (FAILED(hr))
return E_FAIL;
// Set the tile's icon on the GD interface
sidebar_site->put_icon(tile_icon);
}
return S_OK;
}
Setting the Sidebar Tile Appearance and Adding an About Box
We continue customizing our plug-in by setting its
display font and colors. Esthetically, these properties should use the
same values as the other tiles in the Sidebar. So the code below first
gets those values and then sets the relevant properties of our plug-in
to them.
We also add an About box to the tile. Similar to
setting the title and icon values, we need to edit both your project's
CPP and Header files.
- Open the plug-in object's Header file (For our example, MyDiskMonitorObj.h).
- It's a good idea to move the constructor and destructor
methods, FinalConstruct() and
FinalRelease(), to the main CPP file from the
the Header file. Remove the lines in the header
file relating to these methods and replace them with the
following code, which can be placed anywhere within the
class' scope (replace the text in red
with names appropriate to your project's name):
public:
CMyDiskMonitorObj();
~CMyDiskMonitorObj();
HRESULT FinalConstruct();
void FinalRelease();
- Open the plug-in object's CPP file (for our example, MyDiskMonitorObj.cpp).
- We need to implement the
FinalConstruct()
and FinalRelease() constructor and
destructor methods. Add the following code at the end of the CPP file. Notice
that the helper_ object
is manually released in FinalRelease(). Therefore, to keep code out of the header
file, we move these methods to the CPP file.
HRESULT CMyDiskMonitorObj::FinalConstruct() {
return S_OK;
}
void CMyDiskMonitorObj::FinalRelease() {
helper_.Release();
}
CMyDiskMonitorObj::CMyDiskMonitorObj() {
}
CMyDiskMonitorObj::~CMyDiskMonitorObj() {
}
- Now, we'll set your tile's display options and implement its About
dialog. The Helper is useful for this since it assists you in being
consistent with other plug-ins' appearance. Insert the following code at the
end of the SetSite() method, just
before the return S_OK
line. Inserting it there causes the code to run during your plug-in's
creation.
In this code, we first enable access to the helper, then set the tile's
display options. In our example, we do not use any modifers, which can be
used to do things like add back and forward arrows to the titlebar. See the API
documentation or IDL file for the available options for these two parameters.
Next, we set the About dialog's text, with the
\n causing a new line in the dialog.
Per the convention for all plug-ins, the first two dialog lines are displayed
at the top beside the icon, with the remaining lines displayed below.
Finally, we call SetIcons()
to set the icons for both the About dialog and alerts window. We use the same
icon as the one beside the tile title, but you can use any icon you wish.
You might want to use a 32x32 icon for
tile_icon and let the libraries
rescale it to the needed size.
// Set the tile's icon on the GD interface
sidebar_site->put_icon(tile_icon);
// Create the helper object
CComQIPtr<IGoogleDesktopDisplayPluginHelper> helper(helper_);
if (helper_ == NULL)
return E_FAIL;
// Set the display options for the tile
UINT content_flags = GDD_CONTENT_FLAG_HAVE_DETAILS;
UINT plugin_flags = GDD_PLUGIN_FLAG_NONE;
helper->SetFlags(static_cast<GoogleDesktopDisplayPluginFlags>(plugin_flags),
static_cast<GoogleDesktopDisplayContentFlags>(content_flags));
// Set the maximum amount of items possible to display to be 10. Anything
// more will be automatically removed
helper->put_max_content_items(10);
// Set the about dialog text
helper->put_about_text(_T("My Disk Monitor\nCopyright (C) 2005\nMonitors ")
_T("disk changes on your computer\n\nAs removable media is inserted or removed in ")
_T("computer, a notification will be displayed as an item in the plug-in's tile."));
// Set the plug-in's icons (also used in About dialog)
helper->SetIcons(tile_icon, tile_icon);
}
return S_OK;
Using the Helper to Insert Items
In this section, we show how to add items to our Sidebar tile
via the Helper object.
- Open the plug-in object's Header file (for our example,
MyDiskMonitorObj.h).
- Insert the following code at the end of the file.
Red text
indicates code that should be changed to apply to your
project rather than our example.
The code is simpler than it might at first appear to be.
It creates a class to provide callbacks that you can
use to further customize the items that are added to the
tile's contents. For example, when the user clicks on a
tile item, you could have it throw one of the listed events
(DrawItem, OpenItem, etc.).
While you could
create new tile items without having to manage a new class,
you wouldn't be able to easily handle their callback
messages.
class CMyDiskMonitorContentItem :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<IGoogleDesktopDisplayContentItemHandler,
&IID_IGoogleDesktopDisplayContentItemHandler, &LIBID_MyDiskMonitorLib,
/*wMajor =*/ 1, /*wMinor =*/ 0> {
public:
BEGIN_COM_MAP(CMyDiskMonitorContentItem)
COM_INTERFACE_ENTRY(IGoogleDesktopDisplayContentItemHandler)
// the next statement causes the content item to be aggregated from the
// ContentItemHelper object that is exposed by the API. The variable
// 'm_contentItemHelper' will hold the helper object.
COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND(helperitem_.p,
CLSID_GoogleDesktopDisplayContentItemHelper)
END_COM_MAP()
public:
DECLARE_GET_CONTROLLING_UNKNOWN()
CMyDiskMonitorContentItem() {
}
void FinalRelease() {
}
// IGoogleDesktopDisplayContentItemHandler
STDMETHOD(DrawItem)(GoogleDesktopDisplayTarget target, HDC dc, const RECT *bounds);
STDMETHOD(GetHeight)(GoogleDesktopDisplayTarget target, HDC dc, long width, long *height);
STDMETHOD(OpenItem)();
STDMETHOD(ToggleItemPinnedState)();
STDMETHOD(GetIsTooltipRequired)(GoogleDesktopDisplayTarget target,
HDC dc, const RECT *bounds, VARIANT_BOOL *is_required);
STDMETHOD(OnDetailsView)(BSTR* title,
GoogleDesktopDisplayDetailsViewFlags* flags,
IUnknown** details_control,
VARIANT_BOOL* cancel);
STDMETHOD(ProcessDetailsViewFeedback)(GoogleDesktopDisplayDetailsViewFlags flags);
STDMETHOD(OnRemoveItem)(VARIANT_BOOL* cancel);
public:
CComPtr<IUnknown> helperitem_; // aggregated helper object exposed by the API
};
typedef CComObject<CMyDiskMonitorContentItem> CMyDiskMonitorContentItemImpl;
- Open the plug-in object's CPP file (for our example, MyDiskMonitorObj.cpp)
- Insert the following code at the bottom of the file. It
is a placeholder to provide for when you want to add actions
that occur when a user clicks on an item or want to do more
advanced item editing. As always, red text indicates wording
that should be changed to match your project rather than our
example.
STDMETHODIMP CMyDiskMonitorContentItem::OpenItem() {
return E_NOTIMPL;
}
STDMETHODIMP CMyDiskMonitorContentItem::GetIsTooltipRequired(
GoogleDesktopDisplayTarget target, HDC dc, const RECT *bounds,
VARIANT_BOOL *is_required) {
return E_NOTIMPL;
}
STDMETHODIMP CMyDiskMonitorContentItem::DrawItem(
GoogleDesktopDisplayTarget target, HDC dc, const RECT *bounds) {
return E_NOTIMPL;
}
STDMETHODIMP CMyDiskMonitorContentItem::GetHeight(
GoogleDesktopDisplayTarget target, HDC dc, long width, long *height) {
return E_NOTIMPL;
}
STDMETHODIMP CMyDiskMonitorContentItem::ToggleItemPinnedState() {
return E_NOTIMPL;
}
STDMETHODIMP CMyDiskMonitorContentItem::OnDetailsView(BSTR* title,
GoogleDesktopDisplayDetailsViewFlags* flags, IUnknown** details_control,
VARIANT_BOOL* cancel) {
return E_NOTIMPL;
}
STDMETHODIMP CMyDiskMonitorContentItem::ProcessDetailsViewFeedback(
GoogleDesktopDisplayDetailsViewFlags dv_flags) {
return E_NOTIMPL;
}
STDMETHODIMP CMyDiskMonitorContentItem::OnRemoveItem(VARIANT_BOOL* cancel) {
return E_NOTIMPL;
}
- Insert the following code at the end of the
SetSite()
method and before the success checking
scope. Red text should be changed to match your project,
rather than our example.
This is where we actually insert an item. Creating the item
requires many parameters, but once it's created and added
to the tile Google Desktop will take care of properly
displaying it.
Specifically:
- The current system time is used as the entry time
value. This can be modified to be any value you might
prefer.
- The item header will display the item's number to
show that items are inserted into the list such that
the newest item is at the top.
- All of the put_ calls set different
properties of the item. Note that we do not set all the
possible properties; see the IDL file and Display API
documentation for the complete set
of properties that you can manipulate.
- Finally, the item is added to the tile and
displayed by Google Desktop.
// Set the plug-in's icons (also used in About dialog)
helper->SetIcons(tile_icon, tile_icon);
// Insert 10 items. Notice that they will get inserted with #10 being shown at the top
for (int cur_item_num = 1; cur_item_num <= 10; ++cur_item_num) {
// Create an object for the new item
CMyDiskMonitorContentItemImpl *item_object = NULL;
CMyDiskMonitorContentItemImpl::CreateInstance(&item_object);
CComPtr<IGoogleDesktopDisplayContentItemHelper> item_helper;
item_object->GetUnknown()->QueryInterface(IID_IGoogleDesktopDisplayContentItemHelper,
reinterpret_cast<void**>(&item_helper));
// Get the system time for a time value
SYSTEMTIME system_time;
GetSystemTime(&system_time);
DATE time;
SystemTimeToVariantTime(&system_time, &time);
// Format the string to display for the heading
CString heading_str;
heading_str.Format(_T("Heading #%d"), cur_item_num);
CComBSTR heading(heading_str);
// Set the properties for the new item
item_helper->put_heading(heading);
item_helper->put_tooltip(_T("Tooltip"));
item_helper->put_source(_T("Source"));
item_helper->put_time_created(time);
item_helper->put_flags(static_cast<GoogleDesktopDisplayContentItemFlags>(
GDD_CONTENT_ITEM_FLAG_TIME_ABSOLUTE));
item_helper->put_image(NULL);
item_helper->put_layout(static_cast<GoogleDesktopDisplayContentItemLayout>(
GDD_CONTENT_ITEM_LAYOUT_NEWS));
// Create the item that will be added
CComQIPtr<IGoogleDesktopDisplayContentItem> item(item_helper);
GoogleDesktopContentItemDisplayOptions item_flags =
static_cast<GoogleDesktopContentItemDisplayOptions>(GDD_ITEM_DISPLAY_IN_SIDEBAR);
// Add the item with the helper
helper->AddContentItem(item, item_flags);
}
}
return S_OK;
}
- Open the stdafx.h
file,
which was automatically generated by the ATL Wizard as part of your project.
-
Since CString was used
in the CPP file, we need to do an
#include for the
CString methods.
Insert the following code below the existing
#include statements:
#include "resource.h"
#include <atlbase.h>
#include <atlcom.h>
#include <atlstr.h>
using namespace ATL;
Using the Helper to Dynamically Insert Items
- Open your project's
stdafx.h file.
- Since Google Desktop requires Windows 2000 or later, you should
set the WINVER compiler variable to be 0x0500. As a first step,
remove or comment out the following WINVER and _WIN32_ referencing
code:
// Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#ifndef WINVER // Allow use of features specific to Windows 95 and Windows NT 4 or later.
#define WINVER 0x0400 // Change this to the appropriate value to target
//Windows 98 and Windows 2000 or later.
#endif
#ifndef _WIN32_WINNT // Allow use of features specific to Windows NT 4 or later.
#define _WIN32_WINNT 0x0400 // Change this to the appropriate value to target Windows 2000 or later.
#endif
#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later.
#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
#endif
#ifndef _WIN32_IE // Allow use of features specific to IE 4.0 or later.
#define _WIN32_IE 0x0400 // Change this to the appropriate value to target IE 5.0 or later.
#endif
- Insert the following line, which replaces the lines you just
removed/commented out:
#define WINVER 0x0500 // Win2k and above
- Open the plug-in object's Header file (for our example,
MyDiskMonitorObj.h).
- Insert the following code at the top of the file, right below the
existing #include
statements.
This code creates a new class for a message-only window. Often,
it is very useful for a plug-in to create a new message-only window
to receive Windows messages or as a base for timers. In our example,
it will receive notifications about removable media being inserted
or removed.
On initialization of the Sidebar tile, this class's
StartNotifications() method
should be called. When destroying the plug-in, its
StopNotifications()
method should be called to handle the clean up. In this example code,
generally undocumented functions are used to monitor file
changes from Explorer, which is why we have to manually define
many of the structures and constants.
#include <shlobj.h>
#include <atlwin.h>
#define WM_SHELLNOTIFY WM_USER+5
typedef struct {
DWORD dwItem1;
DWORD dwItem2;
} SHNOTIFYSTRUCT;
class CMyDiskMonitorObj;
class CMyDiskMonitorObjMsgOnlyWindow :
public CWindowImpl<CMyDiskMonitorObjMsgOnlyWindow> {
public:
bool StartNotifications(CMyDiskMonitorObj *plugin);
bool StopNotifications();
BEGIN_MSG_MAP(CMyDiskMonitorObjMsgOnlyWindow)
MESSAGE_HANDLER(WM_SHELLNOTIFY, OnShellNotify);
END_MSG_MAP()
LRESULT OnShellNotify(UINT /*uMsg*/, WPARAM
/*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
private:
CString GetPathFromPIDL(DWORD pidl);
private:
CMyDiskMonitorObj *plugin_;
ULONG notification_;
};
- Scroll to the end of the
CMyDiskMonitorObj class definition
and insert the following code. It creates an instance of the
message-only window that receives notifications about the
removable media.
public:
CComPtr<IUnknown> helper_;
CMyDiskMonitorObjMsgOnlyWindow message_window_;
public:
STDMETHOD(SetSite) (IUnknown* site);
};
- Open the plug-in object's CPP file (for our example, MyDiskMonitorObj.cpp)
- Go to the SetSite() method. From
there, go to where where the dummy items are added as shown previously in
this tutorial. This code is located below the SetIcons() call
- Remove the loop which was created earlier and replace it with the following code:
// Set the plug-in's icons (also used in About dialog)
helper->SetIcons(tile_icon, tile_icon);
// Start notifications on for Explorer file creations
if (message_window_.StartNotifications(this) == false)
return E_FAIL;
}
return S_OK;
}
- Insert the following at the bottom of the file. When the
StartNotifications() method
is called, the object will create a
message-only window. If successfully created, it will then set
itself up to receive notifications on all file
creations. StopNotifications()
also should be called once the plug-in gets
unloaded in order to release the resources taken by the
notifications.
bool CMyDiskMonitorObjMsgOnlyWindow::StartNotifications(CMyDiskMonitorObj *plugin) {
plugin_ = plugin;
// create a message-only window
if (Create((HWND)-3) == NULL)
return false;
// Specify to listen for file change events from all hard drives
LPITEMIDLIST id_list;
if (SHGetSpecialFolderLocation(m_hWnd, CSIDL_DESKTOP, &id_list) != NOERROR)
return false;
SHChangeNotifyEntry notify_settings;
notify_settings.fRecursive = TRUE;
notify_settings.pidl = id_list;
// Start listening for file events
notification_ = SHChangeNotifyRegister(m_hWnd,
SHCNE_DISKEVENTS,
SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED | SHCNE_DRIVEADD | SHCNE_DRIVEREMOVED,
WM_SHELLNOTIFY,
1, ¬ify_settings);
if (notification_ == 0)
return false;
return true;
}
bool CMyDiskMonitorObjMsgOnlyWindow::StopNotifications() {
SHChangeNotifyDeregister(notification_);
return true;
}
- Insert the following code at the end of the file. This code
block inserts an item whenever a notification event happens in the
message-only window. Note that a large part of this code is what
was in the loop in the previous part of this tutorial where simple
dummy items were being inserted. Below, it has been slightly
modified both to change the text output and to use the plugin_ object to add the
item. GetPathFromPIDL is
required in order to do a conversion of the parameter data to a
displayable form.
LRESULT CMyDiskMonitorObjMsgOnlyWindow::OnShellNotify(UINT msg,
WPARAM wp, LPARAM lp, BOOL& handled) {
// Get the before and after filenames
SHNOTIFYSTRUCT *notification = (SHNOTIFYSTRUCT *)wp;
CString fullpath = GetPathFromPIDL(notification->dwItem1);
// Verify that the path is not empty
if (fullpath.IsEmpty())
return S_OK;
// Create an object for the new item
CMyDiskMonitorContentItemImpl *item_object = NULL;
CMyDiskMonitorContentItemImpl::CreateInstance(&item_object);
CComPtr<IGoogleDesktopDisplayContentItemHelper> item_helper;
item_object->GetUnknown()->QueryInterface(IID_IGoogleDesktopDisplayContentItemHelper,
reinterpret_cast<void**>(&item_helper));
// Get the system time for a time value
SYSTEMTIME system_time;
GetSystemTime(&system_time);
DATE time;
SystemTimeToVariantTime(&system_time, &time);
// Display the correct information depending on the event data
if ((lp == SHCNE_MEDIAINSERTED) || (lp == SHCNE_DRIVEADD)) {
item_helper->put_heading(_T("Media inserted"));
} else if ((lp == SHCNE_MEDIAREMOVED) || (lp == SHCNE_DRIVEREMOVED)) {
item_helper->put_heading(_T("Media removed"));
}
CComBSTR source(fullpath);
item_helper->put_source(source);
// Set the properties for the new item
item_helper->put_tooltip(_T("Tooltip"));
item_helper->put_time_created(time);
item_helper->put_flags(static_cast<GoogleDesktopDisplayContentItemFlags>(
GDD_CONTENT_ITEM_FLAG_TIME_ABSOLUTE));
item_helper->put_image(NULL);
item_helper->put_layout(static_cast<GoogleDesktopDisplayContentItemLayout>(
GDD_CONTENT_ITEM_LAYOUT_NEWS));
// Create the item that will be added
CComQIPtr<IGoogleDesktopDisplayContentItem> item(item_helper);
GoogleDesktopContentItemDisplayOptions item_flags =
static_cast<GoogleDesktopContentItemDisplayOptions>(GDD_ITEM_DISPLAY_IN_SIDEBAR);
// Add the item with the helper
CComQIPtr<IGoogleDesktopDisplayPluginHelper> helper(plugin_->helper_);
helper->AddContentItem(item, item_flags);
return S_OK;
}
CString CMyDiskMonitorObjMsgOnlyWindow::GetPathFromPIDL(DWORD pidl) {
CString out(_T(""));
wchar_t path[MAX_PATH];
if (SHGetPathFromIDList((struct _ITEMIDLIST *)pidl, path))
out = path;
return out;
}
- Finally, so that resources are properly freed when the plug-in closes,
insert the following code in the FinalRelease() method:
void CMyDiskMonitorObj::FinalRelease() {
if (message_window_.IsWindow()) {
message_window_.StopNotifications();
message_window_.DestroyWindow();
}
helper_.Release();
}
Responding to Double Clicks on Tile Items We need to handle how the items in our tile respond
when users double-click on them. Per the UI guidelines, double-clicking opens
the item in a new application window and also selects it in the Sidebar.
- Open the plug-in object's CPP file (MyDiskMonitorObj.cpp).
- The item class' OpenItem() handles opening the item, so
we need to customize it to do the right thing when opening our items.
For our example, replace the generated OpenItem() method with the following
version:
STDMETHODIMP CMyDiskMonitorContentItem::OpenItem() {
CComQIPtr<IGoogleDesktopDisplayContentItemHelper> helper(helperitem_);
CComBSTR drive;
ATLVERIFY(SUCCEEDED(helper->get_source(&drive)));
ShellExecute(NULL, _T("open"), drive, NULL, drive, SW_SHOWNORMAL);
return S_OK;
}
Responding to Single Clicks on Tile Items with Details View
Per the Sidebar UI guidelines, single-clicking a
tile's content item opens that item's details view. The following
code implements that behavior for our content items.
- Open the plug-in object's CPP file (MyDiskMonitorObj.cpp).
- OnDetailsView() in the item class handles opening the details view. Customize it to handle your particular items. For our example, the
details view is put together depending on the volume information (if
it was inserted). The SetContent() method adds text to
the details view. When completed, the Detach() method makes
the data displayable on the details view window.
Replace the generated OnDetailsView() method with the
following version:
STDMETHODIMP CMyDiskMonitorContentItem::OnDetailsView(BSTR* title,
GoogleDesktopDisplayDetailsViewFlags* flags, IUnknown** details_control,
VARIANT_BOOL* cancel) {
// Get the item to be displayed
CComQIPtr<IGoogleDesktopDisplayContentItemHelper> helper(helperitem_);
ATLASSERT(helper != NULL);
if (!helper)
return E_INVALIDARG;
// Here we can create any ActiveX control for displaying the details, and
// return it via the details_control parameter. We use our
// CMyDiskMonitorDetailsView control to draw the details view content.
CComPtr<IGoogleDesktopDisplayDetailsViewHelper> details;
HRESULT hr = details.CoCreateInstance(CLSID_GoogleDesktopDisplayDetailsViewHelper);
if (SUCCEEDED(hr)) {
// Get source drive that was inserted/removed
CComBSTR drivePathBSTR("");
helper->get_source(&drivePathBSTR);
CString drivePath(drivePathBSTR);
// Get the drive label
CString driveLabel;
GetVolumeInformation(drivePath, driveLabel.GetBuffer(255), 255,
NULL, NULL, NULL, NULL, NULL);
driveLabel.ReleaseBuffer();
// Get the drive size total and free space
ULARGE_INTEGER totalSpace, freeSpace;
CString totalSpaceOUT, freeSpaceOUT;
GetDiskFreeSpaceEx(drivePath, NULL, &totalSpace, &freeSpace);
totalSpaceOUT.Format(_T("%llu"), totalSpace);
freeSpaceOUT.Format(_T("%llu"), freeSpace);
// Set the text in the details view
CString output;
output += _T("Drive: ") + drivePath + _T("\n");
output += _T("Label: ") + driveLabel + _T("\n");
output += _T("Total size (Bytes): ") + totalSpaceOUT + _T("\n");
output += _T("Free space (Bytes): ") + freeSpaceOUT + _T("\n");
// Display the output in the details window
CComBSTR outputBSTR(output);
CComBSTR statusBSTR("");
helper->get_heading(&statusBSTR);
details->SetContent(statusBSTR, NULL, outputBSTR, VARIANT_TRUE, GDD_CONTENT_ITEM_LAYOUT_NEWS);
helper->get_source(title);
// Return the data to display
*details_control = details.Detach();
*flags = static_cast<GoogleDesktopDisplayDetailsViewFlags>(
GDD_DETAILS_VIEW_FLAG_TOOLBAR_OPEN);
}
return hr;
}
|