Navigation

Search

Categories

On this page

Instantiating Proxy for Out-of-Process ServicedComponent/Managed COM+ Component
Preserving ViewState in ASP.NET CompositeControl
Migrating Visual Source Safe to TFS Source Control
Migrating ASP.NET App with AjaxControlToolkit from .NET Framework 2.0 to 3.5
Manually Binding ASP.NET TreeView to XML Data From SQL Server
Visual Studio 2008 Deployment Project: Custom Actions and File Upgrade Flows Have Changed
ASP.NET IHttpAsyncHandler vs IHttpHandler
Microsoft Azure Critique/Review
Why Managed Windows Services Hog Memory and Eventually Crash
Thousands Separator When Formatting Numeric String in .NET (C#, VB.NET) Programming
AD Groups Must Have "Global" Scope to be handled properly by WSS and Reporting Services in TFS
Download WebService Studio 2.0
VS 2008: Windows SDK 6.0 Needed for WCF "Service Configuration Editor" Utility
How Windows Performance Counters of "Average" Types Linked to Their Bases
Where Are the Third-Party ASP.NET Theme/Skin Galleries?
Microsoft "Acropolis" six years too late. I liked CCmdTarget of MFC back in nineties.
.NET API for Programmatic MP3 Tag (ID3v1 and ID3v2) Access and Modifications
MSI-based setup packages custom actions made in Visual Studio may not work correctly in upgrade mode

Archive

Blogroll

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

RSS 2.0 | Atom 1.0 | CDF

Send mail to the author(s) E-mail

Total Posts: 71
This Year: 2
This Month: 2
This Week: 1
Comments: 32

Sign In
Pick a theme:

 Sunday, July 25, 2010
Sunday, July 25, 2010 3:23:48 PM (Eastern Standard Time, UTC-05:00) (  |  |  )

It appears that a client proxy instance for and out-of-process ServicedComponent expects the client to have a compile-time reference to the ServicedComponent assembly, unless ServicedComponent is either of different CLR version or has different bitness (either client or server is x64 while another one is x86). This creates a bizarre problem: if the client has no design-time reference to the ServicedComponent, Activator.CreateInstance(compoentClsID) fails if the component is of the same bitness and CLR version with a cryptic "Cannot load type" RemotingException, while working perfectly fine when ServicedComponent is of different bitness or compiled targeting different .NET Framework version. Google offered no insight, so I started thinking of why matching CLR and bitness would lead to failure. I started suspecting that when client and server have mismatched CLR/bitness attributes, runtime must be doing cross-process marshalling in somewhat different manner than when attributes match. Now what I needed is to ensure that same "deep proxying" is taking place when client and server have matching CLR/bitness. On the hunch I decided to use overloaded Type.GetTypeFromCLSID(clsID, "localhost"), and lo and behold, it worked! Now I have a client that at design time is only aware of ServicedComponent's interface, but does not hold a direct reference to it, and yet it is able to talk to multiple out-of-process ServicedComponent implementing same interface while having different CLR and bitness attributes. At the end of the day it turned out to be possible to instantiate ServicedComponent while knowing neither its CLSID at the design time nor having a hard reference to the assembly implementing ServicedComponent.

Comments [0] | | # 
 Friday, July 16, 2010
Friday, July 16, 2010 10:33:00 AM (Eastern Standard Time, UTC-05:00) (  |  |  )

I thought I knew ASP.NET. I started ASP.NET programming in 2001, and I started it with customer/server control development and was cranking them out with no problem. So theoretically I should not have struggled for six hours with a CompositeControl not being able to save nested controls' state between postbacks.

All it came down to is calling EnsureChildControls() from the OnInit().

[ToolboxData("<{0}:BogusCustomControl runat=server></{0}:BogusCustomControl>")]
public class BogusCustomControl : CompositeControl
{
   
Button btn = new Button();

    protected override void OnInit(EventArgs e)
    {
       
base.OnInit(e);

        this.EnsureChildControls(); // << This is it! This makes ViewState work for a CompositeControl
    }

    protected override void OnLoad(EventArgs e)
    {
        if (!this.Page.IsPostBack)
        {
            // Set value once to test whether it's preserved between postbacks
           
this.btn.Text = DateTime.Now.ToLongTimeString();
        }

        base.OnLoad(e);
    }

    protected override void CreateChildControls()
    {
       
this.btn.ID = "whatever";
       
this.Controls.Add(this.btn);

        base.CreateChildControls();
    }
}

 

Comments [0] | | # 
 Wednesday, May 06, 2009
Wednesday, May 06, 2009 9:40:59 PM (Eastern Standard Time, UTC-05:00) (  |  |  )

After spending a day and a half on migrating Visual Source Safe (VSS) to Microsoft Team Foundation Server (TFS) source control, I want to share a few points that may save somebody a little bit of time.

Migration process consists of two phases: a) migrating data from VSS to TFS, and b) switching Visual Studio projects' source control bindings from VSS to TFS.

Data migration is done more or less the way Microsoft describes it: analyze, map users, and finally, migrate data. This part of the process didn't go as smooth as it could because my VSS data lived on a machine that is not a member of the domain, while TFS database lives on a domain computer. Unfortunately I wasted a lot of time before I found that out: after all, "analyze" step worked leading me to believe that migration itself will be possible, but in the end security problems didn't allow data migration. So here's the time saver hint #1: copy your VSS data (a folder with the srcsafe.ini file) to the domain computer where migration process will take place. Also, please keep in mind that the machine where you will run migration utility should:
- Have SQL Server or SQL Server Express installed;
- Have Visual Source Safe 2005 installed;
- Have Visual Studio 2008 installed. This one is important. MS says it's enough to have only Team Explorer for the migration process, but that's not quite correct: Team Explorer package of the VS does not contain "Visual Studio 2008 Command Prompt" BAT file necessary for the process. It's possible to work around it and create your own BAT file that sets all the paths properly, but it will take time. Running migration on the machine with the real Visual Studio is a time saver tip #2.
Once these requirements are observed, data migration problems are limited to the tedium of mapping VSS folders to TFS folders - if you want to consolidate and re-organize projects while moving them to TFS. If your VSS structure was OK as is, then you can simply move VSS to TFS structure without changing it.

Switching Visual Studio projects' source control bindings is no less a time sucker than data migration. This part should be done at one of the developers' machines, with Visual Studio 2008 with Team Explorer installed and projects that are being switched over from VSS to TFS already present as local files. 
Here's a high-level sequence of steps required for changing source control bindings:
- Open a solution bound to the VSS in the Visual Studio.
- Select the solution in the Solution Explorer, and then do File | Source Control | Change Source Control, then select all items in the list and hit Unbind button.
- Select Tools | Options | Source Control and then select Team Foundation Server from the list. Hit OK to close the dialog.
- Use Team Explorer to open TFS source control window, and there use Workspaces drop-down list to select "Workspaces..." item and update mappings of your local file folders to TFS folders for this machine's workspace.
- Once done adjusting TFS to local folders mappings, select solution in the Solution Explorer and do File | Source Control | Change Source Control again. Now select all items in the dialog and hit Bind button. If all projects got "Valid" status next to them, it means your TFS-to-local-folders mappings are done correctly. If some project bindings are Invalid, find where these project folders are located on your file system and map them to corresponding TFS folders (see previous step) in your workspace. After that try to re-bind your projects to TFS source control again. Once you got all your projects in the Valid state, click OK to close the window, and at this point you are likely to get a nagging message from VS telling that you need to get latest version from TFS. Accept defaults.
- Get latest version for the solution. Project files are likely to need manual conflict resolution. I don't know why it's considered to be a conflict when it's just a change to the project files reflecting new source control bindings. Choose default type of resolution - Overwrite.
- After this Visual Studio may revert some projects to unbound state - leaving them off them source control. All you need to do is to, again, bind your projects. This time binding process offers to do regular Check Out for project files in question. Accept defaults and in the end you should end up with the solution that has a solution file and maybe some project files checked out, but otherwise the solution should be bound to the TFS now.
- Test-build the solution, and if everything is alright, check in modified solution and project files.

If this list seems convoluted - that's because the process of re-binding from VSS to TFS itself is incredibly awkward. Imagine making up this list of steps by trial and error. Hopefully using this list, as much pain as it is, will save you some time.

Comments [0] | | # 
 Wednesday, March 11, 2009
Wednesday, March 11, 2009 5:09:40 PM (Eastern Standard Time, UTC-05:00) (  |  |  )

It all started with a need to use TimeZoneInfo class of .NET Framework 3.5. My ASP.NET application, however, was of ASP.NET 2.0, even though I develop it in Visual Studio 2008. Since moving from ASP.NET 2.0 to 3.5 should be pretty easy, I promptly switched target .NET Framework to 3.5 in the project settings, recompiled and ran it without a problem - at first. Suddenly, one page failed with "control with id 'whatever' requires a ScriptManager on the page", while ScriptManager was already there - set at the master page level. Googling provided solutions that didn't work.

What it turned out to be is that my old AjaxControlToolkit version was apparently incompatible with .NET Framework 3.5. Once I got Ajax Control Toolkit made specifically for ASP.NET 3.5, my pages started working properly.

Comments [0] | | # 
 Sunday, January 18, 2009
Sunday, January 18, 2009 10:57:38 AM (Eastern Standard Time, UTC-05:00) (  |  |  )

I had this seemingly simple task yesterday: populate TreeView control (ASP.NET) with data from SQL Server 2005. The query returning data from SQL Server was spanning three joined tables with one-to-many relations, returning the denormalized set of records that has some redundancy due to information from parent tables replicated in each record (like having Company and Department information in each Employee record). Populating hierarchical TreeView from flat recordset is pretty inconvenient, so I decided to give a shot to the XML output feature of SQL Server 2005.

Converting flat denormalized result set output into hierarchical XML was as easy as adding "FOR XML AUTO" to the end of my query. I ran the query and the output was exactly what I wanted - no redundancy at all, with child records neatly nested inside the parent table XML nodes (for proper record nesting be sure to use correct order of the selected columns: parent table columns should precede child table columns in the T-SQL SELECT statement). Now I needed to bring this result back from SQL to the business tier of my C# application. I extensively use DataSets with the the convenience of built-in data-access methods created using DataSet Editor's query wizards. I right-clicked an appropriate table adapter and went through the "Add Query..." wizard, mapping my new stored procedure returning XML to the new data access method. At the end of the wizard "Tabular Data" option for the query result type was understandably grayed, because XML is hierarchical, not tabular. But generated method turned out to return Object type, and you would never know it unless you looked at the generated source code - properties of the generated methods do now show the return type of the method. I test-ran the method and it turned out to return what you would expect - XML text with all the data.

One could stop digging right here and simply use XmlDocument and XPath to parse out and the walk the XML, populating tree nodes in the process, but I wanted to turn XML data into a strongly-typed hierarchical structure instead of walking XML nodes. In .NET all you need to turn XML into a strongly-typed structure is XML schema. A nice little utility called xsd.exe converts XSD schemas into C# or VB.NET classes, which are XML-serializable. SQL Server 2005 is sweet enough to generate schema, along with bringing the XML data. I temporarily modified my query to have "FOR XML AUTO, XMLSCHEMA" appendix, ran it in SQL Server and got the XML preceded by the schema. I copied schema over to an XSD file and undid the change to the query, reverting the appendix back to "FOR XML AUTO".

Now, having created XSD schema defining XML structure returned by my query, all I needed to do is to feed the XSD file to XSD.exe utility to produce C# file with classes corresponding to m XML nodes, right? Almost. This was the part where things stopped going smoothly. First, xsd.exe complained, as it always does, that it could not find sqltypes.xsd schema, which is imported by the schema produced by SQL server. If you ever used either xsd.exe or wsdl.exe against schemas that import other files, you probably know that all the imported files need to be downloaded and saved locally, and in the case of xsd.exe, all imported schemas should be explicitly listed in the command line. Here's the example of xsd.exe command line (run Visual Studio Command Prompt to load command prompt window with all environment variables set):
   xsd.exe yourschema.xsd sqltypes.xsd /classes /namespace:WhateverIsYourNamespace
This command will produce yourschema_sqltypes.cs file with strongl-typed C# classes wrapped into the WhateverIsYourNamspace namespace. If you expect to simply include this file into your project and rejoice at this point, not so fast. Although the code will compile, at run time using XmlSerializer.Deserialize() produced odd results - it complained that my root element with xmlns='' attribute was not expected. That was because schema got the default name when it was generated by SQL Server, while actual XML data did not reference the schema at all. To fix that I had to remove XmlTypeAttribute and XmlRootAttribute attributes from declarations of the generated classes. I had to keep XmlRoot attribute for the class representing root node, by its declaration was simplified to look like [System.Xml.Serialization.XmlRootAttribute(IsNullable = false)].

Once that part was done, deserialization of XML into strongly-typed hierarchical data structure started working just fine.

One more quick note: when .NET classes are generated from XSD schema, nullable data fields get done in a slightly different way compared to the DataSet. In the dataset, you'll get "type<Nullable>" or "type?" declaration for nullable data columns of value types (int, DateTime, etc). .NET classes generated by xsd.exe won't have nullable fields. Instead they will have extra boolean fields telling whether the field is nullable. For example, if your SQL data table has nullable BirthDate, the C# class produced by xsd.exe will have boolean BirthDateSpecified property, which does what BirthDate.HasValue would do in the dataset.

The whole process was not terribly smooth, but also not prohibitively burdensome. Of course, much better design would be if Microsoft allowed to specify XML schema for the TreeView control, and then to let data-bind the TreeView control to a SQL query or an object method that returns XML. This way it would be still strongly-typed, but the whole business of building XML-serializable C# classes out of the schema would not be necessary. This approach would also allow design-time definition of TreeView node formatting, just like GridView does it with strongly-typed datasets. Another useful feature would be automatic generation of C# classes - just like generation of strongly-typed datasets - for data-access methods returning XML.


 

Comments [0] | | # 
 Tuesday, December 02, 2008
Tuesday, December 02, 2008 4:13:32 PM (Eastern Standard Time, UTC-05:00) (  |  |  |  )

Updated on 7/25/2009.

Visual Studio setup project produced MSI packages with problems for a while now. It looks like with introduction of Visual Studio 2008, setup project added a couple of new twists, compared to VS'05:
1. Order in which custom actions are called has changed.
2. When installation is an upgrade, old binary files (EXE and DLL) will only be replaced by new ones if new binaries have higher file version.

Here's what used to happen. MSIs produced by VS 2005 had the intuitive order:
- Uninstall step from the old installer version.
- Install and Commit steps from the new installer version.

In other words, installing an MSI created by VS 2005 was a very rough equivalent of uninstalling the old version, followed by installing the new one. VS 2008 order has become a bit more complex, arguably smarter, but also less intuitive.

MSIs produced by VS 2008 seem to have this new order:
- Install step from new installer version.
- Commit step from the new installer version.
Note that Uninstall step of old version installation is not called at all during an upgrade-installation of an MSI generated by VS'08 (with RemoveOldVersion set to True). Funny, but even though Uninstall steps does not seem to be invoked, the custom action assembly of previous version is still getting loaded, which may lead to installation crash if old version uses .NET Framework 1.x. Custom action order change is the biggest departure from VS'05 MSIs. Also, only files that have been changed in the new version of the installed package, will be replaced in the upgrade mode, while unchanged files will remain.

Also, upgrade flow of the MSIs generated by Visual Studio 2008 also replaces binary files only if their FileVersion property has changed. Since this was not a requirement in Visual Studio 2005, you may want to go through your AssemblyInfo.cs files and ensure that they either have the wildcard in their version name ([assembly: AssemblyVersion("1.0.*")]), or you manually increment version before releasing a new build. If AssemblyVersion and FileVersion are in sync, you can remove FileVersion attribute from the AssemblyInfo.cs.

You are most likely going to experience the effect of these changes after porting your Visual Studio 2005 windows service installer project to Visual Studio 2008, and getting the "Error 1001. The specified service already exists." error. Explaining this all would make this rather a long story, but the gist of it is this: ServiceInstaller's Install() method attempts to register the service even if service is already registered, which is the case in the upgrade service installation (remember, Uninstall step, which unregisters a service is not called anymore). ServiceInstaller's Install() throws the above-mentioned exception if service is already registered.

To successfully upgrade a Windows Service, it needs to be stopped before files can be replaced. If service was not stopped and files are replaced - target system is likely to be required to reboot at the end of the installation process. Stopping service during upgrade installation from Install custom action will be too late - at this point files are already replaced and reboot is imminent. You see what happens here: it appears that upgrade installation of a windows service created in Visual Studio 2008 will *always* lead to rebooting the target machine. Given the fact that windows services are very often created for server applications deployed on high-availability machines, it's seems that windows service installation done by the book in Visual Studio 2008 is all but useless.

Here are two solutions.

Solution #1 (for Windows Service Installers): Make your MSI act the old (VS'05) way

Keep your old custom steps and do this. It was a pain to copy the script to clipboard from IE. I had to do View | Source to copy & paste the script. Also, if you save the MSI_SetActionSequence.js file in the solution folder, your post-build event command will be exactly this:
cscript.exe "$(ProjectDir)..\MSI_SetActionSequence.js" "$(BuiltOuputPath)" InstallExecuteSequence RemoveExistingProducts 1525
(Path to MSI_SetActionSequence.js may vary.)

Solution #2 - Update your VS'05 custom actions code to comply with new (VS'08) way

When registering a service, two things need to be done differently compared to how you did it in Visual Studio 2005 setup project:
   1. Invoke Install step only for clean (non-upgrade) installation.
   2. Commit step needs to restart the service in the case of upgrade installation.

Here's a bit more details and a few snippets that should help.

1. First, in your setup project, select Install custom action of the service installer, and set its Condition value to NOT PREVIOUSVERSIONSINSTALLED. This will eliminate calling ServiceInstaller's Install() custom action for upgrade installation.

2. Select Commit action and set its CustomActionData value to /OldProductCode="[PREVIOUSVERSIONSINSTALLED]". This will pass the ProductCode of the old version to the Commit custom action - if it's an upgrade installation, and blank string if it's a new installation. You can use it in the Commit() code to determine whether it's an upgrade installation and restart the service:


private
bool IsUpgrade
{
   
get
   
{
      
return !string.IsNullOrEmpty(this.Context.Parameters["OldProductCode"]);
   }
}

public override void Commit(IDictionary savedState)
{
   
base.Commit (savedState);

   
if (this.IsUpgrade)
   {
      
this.StopService(); // Implement your StopService() method
   }

   this.StartService();  // Implement your StartService() method
}


Making VS'05-generated Installers Act Like VS'08-made

Despite breaking windows services installers, changes to the installation process introduced by Visual Studio 2008 do benefit other types of installations, because being able to tell whether it's an upgrade installation and make the installer act accordingly is quite valuable. Developers of Visual Studio 2005 can have the same functionality if they modify their final MSI by running this PostBuildEvent command in your Setup project:
cscript.exe "$(ProjectDir)..\MSI_SetActionSequence.js" "$(BuiltOuputPath)" InstallExecuteSequence RemoveExistingProducts 6650
(Path to MSI_SetActionSequence.js may vary.) 

If you go this route, then you likely will need to use a pattern shown from the "Solution #2" shown above:
-
Install custom step should be called on the NOT PREVIOUSVERSIONSINSTALLED condition.
- And to
tell whether your code runs in the upgrade mode, Commit custom steps should have /OldProductCode="[PREVIOUSVERSIONSINSTALLED]" parameter passed to it so Commit() implementation could use this.IsUpgrade property as shown above.

Comments [0] | | # 
 Monday, November 10, 2008
Monday, November 10, 2008 3:52:23 PM (Eastern Standard Time, UTC-05:00) (  |  |  |  )

Since I use ASP.NET HTTP handlers quite often, I decided to figure out what advantages IHttpAsyncHandler has compared with IHttpHandler. As I looked at many people's claim that IHttpAsyncHandler somehow magically improves performance by shifting request processing on to another thread, I realized that MSDN documentation of IHttpAsyncHandler is laking in a fundamental way: it fails to mention that simply moving request rendering onto another thread does not yield any benefit.

When you simply move your request handling logic from the original ASP.NET thread to your own thread (IHttpAsyncHandler), you gain exactly nothing because both threads come from the same pool. The benefit of IHttpAsyncHandler comes in only when your request processing thread is blocking, waiting for another thread. For example, if your request processing calls a web service, your request processing thread will wait for the IO completion happening on another thread - where request is sent to the web service. In this case there will be two threads involved: your request processing thread, and the IO thread where outgoing web service request is being executed. This is the situation where IHttpAsyncHandler can help: instead of holding on to the request processing thread while waiting for the outgoing request to complete, one can release the original request processing thread back to the pool, and finish response rendering on the IO thread after the web service request has completed. This way, instead of holding two threads for the duration of the relatively long-running process of invoking a web service, your request processing is using only one thread.

So, the bottom line is this:
- You should only concert yourself with IHttpAsyncHandler if you are running out of request processing threads.
- If you do, check your logic for whether it's waiting for an IO completion (or is using other threads for other reasons), and only if that's the case, switch to IHttpAsyncHandler.
- Otherwise simply increase the size of the ASP.NET thread pool in the web.config and stick with good ole' IHttpHandler.

Comments [0] | | # 
 Friday, November 07, 2008
Friday, November 07, 2008 9:53:48 AM (Eastern Standard Time, UTC-05:00) (  |  |  |  )

Microsoft marketing folks are incorrigible. When explaining new technology they invariably fail to make anything any more clear. Case in point: Microsoft Azure. Parsing through the Azure site left an impression that MS don't really want anyone to find out what in the world they are really doing.

For those who don't have time to filter through MS marketing noise, consider reading this very concise, pretty funny even if somewhat crude-worded Windows Azure review:
http://www.theregister.co.uk/2008/11/03/dziuba_azure/print.html

Update: This white paper penned by David Chappell is most to-the-point Azure doc so far.

Update 2: Azure pricing information.

Comments [0] | | # 
 Wednesday, October 29, 2008
Wednesday, October 29, 2008 9:52:59 PM (Eastern Standard Time, UTC-05:00) (  |  |  |  )

It's very easy to write a windows service using C# or VB.NET. Easy to write, easy to install, but for a price.

It's an often overlooked fact, but in .NET runtime, Garbage Collector does not merge together freed memory chunks, if they are larger than 85K. What does it mean? It means that if your managed windows service allocates and frees buffers larger than 85K on a continuous basis, your service will crash because it will eventually run of memory due to Large Object Heap (LOH) fragmentation. Again, it will only happen if your managed windows service allocates objects of 84,000+ (give or take) byte, but IT WILL HAPPEN!

There are workarounds, somewhat expensive, like wrapping your service logic in COM+ server-activated process, which can be set up to recycle - just like IIS AppPools are recycled. Or one could create a proprietary memory manager with a pool of large buffers, making of which, of course, would be kind of ironic since the whole point of having garbage-collected memory manager was to eliminate hassles of memory management.

Anyway, the purpose of this post is to raise awareness among fellow windows service developers. If your service is high-throughput, high memory usage, it will go down in flames even if your code is perfect. The choices are: a) ensure all your memory allocations do not take more than 84K, b) implement your own memory manager, or c) implement worker process recycling.

Good luck to all of us.

Comments [0] | | # 
 Saturday, July 26, 2008
Saturday, July 26, 2008 10:18:35 AM (Eastern Standard Time, UTC-05:00) (  |  |  |  )

It's much easier to read large numbers when thousands are separated by commas. But I can never remember how the numeric format with thousands comma-separated is defined for .NET String.Format() method and for the databinding. So more as a note to self, here it is:

string output = string.Format("{0:#,#}", 123456789); // Will produce 123,456,789

The same goes for data binding data sources to data controls like DataGridView. Specify format as "{0:#,#}".

Comments [0] | | # 
 Friday, July 11, 2008
Friday, July 11, 2008 4:22:14 PM (Eastern Standard Time, UTC-05:00) (  |  |  )

I went through the exercise of setting up Microsoft Team Foundation Server 2008, and needed to do group-level-only rights assignment, so that IT folks could manage security by simply moving people in and out of the Active Directory groups to grant/revoke TFS access rights, instead of setting up individual user rights in TFS, Windows Sharepoint Services and Reporting Services. Initially I created some groups for TFS with the "Domain local" scope, which allowed me to nest other, "Global", groups in them. But I noticed that with WSS and RS, assigning rights to "Domain local" groups does nothing - WSS and RS act as users are not members of the group, while TFS services were working properly. I had to re-create AD groups and make them of "Global" scope to make WSS and RS working properly.

Comments [0] | | # 
 Monday, May 19, 2008
Monday, May 19, 2008 9:32:03 AM (Eastern Standard Time, UTC-05:00) (  |  |  )

WebService Studio 2.0 (a.k.a. Web Service Studio) is a quick & dirty web service client tool that can import your web service's WSDL and allow you to call web service's methods without having to create your own test client.

WebService Studio used to be hosted on Microsoft's GotDotNet web site, but ever since GotDotNet was replaced by Codeplex, Web Service Studio was nowhere to be found. Fortunately, some kind stranger made WSS available for download at his blog: http://mattharrah.com/blog/web-tools/net-web-service-studio-20/.

Update: BTW, if you are planning to use WebService Studio to test WCF web services, you will need to configure your web service to use basicHttpBinding instead of wsHttpBinding.

Comments [0] | | # 
 Thursday, May 15, 2008
Thursday, May 15, 2008 3:53:38 PM (Eastern Standard Time, UTC-05:00) (  |  |  |  )

After installing Visual Studio 2008 on a new machine and starting playing with a simple Windows Communication Foundation project, I attempted to change service's WCF settings using WCF Service Configuration Editor utility (SvcConfigEditor.exe). However, I got the "Windows SDK is not installed correctly" error. "Internets" were surprisingly mum on the subject, so I had to figure out the solution myself.

To fix the problem, I had to install Windows SDK 6.0 manually. After I did that, the problem went away. Just quit Visual Studio 2008 before installing Windows SDK.

Update: Even after reinstalling Windows SDK, first time right-clicking on the web.config in the Visual Studio '08 Solution Explorer does not bring "Edit WCF Configuration" item to the menu. However, after I did Tools | "WCF Service Configuration Editor", "Edit WCF Configuration" item started showing up upon right-clicking the .config file.

Comments [0] | | # 
 Thursday, March 20, 2008
Thursday, March 20, 2008 5:04:21 PM (Eastern Standard Time, UTC-05:00) (  |  |  |  )

Some time ago I added performance counters to the application I was working on, and for some inexplicable reason all counters of "Average" type, like AverageCount64 or AverageTimer32, didn't work at all, always having 0 value. Then I had no time to find out why it was not working, but today I did. As you may know, "Average" counters are made of two distinct counters: the base counter and the average counter itself. The mystery was that by looking at all the samples returned by Google, it was unclear how the Base and the Average itself are linked together. It looked like you create the Base and the Average, add them to the collection and somehow magically Windows figures they need to be linked together when averages are calculated. After some research it looks like the two are linked by counter name! It appears that base's name should be the name of real counter, plus word " base". For example, when you define your counter category that has average performance counter, you do something like this:

   counters.Add(
new CounterCreationData("whatever", "whatever desc", PerformanceCounterType.AverageCount64));
   counters.Add(new CounterCreationData("whatever base", "whatever base desc", PerformanceCounterType.AverageBase));

To my surprise, changing the "whatever basevalue of the counter name in both CounterCreationData and PerformanceCounter to something like "whatever base1" breaks the perf counter! It looks like there is a naming convention requiring that AverageBase proformance counter has the CounterName property value on both CounterCreationData and PerformanceCounter to be counter name plus " base", but I never saw this mentioned anywhere - neither by MSDN, nor by Codeproject articles. So, since average perf counters always come in pairs, linked by name, these helpers should make creating average perf counters simpler (uinsg C#/.NET):

        private static void AddAverageCounterDefinition(CounterCreationDataCollection counters,

                        string counterName, string counterDescription, PerformanceCounterType averageType)

        {

            counters.Add(new CounterCreationData(counterName, counterDescription, averageType));

            counters.Add(new CounterCreationData(counterName + " base", string.Empty, PerformanceCounterType.AverageBase));

        }

 

        public class AveragePerfCounter

        {

            private PerformanceCounter averageCounter;

            private PerformanceCounter averageCounterBase;

 

            public AveragePerfCounter(string categoryName, string counterName)

            {

                this.averageCounter = new PerformanceCounter(categoryName, counterName, false);

                this.averageCounterBase = new PerformanceCounter(categoryName, counterName + " base", false);

            }

 

            public void IncrementBy(long val)

            {

                this.averageCounter.IncrementBy(val);

                this.averageCounterBase.Increment();

            }

        }

 

After this, when creating performance counter definition, you could use following code instead of the one shown by the very first snippet:
      AddAverageCounterDefinition(counters, "whatever", "whatever desc", PerformanceCounterType.AverageCount64);
It will add " base" to the name of the sidekick automatically.

And to create corresponding performance counter, you now can do this:
      AveragePerfCounter avgCount = new AveragePerfCounter("MyCategory", "whatever");
     
avgCount.IncrementBy(new Random().Next(100));

 

Comments [0] | | # 
 Tuesday, February 26, 2008
Tuesday, February 26, 2008 2:33:32 PM (Eastern Standard Time, UTC-05:00) (  |  |  |  )

When ASP.NET 2.0 and Visual Studio 2005 came out I hoped that ASP.NET themes will be developed en masse by third parties and sold like those on TemplateMonster.com. Today, tired of ugly GridViews in my apps, I decided to find an ASP.NET theme for at least a GridView, but to my surprise, the only thing I found was this, which is not even a skin. There are millions of sites, books and blogs telling how to make themes in ASP.NET 2.0, but it looks like market for third-party templates has never materialized. Given how fierce the competition in the graphics & UI design world is, I wonder why everyone is missing a chance to take this niche. Microsoft has a few starter themes, but just a few and without live test-drive sites - one has to download and install Visual Studio plug-ins and build the site to see it in action. All this is very strange: it's hard to believe there is no business model in making skinnable themes for ASP.NET applications.

Comments [0] | | # 
 Tuesday, June 19, 2007
Tuesday, June 19, 2007 9:42:13 AM (Eastern Standard Time, UTC-05:00) (  |  |  |  )

Microsoft is showing off its new Acropolis framework for .NET. It seems to be a little more than good old CCmdTarget of late MFC. 

Back in 2001 when I was making a transition from C++/MFC to C#/.NET two things I missed the most were C++ templates and CCmdTarget/Doc/View architecture of MFC-based Windows UI. I could not believe Microsoft didn't port CCmdTarget at the time and naturally wrote my own. But pretty soon it was obvious that with C# and Visual Studio .NET writing ASP.NET web applications was easier than making Windows UI apps, and people wanted web UI more than windows UI.

Combine dwindling demand for Windows UI with inferior development tools and you end up in the situation where software architects don't even debate whether their next enterprise application should have Windows UI or web UI. It's assumed and understood that it will be a web-based application. If you think an application needs to have Windows UI - you will face an uphill battle convincing other project stakeholders it's the right way to go.

Simply put, Windows UI is so out, and web UI is so in that incremental improvements in Windows UI world like WPF and Acropolis is too little and way too late to save the day. We've got AJAX, thank you very much. In my arrogant opinion enterprise apps will not go back into Windows UI world. The last bastion of Windows UI applications is SOHO market, but that is about to change with HttpVPN making it possible to make easily redistributable web applications for consumers and small businesses. Once that happens, Windows UI will become just gaming and other graphics-heavy applications platform.

Comments [0] | | # 
 Tuesday, February 27, 2007
Tuesday, February 27, 2007 11:37:38 PM (Eastern Standard Time, UTC-05:00) (  |  |  |  )

In attempt to create a less dry than your usual "Hello World" ASP.NET application to showcase UltiDev Cassini Web Server, I decided to write a simple web-based MP3 player application using Maсromedia (now Adobe) Flash. I was very surprised by how long and frustrating was my search for a free .NET-based API allowing programmatic access to ID3 tags in MP3 files from C# and VB.NET. I started working with something I found on Codeproject.com, but that piece turned out to be buggy beyond any degree of practicality. My second sweep across Internet yielded a much better (if only somewhat over-engineered) solution - the UltraID3Lib. Its UltraID3 class is the starting point of the journey. The library worked out for me very well. Thumbs up.

Comments [0] | | # 
 Thursday, February 08, 2007
Thursday, February 08, 2007 11:42:07 PM (Eastern Standard Time, UTC-05:00) (  |  |  |  )

All! If you use Visual Studio 2003 or 2005 to create MSI-based setup packages, here's a good one for you: if your installation uses Uninstall and Install/Commit custom actions implemented as an installer class - you are in trouble. In the process of upgrading your product MSIEXEC.exe first loads an assembly with Uninstall custom action implementation - to complete previous version uninstallation. After that it tries to load installer class of the new version to do Install and/or Commit custom actions of the new version. At this point things can get really bad. If your custom action assembly is not signed/strongly named (and in my experience sometimes even if it is signed) MSIEXEC.EXE will fail to load custom action assembly from the new version and will run Install/Commit custom steps from the old one. This means that if you added new code to your Install/Commit steps it simply won't be executed during upgrade. Even worse: Install/Commit custom actions of the old version will run instead of the new one!

This happens due to completely bizarre, to put it mildly, logic of .NET Assembly.LoadFrom() method. .NET Framework has a rule that after assembly is loaded it can't be unloaded unless it was loaded into a separate AppDomain: appdomains can be unloaded and assemblies can't. Two assemblies may end up looking the same to LoadFrom() if they have the same name even if they are located in different folders or have different versions. So what happens here is this: after MSIEXEC.exe loaded assembly named 'X' to do Uninstall custom step, the subsequent attempt to load assembly named also 'X' from another folder to do Install/Commit step does not happen. But get this: one would expect that if you asked LoadFrom() to load assembly 'X' from folder 'Y' it should either load it or tell you it can't. Instead due to some truly twisted logic, LoadFrom() won't fail if it can't load new 'X' assembly - it will simply return the reference to the one that is already loaded. So much for solving DLL hell problem!

Microsoft knows about the problem since 2004
http://support.microsoft.com/kb/555184/

It didn't, however, fix it yet:
http://support.microsoft.com/kb/906766

They recommend giving unique names to custom action assemblies for each new release. Alternatively they say signing an assembly will make problem go away. I tried signing and in my small test project it made problem go away, but not in the "real" one. I am stuck with having to rename custom action installer assemblies for each release. All Microsoft needed to do is this: force installer to create new appdomain and load old version's Uninstall custom steps assembly there and let it run. After it's done, unload the appdomain and create the new one where you load new version's custom action assembly with Install step implementation. That would make it unnecessary to give assemblies unique names - strong or physical. My understanding is that Visual Studio adds a small shim DLL to the MSI package that loads .NET installer classes from the custom action assemblies. This means they don't even need to wait for another MSI API release to fix it - every new Visual Studio or a even a Service Pack for Visual Studio could have fixed the issue that is still with us more than three years later.

Comments [2] | | #