.NET2.0 C# Interop: How to call COM code from C#? - c#

In my last development environment, I was able to easily interact with COM, calling methods on COM objects. Here is the original code, translated into C# style code (to mask the original language):
public static void SpawnIEWithSource(String szSourceHTML)
{
OleVariant ie; //IWebBrowser2
OleVariant ie = new InternetExplorer();
ie.Navigate2("about:blank");
OleVariant webDocument = ie.Document;
webDocument.Write(szSourceHTML);
webDocument.close;
ie.Visible = True;
}
Now begins the tedious, painful, process of trying to interop with COM from managed code.
PInvoke.net already contains the IWebBrower2 translation, the relavent porition of which is:
[ComImport,
DefaultMember("Name"),
Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch),
SuppressUnmanagedCodeSecurity]
public interface IWebBrowser2
{
[DispId(500)]
void Navigate2([In] ref object URL, [In] ref object Flags, [In] ref object TargetFrameName, [In] ref object PostData, [In] ref object Headers);
object Document { [return: MarshalAs(UnmanagedType.IDispatch)] [DispId(0xcb)] get; }
}
I've created the COM class:
[ComImport]
[Guid("0002DF01-0000-0000-C000-000000000046")]
public class InternetExplorer
{
}
So now it's time for my actual C# transaction:
public static void SpawnIEWithSource(String szHtml)
{
PInvoke.ShellDocView.IWebBrowser2 ie;
ie = (PInvoke.ShellDocView.IWebBrowser2)new PInvoke.ShellDocView.InternetExplorer();
//Navigate to about:blank to initialize the browser
object o = System.Reflection.Missing.Value;
String url = #"about:blank";
ie.Navigate2(ref url, ref o, ref o, ref o, ref o);
//stuff contents into the document
object webDocument = ie.Document;
//webDocument.Write(szHtml);
//webDocument.Close();
ie.Visible = true;
}
The careful readers notice that IWebBrowser2.Document is a late-bound IDispatch.
We're using Visual Studio 2005, with .NET 2.0 on our, and our customer's, machines.
So what's the .NET 2.0 method to invoke methods on an object that, on some level, only supports late-bound IDispatch?
A quick search of Stack Overflow for using IDispatch from C# turns up this post saying what I want is not possible in .NET.
So is it possible to use COM from C# .NET 2.0?
The question is that there is an accepted design pattern that I want to use in C#/.NET. It involves launching Internet Explorer out of process, and giving it HTML content, all the while not using temporary files.
A rejected design idea is hosting Internet Explorer on a WinForm.
An acceptable alternative is launching the system registered web browser, giving it HTML to display, without using a temporary file.
The stumbling block is continuing to use COM objects in the .NET world. The specific problem involves performing late-binding calls to IDispatch without needing C# 4.0. (i.e. while using .NET 2.0)

Update: Based on question updates, I have removed the portions of my answer that are no longer relevant to the question. However, in case other readers are looking for a quick and dirty way to generate HTML in a winforms app and do not require an in-process IE, I will leave the following:
Possible Scenario 1: The ultimate goal is to simply display HTML to your end user and are using Windows Forms
System.Windows.Forms.WebBrowser is the painstakingly easy .NET wrapper for the interface you are trying to manually implement. To get it, Drag and drop an instance of that object from your toolbar (listed as "Web Browser" under the "All Windows Forms" section) onto your form. Then, on some suitable event handler:
webBrowser1.Navigate("about:blank");
webBrowser1.Document.Write("<html><body>Hello World</body></html>");
On my test app, this correctly displayed the haunting message we all have learned to fear and loath.

Late bound IDispatch called is relativly easy in .NET, although piss-poor:
public static void SpawnIEWithSource(String szHtml)
{
// Get the class type and instantiate Internet Explorer.
Type ieType = Type.GetTypeFromProgID("InternetExplorer.Application");
object ie = Activator.CreateInstance(ieType);
//Navigate to the blank page in order to make sure the Document exists
//ie.Navigate2("about:blank");
Object[] parameters = new Object[1];
parameters[0] = #"about:blank";
ie.GetType().InvokeMember("Navigate2", BindingFlags.InvokeMethod | BindingFlags.IgnoreCase, null, ie, parameters);
//Get the Document object now that it exists
//Object document = ie.Document;
object document = ie.GetType().InvokeMember("Document", BindingFlags.GetProperty | BindingFlags.IgnoreCase, null, ie, null);
//document.Write(szSourceHTML);
parameters = new Object[1];
parameters[0] = szHtml;
document.GetType().InvokeMember("Write", BindingFlags.InvokeMethod | BindingFlags.IgnoreCase, null, document, parameters);
//document.Close()
document.GetType().InvokeMember("Close", BindingFlags.InvokeMethod | BindingFlags.IgnoreCase, null, document, null);
//ie.Visible = true;
parameters = new Object[1];
parameters[0] = true;
ie.GetType().InvokeMember("Visible", BindingFlags.SetProperty | BindingFlags.IgnoreCase, null, ie, parameters);
}
The referenced SO question that originally said "not possible until C# 4.0" was amended to show how it is possible in .NET 2.0.
Does C# .NET support IDispatch late binding?

The answers in the post that you link to are actually incorrect. It is generally very easy to deal with IDispatch based objects in .Net. Basically you go through three steps:
Most automation objects (probably well over 90%) that are exposed as IDispatch interfaces have other interfaces that can be used by non-scripting type COM clients (either the IDispatch interface is actually a full COM interface derived from IDispatch or the object supports one or more other IUnknown derived interfaces). In this case, you would simply import the appropriate COM interface definition and then cast the object to the appropriate interface. The cast calls QueryInterface under the covers and returns a wrapped reference to the interface you want.
This is the technique you would use in the scenario that you presented above. The Document object that is returned from the IE automation object supports the IHTMLDocument, IHTMLDocument2, IHTMLDocument3, IHTMLDocument4 and IHTMLDocument5 interfaces (depending on the version of IE you are using). You should cast to the appropriate interface and then call the appropriate method. For example:
IHTMLDocument2 htmlDoc = (IHTMLDocument2)webDocument;
htmlDoc.Write(htmlString);
htmlDoc.Close();
In the rare case where the automation object does not support an alternative interface. Then you should use VB.Net to wrap that interface. With Option Strict set to off (for the wrapper class only) you can use VB's built in support for late bound calls to simply call the appropriate IDispatch methods under the covers. In rare cases with unusual argument types you may need to fiddle a bit with the call but, in general, in VB you can just do it! Even with the dynamic additions to C# v4 VB will still probably have significantly better support for late-bound COM calls.
If for some reason you can't use VB to wrap the automation interface then you can still make any necessary calls from C# using reflection. I won't go into any details since this option should basically never be used but here is a small example involving Office automation.

See this article :
http://www.codeproject.com/KB/cs/IELateBindingAutowod.aspx
Internet Explorer Late Binding Automation
By yincekara
Internet Explorer automation sample code using late binding, without Microsoft.mshtml and shdocvw dependency.
for htmlDoc.write(htmlString);
modify
[Guid("332C4425-26CB-11D0-B483-00C04FD90119")]
[ComImport]
[TypeLibType((short)4160)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
internal interface IHTMLDocument2
{
[DispId(1054)]
void write([MarshalAs(UnmanagedType.BStr)] string psArray);
//void write([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] object[] psarray);

Related

How to pass C# objects through to JNI method in Xamarin?

I'm using Xamarin and need to call through to two static java methods in a jar I've linked to. Here is the java class in the jar:
package com.test;
public class Car {
public static Car makeCar(String name);
public void drawCar(ImageView imageview);
}
I'm not sure how to pass in the parameters for the two methods to the JNI code. The JNIEnv.Call**Method() class takes a JValue[] array for parameters, I'm trying to use it to wrap a C# string and call it all like so:
// C#
string carName = "mustang";
JValue[] paramCarName = new JValue[] {
new JValue(JNIEnv.NewString(carName))
};
IntPtr theClass = JNIEnv.FindClass("com.test.Car");
IntPtr theMethod = JNIEnv.GetMethodID(theClass,
"makeCar", "()Ljava/lang/String;");
IntPtr resultCar = JNIEnv.CallStaticObjectMethod(
theClass, theMethod, paramCarName);
Is that correct usage? I'm having the same problem with calling the second method, which refers to a C# version of android's ImageView:
// C#
// Xamarin provides an ImageView wrapper class.
ImageView imageview = ...;
// Is it alright to use JNIEnv.ToJniHandle here to reference the imageview?
JValue[] paramCarName = new JValue[] {
new JValue (JNIEnv.ToJniHandle (imageview))
};
...
The above currently compiles ok, but I can't run it since I only have the free version. Any info on this would be great as I'm sure I'm misusing this.
Thanks
If you are having trouble getting a jar to bind you may be able to fix the errors with Transforms/Metadata.xml (documentation here). With that file you can suppress bindings for classes or entire packages that cause code generation issues.
If Metadata.xml cannot get the job done, another (less desirable) option is to create your own Java library that wraps the one that won't bind, and exposes only the methods you need to access from C#. Then bind that jar.
But, it sounds like you have the free version of Xamarin (probably why you are trying to avoid jar binding since that requires the paid version) so I'll attempt to fix your JNI code:
// C#
IntPtr classHandle;
IntPtr theClass = JNIEnv.FindClass("com/test/Car", classHandle);
IntPtr theMethod = JNIEnv.GetMethodID(theClass,
"makeCar", "(Ljava/lang/String;)V");
// Just create a new JValue with your C# Android object peer
JNIEnv.CallStaticObjectMethod(classRef, methodRef, new JValue[] {
new JValue(imageView)
});
My JNI is not super-advanced so take the above with some grains of salt. But according to code generated by Xamarin Studio for jar binding, the above should be (close to) correct.

Is it possible to mark an ActiveX object as safe so that IE settings need not be changed?

I have created an ActiveX dll assembly that launches an application. I have called this dll via JavaScript in browser. The app is launched on click of URL.
For this script to work, I need to go to the IE settings and enable "Initialize and script activex controls not marked as safe". Is there any way to code the ActiveX object to mark it as safe?
I have used the SN tool to generate a key (.snk file), compiled the DLL with this key. So the dll is signed but yet it doesnot run unless I enable "Initialize and script activex controls not marked as safe" in IE settings.
This is the article which I have implemented and would like to mark it as safe:- Writing an ActiveX control in C#.
I have got a reference to the safe controls concept (IObjectSafety) on this microsoft link:-
I am not able to understand as to where this code needs to be placed. Any help would make it more clear..
I picked up pieces of code (and knowledge) from different places and here is my working version
- a interface:
[Serializable, ComVisible(true)]
public enum ObjectSafetyOptions
{
INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001,
INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002,
INTERFACE_USES_DISPEX = 0x00000004,
INTERFACE_USES_SECURITY_MANAGER = 0x00000008
}
//
// MS IObjectSafety Interface definition
//
[
ComImport(),
Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
]
public interface IObjectSafety
{
[PreserveSig]
long GetInterfaceSafetyOptions(ref Guid iid, out int pdwSupportedOptions, out int pdwEnabledOptions);
[PreserveSig]
long SetInterfaceSafetyOptions(ref Guid iid, int dwOptionSetMask, int dwEnabledOptions);
}
and the implementation:
#region safe for scripting
private ObjectSafetyOptions m_options = ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_CALLER | ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_DATA;
public long GetInterfaceSafetyOptions(ref Guid iid, out int pdwSupportedOptions, out int pdwEnabledOptions)
{
pdwSupportedOptions = (int)m_options;
pdwEnabledOptions = (int)m_options;
return 0;
}
public long SetInterfaceSafetyOptions(ref Guid iid, int dwOptionSetMask, int dwEnabledOptions)
{
return 0;
}
#endregion safe for scripting
Now IE does not nag me with the confirmation dialog.
The following link has a perfect example which is answer to creating as well as marking an activeX control as Safe:-Creating activex objects with c#
Creating activex objects with c#
The purpose of this guide is to provide simple steps for
creating a simple ActiveX Control that can be loaded and used within an IE
hosted web page.
Background
ActiveX is a Microsoft technology used for creating
re-usable components. This technology, also called Object Linking and Embedding
(OLE), along with COM is heavily used within the Microsoft environment and
application development. For the duration of this article, I will use the term
COM to encompass all similar Microsoft related technologies: ActiveX, OLE,
Automation, etc… as it is the framework by which components are created. Anyone
interested in creating reusable components should have some familiarity with
COM. A very good resource is “Essential COM” by Don Box.
The idea behind COM is to allow the creation of components
that can be consumed by a variety of clients. This means that I can create a
component using C++ which can then be used by an application written in VB, or
even a web page which we will be performing shortly. Traditionally, ActiveX
objects were usually written in C++ and VB but can actually be written in any
language as long as they are COM-aware.
This means that we can create COM objects with the .NET
platform. A survey of resources/tutorials on COM and .NET on the internet by and
large only tell you how to consume unmanaged COM objects via Interop. So my aim
with this guide is to show how to use this powerful feature. Also, FYI, there is
a .NET technology very similar to what will be describe below which allows
hosting Windows Forms Controls inside of IE (don’t know the official term/name
for this).
That’s enough babbling lets get going with the example.
Requirements
Familiarity with C#.
Development Environment. I will be using Visual Studio
(2003 should also work. Don’t know about the express editions though.)
HTML, Javascript
IE. ActiveX objects cannot be hosted in any other
browser.
Creating the ActiveX Control
The first step is to create a C# DLL Project. This can be
done with the following steps:
Launch Visual Studio
Select File -> NewProject
Select “Other Language” -> “Visual C#” for the project type
Select “Class Library” template
When the project is created, go ahead and rename Class1.cs
to whatever you want. I renamed it to HelloControl.cs
The ActiveX control we will be creating is the ubiquitous
hello world! The code and explanation follow.
HelloControl.cs
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text;
namespace csharp.activex.sample
{
/// <summary>
/// A very simple interface to test ActiveX with.
/// </summary>
[
Guid( "E86A9038-368D-4e8f-B389-FDEF38935B2F"),
InterfaceType( ComInterfaceType.InterfaceIsDual),
ComVisible( true)
]
public interface IHello
{
[DispId(1)]
string Hello();
[DispId(2)]
int ShowDialog(string msg);
};
[
Guid("873355E1-2D0D-476f-9BEF-C7E645024C32"),
// This is basically the programmer friendly name
// for the guid above. We define this because it will
// be used to instantiate this class. I think this can be
// whatever you want. Generally it is
// [assemblyname].[classname]
ProgId("csharpAx.CHello"),
// No class interface is generated for this class and
// no interface is marked as the default.
// Users are expected to expose functionality through
// interfaces that will be explicitly exposed by the object
// This means the object can only expose interfaces we define
ClassInterface(ClassInterfaceType.None),
// Set the default COM interface that will be used for
// Automation. Languages like: C#, C++ and VB
// allow to query for interface's we're interested in
// but Automation only aware languages like javascript do
// not allow to query interface(s) and create only the
// default one
ComDefaultInterface(typeof(IHello)),
ComVisible(true)
]
public class CHello : IHello
{
#region [IHello implementation]
public string Hello()
{
return "Hello from CHello object";
}
public int ShowDialog(string msg)
{
System.Windows.Forms.MessageBox.Show(msg, "");
return 0;
}
#endregion
};
}
The HelloControl.cs file consists of one class and one interface.
So let’s start with the interface definition and follow up with the CHello class.
IHello Interface
An interface is generally described as a contract. Once an interface is designed
and published for use it should NEVER change. Let me repeat this, it
should NEVER change.
Several attributes have been defined for this interface.
1) Guid – This is a unique identifier. Use guidgen.exe to generate your own
2) InterfaceType – This determines which base class to expose to COM.
The InterfaceType attribute demands a little further explaination. Here is how it is defined.
public enum ComInterfaceType
{
InterfaceIsDual = 0,
InterfaceIsIUnknown = 1,
InterfaceIsIDispatch = 2,
}
All COM interfaces and objects must at a minimum inherit the IUnknown
interface (see Appendix A.). This interface allows clients to create the COM
objects via CoCreateInstance() and search for other interfaces along with
handling object lifetime management.
The IDispatch interface allows objects to be used in Automation-aware only
languages like javascript (see Appendix B.). This interface, allows for client
applications to query for properties and methods that the object supports at run-
time and execute them. This is very much like Reflection in .Net.
Dual simply means a combination of the two above. The main reason for this is to
allow the interface to be created without all of the overhead that IDispatch has
in situations where it’s not needed.
Lastly, you will notice the DispId(#) attribute for each method in the IHello
interface. This is needed when calling Invoke from the IDispatch interface.
CHello class
The last thing to discuss is the CHello class. This class simple implements
the IHello interface and nothing more. The ShowDialog() simply shows that we can
execute objects that require some sort of UI, in this case a messagebox.
The attributes are described in the comments.
Compiling & Registering
Once the code is written up go ahead an compile the ActiveX assembly.
The assembly will be called [projectname].dll inside the bin folder of the project.
The next step is to register. Do this opening up a command prompt and go to the
location of the dll. To register the assembly type:
C:\Regasm [projectname].dll /codebase /tlb
Make sure replace “[projectname]” with the correct name of your dll.
You should get some output that says the assembly was exported correctly.
Something like the following.
Microsoft ® .NET Framework Assembly Registration Utility 2.0.50727.42
Copyright © Microsoft Corporation 1998-2004. All rights reserved.
Types registered successfully
Assembly exported to '[TLB location]
', and the type library was registered successfully
To unregister the assembly pass the “/unregister” flag to Regasm. You will need to
unregister and close IE when changes need to be made to the assembly. If you don’t
unregister your assembly prior to making code changes you may end up with bogus
registry entries and a pain to clean up. The reason for closing IE is to make sure
that all reference to your assembly have been released.
Creating HTML Test Page
We are just about done. All we need to do now is create a test page.
This should be really simple, but here it is nonetheless. The Javascript
to create the ActiveX object is also provided.
Test.html
<html xmlns="http://www.w3.org/1999/xhtml">
<head> <title>C# ActiveX Test</title> </head>
<body onload="myload();">
<h1>This is Our ActiveX Test Page h1>
The message from the ActiveX Control is [
<div id="axmsg"></div>
]
<script type ="text/javascript">
function myload()
{
var myAx = new ActiveXObject("csharpAx.CHello");
if(myAx != null)
{
myAx.ShowDialog("hello from asp.net");
var d = document.getElementById("axmsg");
var s = myAx.Hello();
d.outerText = s;
}
else
lert("NOOOO... we failed");
}
</script>
</body></html>
To create our ActiveX object in Javascript we use the ActiveXObject(). The parameter
for this is generally the PROGID that we specified in its definition. If a
PROGID was not specified in its definition then I think you can pass in
“AssemblyName.ClassName”. The rest of the javascript simply calls our ActiveX
objects methods.
That’s it!!! We should have a fully functional example. This ability to execute ActiveX
objects from javascript is very powerful. Debates and arguments about security
and safety of ActiveX objects are well known and I will not throw more fodder
to the fire. However, imagine creating a Win32 or even a Winform application
where the UI is written in HTML via IWebBrowser2 and all the UI interaction handled
with your ActiveX object, just food for thought. Though, now that I think of it,
WPF is already doing this. Oh Well.
Hopefully you found this guide on how to create an ActiveX object with C# helpful.
-skaoth-
Further Modifications
The example above, though complete (hopefully error free), has one slight problem. When the html page is
loaded we get a nasty message box saying
“An ActiveX control on this page might be unsafe…blah blah blah”
So how do we fix this? Well it is quit simple really. We need to Implement an interface
called IObjectSafety. To do this, add a new .cs file to the project.
IObjectSafety.cs
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Text;
namespace csharp.activex.sample
{
[
Serializable,
ComVisible(true)
]
public enum ObjectSafetyOptions
{
INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001,
INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002,
INTERFACE_USES_DISPEX = 0x00000004,
INTERFACE_USES_SECURITY_MANAGER = 0x00000008
};
//
// MS IObjectSafety Interface definition
//
[
ComImport(),
Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
]
public interface IObjectSafety
{
[PreserveSig]
long GetInterfaceSafetyOptions( ref Guid iid, out int pdwSupportedOptions, out int pdwEnabledOptions);
[PreserveSig]
long SetInterfaceSafetyOptions( ref Guid iid, int dwOptionSetMask, int dwEnabledOptions);
};
//
// Provides a default Implementation for
// safe scripting.
// This basically means IE won't complain about the
// ActiveX object not being safe
//
public class IObjectSafetyImpl : IObjectSafety
{
private ObjectSafetyOptions m_options =
ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_CALLER |
ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_DATA;
#region [IObjectSafety implementation]
public long GetInterfaceSafetyOptions( ref Guid iid, out int pdwSupportedOptions, out int pdwEnabledOptions)
{
pdwSupportedOptions = (int)m_options;
pdwEnabledOptions = (int)m_options;
return 0;
}
public long SetInterfaceSafetyOptions(ref Guid iid, int dwOptionSetMask, int dwEnabledOptions)
{
return 0;
}
#endregion
};
}
Then modify the CHello object so that it inherits from the IObjectSafetyImpl
class so that it looks like
public class CHello : IObjectSafetyImpl, IHello
{
…
};
With that, the dialog about the ActiveX object not being safe should disappear.
References
ATL Internals – Brent E. Rector, Chris Sells.
Essential COM – Don Box.
http://msdn2.microso...yw6(VS.80).aspx
http://msdn2.microso...y/aa768224.aspx
http://www.c-sharpco...tiveXInNet.aspx
Appendix A: IUnknown
interface IUnknown
{
virtual HRESULT QueryInterface(REFIID riid, void **ppvObject) = 0;
virtual ULONG AddRef(void) = 0;
virtual ULONG Release(void) = 0;
};
Appendix B: IDispatch
interface IDispatch : public IUnknown
{
virtual ULONG GetTypeInfoCount(unsigned int FAR* pctinfo) = 0;
virtual HRESULT GetTypeInfo(unsigned int iTInfo, LCID lcid, ITypeInfo FAR* FAR* ppTInfo ) = 0;
virtual ULONG GetIDsOfNames(
REFIID riid,
OLECHAR FAR* FAR* rgszNames,
unsigned int cNames,
LCID lcid,
DISPID FAR* rgDispId) = 0;
virtual ULONG Invoke(
DISPID dispIdMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS FAR* pDispParams,
VARIANT FAR* pVarResult,
EXCEPINFO FAR* pExcepInfo,
unsigned int FAR* puArgErr) = 0;
};

How to extend vbscript with C#

I would like to extend the functionality of vbscript with the code I have written in c#. I have written some classes to automate the SAP GUI and would like to use these classes in all the vbscript files I have.
I have hundreds of vbscript files and know it will take years to convert all them to C#. So I think it will be faster to expose my c# classes to vbscript.
Do you know how to do this or know any references online I can study?
I don't know whether you're running your VBScript from the command-line or from within something like Office.
If the former, you could create one or more command-line apps that you can call from any scripting language and into which you pass parameters & action specifiers just like any other command-line tool. (Also consider moving to PowerShell in this case - it exponentially better than VBScript for command-line scripting & has great integration with .NET).
If the latter, you'll likely need to register your C# classes using RegAsm and then create instances of your C# types as per any other COM type. See this post for more details: How do I call .NET code (C#/vb.net) from vbScript?
VB script runs on the client inside the browser run-time.
The only C# solution I am aware of to run inside the browser, is silverlight. It is still just c# though.
You can access c# code from scripting languages like VB- of java-script, by decorating them with the [ScriptableMember] attribute, like so:
/// <summary>
/// Members that can be called from javascript. (or vbscript)
/// </summary>
public sealed class LINEARVIEWER_SL_SCRIPTS {
[ScriptableMember]
public void ChangeNetwork(string pNetworkFilterId, string pNetworkFilter) {
MainViewModel MainVM = (MainViewModel)((MainPage)Application.Current.RootVisual).DataContext;
long SectionID;
if (long.TryParse(pNetworkFilterId, out SectionID) == false) {
throw new FormatException("'" + pNetworkFilterId + "' not a valid section / network ID.");
}
MainVM.RoadFilterViewModel.SelectSectionAsync(SectionID, /* completed handler = */ null);
}
}
You have to register these classes when the silverligh (c#) application starts up, like so:
private void Application_Startup(object sender, StartupEventArgs e) {
...
HtmlPage.RegisterScriptableObject("LINEARVIEWER_SL_SCRIPTS", new LINEARVIEWER_SL_SCRIPTS());
}
From the java (or vb) script, you can then simply call those methods like so:
function DoAddToLIV(pNetworkFilterId, pNetworkFilter) {
...
gObjLIV.Content.LINEARVIEWER_SL_SCRIPTS.ChangeNetwork(pNetworkFilterId, pNetworkFilter);
...
}
where gObjLIB.Content is the id of the silverlight object inside the html page.
var gObjLIV = null;
function onSilverlightPluginLoaded(sender, args) {
gObjLIV = sender.getHost();
}
You can hook that function to the silverlight object in the html of ASPX page, using this parameter:
<param name="onLoad" value="onSilverlightPluginLoaded" />
Let me know if I missed anything or if you need more examples. I don't mind.

How to find the members of a dynamically loaded com object

I am trying to use an OLE COM object that I don't have any documentation for. I load the com object dynamically by:
dynamic comObj = Activator.CreateInstance(Type.GetTypeFromProgID("The Program ID"));
The Program ID in the registry points to some exe (or at least that is what I think, I cannot tell for sure. Is there a way to know where exactly does it point to). I tried loading the exe in an OLE COM viewer, but I was not able to get any useful information out of it. For example, I was not able to find a method that I knew for sure was there. Actually, after loading the comObject I am able to invoke this method without any problems, but I would like to know if there is a way that I can list/view all the members of this COM object.
Given ProgID you can use API functions and/or look up registry (such as under HKEY_CLASSES_ROOT key) for COM server CLSID and binary that hosts the class, including full path to executable.
If you have a reference to a type library there, you can also load it and check implemented interfaced and interface information. You can also possibly obtain this information from an instance of COM object too, provided that it implements interfaces like IDispatch, IDispatchEx, IProvideClassInfo.
You can enumerate all of the methods through the IDispatch interface, if it supports it.
Here is an MSDN article that uses IDispatch to get member info
I don't remember where I copied this source. I beg a pardon from the author.
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Runtime.InteropServices.CustomMarshalers;
namespace ConsoleApplication1
{
[
ComImport,
Guid("00020400-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
]
public interface IDispatch
{
void Reserved();
[PreserveSig]
int GetTypeInfo(uint nInfo, int lcid,
[MarshalAs(
UnmanagedType.CustomMarshaler,
MarshalTypeRef = typeof(TypeToTypeInfoMarshaler))]
out System.Type typeInfo);
}
class Program
{
static void Main(string[] args)
{
Type t1 = Type.GetTypeFromProgID("WbemScripting.SWbemDateTime");
Object o1 = Activator.CreateInstance(t1);
IDispatch disp2 = o1 as IDispatch;
if (disp2 != null)
{
Type t3;
disp2.GetTypeInfo(0, 0, out t3);
MemberInfo[] mlist3 = t3.GetMembers();
}
}
}
}
You can find the CustomMarshalers.dll in C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\

InternetExplorer being set up as __ComObject instead of SHDocVw.InternetExplorer

I'm having a problem with the InternetExplorer in SHDocVw.dll. I also have mshtml.tlb referenced (while googling, I did read 1 comment that said I should have mshtml.dll referenced, but that this couldn't be done in Microsoft Visual Studio Express, I don't know how true this is though). Here's one small function in its most basic form that won't work for me:
public static HtmlElement GetDocumentControlByID(
ref SHDocVw.InternetExplorer IEObj,
string ControlID)
{
HtmlElement ReturnElement = IEObj.Document.GetElementById(ControlID);
return ReturnElement;
}
The problem is that when I create the IEObj instance, it is considered type System.__ComObject instead of SHDocVw.InternetExplorer, and all subparts are also of type System.__ComObject. When I try any of the following statements...
Document WebDoc = IEObj.Document;
HtmlElement ReturnElement = IEObj.Document.GetElementById(ControlID);
...I keep getting the same error message:
Cannot implicitly convert type 'System.__ComObject' to 'System.Windows.Forms.HtmlElement'
(obviously the convert-to type is different for the IEObj.Document).
I am new to c# (coming from VBA, so I am familiar with programming), but in VBA, the equivelant works perfectly without needing to convert it in any way.
Is it something I'm doing wrong? In case it's my creation of the object, the following is (roughly) the code I used to test the function:
public static void Main(String [] args)
{
SHDocVw.InternetExplorer IEObj = new SHDocVw.InternetExplorer();
IEObj.Navigate("http://sports.ladbrokes.com/");
while (IEObj.ReadyState != 4)
{
}
// There is a textbox that definitely exists
HtmlElement NetControl = GetDocumentControlByID(ref IEObj, "username");
// I was goint to manipulate it after this, but it crashes in the above function.
}
All I really want to do is latch onto various elements so that I can enter text into textboxes, click buttons etc. I would also need to be able to use the Document variables as well (like Document.Body.InnerHtml, etc). The whole project is to be a bunch of functions to be contained in a DLL to be referenced by other projects.
You're trying to use the WinForms HtmlElement class, which isn't a COM object.
You can't mix the native InternetExplorer COM object with the managed classes in WinForms.
You should use the WinForms classes (the WebBrowser control) instead.
In most cases, you don't need COM at all.

Categories