views:

95

answers:

1

Before asking my question, I should admit that my knowledge of .NET interop is sparse, so I realize that I might be making a newbie error.

I am using the GeckoFx library to create a C# application that contains an embedded Gecko (Firefox) browser instance. The app works well using GeckoFx in its original form, but I need to extend it to support XPath queries, using the Mozilla nsIDOMXPathEvaluator interface.

The GeckoFx codebase includes numerous examples of exposing and using the underlying managed Gecko interfaces, and I have followed the same code patterns to expose several new interfaces:

[Guid("75506f8a-b504-11d5-a7f2-ca108ab8b6fc"),
    ComImport,
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface nsIDOMXPathEvaluator
{
    nsIDOMXPathExpression CreateExpression(
        [MarshalAs(UnmanagedType.LPWStr)] string expression,
        nsIDOMXPathNSResolver resolver);

    nsIDOMXPathNSResolver CreateNSResolver(
        nsIDOMNode nodeResolver);

    nsISupports Evaluate(
        [MarshalAs(UnmanagedType.LPWStr)] string expression,
        nsIDOMNode contextNode,
        nsIDOMXPathNSResolver resolver,
        ushort type);
}

[Guid("ce600ca8-e98a-4419-ad61-2f6d0cb0ecc8"),
    ComImport,
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface nsIDOMXPathExpression
{
    nsISupports Evaluate(
        nsIDOMNode contextNode,
        ushort type,
        nsISupports result);
}

[Guid("75506f83-b504-11d5-a7f2-ca108ab8b6fc"),
    ComImport,
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface nsIDOMXPathNSResolver
{
    string LookupNamespaceUri(
        [MarshalAs(UnmanagedType.LPWStr)] string prefix);
}

With the new interfaces exposed, I attempt to use nsIDOMXPathEvaluator to evaluate an XPath expression in the context of the loaded DOM:

var evaluator = Xpcom.CreateInstance<nsIDOMXPathEvaluator>("@mozilla.org/dom/xpath-evaluator;1");
var node = (nsIDOMNode)Document.DocumentElement.DomObject;
var resolver = evaluator.CreateNSResolver(node);
var result = evaluator.Evaluate("//div[0]", node, resolver, 0);

Although the first three variables are correctly populated, the call to Evaluate fails with an exception "Attempted to read or write protected memory.". I am inclined to believe that the problem lies in the way my code marshals string values, as I have seen different exception messages when experimenting with the marshalled type of the expression parameter.

It seems that I am not the only person suffering this problem, as shown in this forum post, but I cannot understand how my implementation differs from the many other working examples in the GeckoFx library.

Any ideas would be greatly appreciated.

Thanks, Tim.

+1  A: 

There are 2 issues here.

1) You're missing an argument in the nsIDOMXPathEvaluator.Evaluate method. You need an nsISupports argument that can be used to collect the result (actually an nsIDOMXPathResult ) if you do not want to create a new nsIDOMXPathResult object.

2) These interfaces don't use normal strings that you can marshal that way. If you look at the document page for nsIDOMXPathEvaluator for instance, you will see that evaluate takes a DOMString rather than string or wstring. In GeckoFx you can use nsAString for DOMString.

So nsIDOMXPathEvaluator.Evaluate will look like this:

nsISupports Evaluate(
    nsAString expression,
    nsIDOMNode contextNode,
    nsIDOMXPathNSResolver resolver,
    ushort type, nsISupports result );

And nsIDOMXPathNSResolver.LookupNamespaceUri will look like this:

nsAString LookupNamespaceUri( nsAString prefix );

Now you should be able to do this:

var evaluator = Xpcom.CreateInstance<nsIDOMXPathEvaluator>("@mozilla.org/dom/xpath-evaluator;1");
var node = (nsIDOMNode)Document.DocumentElement.DomObject;
var resolver = evaluator.CreateNSResolver(node);
var result = evaluator.Evaluate(new nsAString(@"//div[0]"), node, resolver, 0, null);

And now you should not experience any exceptions, and result should be valid. But what you do with result will be another matter. You'll need to implement a few other interfaces to make much use of it, but that should get you on the right path.

Cheers

Gerald
@Gerald: thank you so much - your answer is exactly right. I corrected the two errors in my code and added the interface definition for nsIDOMXPathResult and now I am able to retrieve a node result and use it to initialize a new GeckoNode instance. Thanks again.
Tim Coulter