views:

175

answers:

3

Hi,

I am trying to display an HTML page with embedded JavaScript code inside a System.Windows.Forms.WebBrowser control. The JavaScript code is expected to interact with the embedding environment through the window.external object. Before invoking a method on window.external, JavaScript is supposed to check for the existance of the method. If it is not there, the code should invoke a generic fallback method.

// basic idea
if (typeof(window.external.MyMethod) != 'undefined') {
    window.external.MyMethod(args);
} else {
    window.external.Generic("MyMethod", args);
}

However, checking for a no-argument method with typeof seems to invoke the method already. That is, if MyMethod accepts any positive number of arguments, the code above will work perfectly; but, if MyMethod is a no-argument method, then the expression typeof(window.external.MyMethod) will not check for its type but invoke it, too.

Is there any work-around to this behavior? Can I somehow escape the expression window.external.MyMethod to prevent the method call from occurring?

A: 

I'm actually not sure if it will help, but try window.external["MyMethod"].

If that doesn't help, try to store this value in a variable, and only then check type of that variable. See if that helps.

Fyodor Soikin
Neither variant works, unfortunately. `window.external["MyMethod"]` invokes it, as well as `var f = window.external["MyMethod"]`.
janko
A: 

Try one of these

/* Methods for feature testing
 * From http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting
 */
function isHostMethod(object, property){
    var t = typeof object[property];
    return t == 'function' ||
    (!!(t == 'object' && object[property])) ||
    t == 'unknown';
}

function isHostObject(object, property){
    return !!(typeof(object[property]) == 'object' && object[property]);
}

if (isHostObject(window.external, "MyMethod")) {....
Sean Kinsey
Hi Sean, your `isHostMethod` doesn't work either. The line `object[property]` still invokes the method.
janko
+4  A: 

I have not debugged your exact situation but I believe my psychic powers can work out what is going on here.

The JScript language makes a distinction between use of a function and mere mention of it. When you say

x = f;

that says "assign a reference to the function identified by f to the variable x". It mentions f. By contrast,

x = f();

uses f. It means "call the function identified by f and assign the returned value to x."

In short, functions in JScript are essentially what we would think of as properties of delegate type in C#.

Some languages do not make this distinction. In VBScript, if you say x = f and f is a function, that means to call the function, same as x = f(). VBScript does not make a strong distinction syntactically between the use and mention of a function.

The way this is all implemented is we use COM; specifically, we use OLE Automation. When dispatching a field of an object to get its value, the JScript engine passes flags that mean either "property get" or "method invoke", depending on whether it was use or mention.

But suppose your object being dispatched was written with the expectation that it would be called from VB. Perhaps it was written in VB. It is perfectly reasonable and legal for a VB object to say "oh, I see you're asking me for the value of this method. Since I don't understand the difference between mentioning a method and using it, I'll just invoke it no matter which flag you pass".

I don't know if there is a workaround, but I'd be willing to bet as much as a dollar that what's happening is the invoked object is assuming that the caller wants VB semantics.

Eric Lippert
Hi Eric, thanks for your excellent reply. I expected something like this to happen behind the scenes since I've read your blog comment from 2004 yesterday (http://discuss.techinterview.org/default.asp?joel.3.18644.7). I guess, that the COM stuff is implemented somewhere inside the WebBrowser control, isn't it? I won't be able to patch in another lookup handling? Or can I mark a method explicitly as "COM visible as method and not property"?
janko
If this is the case, you can try to play with `IDispatch` implementation in your C# class. First, I would try the `IDispatchImpl` attribute - try setting it to `CompatibleImpl` and `InternalImpl` and see if either way works. Or, you can provide your own implementation of `IDispatch` - mark your object `[ClassInterface(ClassInterfaceType.None)]` and have it explicitly implement `IDispatch`. I also suspect (don't really know) that the `IDynamicObject` of .NET 4.0 may be used for `IDispatch` mapping.
Fyodor Soikin