Tuesday, August 28, 2007

Ajax not only for Web: Using XmlHTTPRequest in the standalone application.

If the term AJAX is familiar to you, for sure, you know that the heart of this technology is the XMLHttpRequest object. It may be used in, practically, all the modern browsers that support javascript. Although you would instantiate it in Firefox and new versions of Internet Explorer with the instruction:

var d = new XMLHttpRequest();

originally in Internet Explorer it was not a built-in JavaScript object.
In fact, the command to get XMLHttpRequest was:

var d = new ActiveXObject("MSXML2.XMLHTTP");

what reveals the fact that it was realized as an ActiveX object. Indeed, it was implemented in library called Microsoft XML which exposed COM object MSXML2.XMLHTTP. Its type library is present in msxml3.dll file in windows system folder.
So, if it is nothing more than normal ActiveX class, why not to use it in standalone application, for instance written in Delphi, to transfer data between the application and some web server.
In ActiveX-enabled languages that offer dynamic binding (like pascal) it is not more difficult that javascript code embedded in HTML. Here comes an example in Delphi, which fills a memo control with the code of the website http://www.somewhere.com:

procedure TForm1.Button1Click(Sender: TObject);
var
d: OleVariant;
begin
d := CreateOleObject('MSXML2.XMLHttp');
d.open('get', 'http://www.somewhere.com', false, EmptyParam, EmptyParam);
d.send('');
if (d.readyState = 4) and (d.status = 200) then
Memo1.Lines.Text := d.responseText;
end;

Simple, isn't it?
If you prefer static binding (for example, to take advantage of code completion) you may import ActiveX type library Microsoft XML 3.0. Then, your code would look like:

uses msxml2_tlb;

procedure TForm1.Button1Click(Sender: TObject);
var
d: IXMLHTTPRequest;
begin
d := CoXMLHTTP40.Create;
d.open('get', 'http://www.somewhere.com', false, EmptyParam, EmptyParam);
d.send('');
if (d.readyState = 4) and (d.status = 200) then
Memo1.Lines.Text := d.responseText;
end;

You may now be thinking "Wait!, but A in Ajax stands for asynchronous, and here your are blocking program, using XMLHttpRequest synchronously". Yes, we may do it also asynchronously, but one problem arouses here - handling onreadystatechange event. In reality, it is not an event as understood in ActiveX. You may know that, in fact, JScript is incapable of sinking ActiveX events. Thus, Microsoft architects made onreadystatechange to be a property you may assign JScript function to. Therefore, what we have to assign must be, at least, the imitation of JScript routine. As you may have realized, everything in JScript is an object - internally represented by a pointer to IDispatch interface. So are functions. To execute the function you call Invoke with DISPID equal to 0 on its IDispatch.
In Delphi, therefore, JScript's function may be imitated by an object of the class derived from TInterfacedObject and implementing IDispatch interface (providing implementation for Invoke method is enough).

The full example of asynchronous HTTP request follows here:

uses comobj, msxml2_tlb;

type
TAjaxEvenFunc = procedure (d: Variant) of object;
TAjaxEvent = class(TInterfacedObject, IDispatch)
private
d: Variant;
func: TAjaxEvenFunc;
public
constructor Create(const d: Variant; const func: TAjaxEvenFunc);
function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
Flags: Word; var Params; VarResult: Pointer; ExcepInfo: Pointer;
ArgErr: Pointer): HRESULT; stdcall;
function GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount: Integer;
LocaleID: Integer; DispIDs: Pointer): HRESULT; stdcall;
function GetTypeInfo(Index: Integer; LocaleID: Integer;
out TypeInfo): HRESULT; stdcall;
function GetTypeInfoCount(out Count: Integer): HRESULT; stdcall;
destructor Destroy; override;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
d: IXMLHTTPRequest;
begin
d := CreateOleObject('MSXML2.XMLHttp.3.0') as IXMLHTTPRequest;
d.open('get', 'http://www.onet.pl', true, EmptyParam, EmptyParam);
d.onreadystatechange := TAjaxEvent.Create(d, EventHandl) as IDispatch;
d.send('');
d := nil;
end;

procedure TForm1.EventHandl(d: Variant);
begin
if (d.readyState = 4) and (d.status = 200) then
Memo1.Lines.Text := d.responseText;
end;

{ TAjaxEvent }

constructor TAjaxEvent.Create(const d: Variant;
const func: TAjaxEvenFunc);
begin
inherited Create;
self.d := d;
self.func := func;
end;

destructor TAjaxEvent.Destroy;
begin
func := nil;
d := Null;
inherited;
end;

function TAjaxEvent.GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount,
LocaleID: Integer; DispIDs: Pointer): HRESULT;
begin
Result := E_NOTIMPL;
end;

function TAjaxEvent.GetTypeInfo(Index, LocaleID: Integer;
out TypeInfo): HRESULT;
begin
Result := E_NOTIMPL;
end;

function TAjaxEvent.GetTypeInfoCount(out Count: Integer): HRESULT;
begin
Result := E_NOTIMPL;
end;

function TAjaxEvent.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HRESULT;
begin
if DispID <> 0 then
begin
Result := E_INVALIDARG;
exit;
end;
if Assigned(func) and not VarIsNull(d) then
func(d);
end;

Tuesday, August 21, 2007

JScript arrays and COM objects, part 3

I have promised in my previous post that I will present also how to deal with JScript arrays in C++ code with ATL. Below, there is a code of a function, I have come up with, in C++ that gets the JScript array of integers and calculates the sum:

#include <string>
#include <sstream>
#define _C(oleop) hr = oleop; \
if (!SUCCEEDED(hr)) \
return hr;

STDMETHODIMP CHelloWorld::JSArrSum(VARIANT js_arr, LONG* Sum)
{
HRESULT hr;

/* checking if argument is an object */
if (!(js_arr.vt & VT_DISPATCH))
return E_INVALIDARG;

/* retrieving IDispatch */
IDispatch *disp = js_arr.pdispVal;
if (js_arr.vt & VT_BYREF)
disp = *(js_arr.ppdispVal);

/* getting array's length */
DISPPARAMS params;
FillMemory(¶ms, sizeof(DISPPARAMS), 0);
VARIANT res;

DISPID dl;
LPOLESTR ln = L"length";

_C(disp->GetIDsOfNames(IID_NULL, &ln, 1, LOCALE_USER_DEFAULT, &dl));

_C(disp->Invoke(dl, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET,
¶ms, &res, NULL, NULL));

VARIANT len1;
VariantInit(&len1);

_C(VariantChangeType(&len1, &res, 0, VT_I4));
LONG len = len1.lVal;

/* summing elements */

int total = 0;
for (int i = 0; i < len; i++)
{
std::stringstream ss;
ss << ind =" CComBSTR(ss.str().c_str());">GetIDsOfNames(IID_NULL, &ind, 1, LOCALE_USER_DEFAULT, &id));
_C(disp->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &res, NULL, NULL));

VARIANT val1;
VariantInit(&val1);

_C(VariantChangeType(&val1, &res, 0, VT_I4));
total += val1.lVal;
}

*Sum = total;

return S_OK;
}

What it does is simply the following:

  1. It is checked if an argument is some object (hopefully, a JScript array)
  2. IDispatch reference of an array is retrieved from the variant.
  3. The property length is obtained from the array's object as it is for any automation object. We receive some variant value, so we need to convert it to integer.
  4. For each element of an array its DISPID is queried and its value received, converted to integer, and summed up.
  5. It returns the sum of array's values.
It can be called from JScript as following:

var arr = new Array();
arr[0] = 10;
arr[1] = 15;
arr[2] = 2;

var obj = new ActiveXObject("ProgID.OfClass");
alert(obj.JSArrSum(arr));

Wednesday, August 15, 2007

JScript arrays and COM objects, part 2

In the previous post I have been discussing the scenario where we have COM object and the script in JScript and we need to interchange the array of data between them. I have demonstrated how to convert, in your JScript code, the vb-style array received from call to ActiveX object to the array native to JScript. To that, fortunately, the architects of JScript provided us with a tool - VBScript class. However, in reverse situation we don't have any object or routine in JScript scripting engine that would let us or help us converting JScript array to VBArray.

Eric Lippert in his blog states that, although he started writing code to manage conversion to VBArray, it was eventually abandoned and there is no way to do this.
And, actually, this is a truth. JScript engine does not provide us with any way to do that conversion. But why not try looking for it outside the engine but in what is by default available?
Fortunately, here our quest is successful and what we find is that there a COM object in WSH called Scripting.Dictionary. It is, well, as its name says, a dictionary, but what would be most important for you is that it has a method called Items which returns all the items in the dictionary as a VBArray object. So now, I am sure, you know what to do. Here is a code snippet of the function that converts JScript arrays into VBArray:

function GetVBArray(jsArray)
{
var dict =
new
ActiveXObject("Scripting.Dictionary");

for (i in jsArray)
{
dict.add(i, objJSArray[i]);
}

return dict.Items();
}

However, it may have some drawbacks like efficiency. But if it is not critical to your script it will suffice. On the other hand, if you are also an author of the COM object it would be better to just pass JScript array and deal with it in the code, wouldn't it?

If you read my previous post you probably remember that I wrote that in JScript arrays are nothing more than objects. You may also remember that JScript passes objects to automation objects as a pointer to IDispatch interface. Therefore, we may query the IDispatch object about the elements of array, can't we?

Below there is a piece of code in pascal (delphi) of the method that can both accept and process VBArrays and JScript arrays (to be precise, one-dimensional one):

procedure TSomeObject.ProcessArray(SomeArr: OleVariant);
var
i: integer;
ind: array [0..0] of integer;
begin
if VarIsArray(SomeArr) then // (1)
begin
for i := VarArrayLowBound(SomeArr, 1)
to
VarArrayHighBound(SomeArr, 1) do
begin
ind[0] := i;
ProcessElement(VarArrayGet(SomeArr, ind));
end;
end
else if VarIsType(SomeArr, VT_DISPATCH) then // (2)
begin
for i := 0 to SomeArr.length - 1 do
begin
ProcessElement(SomeArr.pop());
end;
end
else
raise EOleException.Create
('Parameter is not an array!',
E_INVALIDARG, '', '', 0);
end;

My example is in Delphi, but if you prefer, for instance, programming in C++ and ATL you can follow the same reasoning. However it will consume more code, since C++ semantics do not allow that you do late binding and you need to do that manually by pairs of calls to IDispatch's GetIDsOfNames and Invoke. In Delphi, we could use its magic of having IDispatch reference in Variant variable and leave it to Delphi to play with name resolving and calls to IDispatch methods.

Now let us analyze the above code. Firstly, in the if in (1) we are checking if the variant contains the traditional VBArray. If so, we process as we normally do with safe arrays. If not, we again examine our variant parameter if its content is IDispatch object. If so, it's probably the JScript array. Therefore, we can try getting the length property or get all elements by calls to pop method. You may also get the value at the given index, but it cannot be done automatically in Delphi. All you need to do is to extract an IDispatch interface, invoke GetIDsOfNames for the given index (which as you know, need not be an integer) and then Invoke retrieved DISPID to get the actual value.

That is all about arrays in JScript and COM object, I wanted to say for now. I hope somebody will find it usable. Maybe, I will also post in the future sample in C++ and ATL, stay tuned ;).

Monday, August 13, 2007

JScript arrays and COM objects, part 1

In one of the projects I am doing I needed to create the COM object that is going to be used from JavaScript. It is a well-known scenario - the JScript implementations lets programmer instantiate any COM object that is an ActiveX automation-enabled, i.e. it implements an IDispatch interface. Then, you can call any method or manipulate any property of the COM object. It looks nice and perfect as long as you are using simple data types - numbers, string. Still, there is no problem even to pass the object. Your property may be even visible as an object to the script - you may pass back the pointer to the IDispatch implementation of some object as the property and it can be treated in script almost like a native JScript object.

However, it no longer looks so bright when you need to pass to/from your COM object an array. The problem is that what you call an array in JavaScript is the completely different thing than what you call an array in your programming language you use to create your COM object. To complicate things more, an array in JScript, an array in ActiveX world and an array in "serious" programming languages like C++ or Delphi are different objects. In C, C++ and pascal it is, simply, the segment of memory which contains consecutive elements of the same certain type. In ActiveX world and Visual Basic which is set in this world, array refers to what is called variant arrays, safe arrays or VBArrays, which are bound inside the VARIANT type and are still, more or less, block of memory. On the other hand, in JScript, the array is the object that has its own properties (in fact one - length) and methods. It is not hard to guess that it is in no way compatible with safe arrays.

Your COM object will use safe arrays as it is a standard way in ActiveX. Let us, firstly, concentrate on returning arrays from the function. We will consider the object Some.Object that exposes the following method:

[id(0x000002C0)]
HRESULT GetSomeArray([out, retval] VARIANT *retArr );
which may have the following implementation in Delphi:
function TSomeObject.GetSomeArray : OleVariant;
var
vals: array [0..2] of Variant;
begin
vals[0] := 'Hello';
vals[1] := 'World';
vals[2] := 'Dude';
Result := VarArrayOf(vals);
end;
In VBScript there would be no problem with dealing with the result of this function. In JScript, however, we get something which is not familiar - it is not an array as JScript understands it.
Is there any way to convert safe arrays to JScript arrays? Fortunately, the answer is affirmative. JScript architects has provided the class VBArray which is the JScript front-end to safe arrays. We can manage it in the following way:
var someObj = new ActiveXObject("Some.Object");
var arr = (new VBArray(someObj.GetSomeArray())).toArray();

alert(arr[0]);
We have used here toArray() method to convert the VBArray to the JScript array. We could also use getItem() method to directly access element of the array. Note, that script engines, both JScript and VBScript have the limitation - they can only process safe array containing elements of type Variant. Therefore, even if you want to pass an array of strings, make the array's element to be of type Variant.

OK, it was not so bad. But, what about passing an array to the function. Here, we encounter the real problem. We will solve it in the next post.

Friday, August 10, 2007

Controlling Winamp programmatically using Delphi

I wanted in one of my project to control Winamp from application written in Delphi. It is well-known that Winamp can be controlled programmatically by finding the Winamp's window. It can be done performing the following Win32 API call:

hwnd_winamp := FindWindow('Winamp v1.x', nil);

Winamp window's class is always Winamp v1.x regardless its actual version. Once you have the handle to winamp's window you can controll it by sending to it certain messages which are specified in Winamp's SDK. However, the SDK on Winamp's website is available only for C++ developers. By googling over the Internet I have come across the translation of those header file to pascal done by Carlos Andres Osorio Gonzalez. However, it is based on the antique version of SDK and is lacking new features. I've decided to extend it, translating the lacking entries from the latest version of C++ SDK.
What I've come up with can be downloaded here or can be viewed here in browser by clicking here.

Enjoy it, but I cannot guarantee that it is free of errors.

Wednesday, August 08, 2007

My website rebuilt

Today I've spent some time to rebuild my website. I've changed its layout into three-column style and took care to make all the subpage to be of the same style.
Now, menu is on the left-hand side, some sponsored links on the right-hand side and the content goes between.
There is also, as a new feature, Google search available for the web and my site. (You can find it just beneath the menu).
If you have sometime, feel free to explore it now. I hope you will enjoy it.

Thursday, August 02, 2007

New version of MultiDesktop & Google Desktop hybrid gadgets

Yesterday, I have released on my website new version of my Google Desktop gadget called MultiDesktop. The aim of a gadget is to provide the user with possibility of having multiple virtual desktop (feature well-known to those of you who are or used to be Linux users). Unlike other software of this type it does not limit the user to 4 desktop and each desktop may have its name to easily identify them. In the new version, created desktops are kept between sessions and there is no need to re-create desktops each time you reboot your computer. For full list of features visit MultiDesktop web page.

How did I develop MultiDesktop?

At the first glance, MultiDesktop may appear to be standard simple Google Gadget. It is packaged inside the .gg file like other gadget. It is a standard format for distributing Google Gadgets. It is nothing more than ZIP file containing XML file specifying the static presentational aspects of gadgets, jscript source code defining its dynamics and plenty of resource files with graphics and internationalized text strings. For more details visit Google Desktop SDK page.

However, in case of MultiDesktop, JScript is used only to build the GUI and front-end to the engine written in C++. It is possible to be done in Google Desktop due to what Google calls hybrid gadget. You can write a DLL library being a COM server, attach it to the Gadget and then instantiate any object defined that implements an IDispatch interface. It can be easily implemented in Microsoft Visual C++ using ATL library or in Delphi.
Note: Remember to set your version to major = 0xFFFF and minor = 0xFFFF. Otherwise, your hybrid engine will not work. To do so in ATL make sure that you have 0xFFFF as two last parameters of IDispatchImpl class. For example, in case of MultiDesktop it is:


public IDispatchImpl<IMDInternal, &IID_IMDInternal, &LIBID_mdengineLib, /*wMajor =*/ 0xFFFF, /*wMinor =*/ 0xFFFF>,

Hello and Welcome to my blog!

This is the very first post I am going to write here.
I've just started the blog where I am going to put posts with topics more or less connected with programming. I will post some solutions I will have come up with, some comments, some thoughts on programming, announce my new projects, share few words on how they are made, etc.

I hope you will enjoy the content of this blog and return here to read new post.
Any comments you may have are very welcome.

Enjoy!

There were visits to this blog since 20.08.2007.