WELCOME

This blog is where I post about my consulting work with Microsoft Technologies, and other random tidbits that don't fit in my Photo Blog or my Iraq Blog.

Monday, December 15, 2008

Callwave: Crappy Customer Service and Deceptive Pricing

I've been a customer of Callwave a company that provides internet fax, voicemail, audio conferencing and a variety of other services.  I've been paying them something like $10/year for a fax number that receives about 20 faxes a year (mostly junk).

So I don't use it much, but it's a dedicated number that I've had on my business card for something like 10 years.

A couple of months ago I received an email from them saying that my credit card on file had expired.  I clicked on the link, updated my credit card number on a very simple form with no additional information, and promptly forgot about it...

Today I noticed that they had charged me $12.95/month for the past 3 months!  A price increase of about $150/year!!!

Nowhere on the form that I entered my updated credit card information did it mention the fact that they were raising my pricing by something like 1000%!!!

Today I went to their site and discovered that apparently the old plan I was on was gone... I sent them a moderately irate email asking them to cancel my service AND refund the $38.85 that they had deceptively charged me.

A classy company could have looked at my account, seen that I was a long time customer, and reached out to me to try to retain my business... And it might have worked... I LIKE the fax service, but I only use it once or twice a year...

Instead I got a snotty/rude email response from Lenny Sanders a CallWave Customer Care Representative, informing me that they had sent me 2 emails in September explaining the new pricing policy, and that I could CALL THEM ON THE PHONE between sometime and sometime pacific time if I wanted to cancel my account.

They could take the time to make a personal response to my email, but apparently they couldn't be bothered to actually act on it... WTF?

The email they sent me in September had a subject line that started with "New fax features..." and were sorted into a junk folder.  When I went back to read them today it is not at all clear that $12.95/month is the ONLY plan they now offer and they my pricing was going to change.  Even if I had read it I would have no idea that I was signing up for $155 per year if I took no action.

Bottom line: Callwave has exhibited:
  • Deceptive marketing and billing practices.
  • Poor customer service.
And so instead of calling them on the phone tomorrow to cancel my account I am taking the following actions:
  • I am writing this blog post so that future Callwave customers can be informed about the character of the company before they consider doing business with them.
  • I am disputing all past charges with my credit card company (I'm assuming that it will cost Callwave more money to deal with is than it would for them to just refund me the money they deceptively billed me).
  • I will continue to blog about this topic until they refund my money and cancel my account.
And if you don't believe me here are some comments from other "satisfied customers":
UPDATE 12/16/2008:  Received another email from Janet Larson at Callwave "Customer Care" who says that they will be happy to review my refund request if I submit it in writing.  I can email Callwave Customer Care at care-20@callwave.com to request cancellation and a refund. 

But wait...  Janet works in Callwave Customer Care, and Lenny who emailed me yesterday works in Callwave Customer Care... but they can't be bothered to forward my email to themselves... instead they want me to write a new email to a different mailbox in their department?

Well I forwarded the entire thread to the new email address and we'll see what they do with it... I'm not optimistic...

Friday, December 12, 2008

FolderShare to Windows Live Sync update broken on Apple OSX

I'm a long time FolderShare user, and Microsoft just "upgraded" FolderShare to Live Sync. But it turns out the upgrade doesn't work on Macintosh... massive FAIL.

From ScrappyDog
After installing the Live Synch client for OSX and trying to login you get the following message: "This version of Windows Live Sync is too old. Please download a newer version from http://sync.live.com.

But this IS the newest version.

Here is the official word from Microsoft as of yesterday:

We've found an issue that is causing sign-in to take longer than expected
and in some cases the sign-in process times out. We've isolated the problem
to a bug in our Mac client. In order to prevent this issue from affecting
all users we are temporarily blocking the Mac clients from connecting to the
service. Once we have the problem fixed we will allow the Mac clients to
connect to Sync again. We're sorry for the inconvenience.

Thanks,
Francisco

--
Francisco Garcia-Ascanio
Windows Live Sync
Program Manager

Wow... We've isolated a problem and the solution is to block all Apple users to prevent negative impact on the Windows users...

A pretty strong message that Apple is definitely a second class citizen in the Microsoft universe. I'm glad I'm not using FolderShare as part of my business...

Nice launch...

Here is the Microsoft Live Sync discussion forum where you can share the pain with other disgruntled FolderShare users: http://www.microsoft.com/communities/newsgroups/en-us/default.aspx?dg=microsoft.public.windows.live.sync

Monday, December 8, 2008

Configuing VS2008 (or VS2010) and TFS to use Beyond Compare

I've been really enjoying using Visual Studio 2008 and TFS for production development for the first time, but I hate the default file compare tool.

Here is a great blog post with simple instructions on how to configure VS2008 to use Beyond Compare (the best windows file comparison tool):

http://ronaldwidha.net/askbobo/articles/the-best-comparison-tool-and-vs2008-integration/

Friday, November 21, 2008

Geek notes: Windows on Amazon EC2 and Terminal Server (RDP) Client for iPhone

I've been playing with the new Windows Servers in Amazon EC2. Hosted Windows 2003 Servers for $0.125 per hour... fire one up in a couple minutes and terminal server into it for a couple hours, and turn it off until you need it again... very cool!

http://aws.amazon.com/windows/

I've been thinking about setting up a virtual developer workstation this way, as well as development/test servers.

On a somewhat related note: terminal server (RDP) client for iPhone!

http://www.dabcc.com/article.aspx?id=8387

Thursday, November 13, 2008

VS2008 question: How do you make a unit test ONLY run as part of an Ordered Test?

The title of this post pretty much says it all. Visual Studio 2008 supports "Ordered Tests", which are a pretty slick way to run a set of Unit Tests in a set sequence.

Back in the day we had to create one unit test that called the other tests in order to achieve this.

So the new "Ordered Test" is cool except for one problem that I haven't been able to figure out: How do I make a unit test ONLY run as part of an Ordered Test?

The whole point of using the Ordered Test in my case is that I have a bunch of methods with data dependencies that can ONLY be run in a specific order. But I can't figure out how to disable them from running individually... grrr!

I'm sure there is a way to do this, but I haven't been able to figure it out. Your help greatly appreciated!

Friday, August 8, 2008

A little more about me

I had to come up with a bio for an upcoming speaking engagement, and I decided I should have a little more fun with it than the dry weak bit of fluff that I've used in the past... Here it is:

Eric Bowen is a former Microsoft Architect Evangelist and currently a Principal Consultant at ILM Professional Services specializing in enterprise architecture and technical project leadership of complex projects using Microsoft technologies. Eric has been programming for 28 years since he taught himself Z80 assembly language on his family's TRS-80 Model I Level II. And he has still never worn the t-shirt that Bill Gates signed when he won the programming contest at the Microsoft PDC in 2001.

In his spare time Eric works as a photojournalist providing independent coverage of the Minnesota National Guard and the war in Iraq. Eric has made multiple trips to Iraq, and his work has appeared locally on Channel 5 Eyewitness News and MinnPost.Com, as well as nationally on the CBS Evening News and in Parade Magazine. Eric is also a former US Army Paratrooper, and former member of the US National Biathlon Team.

Thursday, July 24, 2008

Implementing a simple HelloWorld service in WCF using Interfaces and generic proxies

I needed a complete end to end demonstration of a .NET service implemented in WCF and taking advantage of Interface based contracts and generic proxies. I found lots of bits and pieces on the web, but I thought I would post my complete demo here in case somebody else finds value in it...

First the interface that defines our service contract:


using System;

using System.Collections.Generic;

using System.Runtime.Serialization;

using System.ServiceModel;

using System.Text;

namespace SandBox.SharedClassLibrary

{

[ServiceContract]

public interface IHelloWorld

{

[OperationContract]

string GetHelloWorld(string Request);

}

}



The service itself (note in this case we are hosting the service in a ASP.NET project on IIS 7.0):


using System;

using System.Collections.Generic;

using System.Runtime.Serialization;

using System.ServiceModel;

using System.Text;

using SandBox.SharedClassLibrary;

namespace SandBoxWeb

{

public class HelloWorldService : IHelloWorld

{

public string GetHelloWorld(string Request)

{

return Request + DateTime.Now.ToShortTimeString();

}

}

}



The web.config for the service:


<system.serviceModel>

<behaviors>

<serviceBehaviors>

<behavior name="SandBoxWeb.Service1Behavior">

<serviceMetadata httpGetEnabled="true"/>

<serviceDebug includeExceptionDetailInFaults="false"/>

</behavior>

</serviceBehaviors>

</behaviors>

<services>

<service behaviorConfiguration="SandBoxWeb.Service1Behavior" name="SandBoxWeb.HelloWorldService">

<endpoint address="" binding="wsHttpBinding" contract="SandBox.SharedClassLibrary.IHelloWorld">

<identity>

<dns value="localhost"/>

</identity>

</endpoint>

<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>

</service>

</services>

</system.serviceModel>



The client application which is a console application:

using System;

using System.Collections.Generic;

using System.Text;

using System.Reflection;

using System.ServiceModel;

namespace SandboxConsole

{

class Program

{

static void Main(string[] args)

{

Console.WriteLine(SandBox.SharedClassLibrary.ServiceClient.CallServiceByName("WSHttpBinding_HelloWorldService"));

Console.WriteLine(SandBox.SharedClassLibrary.ServiceClient.CallServiceByAddress("http://localhost:3456/HelloWorldService.svc"));

Console.ReadLine();

}

}

}




public class ServiceClient

{

public static string CallServiceByName(string EndPointName)

{

using (GenericProxy<IHelloWorld> service = new GenericProxy<IHelloWorld>(EndPointName))

{

return service.Channel.GetHelloWorld("Test by Name");

}

}

public static string CallServiceByAddress(string EndPointAddress)

{

Binding _binding = new WSHttpBinding();

EndpointAddress _endpointAddress = new EndpointAddress(EndPointAddress);

using (GenericProxy<IHelloWorld> service = new GenericProxy<IHelloWorld>(_binding, _endpointAddress))

{

return service.Channel.GetHelloWorld("Test by Address");

}

}

}



The (optional) app.config for the console application. This very verbose version was created for demonstration purposes using the "Create Service Reference" command.

Note: The second version of our call doesn't use the app.config at all.


<system.serviceModel>

<bindings>

<wsHttpBinding>

<binding name="WSHttpBinding_HelloWorldService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">

<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>

<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false"/>

<security mode="Message">

<transport clientCredentialType="Windows" proxyCredentialType="None" realm=""/>

<message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default" establishSecurityContext="true"/>

</security>

</binding>

</wsHttpBinding>

</bindings>

<client>

<endpoint address="http://localhost:3456/HelloWorldService.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_HelloWorldService" contract="SandBox.SharedClassLibrary.IHelloWorld" name="WSHttpBinding_HelloWorldService">

<identity>

<dns value="localhost"/>

</identity>

</endpoint>

</client>

</system.serviceModel>



Finally our GenericProxy class that creates the Channel that we use to call the service:


public class GenericProxy<T> : System.ServiceModel.ClientBase<T> where T : class

{

T _serviceInstance;

public GenericProxy(string endpointName)

: base(endpointName)

{

}

public GenericProxy(Binding ServiceBinding, EndpointAddress ServiceEndpointAddress)

: base(ServiceBinding, ServiceEndpointAddress)

{

}

public new T Channel

{

get

{

if (_serviceInstance == null)

{

//this.ChannelFactory.Credentials.UserName.UserName = "user";

//this.ChannelFactory.Credentials.UserName.Password = "123456";

_serviceInstance = this.ChannelFactory.CreateChannel();

}

return _serviceInstance;

}

}

}

Tuesday, July 15, 2008

My client doesn't allow the use of Foreign Key Constraints in SQL Server

I'm working on a project at a Fortune 500 Microsoft customer and ran across something interesting: They DO NOT allow foreign key constraints in production SQL Server databases. The theory being that they have negative performance impact, and that developers can check for related data as needed in stored procedure code.

This is new to me, but I wanted to double check with some SQL Gurus before I call their baby ugly... :-)

Here is a great response from my friend Brian:

"Their baby is UGLY! But you really can't tell a parent that!

They are correct, you do take a noticeable hit ( seen up to 5% on heavily updated DB's)
In particular you can see a lot locking and deadlocks.

However I've yet to see development staff that consistently check the data or deal properly with errors and/or partial rollbacks! I'll put money on the fact they have corrupt data!

I like FK's because they 'document' the DB and I use them to automate data archiving.

That said, I am in the process of converting my current app to ignore FK relationships.
(We do 24,000,000 + insert/update/deletes daily so performance is an issue)

I define relationships in the DB.
Enable then in Dev and Test to facilitate testing.
Disable them in perf and prod for performance

Once a day/week check the FK's. Not much more expensive than DBCC CheckDB
IF we find corrupt data, we enable the FK so we throw errors to facilitate route cause analysis.
Note this happens regularly after a code release ;) A little less often when new customer/data patterns hit the DB.

While this is not a 'Best Practice' it is a better than what they are doing today. "


Example code:

-- Create the tables and populate with valid data
use tempdb
go

if object_id('child') is not null drop table child
if object_id('parent') is not null drop table parent
go

create table parent (id int primary key)
create table child (id int primary key, pid int)
alter table child with nocheck add constraint FK_child_parent foreign key ( pid) references parent(id) -- Use 'with nocheck' so FK can be added even if existing data is corrupt
go

insert into parent values (1)
insert into parent values (3)
insert into child values (1, 1)
insert into child values (3, 3)
go

-- Try adding invalid data
insert into child values (2, 2)
go

-- Disable FK and try again
alter table child nocheck constraint FK_child_parent
insert into child values (2, 2)

-- Check the table
DBCC checkconstraints (FK_child_parent)

-- Enable the FK so code now throws errors
alter table child with nocheck check constraint FK_child_parent -- Use 'with nocheck' so existing corrupt data is ignored, but new data will error
insert into child values (2, 2)

Monday, June 23, 2008

SQL 2008 Frustrations

I have been trying to use SQL 2008 for a new project, but I just broke down and decided to go back to SQL 2005 so that I can be productive.

Even after installing Visual Studio 2008 SP1 Beta I still can't get Visual Studio 2008 to play nicely with SQL 2008... Tools like Schema Compare apparently still don't support "SQL 10" according to the error messages, and there still aren't any SQL 2008 specific database project types.

To be clear I haven't had any problems with SQL 2008 itself, just a lack of tools support.

Saturday, June 21, 2008

Minnesota Developers Conference 2008

I'll be speaking on Enitity Framework development at the Minnesota Developers Conference 2008 on September 9, 2008 in Minneapolis.

Check out the website (http://mdc.ilmservice.com/) there are a ton of great speakers! It should be a great event, so register early.

Wednesday, June 18, 2008

Peeking behind the curtain at a Microsoft presentation

With 45 minutes to go before a presentation to 500 developers in Omaha, Microsoft Evangelist Mike Benkovich (right) is writing a new demo from scratch with help from his manager Brian Moore (center).

It's not that he wasn't prepared for a presentation that he's given several times before in other cities, it just that he thought his existing demos were a little "boring".

Nothing like an untested demo in front of a live audience to make things exciting!

Microsoft opening new development center for Expression Suite in Minneapolis

MINNEAPOLIS, MINNESOTA - Microsoft is actively hiring developers to staff a new development team that will be based in the Twin Cities. The group will be working on parts of Microsoft's new Expression Studio tools for creative professionals.

Expression Studio competes directly with products from Adobe, and Doug Olson, and Bob Pappas, the managers for the new Microsoft team are both alumni of Adobe product development teams based in Arden Hills, Minnesota.

Microsoft has an existing sales and consulting office located in Bloomington, Minneapolis, but there isn't space available in that office for the new Expression team, and so they are currently shopping for office space, location TBD.

The open positions are for senior Software Development Engineers and Software Development Engineer Testers working with C# and WPF. This a rare opportunity to work on a cutting edge Microsoft software product, and live in Minneapolis. Interested candidates should visit http://members.microsoft.com/careers/default.mspx.

Bill Gates keynote at the Office Developers Conference 2008

I'm working the "Architects @ Microsoft" booth at the Office Developers Conference in San Jose this week, but my booth doesn't open until 12:30, and so I had time to take some pictures at the Bill Gates keynote before I get to work in the exhibit hall...

I was standing at the back of the hall with one of the convention center security guards, who doesn't now anything about Microsoft, but has probably watched a lot of keynote speeches over the years. About an hour into Bill's presentation he turned to me, and said, "Wow, he really knows his stuff doesn't he."

I theory Bill should be coasting into his upcoming retirement from day to day involvement at Microsoft, but you would never guess it by watching him speak to a thousand developers for an hour and a half here at ODC 2008.

The view from mission control at the back of the hall. The technician on the left is running the teleprompter for the speakers (no most of them don't do it all from memory... but I'm not sure about Bill).

FedEx had a major demo in the keynote showing off a cool new OBA application they've developed to integrate package shipping and tracking into Outlook. At the end of the demo this FedEx delivery guy ran up on stage with a package for Bill.

The box contained a custom Guitar Hero guitar that is going to be auctioned off to some lucky ODC attendee.

Silverlight news: Tafiti source code released and a new Silverlight development site

A couple pieces of great news for Silverlight developers:

1. Microsoft has just released the source code for Tafiti (http://tafiti.com) on CodePlex: http://codeplex.com/WLQuickApps Tafiti is one of the most sophisticated SilverLight applications I'm aware of at this time, and so this should be an interesting body of code for aspiring SilverLight developers to dig through. Note: This Silverlight 1.0 code, NOT Silverlight 2.0 alpha code, and so it is using JavaScript not C#.

2. Microsoft and CMP Media have just launched a new Resource Center for MS Silverlight site for developers (http://www.ddjresources.com/silverlight/) this looks like another great resource.

My new approach to personal email management

I have adopted a new approach to personal email management that I thought was innovative enough that it is worth sharing.

The problem being solved is to try and find more effective ways to manage my inbox, while reducing the number of people and businesses that I give my primary personal email address to. A secondary goal is to track which businesses use my email address to spam me with unwanted third party junk email, and then easily block that email.

So here is the approach: I have a dedicated domain name "scrappydog.com" with just one email user (me). All email to any email address in the scrappydog.com domain lands in my inbox. This allows me to created a separate dedicated email address for every organization I do business with, and I can create a one off random address for any interaction I want.

Here some examples:

  1. wellsfargo@scrappydog.com
  2. americanexpress@scrappydog.com
  3. somegirlimetatthebar@scrappydog.com
  4. criticalworkstuff@scrappydog.com

Then I can create a single inbox rule to route email addressed TO wellsfargo@scrappydog.com straight to the trash after I close my account there, instead of ten different rules for all of the different addresses they send me email FROM based on different marketing campaigns.

Important point to note: What makes this so simple is that I'm routing ALL the email for this domain to a single inbox, and so there is NO SETUP for a new address, I just make them up on the spot. And I can easily create rules to make them go away.

SharePoint Connector for Confluence

Atlassian has just announced a SharePoint Connector for Confluence, their very popular wiki product (Atlassian also makes JIRA the issue tracking system).

Confluence is a Java based product, and SharePoint (MOSS 2007) already has wiki functionality included. So why am I, a Microsoft employee, highlighting this announcement? The wiki functionality in MOSS 2007 is great, but it's limited, and it doesn't meet the needs of all customers. Confluence is a mature popular product which some of my large enterprise customers are currently using. Those customers are also using MOSS 2007 and this product will let them bridge the two together into a single user interface with shared search provided by MOSS 2007.

I think this is a great example of the sort of Software + Services (S+S) integration that we are working to highlight with core parts of the Microsoft stack acting as the glue to tie enterprise applications from a variety of vendors together for improved user experience.

Here is a link to product information on the Atlassian site: http://www.atlassian.com/sharepoint/default.jsp

Meeting with a startup

I had an interesting meeting with a new startup this morning. I can't tell you their name, or much about them, but I there are still some interesting bits worth sharing.

This is a very early stage company that just secured $3+ million in VC money. Up until last week they were primarily funded by the three founders credit cards. They are doing business in the financial services space, and while they aren't going to be dependent on the web for their revenue, they do see the web as a key differentiator and value add to their business model. I was asked to come in and give them a briefing on the Microsoft platform and how it fits with their plans.

We had an interesting discussion of the process of selecting a technology platform in a green fields opportunity like this. A key point that I made was that in a situation like this where a startup company is planning to develop a large custom application from the scratch the most important consideration isn't necessarily the licensing cost or capabilities of the platform nearly as much as the availability of talent to actually do the work.

Here in Minneapolis-St Paul the market isn't really a very active LAMP (open source) market in terms of use by big business or in terms consulting resources. So my point was that they could choose "free" tools in the LAMP space, but they might have a difficult time staffing up to do the work. By contrast there is a lot of .NET (and Java) development going on here in the Twin Cities, and the pool of talent is much larger.

There are obviously lots of factors that go into a "bet the business" decision on a technology platform, but being able to staff up your startup is certainly a key one.

From a technology capabilities perspective BizTalk and SharePoint are a great fit for this business, and as they move forward I'm hoping to follow their story, and report on their progress. Stay tuned...

Subtle MSBuild bug (feature)

I've got a fairly complex MSBuild script that has a subtle problem that has taken me a while to diagnose. The symptoms where basically that the first "fixed" build would often fail. What I mean by this is that after a compile problem was fixed the next build would fail to create an *.msi file because it said there were missing files. Here is an outline of my build process:

  1. Get the latest code from Source (Un)Safe.

  2. Clean (delete all bin and obj directories).

  3. Compile 65 VB.NET 2005 projects individually in dependency order (NOT using a master solution file).

  4. Copy most of the build output to a common bin directory.

  5. Run an InstallShield script that packages the files in the common bin directory into an *msi file.

  6. Copy the resulting *.msi file to a turnover directory.

  7. Email QA to let them know there is a new build available.

Here is a simplified version of the MSBuild script for step 4:


<Target Name="CompileWithStop">



<MSBuild



Projects="@(ProjectReferences)"



Targets="Build"



StopOnFirstFailure="True" />



</Target>





<ItemGroup>



<CompileOutput Include="..\Source\**\bin\**\*.exe" Exclude="..\Source\**\bin\**\*.vshost.exe" />



</ItemGroup>





<Target Name="Copy" >


<Copy SourceFiles="@(CompileOutput)"

DestinationFolder="$(OutputDirectory)"></Copy>



</Target>



And here is the bug: It turns out that all ItemGroups in an MSBuild script are processed BEFORE all Targets! So this means that my CompileOutput item actually is based on the files that were generated by the PREVIOUS build! The insidiously subtle part of this is that builds usually generate the same output, so the script "works" except for the first time new file is added, or the first time after a broken build that didn't generate all of it's output files.

Fortunately there is a solution. The CreateItem task can be used to create an Item within a Target. The MSBuild snippet below fixed my problem:



<Target Name="Copy" >



<CreateItem Include="..\Source\**\bin\**\*.exe" Exclude="..\Source\**\bin\**\*.vshost.exe">



<Output TaskParameter="Include" ItemName="CompileOutput" />



</CreateItem>





<Copy SourceFiles="@(CompileOutput)"



DestinationFolder="$(OutputDirectory)"></Copy>



</Target>



This "feature" is apparently "by design", but it is definitely counter intuitive. I had incorrectly assumed that an ItemGroup was processed when (and only IF) it was referenced...

Here is the reference that solved it for me: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=390321&SiteID=1

Determining your Terminal Server Session ID from C#

I ran into an interesting .NET Remoting problem yesterday: Users on Terminal Services were stomping on each other running an app with a "private" remoting server that hadn't taken into account the possibility that multiple user could be trying to run it simultaneously on the same computer.

So the next question became: How do we uniquely identify separate instances on the same server? The quick answer was username, but that would fail if a user had multiple terminal server sessions. The better answer was to use the terminal server session id. Here's a code sample to determine your session id:

[DllImport("kernel32.dll")]

static extern bool ProcessIdToSessionId(uint dwProcessId, out uint pSessionId);

static void Main(string[] args)

{

Process _currentProcess = Process.GetCurrentProcess();

uint _processID = (uint)_currentProcess.Id;

uint _sessionID;

bool _result = ProcessIdToSessionId(_processID, out _sessionID);

Console.WriteLine("ProcessIdToSessionId Result: " + _result.ToString());

Console.WriteLine("Process ID = " + _processID.ToString());

Console.WriteLine("Session ID = " + _sessionID.ToString());

Console.ReadLine();

}



Notes:
  1. On a standalone workstation the session id is zero (0).
  2. The console session on a server is also zero (0).

Outlook - Gmail emoticon wierdness

I've been getting some emails lately at my personal Gmail account from people at Microsoft with a freestanding "J" or "L" at the end of some sentences... emoticon like syntax and placement, but more cryptic... I thought I had missed the memo on some new internet meme or shorthand, but it turns out it's just a "feature" of how Outlook translates some standard emoticons into images.

Here is a test message in Outlook.


And here is how the message was received in Gmail.


Proving yet again that Microsoft and Google just don't want to talk to each other... :-) (or should I say J)

VB6 to VB.NET 2005 migration wizard issue: Untranslated statement

Here is the evil VB6 to VB.NET 2005 migration issue of the day:

The migration wizard generates about a zillion 'UPGRADE_xxxx comments in the the migrated .NET code, and many of the them are pretty trivial. On our current project we've gotten a little complacent about most of the ones that aren't "'UPGRADE_ERROR:", some of the "'UPGRADE_WARNING:" messages are actually critical code breakers too.

I just discovered that we have a few of these sprinkled through our code:
'UPGRADE_WARNING: Untranslated statement in . Please check source code.
This means that the Migration Wizard LEFT OUT some code it didn't understand, and you need to go back to the VB6 source, and copy and paste it into .NET!

Fortunately this doesn't happen very often (16 occurances in 956,000 lines for us), but it leads to some ugly bugs if you don't search for all occurances of the warning message and fix them.

VB6 migration show stopper of the day: Ocx's with runtime licensing

Apparently the VB.NET 2005 Migration Wizard doesn't know how to migrate runtime licensing keys for Ocx controls.

Steps to reproduce:
  1. Create a one page hello world app in VB6, and add an Ocx that requires a runtime license.
  2. Migrate the app to VB.NET 2005, and compile it on a workstation with the appropriate design time license installed (I've tested this with several older Farpoint controls).
  3. Run the .NET app on the developer workstation that built it. It runs fine.
  4. Copy the compiled application to a test workstation that does not have the design time license installed, and try to run it. It fails with a license not found error.
Work arounds (both very painful):
  1. Delete each offending control in a .NET form, and then re-add it (by drag and dropping it on the form in the designer). This will cause the runtime license to be created, but you need to rename, resize and reconfigure the control. Repeat this process several thousand times...
  2. You can also copy known good OcxState data from another form with the same type of control into the *.resx files for the .NET forms. The advantage of this approach is that you don't have to rename and reposition the control on the form, but you still have to manually restore other formating and configuration properties.
Bottom-line: This is a huge PITA. I really hope that there is another fix that we haven't discovered yet.

VB6 Migration: Non-zero based arrays

VB6 allows you to define arrays that are non-zero based, for example:

Dim RecentPolicies(1 to 4) as String

In .NET all arrays are zero based and the migration wizard generates the following code:

'UPGRADE_WARNING: Lower bound of array RecentPolicies was changed from 1 to 0. Click for more: 'ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?keyword="0F1C9BE1-AF9D-476E-83B1-17D43BECFF20"'

Public RecentPolicies(4) As String

This is frankly evil, the migration wizard does NOT fix any of the related code, and so have go fix are you code. For example:

RecentPolicies(intI) would need to be changed to: RecentPolicies(intI - 1)

But I was thrilled to discover yesterday that Francesco Balena has come up with a really slick solution to this problem, which is essentially a new type that supports non-zero based arrays! So instead of the ickyness described above you can simply change your VB.NET 2.0 code to:

Dim RecentPolicies As New VBArray(Of String )(1, 4)

This is incredible time savings! To get the code read Francesco's original blog post here: http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,08e740fc-f486-4b5b-8796-6aad43e08815.aspx

Caveat: This is a Visual Basic 2005 specific solution (due to the use of generics).

Being inherintly sceptical of new things, I wrote the following set of NUnit tests to prove that his code works:

Imports NUnit.Framework

_

Public Class VBArrayTests

_

Public Sub TestLowerBound()

Dim _array As New VBArray(Of Short)(1, 3)

Assert.AreEqual(1, LBound(_array), "Lower bound should equal 1")

End Sub

_

Public Sub TestUpperBound()

Dim _array As New VBArray(Of Short)(1, 3)

Assert.AreEqual(3, UBound(_array), "Upper bound should equal 3")

End Sub

_

Public Sub TestZeroIndex()

Dim _array As New VBArray(Of Short)(1, 3)

Try

_array(0) = 55

Catch ex As System.IndexOutOfRangeException

Assert.IsTrue(True, "System.IndexOutOfRangeException exception was thrown on zero index")

End Try

End Sub

_

Public Sub TestMaxIndex()

Dim _array As New VBArray(Of Short)(1, 3)

Try

_array(4) = 55

Catch ex As System.IndexOutOfRangeException

Assert.IsTrue(True, "System.IndexOutOfRangeException exception was thrown on index of 4")

End Try

End Sub

End Class

Enabling IIS 6 Metabase Compatibility in IIS 7

IIS7 has done away with the metabase, but some pre-IIS 7 applications (such as Virtual Server R2 SP1) rely on the metabase as part of their installation process. This is a Windows feature that you can turn on and off throught the control panel (it is not a seperate install).

Running Virtual Server R2 SP1 on Vista

I've always preferred using Virtual Server to Virtual PC, and today I was setting up Virtual Server R2 SP1 on a new Vista machine, and I thought it was worth noting a couple of issues:

1. The Virtual Server Administration Website is a CGI application, but CGI applications are not enabled by default in IIS 7 on Vista. To enable CGI you need to go to:

> Control Panel

> Programs and Features

> Turn Windows features on or off

> Internet Information Services

> World Wide Web Services

> Application Development Features and check the CGI box.

2. When you try to browse to the Virtual Server Administration Website for the first time you will probably get the following error:

"The following error occurred:
An error occurred accessing the website application data folder."

The solution is that you need to open Internet Explorer as an Administrator (right-click on the IE icon and select "Run as administrator").

3. Installing the Microsoft Loopback Adapter on Windows Vista:

> Control Panel

> Add Hardware

> Click Next on the “Welcome to Add Hardware Wizard”

> Click the “Install the hardware that I manually select from a list” button, and then click Next

> Choose Network adapters click Next

> Under Manufacturer choose Microsoft

> Under Network Adapter choose Microsoft Loopback Adapter.

> Click Next

> Click Next

(The point of installing the Microsoft Loopback Adapter is to allow you to connect the host PC to the Virtual Server "Internal Network" without exposing the guest operating systems on a "real" network.)