Friday, June 20, 2008

Adding SQL Server logins and user accounts for multiple databases in one pass

We have a few applications that include bits that are service applications.  These applications talk to SQL Server databases and they authenticate using SQL Server logins, as opposed to using Windows Authentication.  We recently changed the account that the services were using to a new account with a more robust password.  When the services are deployed, they will have an update mechanism that will add the new login account.  For internal testing, I wanted to give our testers the ability to add the server login and database user for all of their test databases in one shot.  I ended up with a script that looked like this:

   1: use [master]


   2:  


   3: -- create server login if if does not already exist


   4: if not exists (SELECT 1 FROM sys.sql_logins where name = 'myservice') 


   5: create login myservice with password = 'wEEzPHUxce0xhzQFEeF8OHS2KqYYkxTA', Check_expiration = OFF


   6:  


   7: -- Create a cusror that will let us iterate through a list of user 


   8: -- created databases, skipping over the system databases


   9: declare c cursor fast_forward for 


  10:   select name 


  11:   from master.dbo.sysdatabases 


  12:   where name not in ('master', 'tempdb', 'pubs', 'model', 'msdb')


  13:  


  14: declare @name varchar(128)


  15: declare @sql varchar(800)


  16:  


  17: open c


  18:  


  19: fetch next from c into @name


  20:  


  21: while @@fetch_status = 0


  22: begin


  23:   print @name -- display the name of the database


  24:  


  25:   -- Drop the user and schema. this may need to be augmented depedning how you define your roles


  26:   select @sql = 'use ['+@name+'];if exists (select 1 from information_schema.tables where Table_name in (''SomeTableUniqueToMyApp'', ''AnotherTableUniqueToMyApp'')) if exists (select 1 from sys.database_principals where name = ''myservice'') begin IF EXISTS (SELECT 1 FROM sys.schemas WHERE name = ''myservice'') begin DROP SCHEMA myservice end drop user myservice end'


  27:   execute (@sql)


  28:   


  29:   -- Add the user


  30:   select @sql = 'use ['+@name+'];if exists (select 1 from information_schema.tables where Table_name in (''SomeTableUniqueToMyApp'', ''AnotherTableUniqueToMyApp'')) if not exists (select 1 from sys.database_principals where name = ''myservice'') create user myservice for login myservice'


  31:   execute (@sql)


  32:  


  33:   -- Define the user's role  


  34:   select @sql = 'use ['+@name+'];if exists (select 1 from sys.database_principals where name = ''myservice'') execute sp_addrolemember db_owner, myservice'


  35:   execute (@sql)


  36:   


  37:   fetch next from c into @name


  38: end


  39:  


  40: close c


  41:  


  42: deallocate c




This script starts off in the master database and will require admin access rights to the server.  If we break down the script, we can review what each section does and why it’s doing it



Lines 4-5 will create the login if it does not already exist.  The Check_Expiration option is set to “OFF” to verify that password expiration policy will not be applied to this login account.  The BOL for SQL Server 2005 states that this option defaults to “OFF”, but I like to explicitly set that option for a couple of reasons.  One reason is that it stands out when you read the script.  Another reason would be that a future service pack or later version of SQL Server could default that setting to on.



Lines 9-12 create a cursor that provides a list of databases.  We skip over the system tables to save some processing.



Lines 17-22, 37-42 handle the mechanics of opening up the cursor and iterating through each row in the set of database names.



Lines 26-27 create a dynamic SQL statement that performs the following steps and then executes that statement:




  1. Change the database context to the current database that was located by the cursor


  2. Uses the information_schema.tables view to use this database only if contains two tables that are known to exist in my application database.  This will prevent the user from being added to the wrong database.


  3. If this user has a schema, drop the schema.


  4. Drop the user from the database.


  5. Execute the dynamically created SQL statement.



Lines 30-31 create a dynamic SQL statement that performs the following steps and then executes that statement:




  1. Change the database context to the current database that was located by the cursor


  2. Uses the information_schema.tables view to use this database only if contains two tables that are known to exist in my application database.  This will prevent the user from being added to the wrong database.


  3. Create the user and map that user to the login account created at line 4


  4. Execute the dynamically created SQL statement.



Lines 34-35 create a dynamic SQL statement that performs the following steps and then executes that statement:




  1. Change the database context to the current database that was located by the cursor


  2. Uses the information_schema.tables view to use this database only if contains two tables that are known to exist in my application database.  This will prevent the user from being added to the wrong database.


  3. Adds the user to the db_owner role.  Your application may need fewer rights for the user.


  4. Execute the dynamically created SQL statement.



We do give our customers the ability to run with just Windows Authentication, but configuring services to run under domain accounts is an additional set of steps that most of our customers prefer not to use.  It’s a much simpler OOBE if the services use SQL Server authentication.

Wednesday, June 18, 2008

Sign the "danah boyd proper case" petition

Jeff Atwood is on a righteous crusade against those who insist on using only lower case for their names.  He has a petition and he wants you to sign it.  It’s all in good fun, but it raises a subtle point.  There are times where you have to import data from one application to another and you may need to change the case of text coming.  You could data coming out of an old system written in COBOL, where everything was uppercase and the people using the data wants the text changed to mixed case.

Going from all upper case to mixed case is an approximation.  You are filling in details (the case of each letter), where that information did not exist before.  You can make a close approximation by assuming certain rules.  Like states are always start with the first letter of each word capitalized.  And the same goes for proper names.  The people who deviate from the rules of grammar just have to live in world where we prefer to use the proper case.

Getting back to the petition, how do you deal with people who want to use their own capitalization rules?  The bigger question is should your application even enforce the proper case?   I think you should let people type in their names in the format that they want.  If they wanted to go through life with the Caps Lock key on, that’s their prerogative.

Tuesday, June 17, 2008

Migrating Delphi versions with DevExpress

I usually run on the latest version of Delphi while the other Delphi developers in our department run a version or two back.  I’m the pretty much the sole Delphi developer on our .NET projects and I switch between VS 2008 and Delphi 2007 as needed.  The core Delphi team is on Delphi 2006 because of how our product development and testing cycles work.  We don’t switch compiler versions without coordinating with our QA department.

The core team is moving up to Delphi 2007 later this year for the next release cycle.  The current version of the product will stay in Delphi 2006 and the versions of the 3rd party components will be locked.  While development on the next release goes on, we may need to release updates to the current version.  So we lock the 3rd party components so that QA only needs to test the code specific to the update, plus general regression across the application.

With some component vendors, this makes upgrading more challenging.  We use the Developer Express components for Delphi Win32 and for .NET.  DevExpress has wonderful components, but they use monolithic installers that install everything for each compiler per platform.  If I install the latest version of the DevExpress components for Delphi 2007, I’m also going to get the latest version for Delphi 2006.  You can’t install them separately, not without seriously confusing their installer.  Installer technology is scary enough, the last thing you want to is mess areound with the installer.  I wanted something robust, hand tweaking my system to manipulate the installer would be to fragile to have an entire team try it.

After a few emails with DevExpress support, I came with another method.  I wrote a Delphi app that would manually downgrade a DevExpress install for a specific compiler.  You would run this app after running the DevExpress installer and it would rip out the DevExpress current installed bits and replace them with a cached copy of the version that we need to stay at.  It does the following steps:

  1. Check to see if Delphi 2006 is running and stop with a warning message if it’s running.  Here’s how to check to see if Delphi 2006 is running:
    function IsDephi2006Running: boolean;
    var
    handler: THandle;
    data: TProcessEntry32;
    Delphi2006IsRunning: boolean;
    function GetName: string;
    var
    i: byte;
    s: string;
    begin
    result := '';
    i := 0;
    while data.szExeFile[i] <> '' do begin
    result := result + data.szExeFile[i];

    if uppercase(result) = 'BDS.EXE' then begin
    s := uppercase(ProcessFileName(data.th32ProcessID));

    Delphi2006IsRunning := pos('BDS\4.0\BIN\BDS.EXE', s) > 0;
    end;
    inc(i);
    end;
    end;
    begin
    Delphi2006IsRunning := false;
    handler := CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);

    if Process32First(handler, data) then begin
    GetName();
    while Process32Next(handler, data) do begin
    if Delphi2006IsRunning then
    break;
    GetName();
    end;
    end;

    result := Delphi2006IsRunning;
    end;




  2. See if the Package Cache key, HKEY_CURRENT_USER\Software\Borland\BDS\4.0\Package Cache, exists in the registry.  If it does rename it.


  3. Deregister the DevExpress components from the IDE. 
    In Delphi 2006, the component packages are registered in HKEY_CURRENT_USER\Software\Borland\BDS\4.0\Known Packages.  The rollback program has a list of list of every DevExpress 5 and 6 package.  My code rips them all out. Before they are removed, their location is cached as this is the folder where the compiled library files are located.  For Delphi 2006, this defaults to “C:\Program Files\Developer Express.VCL\Library\Delphi10”.


  4. Using the location the library folder for the DevExpress compiled units, we delete all of the files in that folder.  If you want to be extra cautious, you could just rename that folder and create a new one with the original name.


  5. Copy the cached copy of the rollback version of the DevExpress compiled units to the library folder.  I keep a clean copy of the library folder in a protected location on our network.  If you need to rollback your DevExpress components, you’ll want to cache those fiels before doing anything else.  You will also need to cache the list of packages to install.


  6. Register the rollback packages into the Known Packages key used in step 3.


  7. Copy the runtime packages to the bin folder.  You can get the location of the bin folder by reading the value of RootDir in HKEY_CURRENT_USER\Software\Borland\BDS\4.0.  I keep a cached set of the runtime packages in the same folder that I keep the cached library files.



This process goes on the assumption that the DevExpress source code is not your Delphi search paths.  We typically leave the source code of the 3rd party components off the search path and just use the compiled units.  You get a faster compile and you don’t have to see other vendors compiler hint and warning messages.  If you want the source code on the search path, the you will need to cache all of the source code files.  At that point, it would be easier to cache the entire “\Program Files\Developer Express.VCL” folder.  And that’s how we upgrade selcted portions of our development environment.

Proof by lack of evidence

CNET’s Matt Asay wrote an article for CNET’s News.com that was just so bad, it gets the “Epic Failtag.  You can get the gist of how bad it is by the first few lines:

Vista's big problem: 92 percent of developers ignoring it

And to think Microsoft used to be popular with the developer crowd...

Not anymore. A recent report from Evans Data shows fewer than one in 10 software developers writing applications for Windows Vista this year. Eight percent. This is perhaps made even worse by the corresponding data that shows 49 percent of developers writing applications for Windows XP.

Wow, I’m not sure where to start on this one.  I started by visiting the Evans Data web site that Asay refered via this link.  It was vague and provide no actual metrics.  How that “one in 10” number was arrived that was never specified.  Sounds pretty bogus to me.

What they did say was this:

Only eight percent of North American software developers are currently writing applications to run on Microsoft's Vista operating system, while half are still writing programs for XP, according to Evans Data's Spring 2008, North American Development Survey. These same developers forecast a fragmented Windows market in 2009 with only 24 percent expecting to target Vista and 29% expecting to continue with XP.

8% of what?  Is that 8% of all developers writing for the Windows Desktop or is 8% of all North American software developers.  The former is a subset of the latter.  Did they exclude web developers from that count?

The other question is what do they mean by writing for Vista?  Does that mean writing for features specific to the Vista platform or does it mean writing the code so that it behaves under Vista?  Did they break that out by managed code as compared to unmanaged code?  If you are writing managed code like for the .NET Framework or Java, then you are not targeting an OS, you target the managed code framework.

Since Matt conveniently left out any actual numbers, I can use myself as a sample set.  A sample size of 1 is just as relevant and/or meaningless as an undefined sample size. I write shrink-wrapped applications for the Windows desktop market.  I do both Win32 coding (with Delphi 2007) and for the .NET Framework (with Visual Studio 2008).  All of my code is tested on XP, Vista, Server 2003, and Server 2008. 

With the .NET Framework, I have no code that is OS specific and I didn’t have to change any of it for Vista or Server 2008.  For the Delphi code, some minor changes were made to account for the location of the local application data folder.  That was all we had to do.  It’s something like 5 lines of Delphi code out 500,000+.  Of course by using Delphi 2007, we get the benefit of the Delphi VCL being Vista aware.  We get the Aero Glass effects and the new UI for dialogs without any code changes.

Does that mean we are not targeting Vista?  No, it just means for our applications, that Windows XP and every OS that comes after it is tested and supported.  That leads back to the critical failure point of Matt Asay’s article.  He’s making broad assumptions based on no evidence.  I’m reminded of that old quote by Leonard H. Courtney, “There are three kinds of lies: lies, damned lies, and statistics.”

I don’t usually read news.com anymore, in fact I came across Matt Asay’s article in a blog post by Steve Trefethen.  So I’m not familiar with Matt Asay’s body of work.  I don’t know if he wrote this out of ignorance out or as Steve put it: “written for no other reason than to generate traffic for c|net.”  I would like to know how much actual research Matt performed for his article.

The title of this post comes from the “You Are Wrong Because” portion of Scott Adams book, “The Joy of Work: Dilbert's Guide to Finding Happiness at the Expense of Your Co-Workers”.  The context was that Adams has stated that irrational people are easily persuaded by anything that has been published.  I think that’s applicable with with Matt Asay’s article.

{updated on 6/23/08]
I removed the link to Matt Asay’s article because I didn’t want to give him any more traffic.  It’s not that there would be a lot coming from my blog, it’s more of a principle thing.  I had originally included a link so that people could make up their own mind, but I think I have enough of his article to make my point.  And if you really want to read it, you know where to find it.

Monday, June 16, 2008

What’s the deal with the idiots wearing Bluetooth earpieces at a dance recital?

On the Saturday that just passed, our family was at the Palace Theatre for our daughters dance recital.  They had a lot of fun and really enjoyed their time performing on stage.  One thing that annoyed me were the people who insisted on wearing their Bluetooth headsets during the performance.  With the lights lowered, the blue lights blinking were a major distraction.

Don’t these people know that they look like dorks wearing their blue light idiot badges in a performance hall?  You wouldn’t answer the phone (well, you shouldn’t) at a dance recital, that would be rude and disrespectful to the performers and their family and friends.  One of the mothers in my younger daughter’s class was wearing one.  I kept thinking, “Are you out of your mind?”.  I had to stand near her because all of the girls had rows assigned to their classes.  Out of the corner of my eye, I kept seeing this blue light blinking and it was very distracting.

Blue LEDs are brighter than other colors and in a dark room, they will draw your attention.  Our brains are wired that way.  In my home office, my PC’s case has a couple of blue LED’s and they light up the room.  No need for night lights in my office.  Between the brightness and how our eyes perceive the color blue, bright blue LEDs can cause eye strain.

I have a Bluetooth headpiece, but I only use it in the car.  I don’t have anything against Bluetooth technology, but there is a time and place for everything.  And a dance recital is neither the time nor the place.  Think about it for a minute, it’s not practical to use a Bluetooth earpiece in performance hall.  If someone calls you, you wouldn’t want to take the call in your seat, you would walk out to the lobby to take the call.  You would be better off just to set the phone to vibrate and keep it in your pocket.

Unless they are wearing the earpiece as a fashion accessory.  “Hey, look at me!  I have a device embedded in my ear advertising my dorkness every two seconds”.  Come on, if you want to carry a techno device to look cool, you’re 4 years too late with Bluetooth.  If that’s what you want, then get an iPhone, it’s new enough to make you look cool. 

As a side note, a few weeks ago I saw some guy with Bluetooth earpieces in both ears.  We called it “Double-dorking”.

Sunday, June 15, 2008

Tim Russert, 1950-2008

I was shocked to hear about the passing of Tim Russert.  I always enjoyed watching "Meet the Press" and I should have watched it more.  I've been kicking myself forgetting to TiVo this morning's show.  On this morning's "Meet The Press", Tom Brokaw hosted a tribute to Tim Russert.  Fortunately, the show is available on the MSNBC site, and I'm grateful for that.  I don't know know it will be available, but the following link should work right now.

 

If you want to view a larger version, even fullscreen, try the following: http://www.msnbc.msn.com/id/21134540/vp/25173667#25173667.

He was the master of the interview, his show was not the one to do if you were looking for softball questions.  Even though he came out of politics, first with the late Senator Daniel Patrick Moynihan, then with the Governor of NY, Mario Cuomo, Tim Russert was non-partisan and fair.  His untimely departure leaves a great void and he will be missed.

Thursday, June 12, 2008

At least I passed the robot test

Name That Robot 

That's almost as important as knowing how long I could survive in the vacuum of space?

 How long could you survive in the vacuum of space?

File this under "end of the day diversions"...

Unblocking attachments with Outlook

Today, one of the other programmers needed to send me some odd registry entries that were on his machine.  So I asked him to export the registry settings to a .reg file from regedit and then and email me the .reg file.  Outlook blocked the file attachment because the .reg file extension is on the Outlook equivalent of the “No Fly” list.  Outlook has two levels of attachment security, Level 1 and Level 2.  Level 2 attachments will prompt you to save the file to your hard disk.  Level 1 attachments are blocked at the client by Outlook and can not be accessed.  The list of file types classified as Level 1 are listed here.  It has the usual suspects and this include .reg files.

That’s annoying.  I wanted that .reg file.  Fortunately, you can white-list specific file types in the Level 1 list and allow access to them.  I found the following instructions on the HowTo-Outlook web site at this page.

  1. Make sure Outlook is closed.
  2. Open your registry editor by opening the Run command and type regedit (regedt32 for Windows 2000)
  3. Locate the following key
    Outlook 2000 [HKEY_CURRENT_USER\Software\Microsoft\Office\9.0\Outlook\Security]
    Outlook 2002 [HKEY_CURRENT_USER\Software\Microsoft\Office\10.0\Outlook\Security]
    Outlook 2003 [HKEY_CURRENT_USER\Software\Microsoft\Office\11.0\Outlook\Security]
    Outlook 2007 [HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Outlook\Security]
  4. Go to Edit-> New-> String Value and name the value Level1Remove (case sensitive!)
  5. Double-click on the newly created value and enter the extension including the “dot” that you want to open in Outlook. For instance .exe
    If you need to enter more than one extension you’ll have to type separate them by a semicolon like this; .exe;.bat;.url
  6. Press OK on the input box and close the registry editor
  7. When you open Outlook the attachments which hold those extensions aren’t blocked by Outlook anymore.

In my case, I just used ".reg" as the file extension.  To go back to blocking the extensions, just remove or rename the Level1Remove string value from that registry key.

This isn't that hard to do, but it's a pain to have start and stop Outlook just to get the new settings to take affact.  I can understand why the restart is required, it prevents malicious code from changing the setting without you being aware of some going on with Outlook.

When chicken bombs are outlawed, only outlaws will have chicken bombs

From the The Hartford Courant:

...A motorist on Powder Forest Drive Friday morning noticed what looked like a whole chicken — the kind bought at grocery stores for roasting — with a pipe bomb stuffed inside, police said Monday.

When they arrived on the scene around 9 a.m. officers found the roaster had an improvised explosive device where the fowl's innards should have been.

They closed the road for part of the morning as the Hartford Police Department's bomb squad was called to detonate the device, police said.

The full article can be read here.  Are chicken bombs the latest meme?  I saw references to this story on bunch of sites.  One of more entertaining links is here, scroll down through the comments.

Wednesday, June 11, 2008

Backing up your Blogger hosted posts

This blog is hosted on Google's Blogger platform.  It has it's own domain name, but it's still on Blogger.  While I have faith in our Google Overlords, I still like keeping a local backup of the blog posts.  There's a nice open source Blogger backup utility named "Blogger Backup".  It's written in C# in VB.NET and is hosted on CodePlex.

When it's running, you'll see a window that looks like this:


Sell photos on photrade | By Chris Miller

You save each post as it's own file, or put them all inside one big, honkin' file.  The posts are saved as XML to make it easier to process.  You can use Blogger Backup to restore the posts back to your blog.  That would be handy if you accidentally delete a blog post.  You could also use the saved XML files to port your blog to another platform.  While I have no intention of moving this blog off of Blogger, it's good to know that I would have a way of migrating all of my posts to a new platform.

Blogger Backup is a .NET application, you'll need to have the .NET Framework 2.0 installed.  To communicate with Blogger, it uses the GData.Net (Google Data API for .NET) library.  I wasn't familiar with GData, it's a Apache licensed API that provides programmable access to many of Google's services:

  • Base
  • Blogger
  • Calendar
  • Spreadsheets
  • Google Apps Provisioning
  • Code Search
  • Notebook
  • Picasa Web Albums
  • Document Feed
  • Contacts
  • YouTube
  • Google Health

GMail is not on the list, but that would be asking for trouble.  The last thing we need is a Google API for sending spam through GMail.  The API is not limited to .NET, they have versions for Python, Java, and Objective-C.  I think you could do some interesting things with the GData API and their Calendar.

If you are wondering about the screenshot, I'm playing around with the beta for Photrade.  It has some cool features, it's worth checking out.

[Updated on 6/12/2008]
For some reason I thought Blogger Backup was written in C#, when it’s actually VB.NET.

Photrade has updated their license agreement for everyone's benefit

Yesterday, I posted about a new site called Photrade and it's license agreement.  Basically, section F of their license gave them the permanent right to do anything to your images with out compensation.

Their site looks cool and it provides features that I haven't seen anywhere else, but the license was too broad for my comfort.  Within 4 hours, they had revised their license.  The updated license gives Photrade the rights to use your images in order to operate the web site and to provide the functionality that the web site gives you.

That's really cool.  The updated license is completely appropriate for their business needs with out reserving any permissions that they would not need to operate their site.  The change in the agreement is very similar to the changes that Adobe made with licensing for Photoshop Express, except that Photrade revised the license with 4 hours. 

From the comments posted to my earlier post, it sounds like the original license was drawn up by lawyers seeking to provide the greatest level of protection to their client.  My guess is that no one looked at section F too close enough to realize how broad the rights were that were being granted to Photrade.  I'm sure that the people behind Photrade had no intention on reselling the images, but since the original license gave them that right in perpetuity, that part needed to be revised.  And it was revised in under 4 hours.  For revising and publishing a legal contract, that turn around time is very impressive.  Without knowing the people behind Photrade, the speed in which they revised their license says a lot about their character.

Now that I'm comfortable with the license agreement, I'm looking forward to trying Photrade.  For more information about Photrade, take a look at their site tour video.

Tuesday, June 10, 2008

Photrade lets you sell your photos online but retains perpetual and royalty-free license to your images

I have updated this post with additional details about how Photrade has revised their license agreement.  I also put additional comments in a new post.

I'm still playing around with Twitter and one of the accounts that I follow is photojojo.  The photojojo site is pretty cool and has some decent photography tips.  A little while ago, Photojojo tweeted a free invite code to the beta of www.photrade.com.

Hmm, a site where I could sell my images.  There a few ways of earning money on their site.  From their FAQ page:

There are 3 different ways to earn ad revenue:
1) From banner ads on your Photrade galleries. Earn every time someone sees your galleries.
2) From Photrades unique Patent-Pending Picture-in-Picture advertising (during our Beta test this ad space is donated to charity and/or used for test ads), where the ads are directly IN the photo when it gets shared. Earn Ad revenue every time someone sees your photo on ANY website (ie. in your blog or on your myspace).
3) From ads on a splash screen where users land when they click on your photos from another site. Earn ad revenue every time someone clicks on your photo when it iss hosted on another site.

You can sell your images and add a markup.  There is a base price for images and you can set a mark up value and collect the difference between the base price and the markup.  Plus you can sell licenses as stock images.   The idea of generating ad revenue by using your own artwork in blog postings looks intriguing.  If someone hotlinks your image, they would be hotlinking your advertising.

Sounds cool, but I declined to complete the membership form.  To complete the sign up process, you have to agree to the terms of the Photrade License Agreement.  Never agree to a license agreement without reading it.  That is really important when your own intellectual property is involved.  Most of the license is common boiler plate code, but I stopped when I hit section (F).  The following is section F in it's entirety:

(F) Company does not claim ownership in Your Content. At all times, You retain all rights in Your Content. However, each time You upload Your Content, You irrevocably grant to Company, its parent, subsidiaries, affiliates, and advertising or other partners a non-exclusive, perpetual, worldwide, royalty-free license in and to Your Content and intellectual property rights therein. Such license shall include, without limitation, the right in perpetuity, without any credit or compensation to You, to use, reuse, modify, alter, display, backup, archive, publish, sub-license, perform, reproduce, disclose, transmit, broadcast, post, sell, translate, create derivative works of, distribute and use for advertising, marketing, publicity and promotional purposes, all or any portion of Your Content, and Your name, voice, likeness and other identifying information, in any form, media, software or technology of any kind now known or developed in the future for any purposes whatsoever including, without limitation, developing, manufacturing and marketing products or services using Your Content. Intellectual property rights shall include all patents, trademarks, service marks, trade names, trade identities, copyrights, trade secrets, rights of publicity, logos, domain names, know-how, source code and object code, mask-work rights, inventions, moral rights, author's rights, goodwill and other intellectual property and proprietary rights whatsoever. You hereby waive any moral rights You may have in and to any of Your Content, even if such material is altered or changed in a manner not agreeable to You.

I marked in bold the part where I lost interest in signing up.  Basically, anything you upload becomes freely available to Photrade and they can do anything they want with it.  If you upload a spectacular snapshot of a sunset from your vacation, Photrade can sell it and they don't have to share any of the revenues with you.   That's the same sort of nonsense that Adobe tried to pull when they release Photoshop Express.  That didn't go over too well and Adobe revised their terms of service to make it clear they wouldn't sell the images.

Photrade's license looks pretty evil.  Their intentions may be pure (and I am assuming that they are), but that license allows them to anything to your images and your personal information forever.  And that's a mighty long time time, according to Prince.  I would consider signing up if they revamp that license to make it more like Adobe's.  It would be nice if you could license your images with a Creative Commons license like Flickr does.  The concept looks good, but that scary fine print make it a non-starter for me.

[Updated on June 11th, 2008]

As noted in the comments, Photrade has revised their license.  The new section F is more more reasonable:

(F) Company does not claim ownership of Your Content. At all times, You retain all ownership rights in Your Content. However, we do need certain rights from you, with respect to Your Content, in order to operate the Website and in order to enable you to do all the things this Website affords you the ability to do. Therefore, by uploading or transmitting Your Content to Website, you grant Company a worldwide, royalty-free, non-exclusive, fully sublicensable license to use, reproduce and modify Your Content solely for the purposes of operating the Website and enabling your use of the Website. To the extent that you choose to make Your Content available for sale or licensing to Users, you additionally grant Company the rights to distribute, publicly perform and publicly display Your Content (in whole or in part) for the sole purposes of operating the Website and enabling your use of the Website and to sublicense Your Content to other Users.

Basically, this gives Photrade a license to your images in order to present them on their site and enable the usage of the image.  This gives Photrade the legal protection that they need to run their site while still protecting the users.

I'm impressed, they made this change in just a few hours.  That's not an easy task to pull off when you are changing the contents and coverage of a legal document.

Thursday, June 05, 2008

Displaying what your TiVo recorded as a blog badge

There have been some people blogging about having what was recorded on TiVo being displayed on their blog.  Something like how you have display what you have been listening to with Last.FM.  Personally, I wouldn't want the world to know what was just recorded on my TiVos.  One TiVo gets filled up with cooking shows and NY Mets baseball, the other one gets an eclectic collection of kid shows and the SciFi channel, with some Animal Planet for extra seasoning.

The programmer in me keeps thinking about from a how to build it viewpoint.  There are some scripts out there that will pull the recorded show information from the TiVo mini web server.  That part is pretty easy.  Getting it to your blog is another story.  Unless you host your own blog, you need some place to store the data so a blog widget can render it in Web 2.0 approved colors (plus the bottom reflection with alpha channel blending goodness).  Since your TiVo is behind a firewall (well, it should be behind one), your blog can't pull the data directly form the TiVo.  And you really don't want to pull the data, that's too much traffic.

What you want to do is to push the data up once you find a place to host the data.  Since we don't want to hack the TiVo, the TiVo wont be pushing the data.  You'll need a home PC to query the recorded show history and push the data up.  A pretty simple task, but if you step back and think a little more about the whole TiVo experience, there is a better way.

Your TiVo box is connecting back to the TiVo mothership on a regular basis.  It gets program updates, verifies that it's subscription is still good, sends usage data back to TiVo.  Other than creating the infrastructure, it would be pretty easy for the TiVo company to supply to you what your TiVo has recorded.  That's where Louis Gray was heading with his article about TiVo adding social network related functionality.

I wonder if anyone at TiVo has thought of doing this.  They already have the hardware and networking stuff in place.  It's basically just taking the data that TiVo is already sending back and allowing the users to display and share that data.  It adds a coolness factor that you wont get with a cable company DVR.  I mean TiVo spent resources enabling you to program your TiVo from a mobile phone, why not social networking?

Wednesday, June 04, 2008

Not a fan of Vibrant Media's intellitxt popups (and how to get rid of them)

I was reading a blog about pulling an XML feed from my TiVo and I kept being distracted by popup ads over key words in the article.  You've probably seen them on other sites, the words have a double underline and they are not actually links. 

In the middle of a sentence, you see a word like this Internet and it looks almost like a regular link.  Except the color is wrong and the underline is not the same a regular link.  When your mouse hovers over them, a popup ad that has some contextual relationship to the text appears.  In the fake link above, nothing will happen when you mouse over it.  I just used an inline CSS style to do the double underlining.

Vibrant Media calls it "in-text advertising".  I call it a popup and I want it to go away.  As in go away right now.

I can understand the desire to have ads on your blog and I have them here myself.  I'm using Google to supply the ads on this blog and they are pretty unobtrusive.  I don't have any beef with web based advertising as long as it doesn't get in the way of the reading the web page.  I don't like popups and if I can avoid them, I will. 

In this case it was pretty easy.  I am using Firefox 2, so I installed the Adblock Plus addin.  That little gem will give you fine control over what ads you will see and experience.  For Vibrant based popups, it's pretty easy.  For a web site to use Vibrant's popups, they need to register an account with Vibrant and add some code to their web page that scans the text and creates something funky with Javascript when the page is rendered.  The web site author gets a url that includes their blog name on the intellitxt.com domain.  With AdBlock Plus, I just added a filter for the text http://*.intellitxt.* and after I refreshed the web page the popups were gone.  For Opera users, this link will show you how to block Intellitxt popups.

I would prefer not using AdBlock Pro. When I installed it, it prompted me to pick a subscription for automatically picking up ad filters.  When I did this, it took out most of the ads on every page.  And I don't have a problem with Google Ads or other ads that don't interfere with the consuming of the content.  So I yanked the subscription list out.  From here on out, I'm just going to kill the popups and leave the other ads in place.

One more reason why I'll never go back to Time Warner Road Runner

Time Warner Cable is implementing metered broad band Internet usage in a few test markets.  I switched to FiOS the day it was available and it's been a positive experience.  I'm paying less money for more bandwidth.  And it's consistent bandwidth.  I usually get a little over 20 Mbs for downloads and 5 Mbps for uploads.  When I dropped Road Runner, about two years ago, I was getting 5 Mbs down and 384 Kps up.  The fun part was watching the bandwidth performance drop when neighborhood kids came home from school.  We shared the same Road Runner pipe and the more people that joined, the slower it got.  FiOS doesn't have the problem.

I think the 5 Mbps upload speed has been the biggest benefit.  If I upload photos to my SmugMug account, it takes seconds where with Road Runner it would take minutes.

Time Warner's meter usage would allow 40 gb of traffic a month for their 15 mbps account.  That sounds like a lot but if you are downloading movies that you have bought from iTunes, Amazon UnBox, Tivo's TivoCast, or NetFlex; then you'll go through that 40 gb fairly quickly.

Thursday, May 29, 2008

What gives MediaDefender the right to launch a DoS attack against Revision3

I just started reading Molly Wood's blog and that's where I first heard about the Denial of Service (DoS) attack that Revision3 suffered from over the Memorial Day weekend.  Jim Louderback, the CEO of Revision3, wrote a detailed explanation of what happened and who was responsible for the attack on a Revision3 blog post.

Before I go any further, Revision3 uses Bittorrent (BT) to distribute their own content to their viewers so that bandwidth demands on Revision3's site are greatly reduced.  What Revison3 is doing is legal and is what Bittorent was designed for.

In a nutshell, an anti-piracy organization named MediaDefender had been illegally storing Bittorrent (BT) tracker files on Revision3's BT server for months.  They used a back door into the Revision3 server.  At some point, the illegally placed files were detected by Revision3 and they closed the security hole that allowed MediaDefender to gain unauthorized access to their servers. 

That's when the bovine excrement hit the rotating air circulation device.  The MediaDefender server lost contact with their BT Tracker files and launched a DoS attack.  What they did was flood the Revision3 servers with SYN requests, causing a "SYN Flood" type of attack.  In layman's terms, it's the equivalent of kids ringing your door bell and running away before you open it.  Except here it was happening to Revision3 8000 times a second.

This attack basically stopped anything Internet related at Revision3.  Their web servers, RSS feeds, BT feeds, and everything else outwardly facing was just dead in the water.  It also took out their email.  This caused real and measurable costs to Revision3.

I'm not a lawyer, but I'm pretty sure that it's not legal to gain unauthorized access to another company's servers.  The DoS attack was definitely illegal and the FBI has already been brought in.  They suffered measurable losses and MediaDefender should be held legally responsible for the damage they caused.  They should pay compensatory damages for the employee time wasted, for the extra bandwidth costs, and for the lost revenue while the Revision3 sites were down.  In addition, they should pay punitive damages to give them a strong financial incentive not to launch any more DoS attacks.

Jim's description of the events was well written and very even handed and should be read by everyone.  Any company using BT or other forms of P2P technology to distribute their own content is at risk being attacked by MediaDefender.  This isn't the first time that MediaDefender has been accused of launching DoS attacks.  What MediaDefender did was wrong, both morally and legally.  How can this type of attack be prevented in the future?  To quote from an old episode of Star Trek:

"...I've found that evil usually triumphs...unless good is very, very careful." - Dr McCoy.

You can't beat an outfit like MediaDefender by resorting to their tactics with DoS attacks against their servers.  First of all it wouldn't work, and more importantly it's just as wrong as their attack.  I think Revision3 should sue for damages and we should all contact the companies that use MediaDefender.  We should tell them that we don't want to buy products from companies that are associated with MediaDefender.

Getting back to Molly, I first heard about her blog when she mentioned it on an episode of Cranky Geeks.  I get Cranky Geeks as a TiVoCast download to my my TiVo and I just started watching it.  After two episodes, I've had enough of Dvorak but it was worth listening to the viewpoints of the other debaters on the show.  Molly's blog is well written and is focused on digital rights and copyright issues (and abuses).

postscript:
The inspiration for the Star Trek quote came from the comments for a post on The Coding Horror blog.  That post discussed the issues of how to block comment spamming on blogs and forum sites.  It's a great quote and I hope the guy who first used it in the Coding Horror comments doesn't mind me using it here.

Brian Peeks new book is already listed on Amazon

Brian Peek (probably the most well known member of our TVUG user group) has a book coming out, "10 Coding4Fun Projects with .NET for Programmers, Hobbyists, and Game Developers", and it's listed on Amazon for pre-orders.  Brian is probably best known for his managed code API for working with the Nintendo Wiimote.  You can get the current version of the code from it's CodePlex page.  His blog is worth reading if you want to control hardware devices with .NET.

Not too long ago Brian demonstrated his code at a TVUG meeting.  He used the Wiimote to control a radio controlled car.  It was pretty cool watching the car turn when he rotated the Wiimote.  You can download the code from his presentation from his blog at this page.  Other people have used his library to do some interesting things.  It sounds like this book would be a cool Christmas gift.

Tuesday, May 27, 2008

Does knowing C make you a better programmer?

Joel and Jeff had a conversation about the merits of knowing how to program in C.  Joel's take was knowing how to program in C makes you a better program.  I'm in that camp, with some other people (Darren and Eric).

After college, I did mostly C programming.  That was back in the days when 640K wasn't the punch-line to a joke, but a real limit that drove you nuts.  Having a good working knowledge of C gave you a better understanding how memory was allocated and how it was used.  Pointer arithmetic was part of the natural order of programming.  It made you a careful with what you allocated and how you managed it.

There's a still a place for unmanaged code, whether it's C, C++, Delphi, or some other language.  You get a lot functionality with managed code (more ways of accessing data, garbage collection, etc), but there's no free lunch and there's a substantial footprint for each process that uses the .NET Framework.  I'm typing this with Windows Live Writer, an app that's written for the .NET Framework.  It's using 54MB right now as I type.  That's a lot of memory for a WYSIWG blog tool.  Just because you have a garbage collector doesn't mean that it's being used effectively.  It's still good to clean up after yourself and a good C/C++ programmer does that.

Eric Sink compared knowing C with knowing Morse code.  It's a flawed argument in that Morse code is not the underpinnings of the technology that came after it, but it's close enough to the spirit of the argument to have some merit.  My dad learned Morse code 30+ years ago when he got his ham license.  It was a big deal back then and you couldn't get your ham license with being able to demonstrate your proficiency with Morse.

Once he got his license, he rarely if ever used Morse to send out a message.  But every now and then, he would pick up a faint signal that had bounced around the ionosphere and it would be in Morse.  He would sit at his radio and pick out the words that of the dashes and dots.  

But how can you use that analogy to demonstrate the value of C to a managed code programmer?  For many programmers, I'm sure it doesn't mean anything to them.  If you are just doing web programming for some giant LOB app, only knowing VB.NET or C# probably isn't much a of a liability.

On the other hand, If you do any P/Invoke work, you'll calling DLLs that were mostly written in C or C++.  Having a knowledge of those languages makes it a little easier to understand the data structures and calling conventions used by the DLL.  If you working with binary data that came from unmanaged code, knowing how that data is represented in it's native environment will make it easier to understand what it is and how to use it.

Darren Stokes made the assertion that programmers who know C seem to solve complicated problems faster than those who don't know C.  I don't know of you can make that case.  With the push to teach only managed code at school, the programmers that came up knowing C are a bit older than the programmers who don't know C.  That programmer who has the C experience is probably someone who has been around longer and has that much more experience to draw from.  I wonder if anyone has down any actual studies that compare the level of productivity from programmers with C experience compared with programmers that only know only managed code.

Ack! The Windows Installer does not permit installation from a Remote Desktop Connection

I'm testing a installer that I had migrated from Wise For Windows to InstallAware. I testing the installer from inside a virtual machine. I used do it with VMware Workstation, but now I'm using virtual machines hosted on our ESX server. That's usually much faster and doesn't my bog down my development machine.

I connected to a virtual Server 2003 session using Terminals. If you spend anymore of time using Remote Desktop, then you'll want Terminals. It lets you have multiple sessions open in a single instance of the client by displaying each session in a tab. In addition to RDP, it supports VNC, Telnet/SSH, and a few other protocols. And it's an open source project, you can't beat the price tag.

With a RDP connection, you have the ability to automagically have the remote connection access your local resources (drives, printers, serial ports, etc). I was using the local access to disks to allow the remote connection to access my hard drive. I figured that would be a quick way to compile the installer on my local machine and run it remotely through the RDP connection.

Or so I thought. As a prerequisite for this app, I need to install the ASP.NET 2.0 AJAX Extensions. That comes as a Windows Installer .msi files. When I ran that .msi, I got the following error message:


In other words: FAIL. To add insult to injury, that's not even a standard Windows error dialog. With most Windows error dialog boxes, you can press Ctrl-C and the contents of the dialog box will be copied to the clipboard. You'll get all of the information from the dialog, something like this (from a different installer):

---------------------------
Microsoft .NET Framework 2.0 SP1 Setup
---------------------------
The Windows Installer package:


c:\c6b28b8b6e56383fbc455e9977dd00\vs_setup.msi


could not be opened.



Choose Retry to try again. Choose Cancel for exit setup.
---------------------------
Retry Cancel
---------------------------



That's very handy for reporting errors or for researching with the text from the dialog. I hate it when I see a bug logged in our internal bug tracking system that has embedded a screen shot of an error message. They screen shot the whole page (hello? Alt-Prtscn anyone), save the image, then attach it as a file attachment. Instead, they have press CTRL-C and then CTRL-V and be done with it. Rant over, back to original rant.


For some reason, the AJAX installer team chose not to follow that convention. So I went to Google and manually typed in the text of the error message (Oh, the huge manatee!) and start viewing the results. Sure enough, it's in the MS Knowledge base as 927063. Windows Installer does not allow installs in Server 2003 from a RDP connection shared drive. When you allow access to your local C: drive to the remote session, it sees it as \\TSClient\c and flat out disallows installs from anything in the \\TSClient namespace. This was determined to be a security risk and the tssclient blocking was added Windows Installer 2.0 at the time of the Windows Server 2003 release.


This KB article lists two resolutions, use the standard UNC notation to reference that drive or map a drive letter to that location. To that list, I'll a third option. Copy the file from the \\tsclient share to the remote desktop and run it from there. For one off tasks, that's usually faster than accessing a UNC named object.


Side note: While writing this post, I read on the Terminals home page on CodePlex that Microsoft will be removing the ability to connect to the console session through the RDP protocol in version 6.1. Being able to connect to the console is a little known feature of the RDP client, mstsc.exe. I use it when people leave their RDP sessions hanging on a 2003 Server box that only has the admin remote connections enabled. I can connect over the console session and blow away the left over sessions. I can query and kill the remote sessions via the command line, but sometimes you just need one session open. If you want to see this feature left in the RDP stack, place your vote here.

Saturday, May 24, 2008

IPv6 and Vista

The other day, I read a post that Carl Franklin had made on his blog about a new Vista laptop that he had to buy on short notice.  The gist of his post that Vista ran pretty well, but he did tweak some of the settings.

One of the settings that Carl had tweaked was to disable IPv6 on all of the network adapters.  For some reason that jumped out at me.  I posted a comment on his blog asking why he did it.  I'm all for disabling services that are not being used, but I like to know what the reason is behind it. 

I did a quick scan through Google, and initially found many hits for how to disable IPv6 with very little explanation on why you should do it.  Carl responded to my comment that he had heard anecdotal evidence that IPv6 can cause conflicts under Vista.

For me, the decision to disable IPv6 on my Vista boxes boils down to two questions:  Do I need IPv6 functionality?  If I don't need IPv6, does having IPv6 enabled cause any problems under Vista?  Time to go back to Google and due some more searching.

One bit of trivia:  While I was searching for why you would want to disable IPv6 on Vista, I came across a few articles about how Google owns a large block of IPv6 space that works out to be 7.9 x 1028 IP addresses. That's a large number, no matter how you slice it.   Why does our Google Overlord need that many addresses?

I've seen the badge below displayed on a few sites. 

Pretty scary looking, huh?  There is a real concern with the number of IPv4 addresses being used up.  At the current rate of assignment, we just have a few years left before the all of the addresses have been assigned.  That's why they came up with IPv6, to provide for a virtually unlimited number of IP addresses.  Even with Google hogging billion and billions of IPv6 addresses, we'll still more than enough to serve this planets needs for the for seeable future.  Right now, the continued use of NAT allows more users to share the same IP address. 

We are seeing all of the IPv4 addresses being assigned, but that doesn't mean that they are all being used.  If you look at the global IPv4 allocation list, you'll see that large chunks of the IPv4 space is in the hands of a small number of companies.  For example, Apple owns all the 017.xxx.xxx.xxx addresses.  That's over 16 million distinct, global IP addresses.  What are they doing with all those addresses?  And some people still question the need for IPv6 in the first place. 

But I digress, I think we'll be safe with just IPv4 addresses for the next few years.  At this point, there are no IPv6 only addresses that I want to visit.  They exist, I just don't need to visit them.  So the answer to my first question is: No, I don't need IPv6.

So what about leaving IPv6 enabled in Vista?  What does that cost me?  I read about some reports early in the Vista release with IPv6 issues.  Some of that could be attributed to "version 1.0" network drivers.  I did read some reports of http://localhost not working with IPv6 enabled. 

On an IPv6 enabled box, localhost will evaluate to ::1, not the 127.0.0.1 that we call home.  That is what defined for localhost in %SystemRoot%\system32\drivers\etc\hosts as the IPv6 adddress for localhost.  This apparently causes a problem when testing locally hosted websites.  If you remove (or better yet, just comment out the ::1 entry from the hosts file, that was reported to allow web development to continue using localhost and IPv6 installed.

Klaus Graefensteiner has a pretty good explanation of why this is happening.  As noted elsewhere, he recommends editing the hosts file as the work around. I haven't done any web development on my Vista box, I can't verify that this is still an issue.  I'm actually planning on doing some web development work from my home machine in the near future, issues with localhost will pop up pretty much immediately. 

As far as I can tell, the answer to my second question is "maybe".  If the only issue is the localhost issue, then I'll address it through editing the hosts file as opposed to disabling IPv6 altogether.

I did see the value of one point that Carl made.  If the service is not being used, then disable it.  It's not enough of an issue for me, but resources are limited, then you want to pick off the low hanging fruits from the list of running services.  If you do actually want to disable IPv6 in Vista, it's pretty easy to disable or enable IPv6 support (from numerous sources, this one from TechSupportForum.Com)

  1. Go to Start and type in "ncpa.cpl" (without the quotes) and press Enter
  2. Right click on each network connection and select "Properties"
  3. Remove the checkmark from the box next to "Internet Protocol Version 6 (TCP/IPv6)
  4. Click OK to exit the dialog

Lather, rinse, and repeat for each network connection. IPv6 will still remains active for routing and tunneling.

To complete disable IPv^ in the system, you'll need to add a registry.  Go to registry and create DWORD parameter "DisabledComponents" in HKLM\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters and set it to a value of 0xff and restart the system.

Just remember one thing, if you disable IPv6, you will not be able to use Windows Meeting Space or any application that relies on the Windows Peer-to-Peer Networking platform or the Teredo transition technology.

Bonus Firefox tip:  You can disable Firefox's usage of IPv6 with out disabling IPv6 for the rest of the system.

In Firefox, type about:config and then in the filter box type dns. You'll see an entry named "network.dns.disableIPv6".  Double-click that line so that it reads True, which will effectively disabled IPv6 for Firefox.  You'll need to restart Firefox for this change to take effect.

Thursday, May 22, 2008

Checking for the presence of .Net Framework 2.0 SP1

Installware has a lot of built in support for checking for the presence of the various flavors of the .NET Framework.  In it's current incarnation, 7.5, it doesn't have anything for checking for the presence of Service Pack 1 of the .NET Framework 1.0.  It turned out to be pretty easy to add that check to my installer script.

A couple of years back Heath Stewart did a blog post about how to check for the presence of a .NET Service Pack.  You just look for the the value of "SP" in the registry key for the version of the .NET runtime that you want to check.  For .NET 2.0, that key would be labeled:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727

If you want to check against a language, you just check for the language id.  For example, English is ID 1033.  The key to check for the English version of the .NET 2.0 runtime would be.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727\1033

The SP is DWORD, so you will get back an integer result.  If the .NET runtime has been installed, it will have the SP value.   For the RTM release, it will be 0.

With InstallAware, I use the following syntax to check for .NET 2.0 SP1 and terminate the install if SP1 (or later) has not been installed.

Read Registry Key HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727\SP into CHECKSYSTEM
if Variable CHECKSYSTEM not Greater Than 0
MessageBox: $TITLE$ Setup Error, This product requires that the Microsoft .Net Framework 2.0 Service Pack 1 has been installed.$NEWLINE$$NEWLINE$Setup cannot continue.
Terminate Installation
end




For some reason, InstallAware does not have an "Less Than" operator, you have to do "not Greater Than".  InstallAware is a leaky abstraction of the Windows Installer experience, I just shrug at those little oddities.


[Updated on 5/27/08]
I had a typo in the line that starts with "if Variable CHECKSYSTEM" where it should have been comparing against the value of 0, not 1.  My bad.

InstallAware doesn't automatically remove assemblies from the GAC at uninstall time

I am updating the installer for one of our products to check for the presence of the SP1 version of the .NET 2.0 Framework, when I noticed something odd.  The assemblies that I was putting into the GAC were still there after an uninstall.  The guys at InstallAware seem to think that this is not a problem.  They refer to a topic in the MSDN, Removal of Assemblies from the Global Assembly Cache, which describes how the Windows Installer is not responsible for the removal of entries in the GAC:

The Windows Installer determines whether to allow the removal of a common language runtime assembly based upon a client list it keeps independently of the assembly. The Windows Installer keeps one pin bit to represent Windows Installer clients of the assembly. The installer pins the assembly for the first Windows Installer client and unpins it when the last Windows Installer client is removed. The assembly maintains a pin bit for every client of an assembly.

The Windows Installer is not directly responsible for the physical removal of common language runtime assemblies from the computer. The installer unpins the assembly when the last Windows Installer client is removed. If the Windows Installer is the last client of the assembly, the common language runtime provides the option to force a synchronous cleanup of the assembly. The cleanup process is atomic and there is no provision for a "rollback" at this point. All unpinning of common language runtime assemblies must occur after the user has had a chance to cancel the installation or removal.

That being said, the users want to uninstall everything.  Wise and InstallShield apparently do this.  With InstallShield, there is a property for the installed file called "Permanent".  If it's not set, it will be removed from the GAC if there are no other references to the assembly.

What InstallAware wants you to do is to explicitly remove the assembly during the uninstall portion of the install script.  They provide a command named "Remove Unpinned Assemblies" (it's listed as "Remove Assemblies").  This command will remove all unpinned assemblies that your installer had places into the GAC.  The code should look like this: 

  Apply Uninstall (get result into variable SUCCESS)
Remove Unpinned Assemblies


I'm still not sure if that will always work.  According to InstallAware, there is a bug in Windows Installer where sometimes the installer corrupts the Global Registry cache on .NET 2.0 assembles and there will always be a reference to unreferenced assembly.  The only work around is to delete the (Default) registry key for the assembly in the user and local machine hives.  This mess is documented here in the InstallAware support forum.


The work around sounds worse than the problem.  I'm not going to do a strafing run on the registry to deal with a Windows Installer bug.  Not when the only consequence is that a few assemblies (in my case, it's only a few) get left in the GAC.  There are other ways of removing assemblies from the GAC.  The gacutil.exe utility can be used to check the reference count and to remove the assembly,  The "/lr" command line parameter will list the reference counts for each assembly.  The "/u" parameter will remove an assenbly from the GAC.  For the command-line shy, the free tool GacView provides an Explorer like view of the GAC and it's easy to use to remove assemblies from the GAC.

Wednesday, May 14, 2008

Showing installed app versions with BgInfo

I love it when you can use one cool tool to enhance another cool tool.  Duncan Epping posted an English translation of a Arne Fokkema post about how to use BgInfo to display the version number of VMware Tools that has been installed inside a VMWare virtual machine.

If you have never used BgInfo, it's a great little utility written by Bryce Cogswell of Sysinternals fame.  BgInfo will write a desktop bitmap that will contain useful information about the machine: machine name, ip address, version stuff, free space, etc.  You can set BgInfo to run when you login by placing a shortcut to it in the user's or shared startup folder.

When you work with multiple servers, real or virtual, their desktops all tend to look alike.  BgInfo makes it easy to see at glance which machine you are currently connected to.

What Arne wrote about was that in addition to displaying predefined variable, BgInfo can display version information from any file that has version information.  You point BgInfo to the location of the VMWareServer executable and select version formation and you are done.  The screen shot from Arne's blog shows how simple it is:

BgInfo is very handy for displaying static information.  Since the desktop background is only being rendered when you login in, you wouldn't want to use it to display a dynamic variable, like CPU load.  I can see where you would use this to display other version type of information.  If you are testing against multiple service pack versions of SQL Server, you could display that information.   While I've been talking about using this for a VMware virtual image, this is handy for Virtual PC images or any server that you would remote in to.

Thursday, May 08, 2008

Uninstall IE8 Beta before installing XP SP3

If you running the beta version of Internet Explorer 8 (IE8) under Windows XP, the word is to uninstall IE8 before installing Windows XP Service Pack 3 (SP3).  Once SP3 has been installed, you will be unable to remove IE8 (and IE7).  The reason for this is documented on the IEBlog with this post.  Basically, when you install IE7 or IE8, the existing IE6 (version of IE that comes with XP) is backed up to an uninstall folder.  SP3 includes an updated set pf IE6 files.  If you were to uninstall IE7 or IE8 after installing SP3, you would have a mixture of IE6 files from pre-SP3 and the bits installed as part of SP3.  That would cause all sorts of problems and Microsoft disables the uninstall option for IE7 and IE8 after SP3 is installed.

Not being able to remove IE7 isn't a big issue.  If you have IE7 in place, the odds are that you are used to it and will not be removing it.  The beta version of IE8 is another situation altogether.  When the next beta or release candidate or even the actual release comes out, you'll want to uninstall the beta version first.  It's just the prudent thing to do.  Well not exactly, it's much safer to install anything labeled beta in a virtual machine.  But if you have the IE8 beta installed on a XP box and you are planning on installing SP3, you'll want to remove the beta before installing SP3.  In fact, Microsoft will not offer SP3 through Windows Update if it detects the IE8 beta on your machine.  Once SP3 has been installed, you can install the IE8 beta again.

Tuesday, May 06, 2008

Dealing with the Duplicate Resource: Type 24 when enabling theme support in Delphi 2007

I was writing a little utility in Delphi 2007 to make it easy to work with data stored as the XML data type in SQL Server. While I have been using Delphi 2007 since it was released last fall, this was the first desktop app that I had written from scratch. I threw some components up on the form and I did a quick compile to see how the visual elements looked at runtime. I got the following error message:

[DCC Error] E2161 Warning: Duplicate resource: Type 24 (user-defined), ID 1; File C:\dev\RawDataQuery\RawDataQuery.res resource kept; file c:\program files\codegear\rad studio\5.0\lib\WindowsXP.res resource discarded.

I was dragging and dropping components from an app that I had originally written in Delphi 7 and one of the components was a TXPManifest component. The TXPManifest is component that only appears at designtime. When your code is compiled, it includes the WindowsXP.res resource file, which contains the manifest resource needed to enable XP style themes. In Delphi 2007, you can enable theme support in the project options. It's a checkbox labeled "Enable runtime themes" on the Application page of the Project Options dialog.

You can't have both "Enable runtime themes" checked and an instance of TXPManifest in your application. They both try to pull in WindowsXP.res and the second attempt will FAIL. The fix for this is easy. You can disable the "Enable runtime themes" setting or remove the TXPManifest component from your form and remove the reference to xpman in that form's uses clause.

My personal preference is to remove the component and unit references and just use the project setting. When there are two methods of doing the same thing, I tend to pick the simpler solution. The fewer the number of moving parts, the less likely it is for something to break.

Monday, May 05, 2008

Identity hijacking on Twitter.

The other day, I got an email from Twitter announcing that someone new is following me.

SIOOMA! (Siooma) is now following your updates on Twitter.

Check out SIOOMA!'s profile here:

 http://twitter.com/Siooma

You may follow SIOOMA! as well by clicking on the "follow" button.

Best,
Twitter

If you are on Twitter, then you have seen that type of message before.  It means that a user with the handle "Siooma" is now following my site.  I took a peek at Siooma's Twitter page and sure enough it lists a Fake Steve Jobs blog post as the home page.  Siooma is one of the FSJ's favorite sayings, read that blog post for the full definition - I don't want to spoil the fun by taking it out of context.  I read some of Siooma's previous "tweet" and I don't think it's the real deal.  They were sort of FSJ-like, but they were missing the sharp edge that a real FSJ post would have.  Part of what makes FSJ so fun to read is how close to the truth he appears to get at.

If you do a search on Twitter for FSJ, you get a couple of hits.  None of them are the authentic FSJ.  His Twitter handle is FakeSteveTwit, which oddly enough doesn't come up in that Twitter search.  Doing a parody of a famous person can be entertaining, the "real" Fake Steve Jobs is both entertaining and informative.  He does more than a Mad Magazine style of parody, FSJ covers actual Apple events and applies the FSJ twist to the analysis.  The man behind the satire, Daniel Lyons, is a real journalist and does a good job covering the stock options backdating scandal that involved the real Steve Jobs a while back.  When you read FSJ, you wonder how to close he gets to the actual Steve Jobs and does the real Steve Jobs ever wish he could say some of things that the FSJ says.  None of the FSJ clones on Twitter have that magic.  Even if they could, it would hard to produce that in 140 character limit of a Twitter post.

Siooma is welcome to follow my Twitter feed, but I'm not planning on reciprocating.  FSJ has but some serious time and effort in his blog.  The FSJ wannabees are just riding on his coattails.  When they list his blog as their web site, it comes off that they are pretending to be the FSJ.  Lyons does not have a monopoly on being a fake Steve Jobs, but it's not right to claim his site as your own.  If Siooma and the other clones want to be another FSJ, then they should get their own FSJ blogs and develop their own personas.

Friday, May 02, 2008

How to rename database objects to comply with naming conventions

We have guidelines for the naming conventions of our database objects.  It's pretty much the common pattern you see all over the place. For example, we use the following convention for default constraints:

DF_TableName_ColumnName, such as DF_Student_School

Pinal Dave has great set of database coding standards on his site, more in-depth than ours actually.  We have found that using coding standards makes the database schema easier to read and makes it easier to perform schema updates.  If you don't explicitly name a default, SQL Server will name it for you.  And it wont be pretty or consistent across databases.  We have our own tool for pushing out schema updates to our customers and it assumes the object names will be the same for each database.

When we submit our schema updates internally, we usually catch any deviation from our naming conventions.  It's not a perfect process and every now and then, something slips through the cracks.  We then correct the schema update to use the appropriate naming convention.  if we have been using the schema changes internally, we may have some databases that don't match the published schema for the object names.  When that happens, we run a simple T-SQL script that cleans house.  The following T-SQL will scan the database for default constraints that do not match our naming conventions and rename the ones that it finds.

DECLARE @OldName nvarchar(512)
DECLARE @NewName nvarchar(512)
DECLARE @OldToNewName nvarchar(512)

-- We use a table variable to contain the list of objects to be renamed.
DECLARE @FixSchema TABLE(
sys_OldName [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
sys_NewName [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL
)

-- Get the list of current column defaults
-- The OldName column represents the current name of the default
-- The NewName column represents the name of the default using
-- our naming convention
INSERT INTO @FixSchema(sys_OldName, sys_NewName)
select o.name AS OldName
,'DF_' + Object_Name(o.parent_obj) + '_' + c.Name AS NewName
FROM sysobjects o
JOIN syscolumns c ON o.parent_obj = c.id AND c.cdefault = o.id
WHERE o.type = 'D'

-- Declare cursor on this table variable to access on the non-compliant
-- constraints
DECLARE AddDrop CURSOR FOR
SELECT sys_OldName, sys_NewName
FROM @FixSchema
where sys_OldName <> sys_NewName

OPEN AddDrop

-- Loop through the list of defaults where the default name is not
-- strongly typed
FETCH NEXT
FROM AddDrop
INTO @OldName, @NewName

WHILE @@FETCH_STATUS = 0
BEGIN
-- Assenble a T-SQL command that can be executed dynamically
-- to rename the constraint
SELECT @OldToNewName = 'sp_rename ' + QUOTENAME(@OldName,'''') +
', ' + QUOTENAME(@NewName,'''') + ', ' + QUOTENAME('object','''')

-- Print the command to be executed. Useful for seeing
-- what is being done
PRINT @OldToNewName

-- Execute the T-SQL to rename the object. If the EXEC line is
-- commented out, the code will display what would be changed
-- with making any actual changes to the schema
EXEC(@OldToNewName)
FETCH NEXT
FROM AddDrop
INTO @OldName, @NewName
END

-- Close and deallocate the cursor
CLOSE AddDrop
DEALLOCATE AddDrop

I usually use the INFORMATION_SCHEMA views to peek at the table and column structures.  The INFORMATION_SCHEMA.COLUMNS view will tell which columns have default constraints, but not the name of the constraint. In this case I needed to access the sysobjects table to retrieve the name of the constraint.  The same type of code can be used to rename other objects like check constraints.