Clearscript: Calling AddHostObject throwing Invalid host item

Created on 13 Dec 2018  路  10Comments  路  Source: microsoft/ClearScript

A colleague reported that a C++/CLI sample app (which was previously working) using ClearScript was failing when calling AddHostObject with an InvalidOperationException "Invalid host item".

I've been able to reproduce the error on my computer, with

  • the same C++/CLI project
  • a new C++/CLI project
  • a new C# project

I've also reproduced in 5.5.3 (which is what we use) and 5.5.4 (in a new project using the Nuget package) and with .NET 4.6.1 and 4.7.2.

image

Here's the code for my new C# project:

static void Main(string[] args)
{
      using (var engine = new V8ScriptEngine())
      {
           engine.AddHostObject("message", "hello world");
      }
}

Given how it was working and suddenly seems to not be working it may be an environmental thing like Windows Updates but I'd appreciate some pointers.

I was trying to attach a sample but Github was unhappy with the size. When I get chance (might be Monday) I'll attach a Dropbox link or something.

question

All 10 comments

Hi @JohnLudlow,

AddHostObject doesn't work with fundamental values such as numbers and strings. Because these values are converted automatically to their script equivalents, they can't support some features of AddHostObject.

Instead of AddHostObject, you can do this:

C# engine.Script.message = "hello world"; /* OR */ engine.Script["message"] = "hello world";

In fact, as this works for all objects, we recommend it over AddHostObject in most situations.

Good luck!

@ClearScriptLib Ah I thought that was just for convenience and the "correct" way would be to call the method. Thanks for the clarification. We'll update our code to work with it like a dictionary.

Thanks again

Ok, we have a bit of an issue. Our client app is C++ and C++/CLI and we were using ClearScript because several things "just work".

However, we can't make code like engine.Script["message"] = "hello world" work because it doesn't appear as a dictionary to us and doesn't cast to a dictionary apparently. Also C++/CLI doesn't understand the dynamic-like things like C# can, so engine.Script.message = "hello world" also doesn't work for us.

engine.Script is actually a V8ScriptItem and if we could cast to one of those then I assume we could make that work by calling things on that, but unfortunately it's internal and not visible to us.

Do you have any suggestions for a type we can cast to which would provide what we need? Or do we need to wrap engine in an object written in C# and interact with .Script via that?

Hi @JohnLudlow,

Yeah, apparently C++/CLI doesn't support C#-style dynamic binding.

We're considering adding dictionary interfaces to script objects. In the meantime, you can use helper functions like these:

using namespace System;
using namespace System::Reflection;

Object^ GetScriptProperty(Object^ obj, String^ name) {
    auto reflect = safe_cast<IReflect^>(obj);
    return reflect->InvokeMember(name, BindingFlags::GetField, nullptr, reflect, nullptr, nullptr, nullptr, nullptr);
}

void SetScriptProperty(Object^ obj, String^ name, Object^ value) {
    auto reflect = safe_cast<IReflect^>(obj);
    auto args = gcnew array<Object^>(1);
    args[0] = value;
    reflect->InvokeMember(name, BindingFlags::SetField, nullptr, reflect, args, nullptr, nullptr, nullptr);
}

And then:

SetScriptProperty(engine->Script, "message", "hello world");

Good luck!

@ClearScriptLib Great we'll give that a shot

Thanks

That seems to work as a workaround.

Sort of a side question - are there any considerations for working with ClearScript across host application threads? Is it just the normal thread stuff like lock()

are there any considerations for working with ClearScript across host application threads? Is it just the normal thread stuff like lock()

V8ScriptEngine is thread-safe, so there's no need for manual locking, but you have to be aware of a few things.

V8's unit of isolation is not an engine but a _runtime_. When you use the V8ScriptEngine constructor, you get an engine with a private runtime, whereas all engines created by a runtime's CreateScriptEngine method share that runtime.

Each runtime can be active (executing script code) on one thread at a time, so ClearScript serializes most calls into a given runtime (Interrupt is an exception).

JScript and VBScript are very different, as they have thread affinity, meaning that they can only be active on the thread that constructed them. ClearScript supports this requirement by associating each instance with a Dispatcher; let us know if you need more information on that.

Please reopen if you encounter additional issues with AddHostObject or C++/CLI.

@ClearScriptLib will do. Is this the best way to ask general questions?

@JohnLudlow Yes, unlike the old CodePlex site, GitHub doesn't have separate sections for issues and discussions.

Was this page helpful?
0 / 5 - 0 ratings