Tuesday, May 22, 2012

Don't use Video Clips when Presenting

I recommend do not play video clips in your presentation. Find a different way to deliver the message.

Over the past year I've attended a few presentations where the presenter played a video clip. Often these clips range from two to five minutes.

Here are the problems with the video clips:

  1. I am here to watch the presenter. If I wanted to watch that clip I would watch it on the computer.
  2. Sometimes the clips are excerpts of shows or movies and don't make sense without the context.
  3. Audio can be hard to hear and video can be hard to see because of the lighting.  Often there is scrambling at the beginning of the clip to adjust the audio volume. It works fine on the presenters computer at home, but is then presented on a different projector and probably even a different computer.
  4. During the video clip the presenter often stands awkwardly to the side of the presentation area or look at their computer screen. This loses the audience.
Find a way for you to deliver the message instead of the video clip.

Tuesday, December 13, 2011

Free conference calling

Have you ever needed to setup a conference call without access to a corporate conference calling number?

This summer I had just such a need. For four months I used freeconference.com for weekly morning conference calls. Instead of scheduling a meeting every week I used their reservationless conference feature to have a conference call always ready to go! This enabled the meeting notices to have the same dial in number and access code and alleviated me from having to manage the meetings through their software. No  need to hand out codes to attendees each week.

This software has worked so well I hadn't logged in since I set it up. Fortunately my password manager saved my password so when I accessed the site today I didn't have to dig around for my credentials.

Tuesday, October 25, 2011

Never forget another password

In 1994 when I got on the internet in the sixth grade I had one username and password. I can remember one username and password. Life was good. Fast forward to 2011 I have no idea how many usernames and passwords I have: 50 , 200? Who knows. I've had to use forgot password buttons, forgot login buttons, had my accounts frozen for months, written passwords down in documents, had to call help desk support for assistance. I don't have to do any of that anymore.

What changed? I installed a piece of software called LastPass. This handy software remembers my usernames and passwords for me. I have it set to automatically login to sites I visit requiring passwords. It works by using a master password. I have my master password set to a passphrase, something easy to remember but impossible for computers to guess, like "myfavoriteteacherwasMrs.SavageIn7thGrade"

There have never been any security breaches of LastPass, but I don't use it to store my financial passwords just in case. This means I only have to remember 5 passwords, one for everything non-financial, and then one password per financial institution.

Try LastPass it is free and makes your life better.

Tuesday, September 27, 2011

Page_LoadComplete not being called in a user control

I needed some logic to execute in a page load event after a control event had occurred.

Initially I wrote:

        protected void Page_LoadComplete(object sender, EventArgs e)
        {
            if (newQuestionControl.ShouldShow)
            {
                mpeNewQuestion.Show();
            }
        }


I put a breakpoint and noticed my logic was not being called.

Since I'm in a user control there is no LoadComplete event. I fixed it like this:


        protected void Page_Init(object sender, EventArgs e)
        {
            this.Page.LoadComplete += new EventHandler(Page_LoadComplete);
        }

Tuesday, August 30, 2011

I was on a SQL Server 2008 R2 database I wanted to backup and put on my machine.

First I tried backing it up and got this error:


Backup failed for Server. 'MyDatabaseServerName' (Microsoft.SqlServer.SmoExtended)

System.Data.SqlClient.SqlError: Cannot open backup device 'C:\MyBackupFileName.bak'. Operating system error 5(Access is denied.). (Microsoft.SqlServer.Smo)



For help, click: http://go.microsoft.com/fwlink?ProdName=Microsoft+SQL+Server&ProdVer=10.50.1600.1+((KJ_RTM).100402-1539+)&LinkId=20476

Then I tried generating scripts and got this error:

Microsoft.SqlServer.Management.Smo.SmoException: Could not read metadata, possibly due to insufficient access rights. at Microsoft.SqlServer.Management.SqlScriptPublish.GeneratePublishPage.worker_DoWork(Object sender, DoWorkEventArgs e) at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e) at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)

I figured I must not have rights to some metadata or something. In the wizard I try selecting Tables, Views, and Schemas. My script generates fine. I notice there is no create database in the resulting script. I go back to the Choose Objects screen, unselect "Script entire database and all database objects" and "Select All" objects. My script generates fine.

Monday, February 28, 2011

Storing your SQL Server Database as Text

Have you ever had trouble moving a database from one server to another?  Ever wanted to store the database in source control?  Storing the database as text (a SQL Server Script) enables plain text tools to operate on your database.

Jeff McWherter from Gravity Works showed me how to easily store a SQL Server Database as Text:
  1. In SQL Server Management Studio right click on a database
  2. However over Tasks
  3. Select Generate Scripts
  4. Click 'Set Scripting Options' to skip the first two steps.
  5. Click the 'Advanced' button
  6. For the last option in general change 'Types of data to script' from schema only to 'Schema and data'
  7. Click OK to close the Advanced dialog
  8. Select a location to save the script to
  9. Click Next to go to summary.
  10. Click Next for SQL Server Management Studio to generate the script.
  11. Click Finish and you've got your text!
  12. When executing this script
    1. Ensure the Filename in the CREATE DATABASE Statement is correct when executing this script. (Or you can comment out everything after CREATE DATABASE $YOURDBNAME$ until the first GO to have SQL Server use defaults)
    2. Since this works at the SQL Server level, you will get errors when the script attempts to create users that don't exist on the target server and then errors when it authorizes that user.

P.S. See my post SQL Server Management Studio Generating Change Scripts if you want to use SQL Server Management Studio to graphically modify the database but then script the change.

Monday, February 14, 2011

Windows and Mac are Dodo Birds

18 days ago I received a CR-48 and have used it as my exclusive computer outside of work.

This machine is perfect because :
  1. It is built for the internet.
  2. It is resilient.
  3. It is powerful.
  4. It is convenient.
Chrome OS is built for the internet.  When I'm online I am consuming information and communicating.  Neither of these tasks require the power and complexity of Windows or a Mac operating systems.  Chrome OS gets me online almost instantly and gets out of my way so I can use the web.

Chome OS is resilient.  Suffering data losses in 2001 and 2005 I moved all my important files to Google docs and Windows Skydrive.  Since moving to the cloud I have not lost a bit, and have not cared which operating system I'm on for my personal computing needs.  Chrome OS takes this a step further putting my files and applications in the cloud.  Now if my machine dies I won't waste time reconfiguring my machine the way I want it.

Chrome OS is powerful.  I was surprised I am able to take and edit photos on my CR-48, make phone calls, do online video conferencing with my son, and edit code.  Prior to receiving the CR-48 I assumed some applications didn't make sense as web apps, but now my thinking is flipped.  All apps make sense as web apps and they will all be web apps.  It is just a matter of time.

Chrome OS is convenient.  It is light, it is fast, it is portable, it is quiet, the battery lasts for 8 hours, it is secure, it is hardy.

When Windows Longhorn was announced waaaaaaaay back in the day I was excited about features it was going to deliver like WinFS.  When Windows 7 launched I didn't care about any of the features.  The world has changed.  I don't care about desktop features anymore.  None of them make my life easier and more convenient.  With Windows 8 on the horizon, I can't think of a single desktop feature I want.  Today's compelling use cases are cloud based.  I started using Mac this year.  Which features was I excited about?  Syncing with Google Calendar, syncing with Google Contacts, and storing my data in the cloud.  Chrome OS leapfrogged these operating systems and they will need to catch up or go the way of the dodo bird.

Monday, February 7, 2011

Adding Cascading Deletes

I needed to add delete functionality for an entity in one of our projects.  Unfortunately this table had 30 other tables referencing it.

This SQL generated the correct code for me faster than I could blink:


SELECT 'alter table [' + FK.TABLE_NAME + '] drop constraint [' + C.CONSTRAINT_NAME +']; ' +
'ALTER TABLE [' + FK.TABLE_NAME + 
      '] WITH CHECK ADD CONSTRAINT [' +  C.CONSTRAINT_NAME + 
      '] FOREIGN KEY([' + CU.COLUMN_NAME + 
      ']) REFERENCES [' + PK.TABLE_NAME + ']([' +
  PT.COLUMN_NAME + ']) ON DELETE CASCADE; '
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME
INNER JOIN (
SELECT i1.TABLE_NAME, i2.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2 ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME
WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY'
) PT ON PT.TABLE_NAME = PK.TABLE_NAME
where CU.COLUMN_NAME = 'yourColumnName'

Monday, January 31, 2011

Generating a memory dump for an IIS 6 hang

This post follows up on my sample demonstration and explains how to get a memory dump from a production web server crash.

Environment:
ASP .NET 3.5
IIS 6
Windows Server 2003 64bit
Windbg 6.11.0001.404 AMD64

w3wp was crashing on the server with this in the event log.

Event Type: Error
Event Source: .NET Runtime 2.0 Error Reporting
Event Category: None
Event ID: 1000
Date: 3/3/2010
Time: 1:22:18 PM
User: N/A
Computer: _____________
Description:
Faulting application w3wp.exe, version 6.0.3790.3959, stamp 45d691cc, faulting module kernel32.dll, version 5.2.3790.4480, stamp 49c51cdd, debug? 0, fault address 0x0000000000027ded.


For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

I set adplus to get a memory dump on hang.

After spelunking around I found out I was debugging the wrong dump file w3wp.exe-APPNAME

decided to debug a different dump file: w3wp.exe_-CRMAppPool
.sympath SRV*c:\debugsymbols*http://msdl.microsoft.com/download/symbol
.loadby sos mscorwks
!threads

ThreadCount: 11
UnstartedThread: 0
BackgroundThread: 11
PendingThread: 0
DeadThread: 0
Hosted Runtime: yes
                                              PreEmptive                                                Lock
       ID OSID        ThreadOBJ     State   GC     GC Alloc Context                  Domain           Count APT Exception
XXXX    1 1a14 00000000001aa520   1808220 Disabled 0000000000000000:0000000000000000 00000000055650d0     1 Ukn (Threadpool Worker) System.StackOverflowException (00000000100b10d0)
XXXX    2 2014 000000000019d5c0      b220 Enabled  0000000000000000:0000000000000000 0000000000122e20     0 Ukn (Finalizer)
XXXX    3 2298 0000000005722570      1220 Enabled  0000000000000000:0000000000000000 0000000000122e20     0 Ukn
XXXX    4 1524 000000000556b570    80a220 Enabled  0000000000000000:0000000000000000 0000000000122e20     0 Ukn (Threadpool Completion Port)
XXXX    5  904 000000000556bb40   200b220 Enabled  0000000000000000:0000000000000000 00000000055650d0     0 Ukn
XXXX    7 2228 000000000c7e17b0   880a220 Enabled  0000000000000000:0000000000000000 0000000000122e20     0 Ukn (Threadpool Completion Port)
XXXX    8 2324 000000000c6ee560   200b220 Enabled  0000000000000000:0000000000000000 00000000055650d0     0 Ukn
XXXX    9 19f8 00000000055c7480   200b220 Enabled  0000000000000000:0000000000000000 00000000055650d0     0 Ukn
XXXX    a 2104 0000000005b8b9e0   180b220 Enabled  0000000000000000:0000000000000000 00000000055650d0     1 Ukn (Threadpool Worker)
XXXX    b 213c 000000000d039d10   180b220 Enabled  0000000000000000:0000000000000000 0000000000122e20     0 Ukn (Threadpool Worker)
XXXX    6 2370 000000000de00b90   200b220 Enabled  0000000000000000:0000000000000000 00000000055650d0     1 Ukn

Aha!  three threads with locks and one with a StackOverflowException.
The column with the XXXX is supposed to list out the native thread so I can switch to it and see the problem.
0:000> ~
.  0  Id: 226c.2274 Suspend: -1 Teb: 000007ff`fffa2000 Unfrozen

When this memory dump was taken the native thread had already been cleaned up.

A KB article provided instructions on running an action when a process is orphaned.

Following those instructions I get a log file but no .dmp :(

I modify action.cmd changing %COMMAND% to %COMMAND% >> %LOG%

Now the log displays: 

Microsoft (R) Windows Debugger Version 6.11.0001.404 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.

Cannot debug pid 8220, Win32 error 0n87
    "The parameter is incorrect."
Debuggee initialization failed, Win32 error 0n87
    "The parameter is incorrect."

Hmmm... maybe I can manually take a dump.

Using iisapp I see:

W3WP.exe PID: 11072   AppPoolId: ClientProject
W3WP.exe PID: 10824   AppPoolId: 3rdPartyVendor
W3WP.exe PID: 4836   AppPoolId: AnotherClientProject
W3WP.exe PID: 8220   AppPoolId: CRMAppPool

create problem.  wait 30 seconds:

C:\>"C:\Program Files\Debugging Tools for Windows (x64)\cdb.exe" -c ".dump /o /m
a c:\crash_PID_11072_3_03_2010_10_01_16_54.dmp;q" -p 11072

success!  Using this dump we were able to identify the problem.

Monday, January 24, 2011

Becoming a Better Listener

Today I received my first downvote on StackOverflow (a programmer question and answer site).  This means the downvoter said my answer sucked.  Fortunately they left a comment explaining why.  Immediately I thought "Hey! I'm right!" My answer is technically correct.  I think?.  I read the other two provided responses and noticed they both referenced a 50x50 Green square.  Mine did not.  Then I read the question again.  Oh, they want a green 50x50 square.  I didn't realize that!

I answered the question in as little time as possible without making a good attempt at understanding what they were trying to do.  I don't even know if my code really solves their problem because I copy and pasted code from my blog and put a warning on my answer "this worked two and a half years ago."  I told myself it was OK though because I put a smiley face at the end of the sentence to feel better about submitting an untested answer.

What happened after that?  Two people took time out of their day to improve the formatting and clarity of my answer and then a third person took time out to tell me why my answer wasn't good.  My answer did not provide value to the community.  Better answerers came along and posted more concise answers that directly solved the problem.  I had wasted community member's time.  I deleted my answer.

In the real world I do the same thing.  When people talk often times I have a response ready before they are done talking.  When someone criticizes me I often interrupt stating why I am right.  Listening is key to communication and learning.  I am working on being a better listener, and if you catch me not listening, help me out :)

Monday, January 17, 2011

Geeking out in Lansing

In 2008 I lived in Detroit and struggled to find like minded individuals to hang out with.  Individuals who code for fun and want to fix problems in their communities.  I ended up spending quite a few nights in Ann Arbor which has a similar tech scene to Lansing but a bit larger.  I enjoyed new events like CoffeeHouseCoders, Ann Arbor New Tech, and the TechBrewery.

I'm happy to report Lansing now has similar events so I can get all the goodness I was getting in Southeast MI right here at home.

The inaugural Lansing CoffeeHouseCoders meeting is 1/19/2011.  If you like to code in your free time in a social atmosphere, this is the place for you!

Hackers & Hustlers is a group focused on startup culture.  Attendees will hear entrepreneurs who are actually doing it give a talk or a pitch on their product.  The first meeting is 1/26.

In 2010 Lansing also got a coworking space, Second Gear.  More and more businesses are enabling their work force to work whenever and wherever they want.  For me often times that means I need to be somewhere for an evening event and I spend the day in a coffee shop.  That works OK, but I like to work near other programmers so I can learn from them and bounce ideas off of them.  Second Gear is meeting that need for the Lansing community.

Monday, January 10, 2011

Storing User Settings

I mentioned last week I want to store all settings for my application as user settings so I won't write to app.config and require administrative privileges.  I didn't like the constraints imposed by the .NET Settings implementation, such as configuring user settings at compile time and using attributes.  After implementing my own key value store implementation I'm realizing I may use the .NET settings provider for application settings, since I want it strongly typed, to have default values, and to have validation, and my property bag like implementation for storing settings application extenders write. For example Jane user writes a plugin and can persist settings in the user directory of my application.  This scenario doesn't work using LocalFileSettingsProvider.

Here is my initial key value store implementation:

using System.Configuration;
using System.Xml;
 
public sealed class DebuggerSettings
{
    public string this[string propertyName]
    {
        get
        {
            var store = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
            UserSettingsGroup values = (UserSettingsGroup)store.SectionGroups["userSettings"];
            if (values == null)
            {
                return null;
            }
            ClientSettingsSection myValues = (ClientSettingsSection)values.Sections[typeof(DebuggerSettings).FullName];
            if (myValues == null)
            {
                return null;
            }
            SettingElement setting = myValues.Settings.Get(propertyName);
            if (setting == null)
            {
                return null;
            }
            string returnValue = setting.Value.ValueXml.InnerText;
            return returnValue;
        }
        set
        {
            var store = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
            UserSettingsGroup addSectionGroup = (UserSettingsGroup)store.SectionGroups["userSettings"];
            if (addSectionGroup == null)
            {
                addSectionGroup = new UserSettingsGroup();
                store.SectionGroups.Add("userSettings",addSectionGroup);
            }
            string sectionName = (typeof(DebuggerSettings).FullName);
            ClientSettingsSection clientSettingsSection = (ClientSettingsSection)addSectionGroup.Sections[sectionName];
            if (clientSettingsSection == null)
            {
                clientSettingsSection = new ClientSettingsSection();
                clientSettingsSection.SectionInformation.AllowExeDefinition = ConfigurationAllowExeDefinition.MachineToLocalUser;
                addSectionGroup.Sections.Add(sectionName, clientSettingsSection);
            }
            SettingElement addMe = new SettingElement(propertyName, SettingsSerializeAs.String);
            XmlElement element = new XmlDocument().CreateElement("value");
            element.InnerText = value;
            addMe.Value.ValueXml = element;
            clientSettingsSection.Settings.Add(addMe);
                
            store.Save();
        }
    }
}

Monday, January 3, 2011

Implementing System.Configuration.SettingsProvider

I wanted to store all settings for an application as user settings so my app wouldn't writing to the app.config and require administrative privileges.  I struggled figuring out how the components in System.Configuration worked together, but luckily Reflector, Visual Studio's .NET Framework Source debugging, ProcessMonitor, MSDN articles, StackOverflow posts, and Code Project articles helped get my scenario working.

During this process I learned the SettingsProvider only handles retrieving and saving values to the datastore and how to implement one.  I didn't end up using it since the LocalFileSettingsProvider met my needs, but the code below is a good starting point for someone wanting to implement settings persistence in a different manner:

using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Diagnostics;
 
public sealed class CustomettingsProvider : SettingsProviderIApplicationSettingsProvider
{
    NameValueCollection settingValues = new NameValueCollection();
 
    public override void Initialize(string name, NameValueCollection config)
    {
        Debug.WriteLine("in initialize override");
        base.Initialize(this.ApplicationName, settingValues);
    }
 
    /// <summary>
    /// MSDN states this property should be implemented with this getter and a do nothing setter.
    /// </summary>
    public override string ApplicationName
    {
        get  {  return (System.Reflection.Assembly.GetExecutingAssembly().GetName().Name); }
        set { Debug.WriteLine("set application name called"); }
    }
 
    public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
    {
        SettingsPropertyValueCollection returnValue = new SettingsPropertyValueCollection();
        foreach (SettingsProperty item in collection)
        {
            SettingsPropertyValue addMe = new SettingsPropertyValue(item);
            addMe.PropertyValue = String.Empty;
            returnValue.Add(addMe);
        }
 
        return returnValue;
    }
 
    public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
    {
        Debug.WriteLine("in setPropertyValues");
 
        foreach (SettingsPropertyValue item in collection)
        {
            bool isUserScoped = (item.Property.Attributes[typeof(UserScopedSettingAttribute)] is UserScopedSettingAttribute);
            bool isAppScoped = (item.Property.Attributes[typeof(ApplicationScopedSettingAttribute)] is ApplicationScopedSettingAttribute);
            if (isUserScoped && isAppScoped)
            {
                throw new ConfigurationErrorsException("Property can't be userScoped and appScoped according to msdn: http://msdn.microsoft.com/en-us/library/system.configuration.settingsprovider(VS.80).aspx");
            }
        }
    }
 
    public SettingsPropertyValue GetPreviousVersion(SettingsContext context, SettingsProperty property)
    {
        Debug.WriteLine("in getPreviousVersion");
        throw new NotSupportedException("Get Previous Version is not supported");
    }
 
    public void Reset(SettingsContext context)
    {
        Debug.WriteLine("in Reset");
    }
 
    public void Upgrade(SettingsContext context, SettingsPropertyCollection properties)
    {
        Debug.WriteLine("in Upgrade");
    }
}

Monday, December 27, 2010

Quickly Testing and Changing Code

Sometimes when running software it is hard to make a particular code path execute.  Like if you want to ensure your code behaves correctly when bad data is passed to your function.  I love Visual Studio and the debugging capabilities are one of the things I love about it.  Using the watch window the values of variables can be changed to check those code paths, and by right clicking on a line one can set the next statement of execution wherever they want.

If we have code like this

int customerId = getCustomer("bob");
bool log = Settings.ShouldLog();
if(customerId < 0 && log)
{
    log.LogError("bob is invalid");
}

We can make the log code run by setting a breakpoint on the if line and then changing customerId to -1 and log to true in the watch window.  If we want to change the code in the if block and retest changing these values every time is repetitive.  Changing the breakpoint on the "if" to a conditional breakpoint allows additional code to execute when the breakpoint is evaluated.  We can leverage this feature to change the breakpoint condition to "customerId = -1."  Each time the breakpoint is evaluated customerId will be set to -1.  This is great, but in our example we have two variables which need to be set properly to satisfy the if condition.  To change both variables the conditional breakpoint can be changed to a lambda like this: () => { customerId == -1; log == false;};

I've found this technique can help me rapidly iterate on code changes to ensure edge case scenarios or hard to reproduce data scenarios execute properly for code that isn't under test.

Monday, December 20, 2010

How hard is it to change a column datatype?

One of our clients initially specified one of their columns as an int, and after being deployed for a month realized it needed to support other characters.  I found a couple of ways to do this in SQL Server 2008.

We'll use this sample table:

CREATE TABLE dbo.Orders
      (  OrderId int NOT NULL IDENTITY (1,1),
         OrderNumber int NOT NULL
      ) 
INSERT INTO Orders
      SELECT 234
     
SELECT * FROM Orders



The way I normally do things is by generating a change script in SQL Server Management Studio.  Here is the SQL output by that process.

CREATE TABLE dbo.Tmp_Orders
      (
      OrderId int NOT NULL IDENTITY (1, 1),
      OrderNumber varchar(50) NOT NULL
      )  ON [PRIMARY]
GO
ALTER TABLE dbo.Tmp_Orders SET (LOCK_ESCALATION = TABLE)
GO
SET IDENTITY_INSERT dbo.Tmp_Orders ON
GO
IF EXISTS(SELECT * FROM dbo.Orders)
       EXEC('INSERT INTO dbo.Tmp_Orders (OrderId, OrderNumber)
            SELECT OrderId, CONVERT(varchar(50), OrderNumber) FROM dbo.Orders WITH (HOLDLOCK TABLOCKX)')
GO
SET IDENTITY_INSERT dbo.Tmp_Orders OFF
GO
DROP TABLE dbo.Orders
GO
EXECUTE sp_rename N'dbo.Tmp_Orders', N'Orders', 'OBJECT'
GO


I wasn't thrilled about copying the entire table for our whole dataset.  I thought the change script should create a new column, copy the data to it, remove the old column, and rename the new column to the old column's name.  Googling about for a way to do this I found it was even easier in SQL Server:


ALTER table dbo.Orders ALTER Column OrderNumber varchar(50)


It's a good old one liner :)

Monday, December 13, 2010

Oh how I love Visual Studio

Over the past month I've been working with a lot of JavaScript and I was really missing these features from Visual Studio:

  1. No find in solution - I was actually opening up Visual Studio and using the find feature to search my JavaScript directory structure to find things.
  2. No solution explorer - I had to keep switching over to Windows Explorer to see the files I wanted to open.
  3. No AhnkSVN - I love having source control integrated, so I don't have to worry about adding files, ignoring files, etc.
I found out I can get all my Visual Studio love by creating a web project at the root of my JavaScript Solution.  It gave me back all the features I missed without any problems.

Huzzah!


Monday, December 6, 2010

OpenID on DotNetNuke

I hate remembering passwords and I don't like creating accounts on sites and worrying they are storing my password incorrectly.  I love OpenID because it solves those two issues for me by enabling OpenID sites to use an already existing account for login.  For example I use my gmail account on Facebook and StackOverflow to login.

This post walks through enabling OpenID after installing DotNetNuke.
  1. Click login and login as host
  2. Admin Menu --> Extensions menu option
  3. Authentication System section
  4. click the pencil icon next to 'DNN_OpenIDAuthentication'
  5. check the Enabled? checkbox
  6. Click the 'Update Authentication Settings' to apply the settings.
  7. logout
  8. click the login button
  9. select the OpenID tab
  10. In the OpenID: field enter the url for your OpenID provider (google's url is https://www.google.com/accounts/o8/id)
  11. Click the Login button
  12. Under the 'Register a new account' section change the first name, last name, display name, and email address.
  13. Save the changes, logout, and log back in to ensure it worked.
DotNetNuke leverages the ASP.NET membership provider infrastructure and I plan on incorporating it into future projects I work on.  The biggest issue I see with the technology is that most implementations today require entering your OpenID provider URL.  I think this is bad user interface.  I like how StackOverflow shows pictures of many providers allowing a user to select their provider without needing to memorize or lookup a URL.

Monday, November 29, 2010

Installing .NET Nuke

I read DotNetNuke supports OpenID out of the box and I wanted to try it out.  This post covers the installation of DNN.  A future post will cover configuring OpenID.

Environment:
  • Windows 7 64 bit
  • IIS 7.5
  • SQL Server 2008 R2

Downloaded DotNetNuke Community Edition version 5.05.01

Listened to the DotNetNuke Installation Webinar while doing the following:

Right click properties on the zip file. Uncheck Unblock.

Unzipped the community edition.

read .\Documentation\Readme.txt

Open SQL Server Management Studio connect to .\

Created a new database on my local machine named DotNetNukeTest.  Set initial size to 20 MB.

Expand the security node.  Right click Logins 
Click 'New Login...'
Select SQL Server Authentication

login Name: DotNetNuke
password:IloveGravityWorks
Uncheck Enforce password policy
select User Mapping map to DotNetNukeTest and set role membership to db_owner

Moved the folder to c:\www\

Opened Internet Information Services Manager

Expanded the Sites node.

Right click Sites, Add Site
  Site Name: DotNetNukeTest
  Change Application Pool back to DefaultAppPool
  Physical path: C:\www\DotNetNuke_Community_05.05.01_Install

  Port: 8081

right click C:\www\DotNetNuke_Community_05.05.01_Install -> Properties
select Security tab
click Edit
Click Add
Ensure From this location: is set to your computer name
Object name --> IIS AppPool\DefaultAppPool
OK
Grant the DefaultAppPool user modify permissions
OK
OK

Browse to http://localhost:8081 in Chrome

Select Custom install (I always like doing this for new software)

Next through File Permissions Summary.

Select 'SQL Server 2005/2008 Database' instead of SQL Server 2005/2008 (Express File).
Server: "."
Database: "DotNetNukeTest"

Uncheck 'Integrated Security"
User ID: "DotNetNuke"
Password:'IloveGravityWorks'

I fix my SQL Server authentication and then click Next

change the password, and email fields.  I leave SMTP alone.

on the Install Optional Modules I select all modules:
Announcements (04.00.03), Blog (04.00.00), Documents (04.01.00), Events (05.01.04), FAQs (04.04.00), Feedback (05.00.02), FormAndList (05.01.03), Forum (04.05.03), Help (03.00.02), HTML (Community (05.04.03), IFrame (04.04.00), Links (04.00.01), Map (01.00.09), Media (03.03.00), Messaging (01.01.00), NewsFeeds (04.00.01), Reports (05.01.00), Repository (03.01.15), Store (02.01.36), Survey (04.60.00), Taxonomy (01.01.00), Telerik (05.05.00), UsersOnline (05.01.00), Wiki (04.02.00), XML (04.03.05)
Next
Next through Skins and Containers
Next through Language Packs
Next through Authentication Systems:
Next through Providers
For Portal Administrator set password, email address, and Portal Title.
Click 'Start building your new Site!'

Monday, November 22, 2010

Blacklisted Oh No! - More reliable email

I've had email issues for about 1.5 years.  Often times when sending email to a new contact my email doesn't arrive in their inbox because it gets put in their spam filter or doesn't even reach their inbox at all.

At a party this summer I spoke with some network guys who suggested googling email black list and checking to see if my domain was black listed.

The first hit returned mxtoolbox, and a search for my domain did show I was listed once :(
http://www.mxtoolbox.com/SuperTool.aspx?action=blacklist:davidsilvasmith.com

Clicking on the entry I was taken to the listing website which had a button to whitelist myself.

198.173.106.90 was found in NOMOREFUNN!
no-more-funn.moensted.dk -> 127.0.0.7
31/03/02 importet from fiveten after SPAM


Checking back a day later, my domain was no longer listed, and now 2 months later, my emails seem to be getting through to recipients.




Monday, November 15, 2010

Troubleshooting a failed login with correct user name and password

After setting up a new Database in SQL Server 2008 R2 and configuring DotNetNuke (DNN), an ASP.NET application, to connect to the new database I got received this message:
Connection Error(s):
Index #: 0
Source: .Net SqlClient Data Provider
Class: 14
Number: 18456
Message: Sql login failed

I open up SQL Server Management Studio and reset the user password.  Try again from DNN, same error.

I try connecting via SQL Server Management Studio.  Login failed.

I look in event Viewer and see
Login failed for user 'dotnetnuke'. Reason: An attempt to login using SQL authentication failed. Server is configured for Windows authentication only. [CLIENT: <local machine>]

I open up up SQL Server Management Studio and connect to '.'

I right click the SQL Server 10.50.1600 node and

  1. Select properties
  2. Select Security Page
  3. Under Server authentication change it to 'SQL Server and Windows Authentication mode'


Right click the SQL Server 10.50.1600 node again and select 'Restart' from the sub menu.

Back to the application and login is successful.

Monday, October 11, 2010

Access Denied on Adding a Database

Today I ran into a problem where I couldn't run a create script to create a local database in SQL Server Management Studio 2008 running on 64 bit Windows 7 Professional.  This is the error I received:

Msg 5123, Level 16, State 1, Line 1
CREATE FILE encountered operating system error 5(Access is denied.) while attempting to open or create the physical file 'c:\Program Files\Microsoft SQL Server\MSSQL10.SQLEXPRESS\MSSQL\DATA\xxx.mdf'.
Msg 1802, Level 16, State 4, Line 1
CREATE DATABASE failed. Some file names listed could not be created. Check related errors.

This is how it was fixed on my machine:

  1. Fire up SysInternals Process Monitor
  2. Open the 'Filter' menu
  3. Click 'Filter...'
  4. From the first dropdown select 'Path' in the second dropdown select 'contains'.
  5. For the value enter '\data'
  6. Click the 'OK' button to add the filter.
  7. Retry the operation
  8. Open up one of the NAME NOT FOUND entries with a Detail of Access Denied.
  9. Click the Process tab
  10. Note the process is sqlsvr.exe and the user is NT Authority\Network Service
  11. Navigate to the Data directory in Windows Explorer
  12. Click the Security tab
  13. Click the 'Edit' Button
  14. On the Permissions for 'Data' click the 'Add' button.
  15. For the object name enter 'Network Service'
  16. Click 'OK'
  17. Disregard any errors regarding not being able to set the permissions on files.
  18. Retry the operation in Sql Server Management Studio and it should work!

Monday, October 4, 2010

Unable to rename database in SQL Server Management Studio 2008

Today I wanted to rename a SQL Server 2008 database so I would have a rollback if my create from script went bad.

Upon rename SQL Server Management Studio 2008 gave me the error 'The database could not be exclusively locked to perform the operation. (Microsoft SQL Server, Error: 5030)'

I executed this in a query window connected to the master database:
sp_who2

Looking at the DBName column in the results for each match of the db I was trying to rename I executed 'kill xxx' where xxx was equal to the SPID of the row.

After doing this for all the rows rename succeeded immediately.

Monday, September 27, 2010

Back to Lansing!

Thursday September 9th was my last day at Sonoma Partners, the best Customer Relationship Management (CRM) Dynamics parter in the business.  Sonoma delivers projects on time, on scope, and on budget sometimes receiving amazing unsolicited feedback from satisfied customers.

Amelia Marschall and Jeff McWherter gave me the opportunity to work with talented individuals such as Scott Gowell, and Lauren Colton helping individuals in the greater Lansing area overcome technical challenges and giving back to the community by helping fight extreme poverty and cure preventable diseases, or  working to double the number of college graduates in Michigan.

I started at Gravity Works Design and Development on Monday September 13th.  I've already delivered some great value for customers and had some great times.  I'm looking forward to helping grow the Lansing and Michigan economies.

Monday, June 7, 2010

Windbg Ignoring Sympath when called programatically

Working with mdbglib this weekend my call to .sympath was registering but then none of the symbol files could be found.  This post will show how to resolve this issue and the process I followed to solve it.

Environment Windows Server R2 64 bit.  The app is compiled for x86 and running as a 32 bit app.

  1. lm e
    This listed modules which had errors.
  2. reload -f
    Force a reload of all modules.  Noticed none of them were loading from my path.
  3. Hooked up SysInternals Process Monitor filtered to just my process name and just disk activity.  cleared the output right before doing reload -f
    NOticed it was attempting to load modules from the same directory I was running from.
  4. restarted the application
  5. !sym noisy
    To noisy load symbols.
  6. .reload

    DBGHELP: SymSrv load failure: symsrv.dll
    DBGHELP: wntdll.pdb - file not found
    *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\SysWOW64\ntdll.dll - 
    DBGHELP: ntdll - export symbols
    DBGHELP: wuser32.pdb - file not found
    *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\syswow64\USER32.dll - 
    DBGHELP: USER32 - export symbols
    DBGHELP: System.Windows.Forms.pdb - file not found
    *** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v2.0.50727_32\System.Windows.Forms\fedf1ba58dced4f0b3f8c457648ceed9\System.Windows.Forms.ni.dll
    *** ERROR: Module load completed but symbols could not be loaded for C:\Windows\assembly\NativeImages_v2.0.50727_32\System.Windows.Forms\fedf1ba58dced4f0b3f8c457648ceed9\System.Windows.Forms.ni.dll
    DBGHELP: System_Windows_Forms_ni - no symbols loaded

  7. Copied symsrv.dll into my local directory even though it is already in the app path.
  8. Restarted, !sym noisy, .reload
    same results
  9. Filter Process monitor where the path ends in symsrv.dll
    Noticed the process is trying to load symsrv.dll from C:\Windows\SysWOW64\
  10. Copied symsrv.dll to C:\Windows\SysWOW64
    HACK!
  11. restarted, !sym noisy, .reload
    success!
  12. .chain
    Shows dbghelp.dll is loaded from c:\Windows\system32\dbghelp.dll
    MSDN writes symsrv must be installed in the same directory as the copy of dbghelp.dll being loaded.
  13. .extpath C:\Program Files (x86)\Debugging Tools for Windows (x86)\
    sets dbgeng's extension search path to where symsrv.dll is.
  14. .chain
    dbghelp.dll is still loaded from the system32 directory.
  15. Prior to running the debugger I set the environmental variable _NT_DEBUGGER_EXTENSION_PATH to C:\Program Files (x86)\Debugging Tools for Windows (x86)\
  16. Restart, .chain
    dbghelp is loaded from the correct directory! but symsrv.dll is still broken :(
    Looking at the stack in Process monitor shows dbgeng.dll is being loaded from C:\Windows\SysWOW64\dbgeng.dll which calls dbghelp.dll in the same directory.
    Changing process monitor to filter for requests ending in dbgeng.dll shows when the program runs it is looking in the executing applications directory for the dll, not finding it, and then loading it from C:\Windows\SysWOW64\dbgeng.dll
  17. Head over to pinvoke.net to grab the loadlibrary signature and put it in my application.

    [DllImport("kernel32", SetLastError = true)]
    static extern IntPtr LoadLibrary(string lpFileName);

  18. Call LoadLibrary from C# prior to loading my debugger and now the correct dll is bing loaded, but the debugging commands don't return any values :(
  19. Reading the LoadLibrary documentation and then the dll search order documentation I think calling SetDllDirectory may do what I want.
  20. Back to pinvoke.net to grab this signature
    [DllImport("kernel32.dll", SetLastError=true)]
    static extern bool SetDllDirectory(string lpPathName); 
  21. Call SetDllDirectory prior to loading up the debugger and everything now works as expected!

Here is the code:

internal static class UnsafeNativeMethods
{
    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern bool SetDllDirectory(string lpPathName);
}



public static class Utility
{
    public static void ProvideWindbgInstallationDirectory(string path)
    {
        if (Directory.Exists(path) == false)
        {
            throw new DirectoryNotFoundException(path);
        }
        #warning add a check that the files we want exist.  maybe even that they are the correct version?
        bool setPath = UnsafeNativeMethods.SetDllDirectory(path);
        if (setPath == false)
        {
            int error = Marshal.GetLastWin32Error();
            throw new InvalidOperationException("Setting the installation directory failed with error code: " + error.ToString());

        }
    }
}



Monday, March 29, 2010

Using WindDbg and Sosex to Identify an ASP.NET Deadlock

This post will show how to use create an ASP.NET deadlock and then then use WinDbg and sosex to identify it the idea is to practice this before needing it when debugging a production server :)


The environment for my test is

  1. Create a new web application and put this in the code behind for Default.aspx:
    public partial class _Default : Page
    {
        /// <summary>
        /// Locks on page load.
        /// </summary>
        protected void Page_Load(object sender, EventArgs e)
        {
            object lockA = new object();

            object lockB = new object();

            ThreadStart firstJob = new ThreadStart(() => this.DualLockingCall(lockA, lockB));
            Thread firstThread = new Thread(firstJob);
            firstThread.Name = "firstThread";
            firstThread.Start();

            ThreadStart secondJob = new ThreadStart(() => this.DualLockingCall(lockB, lockA));
            Thread secondThread = new Thread(secondJob);
            secondThread.Name = "secondThread";
            secondThread.Start();

            Thread.Sleep(Timeout.Infinite);
        }

        /// <summary>
        /// lock on a then lock on b.
        /// </summary>
        /// <param name="onThis">The first instance to lock on.</param>
        /// <param name="onThat">The second instance to lock on.</param>
        private void DualLockingCall(object onThis, object onThat)
        {
            lock (onThis)
            {
                lock (onThat)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(5));
                }
            }
        }
    }
  2. Navigate to the url like localhost/projectName/Default.aspx
  3. Open a command prompt and navigate to the debugging tools for windows directory.
  4. Run this command adplus -pn w3wp.exe -o c:\ -hang
    1. The hang command takes a memory dump immediately.  It doesn't wait for a hang.
  5. Note the output directory.  It should be the last line adplus printed out.
  6. Run windbg and press ctrl+d to open a memory dump.  Navigate to the dump adplus just created.
  7. type .loadby sos mscorwks - This will load the sos.dll from the same directory mscorwks was loaded from.
  8. type .load pathToSosex\sosex.dll
  9. type !dlk
  10. On my machine I get this output:
    *** WARNING: Unable to verify checksum for LockItUp.DLL
    *** ERROR: Module load completed but symbols could not be loaded for LockItUp.DLL
    Deadlock detected:
    CLR thread 8 holds sync block 0000000002583228 OBJ:00000001bf1f2be0[System.Object]
                 waits sync block 00000000025831e0 OBJ:00000001bf1f2bf8[System.Object]
    CLR thread 9 holds sync block 00000000025831e0 OBJ:00000001bf1f2bf8[System.Object]
                 waits sync block 0000000002583228 OBJ:00000001bf1f2be0[System.Object]
    CLR Thread 8 is waiting at LockItUp._Default.DualLockingCall(System.Object, System.Object)(+0x25 IL)(+0x74 Native)
    CLR Thread 9 is waiting at LockItUp._Default.DualLockingCall(System.Object, System.Object)(+0x25 IL)(+0x74 Native)

    1 deadlock detected.
Easy cheesy in this simulated test eh?