I need to call a C# BHO method from javascript
I have followed the solution given in this question
I get the error:
'mshtml.HTMLWindow2Class' does not contain a definition for 'myExtension'.
I cannot assign:
dynamic window = browser.Document.parentWindow;
as parentWindow is undefined, I have to cast to (mshtml.IHTMLDocument2) and (mshtml.IHTMLWindow2)
Does anyone have a full working example I could refer to or any help or alternative solutions
I've the same problem. And the following fix seems work in my BHO:
dynamic window = _webBrowser.Document.parentWindow;
var windowEx = (IExpando)window;
PropertyInfo p = windowEx.AddProperty("myExtension");
p.SetValue(windowEx, this);
instead of:
dynamic window = _webBrowser.Document.parentWindow;
var windowEx = (IExpando)window;
PropertyInfo p = windowEx.AddProperty("myExtension");
window.myExtension = this;
Related
Hi I am trying to use C# reflection to call a method that is passed a parameter and in return passes back a result. How can I do that? I've tried a couple of things but with no success. I'm used to PHP and Python where this can be done on a single line so this is very confusing to me.
In essence this is how the call would be made without reflection:
response = service.CreateAmbience(request);
request has these objects:
request.UserId = (long)Constants.defaultAmbience["UserId"];
request.Ambience.CountryId = (long[])Constants.defaultAmbience["CountryId"];
request.Ambience.Name.DefaultText = (string)Constants.defaultAmbience["NameDefaultText"];
request.Ambience.Name.LanguageText = GetCultureTextLanguageText((string)Constants.defaultAmbience["NameCulture"], (string)Constants.defaultAmbience["NameText"]);
request.Ambience.Description.DefaultText = (string)Constants.defaultAmbience["DescriptionText"];
request.Ambience.Description.LanguageText = GetCultureTextLanguageText((string)Constants.defaultAmbience["DescriptionCulture"], (string)Constants.defaultAmbience["DescriptionDefaultText"]);
This is my function to implement the reflection where serviceAction for the case above would be "CreateAmbience":
public static R ResponseHelper<T,R>(T request, String serviceAction)
{
ICMSCoreContentService service = new ContentServiceRef.CMSCoreContentServiceClient();
R response = default(R);
response = ???
}
Something along the lines of:
MethodInfo method = service.GetType().GetMethod(serviceAction);
object result = method.Invoke(service, new object[] { request });
return (R) result;
You may well want to add checks at each level though, to make sure the method in question is actually valid, that it has the right parameter types, and that it's got the right return type. This should be enough to get you started though.
Here's a quick example of calling an object method by name using reflection:
Type thisType = <your object>.GetType();
MethodInfo theMethod = thisType.GetMethod(<The Method Name>);
theMethod.Invoke(this, <an object [] of parameters or null>);
If you're on .NET 4, use dynamic:
dynamic dService = service;
var response = dService.CreateAmbience(request);
You can use Delegate.CreateDelegate to obtain a delegate to the method by name:
public static R ResponseHelper<T,R>(T request, string serviceAction)
{
var service = new ContentServiceRef.CMSCoreContentServiceClient();
var func = (Func<T,R>)Delegate.CreateDelegate(typeof(Func<T,R>),
service,
serviceAction);
return func(request);
}
I am starting to get into C# from years of other languages, and I feel so basic for even asking this, but I have been scouring this site and the net and just cannot seem to get this to sink in and work. I am trying to create a framework in coded UI so that I can have a library of methods to call to perform actions on browser controls. Here is one of the methods I am trying to use:
namespace Functions
{
public class BrowserFunctions()
{
public BrowserWindow browser {get;set;}
public object launchBrowser()
{
browser = BrowserWindow.Launch(new Uri("https://qawww.test.com/"));
browser.Maximized = true;
return browser;
}
}
}
I want to then be able to reference the browser object I am trying to return from this script:
BrowserWindow mybrowser = new Functions.BrowserFunctions.browser;
^^ this line says it is a property but used like a type
//BrowserWindow mybrowser = Functions.BrowserFunctions.browser;
// mybrowser = new BrowserFunctions();
UITestControl txtUserEmail = new UITestControl(mybrowser);
UITestControl txtPassword = new UITestControl(mybrowser);
UITestControl btnLogin = new UITestControl(mybrowser);
I am probably way off base and still trying to learn, but currently failing :) I am grateful to anyone who could help get me going with this.
BrowserWindow mybrowser = new Functions.BrowserFunctions.browser;
This line literally isn't doing anything. You need to either create a new BrowserFunctions object and then assign the browser to it through a set method such as this....
BrowserWindow mybrowser = new Functions.BrowserFunctions.browser();
mybrowser = somebrowser;
Or you have one already existing and you don't need to new it.
BrowserWindow mybrowser = Functions.YourBrowserFunction.yourbrowser;
given the following classes:
class SomeBuilder<T>
{
public static object Build(int index)
{
...
}
}
class SomeHelper
{
public object GetBuildObj(object value)
{
var valuetype = value.GetType();
var methodinfo = typeof(SomeBuilder<>).MakeGenericType(valuetype).GetMethod("Build");
var handler = SomeDynamicHelper.GetMethodInvoker(methodinfo);
var result = hander(null, new object[]{1});
}
}
SomeBuilder was a generic type so i need a call to MakeGenericType() to make things right.
when i pass a normal type like 'class person' for the value, everything just works, that`s fine.
but when i pass a anonymous type like: new { id=1 }, the handler was successfully created. but invoke this dynamic handler i got a MethodAccessException with these messages:
"method "SomeDynamicHelper.(System.Object, System.Objec[])" try to access method "SomeBuilder'1<<>f__AnonymousType0'1<System.Int32>>.Build(int)" failed.
any help would be appreciated, thx.
btw, if you are interested in SomeDynamicHelper, plz see:
http://www.codeproject.com/Articles/14593/A-General-Fast-Method-Invoker
edit1:
i did the call in main like this:
static void Main(string[] args)
{
// pass a normal class, this will be fine
var value = new Person { id = 1};
new SomeHelper().GetBuildObj(value);
// pass a anonymous type
var value = new { id = 1};
new SomeHelper().GetBuildObj(value); // oops, got a exception here!
}
edit2:
based on the comment i changed my code:
class SomeHelper
{
public object GetBuildObj(object value)
{
//this time i do not use the value, but create a new inner value:
var valuenew = new { id = 1 };
var valuetype = valuenew.GetType();
var methodinfo = typeof(SomeBuilder<>).MakeGenericType(valuetype).GetMethod("Build");
var handler = SomeDynamicHelper.GetMethodInvoker(methodinfo);
var result = hander(null, new object[]{1});
}
}
well, this time there is no exception, but...unfortunately a new problem occured...may be i should open a new thread for the new problem.
thank you guys, for your attentions.
edit3:
hi, after some digging, i also found some useful information. say, the SomeDynamicHelper.GetMethodInvoker() code as below:
DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[]) }, methodInfo.DeclaringType.Module);
this is the core we used here to dynamic create a method. for our context, we need to declare the anonymous type in same assembly with the SomeHelper and SomeBuilder. but, if we can`t do the declaration, what should we do?
ok, you can call DynamicMethod() with last parameter(the skipVisibility), set to true!
hope this will help others with the same problem :)
"method "SomeDynamicHelper.(System.Object, System.Objec[])" try to access method "SomeBuilder'1<<>f__AnonymousType0'1>.Build(int)"
From this you can see that dynamic method try to run an internal\private method called Build, for that you got MethodAccessException.
(The anonymous type is kept in a new generated class)
Adding InternalVisibleTo not always helping because it's helping only if you can rewrite the anonymous type assembly (manually or with interception) and only if the type and method are internals and not private.
In dynamic method you can bind the method to type\module, and to skip visibility check, but this help to access private members of the specified module, so again if the type you trying to access is in a different assembly and the type\method are private you can't do anything.
Well almost. There is a "secret" attribute that you inject to your assembly, called IgnoreAccessChecksTo and then in addition to bounded module and skip visibility maybe it will work.
You can try using [assembly: InternalsVisibleTo("Anonymously Hosted DynamicMethods Assembly")] in the same project where you define the anonymous type.
I had a similar error with a C# class I tried to inject using IKVM-converted Guice. The fix was simply making the affected class (in this case probably SomeBuilder) public and everything worked fine.
The only solution I could find was using:
mshtml.HTMLDocument htmldocu = new mshtml.HTMLDocument();
htmldocu .createDocumentFromUrl(url, "");
and I am not sure about the performance, it should be better than loading the html file in a WebBrowser and then grab the HtmlDocument from there. Anyhow, that code does not work on my machine. The application crashes when it tries to execute the second line.
Has anyone an approach to achieve this efficiently or any other way?
NOTE: Please understand that I need the HtmlDocument object for DOM processing. I do not need the html string.
Use the DownloadString method of the WebClient object. e.g.
WebClient client = new WebClient();
string reply = client.DownloadString("http://www.google.com");
In the above example, after executed, reply will contain the html markup of the endpoint http://www.google.com.
WebClient.DownloadString MSDN
In an attempt to answer your actual question from four years ago (at the time of me posting this answer), I'm providing a working solution. I wouldn't be surprised if you found another way to do this, either, so this is mostly for other people searching for a similar solution. Keep in mind, however, that this is considered
somewhat obsolete (the actual use of HtmlDocument)
not the best way to handle HTML DOM parsing (the preferred solution is to use HtmlAgilityPack or CsQuery or some other method using actual parsing and not regular expressions)
extremely hacky and therefore not the safest/most compatible way to do it
you really should not be doing what I'm about to show
Additionally, keep in mind that HtmlDocument is really just a wrapper for mshtml.HTMLDocument2, so it is technically slower than just using a COM wrapper directly, but I completely understand the use case simply for ease of coding.
If you're cool with all of the above, here's how to accomplish what you want.
public class HtmlDocumentFactory
{
private static Type htmlDocType = typeof(System.Windows.Forms.HtmlDocument);
private static Type htmlShimManagerType = null;
private static object htmlShimSingleton = null;
private static ConstructorInfo docCtor = null;
public static HtmlDocument Create()
{
if (htmlShimManagerType == null)
{
// get a type reference to HtmlShimManager
htmlShimManagerType = htmlDocType.Assembly.GetType(
"System.Windows.Forms.HtmlShimManager"
);
// locate the necessary private constructor for HtmlShimManager
var shimCtor = htmlShimManagerType.GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null
);
// create a new HtmlShimManager object and keep it for the rest of the
// assembly instance
htmlShimSingleton = shimCtor.Invoke(null);
}
if (docCtor == null)
{
// get the only constructor for HtmlDocument (which is marked as private)
docCtor = htmlDocType.GetConstructors(
BindingFlags.NonPublic | BindingFlags.Instance
)[0];
}
// create an instance of mshtml.HTMLDocument2 (in the form of
// IHTMLDocument2 using HTMLDocument2's class ID)
object htmlDoc2Inst = Activator.CreateInstance(Type.GetTypeFromCLSID(
new Guid("25336920-03F9-11CF-8FD0-00AA00686F13")
));
var argValues = new object[] { htmlShimSingleton, htmlDoc2Inst };
// create a new HtmlDocument without involving WebBrowser
return (HtmlDocument)docCtor.Invoke(argValues);
}
}
To use it:
var htmlDoc = HtmlDocumentFactory.Create();
htmlDoc.Write("<html><body><div>Hello, world!</body></div></html>");
Console.WriteLine(htmlDoc.Body.InnerText);
// output:
// Hello, world!
I have not tested this code directly -- I have translated it from an old Powershell script that needed the same functionality you're requesting. If it fails, let me know. The functionality is there but the code might need very minor tweaking to get working.
In my WinForms application I need to call javascript function from my WebBrowser control. I used Document.InvokeScript and it works perfect with functions alone e.g
Document.InvokeScript("function").
But when i want to call javascript object method e.g.
Document.InvokeScript("obj.method")
it doesn't work. Is there a way to make it work? Or different solution to this problem? Without changing anything in the javascript code!
Thanks in advance :)
The example in the documentation does NOT include the parenthesis.
private void InvokeScript()
{
if (webBrowser1.Document != null)
{
HtmlDocument doc = webBrowser1.Document;
String str = doc.InvokeScript("test").ToString() ;
Object jscriptObj = doc.InvokeScript("testJScriptObject");
Object domOb = doc.InvokeScript("testElement");
}
}
Try
Document.InvokeMethod("obj.method");
Note that you can pass arguments if you use HtmlDocument.InvokeScript Method (String, Object[]).
Edit
Looks like you aren't the only one with this issue: HtmlDocument.InvokeScript - Calling a method of an object . You can make a "Proxy function" like the poster of that link suggests. Basically you have a function that invokes your object's function. It's not an ideal solution, but it'll definitely work. I'll continue looking to see if this is possible.
Another post on same issue: Using WebBrowser.Document.InvokeScript() to mess around with foreign JavaScript . Interesting solution proposed by C. Groß on CodeProject:
private string sendJS(string JScript) {
object[] args = {JScript};
return webBrowser1.Document.InvokeScript("eval",args).ToString();
}
You could make that an extension method on HtmlDocument and call that to run your function, only using this new function you WOULD include parenthesis, arguments, the whole nine yards in the string you pass in (since it is just passed along to an eval).
Looks like HtmlDocument does not have support for calling methods on existing objects. Only global functions. :(
Unfortunately you can't call object methods out of the box using WebBrowser.Document.InvokeScript.
The solution is to provide a global function on the JavaScript side which can redirect your call. In the most simplistic form this would look like:
function invoke(method, args) {
// The root context is assumed to be the window object. The last part of the method parameter is the actual function name.
var context = window;
var namespace = method.split('.');
var func = namespace.pop();
// Resolve the context
for (var i = 0; i < namespace.length; i++) {
context = context[namespace[i]];
}
// Invoke the target function.
result = context[func].apply(context, args);
}
In your .NET code you would use this as follows:
var parameters = new object[] { "obj.method", yourArgument };
var resultJson = WebBrowser.Document.InvokeScript("invoke", parameters);
As you mention that you cannot change anything to your existing JavaScript code, you'll have to inject the above JavaScript method in some how. Fortunately the WebBrowser control can also do for you by calling the eval() method:
WebBrowser.Document.InvokeScript("eval", javaScriptString);
For a more robust and complete implementation see the WebBrowser tools I wrote and the article explaining the ScriptingBridge which specifically aims to solve the problem you describe.
webBrowser.Document.InvokeScript("execScript", new object[] { "this.alert(123)", "JavaScript" })
for you supposed to be like this
webBrowser.Document.InvokeScript("execScript", new object[] { "obj.method()", "JavaScript" })