Thursday, April 20, 2006 11:51 PM codingsanity

Hosting WinForms Controls in IE

Hosting controls in IE is easy. Most of the time. Simply create a Windows Control Library, and then put that DLL in the path of your html/aspx page which will host the control. Now, all you need to do is put an object tag in your page which references the control. The classid for the object tag must be of the form: "DllName.dll#Namespace.ControlName". Voila, your control will now appear.

Making debugging a pain in the neck

Well, maybe. There's various security issues with the browser and .NET Framework that may bite you badly here. Suffice it to say that you may spend quite a bit of time playing around with various settings. One piece of advice that may assist in your debugging is to remember that IE caches stuff, so when you make any changes to the control itself, go into Tools->Internet Options and use the Delete Files button in the General Tab.

However, that's not enough on it's own, since .NET also stores a "cache" in the form of the download cache. You'll have to clear this out by using "gacutil /cdl". However, before doing this you'll need to close down the IE session that hosted your control, as it will be locking this cache. Note that I said IE session, not IE instance. If you opened any new IE windows from that instance of IE, or opened it from any others, they all need to be closed down, even if they opened before you navigated to the hosting page.

If you're struggling to get your control to even appear, you might want to go into Internet Options, Settings, View Files, and then look for a file called "?FusionBindError...". Copy this file out somewhere onto your hard disk since you won't be able to view it from here. It'll have a weird name (different from it's one in the Temporary Intenet Files folder I mean). This may contain information that will help you find out why your control can't be loaded.

Alternatively, it may be a problem with IIS not being able to find DLLs or their dependencies. The IIS logs come in handy for this, just go look for 404's with .dll in the same line.

I wanna access the control from JavaScript!

Of course. I mean you're hosting it in your page, and obviously you may want to respond to events fired by the control, and you may also want to set properties on the control. The important thing to remember here is that IE is a COM-based system, and doesn't understand .NET events, properties and methods.

IDispatch, how we've missed you

So the first thing you're going to have to do is create a COM IDispatch interface for your control. Before you sign off in hopelessness, this is actually quite easy to do inside .NET. Create a stock-standard .NET interface, and put your members in. Now all the members are going to be methods, even the properties and events, which may seem a bit weird. So you'd define events as:

void EventOne(object sender, System.EventArgs e);

and properties as:

string Property();

This might seem a bit strange, but COM doesn't really have the concept of properties and events, they're all just methods (much as it might seem otherwise). In a sense .NET is the same, it's just better at hiding it.

Right, now we've got our interface, what next? Well, we're going to have to make it COM-compatible. We do this by applying a GuidAttribute to the interface, with a unique GUID. We also need to mark the interface as being of type IDispatch. Don't worry about this too much, it's basically saying it supports something similar to Reflection (yes, in COM you need to tell it this explicitly). Javascript uses IDispatch exclusively to access methods. To mark the interface as such, just apply the InterfaceTypeAttribute using a parameter of ComInterfaceType.InterfaceIsIDispatch.

DISPIDs are love

Now the interface itself is accessible to JavaScript, but the methods aren't. COM has a weird thing where every member of an interface has a number that identifies it called the DISIP. There are good reasons for this, but you really, really do not want to know. Trust me. So what we must now do is decorate each member with the DispIdAttribute. The parameter must be a number 1 or above, and each member must have a unique number. Technically you can give DISPIDs with lower values (way into the negatives even), but most of these have special meanings for COM, so I'd seriously advise against it.

So now we go to your control. Add members and events and stuff just as you normally would. Properties are proper properties, events are proper events. You must implement the items you declared in your interface, but do not implement the interface itself. The reason is that this interface is a special type of interface known as a COM source interface. What we'll do is decorate the control with the ComSourceInterfacesAttribute, passing in the fully qualified name of our IDispatch interface.

Time to USE this stuff

Okay, now we're ready for the JavaScript. We do all the steps to deploy our DLL to IIS, and into the web page as I discussed above, except now we can write script code that will access that object.

Some pit traps to avoid

There's one big caveat though. The assembly must have the "Security:Allow calls to unmanaged assemblies" permission. Probably the easiest way to do this is by granting FullTrust to the assembly via the .NET Framework Configuration Tool.

Oh, one other thing. Make sure all your assemblies have an explicit assembly version set (i.e. no *s in AssemblyVersion). If not, you may get some very weird compatibility errors between client and server.

Configuration

Now what about configuration? One would think that you might put the config for your control in the web.config file, but you'd be very wrong. You see, you're running Internet Explorer, not a web page, so the config has to be for IE. The good news is that the config file isn't on the client machine, but is on the web site. Just create a file called "iexplore.exe.config" and put it in the site root. Note, that's not the virtual directory, but the site root. So if it's localhost, then the config goes at http://localhost/iexplore.exe.config.

Conclusion

I hope this helps. Most of this information is available elsewhere, but trust me it can be a pain tracking it all down. Many other blogs have much more detailed information on the Code Access Security issues, or the Fusion binding issues, or the debugging, but I haven't yet found any with all the information easily summarised.

Code

To help out, I've posted a sample project at my web site. You can also find it as an attachment to this post.

Unzip it to a directory, and then build it. Create a new Virtual Directory on your web site, and copy the TestEvents.html page to that site. Also copy in the IEEvents.dll. Then copy the iexplore.exe.config to the root of your site. For example, C:\InetPub\wwwroot.

Filed under:

Comments

# re: Hosting WinForms Controls in IE

Wednesday, September 27, 2006 4:17 PM by Carlos Martins

Hello, I would like to know why is necessary to have an assembly version other than "0.0.0.0" for the IE subscribe the events fired by the windows forms control. Tanks in advance, Carlos Martins cmartins@isel.ipl.pt

# re: Hosting WinForms Controls in IE

Thursday, September 28, 2006 5:03 PM by codingsanity

I dunno about 0.0.0.0, but if you have auto-generated version numbers, then you do get compatibility issues. So for example if we have version as 1.0.*, and compile, then copy that DLL to client and server, there may be problems with serialization between the two.

I'm not completely sure, but I think it has something to do with serialization. Since serialization creates an assembly, I think what it does is use the version attribute that you used in your main assembly to determine it's version. In the example above this means that our assembly may result in one serialization assembly on the server side with version 1.0.1234.5678, whilst the client side is 1.0.8765.4321, both off a main assembly of version 1.0.1111.2222. So, the serialization code fails. I guess if you overrode the serialization it wouldn't, since you could decide to ignore version, but that would be a pain.

# re: Hosting WinForms Controls in IE

Thursday, September 28, 2006 7:10 PM by Mark Hamblin

I wanted to run your example code, but when I try to load the csproj file, I get the error: "Unable to read the project file 'IEEvents.csproj'. The file 'C:\dev\IEEvents\IEEvents.csproj' is not a valid project file. The project file is missing the 'VisualStudioProject' section." I am using Microsoft Development Environment 2003 Version 7.1.3088. Any ideas on how I can get around this problem?

# re: Hosting WinForms Controls in IE

Friday, September 29, 2006 9:04 AM by codingsanity

Strange, I just tried it myself and it worked fine. Do you have C# installed?

# re: Hosting WinForms Controls in IE

Friday, September 29, 2006 1:14 PM by Carlos Martins

Hello, I was wrong when I said that an assembly with a version number of "0.0.0.0" had problems with the event registration by IE. I had another kind of problems, and I was confused. My tests said that the assembly version information is not relevant in this scenario. I don't known why you talk about serialization and client and server side assemblies. When we host .NET Windows Forms Controls in IE with the tag, the assembly containing the control is downloaded from the Web Server to the local download assembly cache (c:\windows\assembly\download directory), from which it is loaded into de CLR host of the IE for execution. (If the assembly is already in the local cache, and does not exist a more recent file in the server, the doenload step is skipped.) The IE talks with the .NET type using COM Interop. So, I do not understand, why you talk about serialization of the assemblies, and client and server side assemblies. In this kind of scenario the assembly version is not specified in the tag. All we say int the tag is: classid="asm-name.dll#full-type-name". If an assembly with the name specified is located in the application directory of the Web server, it is downloaded to download cache, and its version is not considered at all. The version of the assembly is important in the scenario, when we build assemblies with strong names and specify specific CAS permissions for the assembly, creating a code group using evidence "strong name" specifiying both public key and version. So, if we change the version specified the assembly no more belongs to the code group that grants it specific permissions. I hope this helps. Carlos Martins

# re: Hosting WinForms Controls in IE

Friday, September 29, 2006 1:28 PM by codingsanity

Sorry, I was thinking about a similar problem where the IE hosted .NET control was using remoting to communicate with a back-end server process, I had big serialization errors with that.

# re: Hosting WinForms Controls in IE

Friday, October 06, 2006 10:25 AM by andrew

Hello, i'm so afraid because i can't get this sample working ... I need to implement a similar scenario, but i still have the same problem ... If i run you sample and put an entry in the CAS Commputer-->LocalIntranet New Group with "Full Trust" to URL "http://localhost/IEEvents/*" With this settings Control works but events is not fired ... if i change URL from "http://localhost/IEEvents/*" to "http://localhost/*" Events are fired !?!?!?!? I'm going crazy .... any idea ? thanks all in advance andrew

# re: Hosting WinForms Controls in IE

Friday, October 06, 2006 4:58 PM by codingsanity

Is it perhaps getting the control's DLL from the root of your localhost? Have a look if the DLL is there. You can also check the logs on the server.

Finally, after checking that, do a gacutils /cdl

# re: Hosting WinForms Controls in IE

Friday, October 06, 2006 10:17 PM by andrew

sorry i don't understand at all ... ?!?! my .net dll is located in the same directory of html page ... what do you mean with "Is it perhaps getting the control's DLL from the root of your localhost?" do you mean iis log ? thanks andrew

# re: Hosting WinForms Controls in IE

Friday, October 06, 2006 10:50 PM by codingsanity

What web server are you using to serve up the HTML page?

# re: Hosting WinForms Controls in IE

Saturday, October 07, 2006 10:41 AM by andrew

i'm using IIS on xp pro i tried too webserver provided by vs web developer express ... but with same problem ...!?!?!

# re: Hosting WinForms Controls in IE

Saturday, October 07, 2006 11:40 AM by codingsanity

Run gacutil /cdl, clear your Temporary Internet Files, and then access it through IIS. Have a look at the Fusion binding page (search above for "?Fusion" on how to get this), and look at what it says in there.

# re: Hosting WinForms Controls in IE

Saturday, October 07, 2006 2:32 PM by andrew

my components works ... only event aren't fired ?!?!? here is my fusion log .... why in last lines it search in htt://localhost/myCustomControl.dll ??? my web app is htt://localhost/web/ and the control is in that virual directory as i can see event form js is not attached ?!?!? but if i give to localintranet_zone special permission "Allow calls to unmanaged assemblies" all works ok !!! ---------------------------------------------------------------------------------------- The operation failed. Bind result: hr = 0x80070002. Impossibile trovare il file specificato. Assembly manager loaded from: C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\fusion.dll Running under executable C:\Programmi\Internet Explorer\IEXPLORE.EXE --- A detailed error log follows. === Pre-bind state information === LOG: DisplayName = myCustomControl, Version=1.0.0.1, Culture=neutral, PublicKeyToken=4a21a4d0f53ad996 (Fully-specified) LOG: Appbase = http://localhost LOG: Initial PrivatePath = bin LOG: Dynamic Base = NULL LOG: Cache Base = NULL LOG: AppName = NULL Calling assembly : (Unknown). === LOG: Processing DEVPATH. LOG: DEVPATH is not set. Falling through to regular bind. LOG: Publisher policy file is not found. LOG: Host configuration file not found. LOG: Using machine configuration file from C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\config\machine.config. LOG: Post-policy reference: myCustomControl, Version=1.0.0.1, Culture=neutral, PublicKeyToken=4a21a4d0f53ad996 LOG: Cache Lookup was unsuccessful. LOG: Attempting download of new URL http://localhost/myCustomControl.DLL. LOG: Attempting download of new URL http://localhost/myCustomControl/myCustomControl.DLL. LOG: Attempting download of new URL http://localhost/bin/myCustomControl.DLL. LOG: Attempting download of new URL http://localhost/bin/myCustomControl/myCustomControl.DLL. LOG: Attempting download of new URL http://localhost/myCustomControl.EXE. LOG: Attempting download of new URL http://localhost/myCustomControl/myCustomControl.EXE. LOG: Attempting download of new URL http://localhost/bin/myCustomControl.EXE. LOG: Attempting download of new URL http://localhost/bin/myCustomControl/myCustomControl.EXE. LOG: All probing URLs attempted and failed. ----------------------------------------------------------------------------------------

# re: Hosting WinForms Controls in IE

Sunday, October 08, 2006 10:04 AM by codingsanity

I'm not 100% sure. It looks almost like for some reason it thinks the DLL should be in the wwwroot. Try putting it there, and retrying the whole sequence (esp clearing Temp Internet files and gacutil /cdl).

# re: Hosting WinForms Controls in IE

Sunday, October 08, 2006 2:56 PM by andrew

if i put the dll in c:\windows\inetpub\wwwroot no log entry is retrivied, but event isnt fired .... i see that if try to register code group with strongedname, once i create codegroup fusion log trace this : but my project is under C:\IEEvents and my virtual configured on this directory is "http://localhost/IEEvents" *** Assembly Binder Log Entry (08/10/2006 @ 14.51.38) *** The operation failed. Bind result: hr = 0x80070002. Impossibile trovare il file specificato. Assembly manager loaded from: C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\fusion.dll Running under executable C:\WINDOWS\system32\mmc.exe --- A detailed error log follows. === Pre-bind state information === LOG: DisplayName = IEEvents, Version=1.2.4.0, Culture=neutral, PublicKeyToken=22e0eeb986374702 (Fully-specified) LOG: Appbase = C:\WINDOWS\system32\ LOG: Initial PrivatePath = NULL LOG: Dynamic Base = NULL LOG: Cache Base = NULL LOG: AppName = NULL Calling assembly : (Unknown). === LOG: Processing DEVPATH. LOG: DEVPATH is not set. Falling through to regular bind. LOG: Publisher policy file is not found. LOG: No redirect found in host configuration file (C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\mscormmc11.cfg). LOG: Using machine configuration file from C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\config\machine.config. LOG: Post-policy reference: IEEvents, Version=1.2.4.0, Culture=neutral, PublicKeyToken=22e0eeb986374702 LOG: Cache Lookup was unsuccessful. LOG: Attempting download of new URL file:///C:/WINDOWS/system32/IEEvents.DLL. LOG: Attempting download of new URL file:///C:/WINDOWS/system32/IEEvents/IEEvents.DLL. LOG: Attempting download of new URL file:///C:/WINDOWS/system32/IEEvents.EXE. LOG: Attempting download of new URL file:///C:/WINDOWS/system32/IEEvents/IEEvents.EXE. LOG: All probing URLs attempted and failed.

# re: Hosting WinForms Controls in IE

Sunday, October 08, 2006 3:23 PM by codingsanity

I'm struggling to understand why fusion is looking for the file in C:\Windows\System32 now. I cannot see how simply moving the file to where fusion was looking for it could have caused fusion to change where it looks.

Do you have this file installed in the GAC by any chance? If so, try taking it out, and giving this another go. Basically the fusion log is saying where it's looking, so we want it to be looking somewhere in the http://localhost area, and then you can place the DLL where it's looking. Having it looking in C:\Windows\System32 makes no sense.

# re: Hosting WinForms Controls in IE

Saturday, October 21, 2006 11:58 AM by cmartins

Hello Andrew. The reasons why you must give CAS permissions to a codegroup defined with http://localhost/* to have events in the IE, are the following. The IEHost (the CLR host of IE) creates an AppDomain to execute your .NET control with the permissions of the *site* from which the assembly is downloaded (in your case, http://localhost). Relatively to the subscription of events by the IE and the demand of UnmanageCode permissions, there are differences between the .NET 1.x and .NET 2.0. In .NET v1.x, the demand for UnmanagedCode permission ( because event handler are unmanaged code) is made when the IE registers the event handler with your .NET control. In .NET 2.0, the demand for unmanaged code permission is only made when the event is fired (no when the event listener is registered). So, in .NET 1.x there is no way to use Assert() to confine the CAS stack walk to your assembly, because we do not have access to the code that registers the event handler. Consequently, the stack walk goes till the root permissions of the host (your site permissions). So, to get events in the IE with .NET 1.x you need to give UnamangedCode permission to the site from which the assemblies are downloaded. In .NET 2.0 this is not necessary, because you can give only the UmannagedCode permissions to your assembly (for security reasons, using the full strong name of the assembly as evidence of de CodeGroup) and use "new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();" and "CodeAccessPermission.RevertAssert();" before, and after, the code that fires the event, to limit the CAS stack walk to your assembly. I think that you must use the .NET 2.0, because with it is possible to give UnmanagedCode permissions to particular assemblies, not to all assemblies downloaded from the same site. There is one more warning. When both .NET 1.x and .NET 2.0 are installed in a computer, the IE uses the most recent version (2.0), not the version you use to create the assembly with the control. So, we need to configure the CAS for .NET 2.0, not for .NET 1.x. I hope this helps.

# copy unmanaged c dll to bin web folder how to help

Wednesday, June 04, 2008 7:42 PM by copy unmanaged c dll to bin web folder how to help

Pingback from  copy unmanaged c dll to bin web folder how to help