Friday, April 20, 2007
I've been working on using Windows Forms UserControls hosted inside of IE. It's easy to get started, but there's very little good information to be found on the details of this out there. Especially for running them in an intranet where you don't want to configure the security settings on every client machine. Here are some of the things I've figured out while making this work.
 
I won't go into the basics. You can easily find this information on Google. Try this one for instance. Also, if what you need is pretty graphics and not application logic, consider using Flash or Silverlight instead.
 
I was trying to get my usercontrol to play under the restricted Intranet Zone. This is the Zone that IE uses when there are no dots in the domain name, like localhost or a local intranet server name. It's only slightly more permissive than the Internet Zone, and it's still giving a lot of issues.
 
Allow partially trusted callers
To make the control at all load in the browser in the Local Intranet Zone, the assembly and all references assemblies need to allow partially trusted callers. To enable this, put [assembly: System.Security.AllowPartiallyTrustedCallers] in the AssemblyInfo.cs file of the class library containing the control. You can't reference assemblies that don't have this tag, or your control won't load. Also, be sure you don't call any methods that require more trust than the Local Intranet Zone provides, or you'll get an exception.
 
Troubleshooting
 
Try Full Trust
If you are having trouble getting your control to display, try and set the LocalIntranet Zone to run with FullTrust permissions. This is done in the Control Panel > Administrative Tools > .Net Framework 2.0 Configuration tool (assuming NetFx 2.0). Navigate to the My Computer > Runtime Security Policy > Machine > Code Groups > All_Code > LocalIntranet_Zone node. Right click it and go to Properties. Under the Permission Set tab, change the dropdown from LocalIntranet to FullTrust, and click OK.
 
Now everything running from http://localhost and any other hostname with no dots in it will have permission to format your harddrive. But just make sure your UserControl doesn't do anything malicious and you'll be ok. If your control works now, the problem was security. If it still doesn't then it's possibly a loading error, or an exception in your code.
 
Logging
Internet Explorer gives very little indication on the web page to what may be wrong. But there is a way to have it log what it's doing when trying to load and instantiate your control. This logging is enabled in the registry. This can tell you whether the DLL wasn't found, or of there was an exception when instantiating it. You can also monitor the assembly loading with the Assembly Binding Log Viewer. Open this by starting the Visual Studio 2005 Command Prompt and typing fuslogvw.
 
Also you can take a look at the IIS logfiles for the website you are running the control in to see if the DLLs are getting downloaded. HTTP code 200 means OK, and 304 means it was taken from cache.
 
Avoid the Bin directory
Remember that the bin directory on an ASP.NET website does not allow retrieving DLL files. So don't put your control in there. This rules out adding a Copy Local reference to the control's project to have it copied automatically. Rather set up an xcopy command in the Post-build events of the control's project that copies the compiled files to the web project.
 
Clear Download Cache
The .Net Framework will cache the downloaded assemblies in the Download Cache. Under development this can sometimes cause some problems with the DLL not getting updated. This cache can be cleared with the gacutil utility by issuing the command gacutil /cdl. I recommend adding this as a build action on the project containing the control. Go to properties on the project in Visual Studio and in the Build Events tab put this line in the "Post-build events command line" box: C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\gacutil.exe /cdl. Now the cache will be cleared each time you build a new DLL. Just note that the browser needs to be closed when you clear the cache, or it will hold a lock on the DLL and the clear will fail.
 
Use IIS, not the VS built-in web server
I've had some strange caching issues when running from the built-in web server in Visual Studio. Switching to using IIS instead made everything work much better. Open the properties on the web project in Visual Studio, and under the Web tab choose to Use IIS Web Server, and click the Create Virtual Directory button.
 
Attach the debugger
You can attach the debugger to IE and be able to debug the control. But you will have to manually attach to IE after you have loaded the page. If VS is set ut to attach to IE when you start the project, it will not get attached to the managed host within IE, and you will not be able to debug.
 
This means you won't be able to debug the initalization of your control.
 
Separate assembly baseclass
One more thing to notice: Your control will not work if you inherit from a baseclass in a different assembly, even though that baseclass inherits from UserControl. I guess the IEHost will check for inheritance on UserControl before it loads the dependent assembly. If you need all your controls to be castable to some common type, or to expose the same methods and properties, consider having them all implement a common interface.
 
JavaScript
 
COM visible
You control can interact with JavaScript in the page. The public properties and methods on your control are usable from javascript. Just have the script get a reference to the object tag and then just call the methods and properties by name (only simple types like string, int and double works though). But before javascript can access your methods and properties you need to make your control COM visible. This is done in the AssemblyInfo.cs file by adding (or changing) this attribute: [assembly: ComVisible(true)]. By default this is set to false, but needs to be true.
 
Polling instead of events
UserControls are also able to fire events that can be handled in javascript. But this requires Managed Code permissions, which is not available in the Local Intranet Zone. So events won't work. If you need your javascript to know when something happens in your control without using events, you can make your control expose a property that indicates event state, and then have a javascript timer poll on that property. This isn't as nice as real events, but does the job.
 
Issues
 
Resizing is blocked
A strange issue with the Local Intranet Zone permissions is that resize events to the control are blocked. If you set your object tag to, say, 100% width and height, the control will be sized as expected when the page loads. But if you then resize the browser window, the control's content will not resize, and it will be clipped. Resizing works as it should with Full Trust.
 
One way I've found to work around this strange quirk is to expose a SetSize(with, height) method on my control and have javascript in the page set the correct size on the document.onresize event. With some clever scripting I got this right.
 
Activation
Since the control is an embedded object, it is subject to the Click-To-Activate behaviour of Internet Explorer. This shouldn't be news to you. But the issue is that the control behaves really badly when it's not activated. It's slow to paint when you scroll the page and has all kinds of issues if you try to emulate paging with showing and hiding them. Once they are activated they work quite well. So if you experience problems, make sure the controls are activated before you decide that this technology is broken.
 
WCF
If your control is calling back to the server using a web service, do not implement this using WCF in the control. The System.ServiceModel assembly do not have the AllowPartiallyTrustedCallers attribute, and will cause your control not to load under Local Intranet Zone if you reference it. Rather use normal web services.
 
 
Please let me know if anything I've said here is wrong or inaccurate, or if there are some other issues I should know about. There is far too little info out there on these things. And what is there is hard to find because of the obvious searchability issue between web forms usercontrols and windows forms usercontrols.
posted on 4/20/2007 11:47:05 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [1]

Related Posts:
Taming the Tab key's little brother
Combine() only two paths?
Refactorings
Debugger.Launch()
Microformats
Projector trouble