This article was written and submitted by an external developer. The Google Desktop Team thanks Krzysztof Olczyk for his time and expertise.
The Google Desktop API makes it easy to write powerful, attractive gadgets using XML and JavaScript. However, it may happen that your gadget requires more refined and specific functionality that is not provided by the API.
Fortunately, gadget developers are not restricted to only the objects and functions available in the API. Functionality can be extended easily by writing a DLL library that encapsulates native code inside an ActiveX automation object. This article will show you how to start creating a hybrid gadget employing programming tools such as Microsoft Visual Studio 2005 or CodeGear Delphi (previously known as Borland Delphi).
After reading this article, you will be capable of developing advanced hybrid gadgets with the same ease and confidence you have in developing traditional script-based gadgets.
I will also provide you with a complete example of an ActiveX module written in CodeGear Delphi.
Let us begin by explaining what a hybrid gadget is. From the perspective of the end-user, it is not possible to distinguish between a standard gadget and a hybrid one. Both are packaged in a .gg file and can be installed by double-clicking the file or using the "Add gadgets" dialog. Indeed, they are the same except for the fact that after declaring your object in the manifest, you can create it in your code as if it was a standard JavaScript object: hybrid gadgets contain one or more DLL files that implement ActiveX automation objects. You may feel a bit uneasy now if you are not familiar with ActiveX technology, but you shouldn't worry. It does not have to be a difficult task, especially if you choose the right tools.
Moreover, you don't have to be concerned with any burdensome ActiveX deployment, i.e. registration of a type library.
All you need to do is to add a tag to the manifest file (gadget.gmanifest
) informing Google Desktop that your gadget
is a hybrid, and that you have a DLL library with an ActiveX object embedded. If you take a look at the manifest file of any hybrid gadget,
you will see something similar to the following:
<gadget minimumGoogleDesktopVersion="5.1.0.0"> <about> <name>&GADGET_NAME;</name> <description>&GADGET_DESCRIPTION;</description> <aboutText>&GADGET_ABOUT_TEXT;</aboutText> <smallIcon>plugin_small.gif</smallIcon> <icon>plugin_large.gif</icon> <version>0.7.0.0</version> <author>John Smith</author> <authorWebsite>http://whatacute.site.com<authorWebsite> <id>ABBEDEE1-2EC5-4EAE-BD1A-8BE8B4D191E2</id> <copyright>Copyright (c) 2012 J.Smith </copyright> <authorEmail>contact@me.com</authorEmail> </about> <install> <object name="HybridHello" clsid="973FCA8C-DEA0-46EC-B228-3218B0D7B1C2" src="hybridhello.dll"/> </install> </gadget>Note that the ActiveX object is declared using an
<object>
tag inside the <install>
section.
You have to specify a name for the object, which will be used to identify it in the JavaScript code. You also specify the unique identifier of your class (IDEs generate
this for you) and the filename of the DLL library.
After declaring your object in the manifest, you can create it in your code as if it was a standard JavaScript object:
var myhybridstuff; myhybridstuff = new HybridHello(); myhybridstuff.SayHello(":D");
In order to start developing ActiveX libraries, you need to use one of the development environments that support ActiveX programming. Some examples of such suites are:
The hybrid module can be developed using Visual Studio 2005, which is probably the most popular development suite Microsoft offers. The Visual Studio approach requires coding in C++ and the use of ATL, Microsoft's toolkit library for ActiveX development.
The first thing to do is to start a new ATL project in Visual C++ using a wizard. You don't have to tweak any settings apart from the name of the project. Figure 1 depicts the wizard dialog window where you specify the type of project to create, in this case an ATL project.
Once you have finished with the project wizard, you can remove one of the generated project files, the one that ends in PS
, e.g. HybridHelloPS
.
Now, you can proceed to create a class that is going to be instantiated in JavaScript code. Again, there is
a wizard to help us with this task located in the Project | Add class.. menu. For a hybrid gadget, the class type should be
ATL simple object.
After creating a class, you must manually introduce a change required by Google Desktop.
The modification is to set the version of your class to 0xffff.0xffff
.
This is done in the header file where you implement your class (for instance, HelloWorld.h
):
class ATL_NO_VTABLE CHelloWorld : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CHelloWorld, &CLSID_HelloWorld>, public IDispatchImpl<IHelloWorld, &IID_IHelloWorld, &LIBID_HybridHelloLib, /*wMajor =*/ 0xffff, /*wMinor =*/ 0xffff>Make sure that the parameters
wMajor
and wMinor
are both 0xffff
.
After the class is created, you can add new methods or properties in Class View by selecting
the main interface of your class (IHybridHello
for example) and invoking the Add method
or Add property wizard.
In the wizard, you may specify the data type of the property or arguments in the case of a method. You will find more information about data types below.
Assume you have created a class called HelloWorld
. Then, one of the methods may look like this:
// CHelloWorld STDMETHODIMP CHelloWorld::SayHello(BSTR str) { CComBSTR msg; msg.Append("Hello World "); msg.Append(str); MessageBoxW(0, msg, L"Hello", 0); return S_OK; }Once compiled, the library can be used in your gadget as shown in the first section.
To discover the class identifier in order to specify it in the manifest file, open the .IDL file and search for your COM class (coclass). You will encounter a piece of code similar to the following:
[a uuid(C4D3C406-20E7-47D7-8BC7-0BFDE34DB16E), helpstring("HelloWorld Class") ] coclass HelloWorld { [adefault] interface IHelloWorld; };The value in
uuid()
is the one you are looking for.
If you don't like programming in C++ and prefer the more human-friendly Pascal, you probably should use Turbo Delphi. Fortunately, even the free version—Turbo Delphi Explorer—supports ActiveX development.
The first thing to do is to create a new project - ActiveX library. Delphi will create the template of the new library for you. As in Visual Studio, you need to create a new class, i.e. use the wizard to create an Automation Object (Figure 3).
After you choose a name for your class, Delphi will generate a code template and open the Type Library Editor. Here, you can add or modify the properties and methods for the main interface of your class.
It is necessary to change
the version of your COM object in the type library, otherwise Google Desktop will not be able
to instantiate your class. Set the Version parameter of
your class and main interface to 65535.65535
(i.e. 0xffff.0xffff
in hexadecimal notation, see Figure 4).
Now you can start writing your code. For every function or property, Delphi creates
a template in the unit file.
The function SayHello
would look like the following:
procedure THelloWorld.SayHello(const Str: WideString); begin ShowMessage('Hello world, ' + Str); end;Once compiled, you can use the library in your gadget as shown in the first section. Make sure that you specify a correct unique identifier in the manifest file. Note: Use the identifier found in the GUID field for the
CoClass
, not the one for the interface.
When you start outlining your ActiveX object, either using wizards in Visual Studio or in Type Library Editor in Delphi, you may get confused by the number of possible OLE types you can use for your properties or function arguments. Below, is a summary of various OLE types and how they should be used:
If you want to pass a text string from your script, the best option to choose is the standard
OLE type used for strings - BSTR
.
It is recognized and supported by JScript. In ATL, there is
a helper class CComBSTR
for managing BSTRs
, while in Delphi, the built-in type WideString
is compatible with BSTR
.
You should use the LONG
data type in passing numerical values to ActiveX objects.
It is equivalent
to int
in C++ and Integer
in Pascal.
In the communication between your script and the native code, you are not limited to primitive data types.
You may also return an object to the script, as long as it implements the IDispatch
interface.
Such an object may be used like a normal JavaScript object, except when you want to dynamically add new properties to the returned object; in this case, you'll need your object to implement IDispatchEx
.
It also works the other way around; you may accept a JavaScript object in your hybrid code. What will be passed is a pointer to IDispatch
interface.
In order to read/write a property or execute a method, you need
to invoke the pair of GetIDsOfNames
and Invoke
methods of the IDispatch
interface, all documented in MSDN.
You can find an example of retrieving property value of a JScript object in one of the posts in my blog as well.
You don't have to do as much work in Delphi.
If you assign a reference to IDispatch to a variable of type Variant
, you can deal with the JScript object
as if it was a normal Delphi object. Assume we have the following code in JavaScript:
var myobj = new Object(); myobj.someparam = "Hello"; hybr.Foo(myobj);Then the function
Foo
would be defined as follows:
procedure THybridClass.Foo(Obj: IDispatch); var vObj: variant; begin vObj := Obj; ShowMessage(vObj.someparam); end;
Passing arrays between JavaScript (in particular JScript) code and an ActiveX object is quite problematic. The concept of an array in JScript
is quite different from the one present in ActiveX technologies. Architects of JScript claim there is no easy way
to convert between them and their suggestion is to not use arrays as method arguments or properties at all.
However, in the case of a hybrid gadget, it is known that the array will be used exclusively in a JScript script.
Like everything in JScript, an array is an object and has properties and methods.
The elements of an array can be implemented in the same manner as if they were the properties
of an object. In fact, there is no difference between the code arr.elem
and arr[a"elem"]
in JScript.
Therefore, we can pass an array to the ActiveX object as an IDispatch
reference and query for array elements
using GetIDsOfNames
and Invoke
methods of IDispatch. Additionally, methods pop
and push
may be used
to avoid querying for every element if their keys are not known. I have discussed exhaustively the topic of processing
JScript arrays in my programming blog.
In this article I have outlined the topic of developing hybrid gadgets, a powerful concept in gadget development. Being able to develop hybrid gadgets dramatically increases your possibilities and removes constraints. I encourage you to download the sample I have developed recently in Delphi. In the sample, I use a built-in class in the Delphi library to provide the user with a color selection window. It is the same technique used in my gdAmp gadget and is good way to see how hybrid gadgets are developed.
Krzysztof Olczyk is a computer science student from Poland and is
passionately interested in software development (more precisely technologies like ActiveX, .NET, Java, scripting, and recently, creating Google Desktop gadgets).
Krzysztof studies at Technical University of Lodz, Poland and spent one-year studying as an exchange student at Technical University of Valencia, Spain.
He is the author of popular Google Gadgets like MultiDesktop and
MiniExplorer.
Computer Science is not his only hobby: he is known to be an addict of good rock music and cannot imagine life without music.
He enjoys composing music and playing guitar. Sometimes, he likes to read a good book, like those written by Paulo Coehlo.
Be sure to visit his homepage
containing his projects and his programming blog.
This work is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License.