Showing posts with label .net. Show all posts
Showing posts with label .net. Show all posts

Tuesday, February 13, 2007

NCover Setup - Part II

Following up on my previous post, I'm continuing to set up code coverage in my .NET 2.0 project with some specific criteria.

Kudos to Grant Drake (aka Kiwidude) who is very committed to the cause. I spent some time on his site last night, pouring over some of his FAQ. He left a comment on my blog earlier, citing an FAQ that I missed on his site. Plus he recommended version 1.5.4 of NCover instead of the latest 1.5.5beta. Good stuff!

So to follow through on my previous post.

The first thing I did was move my test namespace and their classes into a separate ClassLibrary. This included all the standard stuff:

  • remove the reference of NUnit from the Core library
  • add an NUnit reference to the Test library.
  • add a Core Library reference to the Test project.

After a quick compile, I dropped down to a command prompt and executed the same statement, except this time I had to specify the name of the Test harness assembly:

ncover.console nunit-console Test.dll //a Core

This worked and ...satisifies my second objective! The added bonus is now the coverage report only contains information about the Core namespace. This is great, but as an aside, since nothing has changed in the code I wonder if this is a bug in the "//a" switch? Update: The //a switch is for assemblies without their extensions, not namespaces.

As a further refinement, I can begin to take advantage of NUnit's Project capabilities. This enhancement simplifies things greatly. It allows me to:

  • test/cover multiple assemblies at a time
  • shield my build script from having any knowledge about the test harness configuration.
  • specify where to locate the configuration file (this should solve my third objective)

To create the Nunit project file:

  • open the NUnit-Gui and choose "New Project". I like to save the nunit file at the root of my solution.
  • add in the assemblies that contain your test fixtures. In my case, I have to specify the relative path to my Test Harness: /Test/bin/Debug/Test.dll

As an NUnit project file is simply an xml file, my nunit project file contains the following:

<NUnitProject>
  <Settings activeconfig="Debug" />
  <Config name="Debug" binpathtype="Auto">
	<assembly path="Test\bin\Debug\Test.dll" />
  </Config>
</NUnitProject>

With this in place, I can now generate my coverage report at the command line with the following statement:

ncover.console nunit-console TestProj.nunit //a Core

So far, this pretty good. The second objective was fairly easy, and although I've violated my first objective, Kiwidude has suggested this might be something I could correct with part of the build script, so I'll get to that soon enough.

Tomorrow, I'll tackle my third objective of being able to resolve configuration settings.

submit to reddit

Monday, February 12, 2007

NCover Setup - Part I

About a year ago, one of my projects established some best practices for test-driven development where we found our sweet spot using .NET 1.1, NUnit, NCover and NCoverBrowser. Things were going great until one day, NCover stopped producing coverage reports. Despite my best efforts, I couldn't get it working again. Panic. Frustration. Pain. Despair. Acceptance: no more coverage reports....

Now, a new project is about to take off, this one using .NET 2.0, and I'm determined to get NCover running on this project. Problem is, documentation for NCover is still no where to be found online, only mixed messages in their forums.

So, for my reference, and potentially yours, I thought I'd share how I set this up. As I'm building an application for an enterprise the requirements can get pretty complex... here's what I'm shooting for:

  1. I typically structure a project so that I have a solution folder which contains copies of all the libraries my project will require to compile. I also bundle third-party tools with the solution folder so that when a developer checks the project out of source-control, the build script has everything it needs to operate without any configuration on behalf of the developer.
  2. I want to structure the solution so that the application code and the test harness are in different assemblies.
  3. In addition to separate test and application assemblies, configuration settings from the application code must be used with the test harness.
  4. I need to drive the code coverage from a NAnt script
  5. I need to integrate the output into CruiseControl.NET

So I downloaded NCover 1.5.5 beta and cracked open a new ClassLibrary project to simulate code coverage. I wrote two classes: a simple class "Core.Class1" and a test fixture "Test.Class1Test". I compiled the solution, dropped to a command prompt and manually tried to execute ncover using relative file paths. "Unable to locate file" errors.

As soon as I put both NCover and NUnit file paths into my PATH environment variable, the error messages started to change. The following statement, executed from the "bin\Debug" folder of my project:

"ncover.console nunit-console Core.dll //a Core"
...worked!

Although adding PATH variables violates objective #1 it did produce a coverage report for the Core and Test namespaces. Since my NUnit test cases are guaranteed to be 100% covered by NUnit, I'll need to find a way to exclude the Test namespace from the report.

I'll reorganize the project into separate assemblies and see how that changes this configuration tommorrow.

Read part two: NCover Setup - Part II

submit to reddit

Sunday, July 16, 2006

VS.NET 2005 ASP.NET Web Application add-on

At work, we've shunned the VS 2003 concept of "Web Sites". Who really thinks you can build an enterprise web-application that is developed against the web-server with FrontPage Extensions?

Fritz Onion wrote an article ages ago that showed how you could treat a standard Class Library as a web application. This is the model we've been using ever since.

I've been wanting to try out the new Visual Studio 2005 Web Application Project since it was released but am just getting around to it.

There's an Update to Visual Studio 2005 you have to download first.

Haven't spent too much time on it yet, but you don't have to hack the IDE's config settings nor set up IIS.

Bitchin'.

submit to reddit

Tuesday, May 09, 2006

Asynchronous client-side error logging

Finally got around to setting up a Bloglines account: handy tool.

While catching up on some of Scott Guthrie's posts, I was intrigued by Logging JavaScript Errors To ASP.NET

The code module is ASP.NET 2.0, and integrates with the Health Monitoring system.

submit to reddit

Wednesday, March 15, 2006

Screen Capture for Invisible Windows

I was recently tinkering around in an admin tool that is used to build web-sites, and found myself wondering if it was possible to create a "preview" of the web-site as an image. The idea being, I want to show a list of pages in the website with thumbnails of page previews; thus the site administrator can see the entire site on a single page and would be able to know what they were editing before they opened the page configuration.

If you're like me, these kinds of problems drive me crazy. I ear mark them as "nifty ideas" and I slowly mull them over in my head when my head has idle time. (What, you don't have head idle time? Between 3-4 in the morning when I change sleeping positions, I have to force myself not to address my mental challenge To-do list. I recently discovered that our brains solve a lot of problems after only a few hours of sleep.)

So although I haven't actually cracked the code open to tackle this problem, here's what I envision the implementation to be:

  1. Use the .NET Process object to launch a window to the web page.
  2. Grab the handle of the Process and pass it to the GDI+ to convert into an image. This CodeProject article gives a really good overview of doing just that.

I'm hoping that if the .NET Process is launched with the ProcessWindowStyle set to "Hidden" that it still has a valid device context used to paint onto the screen. If that's true, there should be no reason why I couldn't convert the hidden window's content into a Bitmap.

Few other things to consider, such as when to determine that the browser has fully loaded the requested page? I might have to consider using the WebBrowser control to load the webpage and wait until the document onload event or page status changes....

Yet another task to play with...

submit to reddit

Tuesday, March 14, 2006

Shell extensions in C#

I work with a lot of Xml files, where the filenames are random GUIDs. Since all the documents have the same structure, I wonder if it would be possible to create a shell extension for Windows Explorer that could add a column to the details tab.

This CodeProject article, Explorer column handler shell extension in C# - The Code Project - C# Programming, looks like a good starting point.

Update 06/07/2008: I've written a few shell extensions following this example.  Although they work, they can seize a lot of memory if you need to spawn a debugger.

submit to reddit

Thursday, January 19, 2006

CodeProject: Server-side handling for css and js

This post on CodeProject talks about creating a custom handler for css and js files, so that relative paths can be resolved. The ability to specify relative paths to your application root is clearly an advantage if your application root is a virtual directory or sub-folder from the web root. But I think this solution could go one further: II6 supports wildcard mapping, so that all requests can be funelled through the asp.net runtime, an advantage if you want to use virtual URLs for both folders and files. I believe that dotText is using wildcard mapping in this manner, and I think they have implemented a very decent handler for static files, such as js, css, images, etc. I wonder if the dotText controls can be extended to support this type of ~ (AppDomainAppVirtualPath) mapping.

submit to reddit

Wednesday, May 25, 2005

Dynamic control generation using XSLT

In a recent project, we needed to render .net controls dynamically. Instead of using ascx controls, we used XSLT to transform static Xml data into dynamic presentation layers; a viable approach to providing multiple look and feels for the site. Here's a quick walkthrough of how it works.

Dynamically loading the HTML is simple, using the built-in ParseControl method of the System.Web.UI.Page class.

Control c = Page.ParseControl( "<b> Literal Text </b>");

The same logic can be applied to parsing the output of an Xslt transformation.

string transformOutput = xslTransformer.Transform();
Control c = Page.ParseControl(transformOutput);

If you want to add server-side controls into the transformation, there are a few gotchas:

  1. XSLT parser needs namespace definitions for .net and custom tag prefixes.
  2. Namespaces appear as attributes in the formatted output, which will need to be stripped out before adding to the Page.Controls collection.

If you're adding asp.net controls or custom controls into your XSLT file, you'll need to declare the namespace at the top of the xslt header, otherwise the XML Parser will complain:

xmlns:asp="http://schemas.microsoft.com/AspNet/WebControls"

Incidentally, if you're using Visual Studio 2003, adding this namespace declaration will give you Intellisense for your asp.net controls in your XSLT file.

With all the proper namespaces defined, you'll bypass any nasty Xslt Parser errors, however, these namespaces will now appear scattered throughout your transformed output as attributes. If you leave these values in and try to parse the output into a control, the .net runtime will try to resolve the namespaces as attributes of the controls and will generate runtime errors.

Fortunately, a quick regular expression can strip all these namespaces out of the transformed output:

string transformedOutput = transformedOutput.Replace("xmlns:\w+=\"[^"]+\"", "");

With the namespaces stripped out of the transformed output, you should be able to parse the string into a control.

using System.Xml;
using System.Xml.Xsl;
using System.Web;

XslTransform transform = new XslTransform();
transform.resolver = new System.Uri("http://localhost"); 
string xslFilePath = Server.MapPath("/controls.xslt");
transform.Load( xslFilePath );

string xmlFilePath = Server.MapPath("/data.xml");
string transformedOutput = transform.Transform( xmlFilePath );
transformedOutput = transformedOutput.Replace("xmlns:\w+=\"[^"]+\"", "");

Control c = Page.ParseControl( transformedOutput ); 
Page.Controls.Add( c );

Cheers

submit to reddit

Monday, May 23, 2005

Poking around in SMTP

A few years back, I had seen a mailer engine that spit out email messages in EML format. I found this engine to be extremely helpful because I could turn off the SMTP service, run the mailer-engine, and then manually inspect the messages in Outlook Express. If the messages were fine, I re-enabled the SMTP service.

At the time, it was clear that the engine was leveraging the CDO.Message object's ability to persist to a file.

Recently, I wondered if this was possible using the .net framework, but was dissapointed to find out that the System.Web.Mail.SMTPServer object only exposed the method "Send".

Enter Reflector.

The SMTPServer implementation is very interesting. Reflection shows that there are three internal classes:

  • CdoSysHelper
  • CdoNtHelper
  • LateBoundAccessHelper

As it turns out, the SMTPServer object acts as a proxy between the legacy COM objects, "CDO.Message" and "CDONTS.NewMail" -- it detects which environment you are using, then delegates the sending of the message to the "Send" method to the appropriate COM object.

What's equally interesting, is that the .net framework doesn't use COM Interop to talk to the COM objects. Instead, it uses the LateBoundAccessHelper to instantiate the COM object by it's ProgId, and then manually sets its properties and methods using Late Binding.

To stream my messages out into EML format I suppose I could use COM Interop to the CDO library, but that would mean I would have to package the interop wrapper (and its dependencies) with my assembly.

I'll have to take a look at extracting this code using a Reflector add-in, and extending the class to save to a file instead of sending directly. It'll be interesting if I can do this without adding an Interop wrapper to CDO. I wonder if there'll be any performance drawbacks in this approach...

yet another side-project...

submit to reddit

Sunday, January 30, 2005

What time is it?

One of my current projects involves relocating my client to a new hosting environment. This is proving to be a challenge as I don't really know the application that well, yet. The code is hybrid asp/.net, and one of the bugs that is totally driving me crazy is actually date formatting. The old server environment was configured with English US as the default locale, whereas the new environment is English Canada. As a result, I have a few database inserts that are using VBScript's Now() function to write the current time-stamp. (Not exactly why the database isn't providing this functionality -- but that's another problem). What's odd is that I changed the default locale, but wasn't seeing any changes in the old asp code -- the date format was still wrong.... I had changed the regional settings (or at least I thought I had) and couldn't figure out why I was seeing the proper date format from the command-line but not in asp. The glaring mistake I made was that I had missed the "Set Default" button at the bottom of the control panel. If you forget to click that -- then you're only applying changes to the current user profile. The link for this article points to a ms kb article on some new options for the regional settings for w2k sp2+. I'll come back to that if it proves to be a problem with my .net code.

submit to reddit

Tuesday, January 11, 2005

svn up

Over the last year, our organization has been migrating our version control systems to subversion. Man, what a ride. Previously, I've grown accustomed to Visual Source Safe, which when looking back seems like -- well, for lack of a better term, like sticking your code into a shredder and setting it on fire. While it does have it's advantages (it doesn't require any brain-power to use) -- it doesn't do complex things well, if at all. If you go down the path of pushing the envelope and try to make it behave like an enterprise version control system, ... it'll bite back. hard. Am I surly? Sure, why not. Unfair? Well, you try repairing a 3 Gig corrupted vss database and call me unfair. Subversion is a far cry from the simplicity of VSS. It's command-line utilities only (there are open-source GUI's, but I haven't heard any good things), the concepts are radically different, and it doesn't plug into the IDE. I'm not complaining -- these are advantages. Subversion requires you to think differently about what you consider source-control. Gone are the days of my team complaining that they "...can't work on that file because it's currently checked out." At any rate, I won't be going back to VSS any time soon. I'm sold on subversion. Coupled with Cruise-Control.net, we've got a pretty sweet development-integration cycle happening. As of late, I've switched projects recently, a form of promotion if you want to call it that, and I'm now responsible for a large account that pushed out about 62 projects last year. This means we are branching and merging in subversion all the time. At the rate we're going, I'll be a subversion guru in a week....

submit to reddit

Thursday, January 06, 2005

Pluggable Architectures with Provider Models in .NET 1.1

Lately, these articles have sparked a lot of interest within our app dev group. Provider Model Design Pattern, Part 1 Provider Design Pattern, Part 2 I had a client that was experiencing problems with their monolithic application, in that they were adding microsites to their site on a weekly basis, and found that they had to recreate the wheel for their application framework for each microsite. At one point, they had up to 20 different assemblies referenced for each microsite. In addition, they wanted to keep the Personal Information in their production database private, thus they didn't want to hand out their schema or access to their database. On top of that, they had a multitude of 3rd party vendors who supplied code to them. Each had their own set of coding standards and beliefs about the application's responsibilities. Including us ;-) We looked at building an abstract framework that all the code would reference. The trick was to make everything extremely generic so that it could be 100% transparent to the developers. This meant pushing code out of base classes and into an abstract version in the framework, or out of the project entirely and into HttpModules. The abstract framework tied to concrete providers, as in the msdn articles above, and allow third party vendors to write and use their own providers for their own environments, and allowed the client to use the same application-framework for multiple small-to-large size applications with different configured providers. I've found these links useful, so I'm posting them here so that I can find them when I need them, but if you stumble upon them, maybe you'll find them useful.

submit to reddit

Wednesday, December 22, 2004

Some handy registry hacks

I found this handy link on Dave Wanta's blog regarding registry hacks. I especially love the command line autocomplete registry hack. I've recently adopted Subversion as my primary version control system, and have found that the command line is the best and only way to go. Having autocomplete at the command line is extremely handy.

submit to reddit

Sunday, November 07, 2004

Relationship between Page and Page Directives

Another adventure into .net... The other day I had a problem where I wanted to create a property for a page, and have it configurable within the designer. To me, this seemed like it should be fairly easy to do, after all I can create a public property for a user control, and then set it in the designer like an attribute:

 < ... runat="server" property="value"/> 
But you can't simply add a custom value in the page directives:
<%@ Page Language="C#" MyPageProperty="value" %>
... you'll end up with an error. So it got me thinking, what is the relationship between Page and Page Directives, and why do I get this error? Enter Reflector. I've grown quite fond of this utility, and I've found it indispensible for inspecting code. Reflector allows you to crack open the internals of the core .net framework, and examine how Microsoft put the framework together. The best place to start is at the machine.config. It tells us that all requests for aspx pages are piped through the PageHandlerFactory. Using Reflector, we quickly see that the factory's main job is to create the handler and pass it off for execution (though we don't really need Reflector to tell us that, if you're familiar with design patterns -- that is what a Factory does). The actual creation of the handler is performed by the PageParser class, specifically the GetCompiledPageInstance method Looking at the PageParser through Reflector is like a good mystery novel, as it's quite complex, and uses a quite a variety of other classes to perform it's job. Most .net developers understand that the first time a page is requested, the asp.net worker process compiles the page and caches it so that future requests will take less time -- this is accomplished through the PageParser. As it turns out, the directives for the Page are used as settings for the PageParser, they get converted using Regular Expressions into a Dictionary object, then processed through the PageParser's ProcessMainDirective method. As the items in the directive are parsed, they are popped off the Dictionary. Afterwards, the CheckUnknownDirectiveAttributes method inspects the size of the Dictionary, and if it contains values it throws the error. If you're still reading, very good, or if you got excited and tried to create a derived class of the PageParser you've already discovered that all of these methods I've described are marked as internal and cannot be overridden. Few things this inspires me to do: 1) Hunt down and find all dependencies to the PageParser, decompile and create a custom PageHandlerFactory. 2) Take a peek at the ASP.NET 2.0 model and see how this has changed.

submit to reddit

Thursday, November 04, 2004

ASP.NET Applications without Web Projects

Super handy link: this article shows you how to hack Visual Studio's config files and projects so that you can create web projects as class libraries. Although Fritz Onion gives a pretty good overview, you still need to rummage through the config files. For your convienence, here is the contents of my config file: C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\ CSharpProjectItems\LocalProjectItems:

UI{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#233610
Code{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#233820
Data{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#233430
Web{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#233740
Utility{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#233550
Resources{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#236260
..\CSharpAddWebFormWiz.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#223610#2263{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45340WebForm.aspx
..\CSharpAddWebServiceWiz.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#224320#2266{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45210Service.asmx
..\CSharpAddMobileWebFormWiz.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#239825#2399{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45710MobileWebForm.aspx
..\CSharpAddWinFormWiz.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#223710#2264{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45350Form.cs
..\CSharpAddClassWiz.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#224520#2262{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45150Class.cs
..\CSharpAddComponentWiz.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#224630#2265{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45170Component.cs
..\CSharpAddWebDataForm.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#238045#2381{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45590DataForm.aspx
..\CSharpAddUserControlWiz.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#229540#2296{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45320UserControl.cs
..\CSharpAddWinDataForm.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#238045#2382{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45600DataForm.cs
..\CSharpAddDataSet.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#227750#2278{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45220Dataset.xsd
..\NewXMLPage.xml{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#224460#2279{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45240XMLFile.xml
..\CSharpAddXMLSchema.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#227570#2276{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45230XMLSchema.xsd
..\NewCSharpFile.cs{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#226180#2267{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45160CodeFile.cs
..\CSharpAddCustomControlWiz.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#229390#2294{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45270CustomControl.cs
..\NewHTMPage.htm{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2042100#2280{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45280HTMLPage.htm
..\CSFormInheritanceWizard.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2341110#2343{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}4531 Form.cs
..\CSharpAddWebControlWiz.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2297120#2298{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45330WebCustomControl.cs
..\CSControlInheritanceWizard.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2342130#2344{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}4530 UserControl.cs
..\CSharpAddWinServiceWiz.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2291140#2292{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45200Service.cs
..\CSharpAddWebUserControl.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#237855#2379{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45580WebUserControl.ascx
..\CSharpAddMobileWebUserControl.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#240056#2401{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45720MobileWebUserControl.ascx
..\StaticDisco.disco{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2393155#2394{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45250Disco.disco
..\CSharpAddGlobalASAX.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2304160#2305{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45428192Global.asax
..\CSharpAddWebConfig.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2312170#2313{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45458192Web.config
..\TextFile.txt{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2316150#2317{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45360TextFile.txt
..\Frameset.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2241160#2284{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45410Frameset.htm
..\XSLTFile.xslt{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2356170#2357{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}4566 XSLTFile.xslt
..\StyleSheet.css{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2314180#2315{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45440StyleSheet.css
..\NewInstaller.vsz{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2269190#2270{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45180Installer.cs
..\Bitmap.bmp{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2384200#2385{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}4562 Bitmap.bmp
..\Cursor.cur{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2386210#2387{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}4563 Cursor.cur
..\Icon.ico{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2388220#2389{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}4564 Icon.ico
..\Resource.resx{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2390230#2391{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}4565 Resource.resx
..\JScript.js{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2306240#2307{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45570JScript.js
..\VBScript.vbs{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2308250#2309{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45370VBScript.vbs
..\WindowsScriptHost.wsf{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2310260#2311{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}45380WinScript.wsf
..\app.config{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}#2403261#2404{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}46000App.config
Once you've made this hack, you'll be able to add webforms (and other web project items) to your class library. I find it's very useful to configure your new hybrid projects this way:
  1. Create a "Web" folder in your project
  2. Configure the Output Path of your project to build to "Web\bin"
  3. In the debug settings, set Enable ASP.NET Debugging to "True"
  4. Change the Debug Mode of the project to "URL"
  5. Set the Start URL to the name of the local IIS site that this code will execute under.
  6. Add System.Web.dll as a reference to your project.
  7. Create the new site in IIS.

Enjoy!

I've got a few other Visual Studio tweaks that I hope to post soon.

submit to reddit