Note: perhaps visualstudio express is not even needed, perhaps only some free windows SDK. What is needed from it is its "midl" tool since there is no opensource alternative yet.
Disclaimer: this tutorial is a work in progress.
The ActiveQt OpenGL example was the nearest to this task and helped doing this task. [http://doc.trolltech.com/4.6/activeqt-opengl.html]
Concepts used in the tutorial
=============================
What is a "shell extension"?
----------------------------
A shell extension is a plugin for the windows shell, namely explorer.exe, or open/save file dialogs in any application (e.g. notepad). An extension can for example change the display of icons in the explorer (which we will do in this tutorial), add items in the contextual menu, etc.
A shell extension is a DLL file that needs to be "registered" so it can be enabled and used by the shell.
Registering an extension
------------------------
An extension will start taking effect after it is registered, and separately in apps started _after_ registration. If the extension is just registered, you won't see its effects in explorer yet. If you start notepad after, you will see the extension effects in notepad, but not in explorer.exe. explorer.exe will have to be restarted (or the computer rebooted) to see the effect in explorer.
Symmetrically, unregistration will not disable the extension immediately in all applications. Applications still running while the extension is unregistered will still have the extension loaded until the apps are exited.
This has an importance while developping the extension, as the extension file DLL can't be overwritten while it's in use, unregistering it is not enough.
Here, we will register/unregister extensions using the "regsvr32" command.
To register an extension:
regsvr32 the_extension.dll
To unregister it:
regsvr32 /u the_extension.dll
The /s flag can be added to the regsvr32 command to enable "silent mode", which disables the annoying dialogbox. But we won't use it at first, since it doesn't report registration errors.
32-bits/64-bits compatibility
-----------------------------
An extension, as a native binary DLL, is very sensitive to byte-architecture.
Whereas a 32-bits program can be run on a 64-bits OS, a DLL is more complicated: it has to be be in the same bytes architecture as the program that loads it.
This means that on a 64-bits OS, 2 DLLs will be needed: a 64-bits one for 64-bits programs like explorer.exe, and a 32-bits one for 32-bits programs like many. Remember that your extension can be used in third-party, non-builtin-in-windows programs, for example any program that has an open/save file dialog.
Basic anatomy of a shell extension and COM
------------------------------------------
A shell extension has to provide an API to applications that will use it, through a protocol called "COM". The shell extension will be a COM server, as it exposes its API, and the apps will be COM clients.
A COM server in a DLL is called an "in-process" server.
The API it implements follows some defined "COM interfaces" (interface is the 'I' in "API", it's a kind of abstract class).
The API is implemented in a class, each instance of this class is a "COM object".
Our shell extension, displaying icon overlays, will implement the "IShellIconOverlayIdentifier" interface.
COM objects need a COM factory to be created by the COM clients.
COM interfaces implementations need declaration in a special language called IDL. These IDL declarations will be generated by an "MIDL" tool.
ActiveQt
--------
ActiveQt is a Qt module that will provide us classes for implementing COM servers and building COM factories. It also provides many other features which we won't need here. All classes names from ActiveQt start with "QAx".
We will use its QAxServer submodule.
- http://doc.trolltech.com/4.6/activeqt.html
- http://doc.trolltech.com/4.6/activeqt-server.html
VisualStudio Express
--------------------
In this tutorial, we will use VisualStudioExpress, which is free, for its "midl" tool, that will generate IDL definitions for our shell extension.
- http://www.microsoft.com/express/
GUIDs
-----
GUIDs, Class IDs (CLSID), Interface IDs (IID) are all UUIDs, 128-bits Universally Unique IDentifiers.
They are used to identify every published component of a shell extension and to tell them apart from all shell extensions in the world.
As they are 128-bits, which is big, a randomly generated UUID is generally enough, there is no need of a central registry to check our UUID is not already used.
For example, "60c580d2-41f2-43ed-b5d1-b435d74d1999" is an UUID.
We can use the VisualStudioExpress builtin generator, or use any uuid generator tool. Qt does not provide one, but there are many, for example, python can be used to generate one:
python -c "import uuid; print uuid.uuid4()"
============
Starting
--------
In QtCreator, start a new "C++ library" project, we can call it "shellext_overlay". This project does not need QtGui.
Create a new C++ class (called "ShellOverlay" for example).
The COM object class
--------------------
Our class will be the class for the COM object. So it will have to inherit QObject (first, because Qt needs QObject always be the first), QAxAggregated and the COM interfaces we implement: IShellIconOverlayIdentifier.
class SHELLEXT_OVERLAYSHARED_EXPORT ShellOverlay : public QObject, public QAxAggregated, public IShellIconOverlayIdentifier {
All COM interfaces inherit the IUnknown interface, so we would have to implement it, but Qt provides a sane default implementation for it, which can be included with the QAXAGG_IUNKNOWN macro in our class declaration.
long queryInterface(const QUuid &iid, void**iface);
???
IShellIconOverlayIdentifier interface
+++++++++++++++++++++++++++++++++++++
The IShellIconOverlayIdentifier defines 3 members we will need to implement, here is their declaration
/*! Query information about the overlay icon
\param pwszIconFile output parameter where to put the array of overlay icon (wchar_t **)
\param cchMax size of the pwszIconFile buffer, in characters (not bytes)
\param pIndex output parameter, index of the icon in the pwszIconFile file (e.g. if the file contains multiple icons), starting at 0
\param pdwFlags output parameter, options for the overlay icon
\return S_OK in case of success
*/
STDMETHOD(GetOverlayInfo)(LPWSTR pwszIconFile, int cchMax, int *pIndex, DWORD* pdwFlags);
STDMETHOD(GetPriority)(int* pPriority);
/*! Query if the overlay is present for a particular file
\param pwszPath path of the file to query (wchar_t*)
\param dwAttrib attributes of the file
\return S_OK if the icon has to be overlayed, S_FALSE else
*/
STDMETHOD(IsMemberOf)(LPCWSTR pwszPath,DWORD dwAttrib);
Implementation of the class
+++++++++++++++++++++++++++
long ShellOverlay::queryInterface(const QUuid &iid, void **iface) {
*iface = 0;
if (iid == IID_IShellIconOverlayIdentifier)
*iface = (IShellIconOverlayIdentifier *)this;
else
return E_NOINTERFACE;
AddRef();
return S_OK;
}
Implementation of the interface
+++++++++++++++++++++++++++++++
In this function, we have to return information about our icon. Our icon will be embedded in the DLL file, whose path is QAxFactory::serverFilePath(). It will be the first icon in the DLL file.
STDMETHODIMP ShellOverlay::GetOverlayInfo(LPWSTR pwszIconFile, int cchMax, int *pIndex, DWORD *pdwFlags) {
QString iconPath(QAxFactory::serverFilePath());
if (iconPath.length() > cchMax)
return S_FALSE;
int len = iconPath.toWCharArray(pwszIconFile);
pwszIconFile[len] = L'\0';
*pIndex = 0;
*pdwFlags = ISIOI_ICONFILE | ISIOI_ICONINDEX;
return S_OK;
}
This function is not important in this tutorial.
STDMETHODIMP ShellOverlay::GetPriority(int *pPriority) {
*pPriority = 0;
return S_OK;
}
In this function, we have to tell if a file's icon has to be modified by ours, or not. If the filename contains a 'q' or a 't', we reply it should.
STDMETHODIMP ShellOverlay::IsMemberOf(LPCWSTR pwszPath, DWORD dwAttrib) {
QString filename
= info.
fileName();
if (filename.
contains(QChar('q')) || filename.
contains(QChar('t'))) return S_OK;
return S_FALSE;
}
- http://msdn.microsoft.com/en-us/library/bb761265%28v=VS.85%29.aspx
Note: perhaps visualstudio express is not even needed, perhaps only some free windows SDK. What is needed from it is its "midl" tool since there is no opensource alternative yet.
Disclaimer: this tutorial is a work in progress.
The ActiveQt OpenGL example was the nearest to this task and helped doing this task. [http://doc.trolltech.com/4.6/activeqt-opengl.html]
Concepts used in the tutorial
=============================
What is a "shell extension"?
----------------------------
A shell extension is a plugin for the windows shell, namely explorer.exe, or open/save file dialogs in any application (e.g. notepad). An extension can for example change the display of icons in the explorer (which we will do in this tutorial), add items in the contextual menu, etc.
A shell extension is a DLL file that needs to be "registered" so it can be enabled and used by the shell.
Registering an extension
------------------------
An extension will start taking effect after it is registered, and separately in apps started _after_ registration. If the extension is just registered, you won't see its effects in explorer yet. If you start notepad after, you will see the extension effects in notepad, but not in explorer.exe. explorer.exe will have to be restarted (or the computer rebooted) to see the effect in explorer.
Symmetrically, unregistration will not disable the extension immediately in all applications. Applications still running while the extension is unregistered will still have the extension loaded until the apps are exited.
This has an importance while developping the extension, as the extension file DLL can't be overwritten while it's in use, unregistering it is not enough.
Here, we will register/unregister extensions using the "regsvr32" command.
To register an extension:
regsvr32 the_extension.dll
To unregister it:
regsvr32 /u the_extension.dll
The /s flag can be added to the regsvr32 command to enable "silent mode", which disables the annoying dialogbox. But we won't use it at first, since it doesn't report registration errors.
32-bits/64-bits compatibility
-----------------------------
An extension, as a native binary DLL, is very sensitive to byte-architecture.
Whereas a 32-bits program can be run on a 64-bits OS, a DLL is more complicated: it has to be be in the same bytes architecture as the program that loads it.
This means that on a 64-bits OS, 2 DLLs will be needed: a 64-bits one for 64-bits programs like explorer.exe, and a 32-bits one for 32-bits programs like many. Remember that your extension can be used in third-party, non-builtin-in-windows programs, for example any program that has an open/save file dialog.
Basic anatomy of a shell extension and COM
------------------------------------------
A shell extension has to provide an API to applications that will use it, through a protocol called "COM". The shell extension will be a COM server, as it exposes its API, and the apps will be COM clients.
A COM server in a DLL is called an "in-process" server.
The API it implements follows some defined "COM interfaces" (interface is the 'I' in "API", it's a kind of abstract class).
The API is implemented in a class, each instance of this class is a "COM object".
Our shell extension, displaying icon overlays, will implement the "IShellIconOverlayIdentifier" interface.
COM objects need a COM factory to be created by the COM clients.
COM interfaces implementations need declaration in a special language called IDL. These IDL declarations will be generated by an "MIDL" tool.
ActiveQt
--------
ActiveQt is a Qt module that will provide us classes for implementing COM servers and building COM factories. It also provides many other features which we won't need here. All classes names from ActiveQt start with "QAx".
We will use its QAxServer submodule.
- http://doc.trolltech.com/4.6/activeqt.html
- http://doc.trolltech.com/4.6/activeqt-server.html
VisualStudio Express
--------------------
In this tutorial, we will use VisualStudioExpress, which is free, for its "midl" tool, that will generate IDL definitions for our shell extension.
- http://www.microsoft.com/express/
GUIDs
-----
GUIDs, Class IDs (CLSID), Interface IDs (IID) are all UUIDs, 128-bits Universally Unique IDentifiers.
They are used to identify every published component of a shell extension and to tell them apart from all shell extensions in the world.
As they are 128-bits, which is big, a randomly generated UUID is generally enough, there is no need of a central registry to check our UUID is not already used.
For example, "60c580d2-41f2-43ed-b5d1-b435d74d1999" is an UUID.
We can use the VisualStudioExpress builtin generator, or use any uuid generator tool. Qt does not provide one, but there are many, for example, python can be used to generate one:
python -c "import uuid; print uuid.uuid4()"
============
Starting
--------
In QtCreator, start a new "C++ library" project, we can call it "shellext_overlay". This project does not need QtGui.
Create a new C++ class (called "ShellOverlay" for example).
The COM object class
--------------------
Our class will be the class for the COM object. So it will have to inherit QObject (first, because Qt needs QObject always be the first), QAxAggregated and the COM interfaces we implement: IShellIconOverlayIdentifier.
class SHELLEXT_OVERLAYSHARED_EXPORT ShellOverlay : public QObject, public QAxAggregated, public IShellIconOverlayIdentifier {
All COM interfaces inherit the IUnknown interface, so we would have to implement it, but Qt provides a sane default implementation for it, which can be included with the QAXAGG_IUNKNOWN macro in our class declaration.
long queryInterface(const QUuid &iid, void**iface);
???
IShellIconOverlayIdentifier interface
+++++++++++++++++++++++++++++++++++++
The IShellIconOverlayIdentifier defines 3 members we will need to implement, here is their declaration
/*! Query information about the overlay icon
\param pwszIconFile output parameter where to put the array of overlay icon (wchar_t **)
\param cchMax size of the pwszIconFile buffer, in characters (not bytes)
\param pIndex output parameter, index of the icon in the pwszIconFile file (e.g. if the file contains multiple icons), starting at 0
\param pdwFlags output parameter, options for the overlay icon
\return S_OK in case of success
*/
STDMETHOD(GetOverlayInfo)(LPWSTR pwszIconFile, int cchMax, int *pIndex, DWORD* pdwFlags);
STDMETHOD(GetPriority)(int* pPriority);
/*! Query if the overlay is present for a particular file
\param pwszPath path of the file to query (wchar_t*)
\param dwAttrib attributes of the file
\return S_OK if the icon has to be overlayed, S_FALSE else
*/
STDMETHOD(IsMemberOf)(LPCWSTR pwszPath,DWORD dwAttrib);
Implementation of the class
+++++++++++++++++++++++++++
long ShellOverlay::queryInterface(const QUuid &iid, void **iface) {
*iface = 0;
if (iid == IID_IShellIconOverlayIdentifier)
*iface = (IShellIconOverlayIdentifier *)this;
else
return E_NOINTERFACE;
AddRef();
return S_OK;
}
Implementation of the interface
+++++++++++++++++++++++++++++++
In this function, we have to return information about our icon. Our icon will be embedded in the DLL file, whose path is QAxFactory::serverFilePath(). It will be the first icon in the DLL file.
STDMETHODIMP ShellOverlay::GetOverlayInfo(LPWSTR pwszIconFile, int cchMax, int *pIndex, DWORD *pdwFlags) {
QString iconPath(QAxFactory::serverFilePath());
if (iconPath.length() > cchMax)
return S_FALSE;
int len = iconPath.toWCharArray(pwszIconFile);
pwszIconFile[len] = L'\0';
*pIndex = 0;
*pdwFlags = ISIOI_ICONFILE | ISIOI_ICONINDEX;
return S_OK;
}
This function is not important in this tutorial.
STDMETHODIMP ShellOverlay::GetPriority(int *pPriority) {
*pPriority = 0;
return S_OK;
}
In this function, we have to tell if a file's icon has to be modified by ours, or not. If the filename contains a 'q' or a 't', we reply it should.
STDMETHODIMP ShellOverlay::IsMemberOf(LPCWSTR pwszPath, DWORD dwAttrib) {
QFileInfo info(QString::fromWCharArray(pwszPath));
QString filename = info.fileName();
if (filename.contains(QChar('q')) || filename.contains(QChar('t')))
return S_OK;
return S_FALSE;
}
- http://msdn.microsoft.com/en-us/library/bb761265%28v=VS.85%29.aspx
To copy to clipboard, switch view to plain text mode
Bookmarks