Tuesday, October 23, 2012

Create Windows Shortcuts for Remote Development

A couple months ago I decided to posted about manually creating shortcuts for handling remote development. Below is a function that will create a shortcut and will attach an icon to the shortcut.
function Create-Shortcut([string] $shortcutName, [string] $target, [string] $username, [string] $iconTarget, [string] $workingDirectory="") {
 $WshShell = New-Object -comObject WScript.Shell;
 $Shortcut = $WshShell.CreateShortcut("$Home\Desktop\$shortcutName`.lnk");
 Write-Host "C:\Windows\System32\runas.exe /user:$username /netonly `"$target`"";
 $Shortcut.TargetPath = "C:\Windows\System32\runas.exe"
 $Shortcut.Arguments = "/user:$username /netonly `"$target`"";
 if ([string]::IsNullOrEmpty($iconTarget)) {
  $Shortcut.IconLocation = "$target, 0";
 } else {
  $Shortcut.IconLocation = $iconTarget;
 }
 $Shortcut.WorkingDirectory = $workingDirectory;
 $Shortcut.Save();
}
Here is the full script that will do the heavy lifting. The only limitation is that you still need to set the shortcuts to "Run as administrator" in the advanced settings. I have not found a successful way to programatically set the flag, so for now, it will have to be a manual process.
param(
 [string] $ProjectPrefix = "TEST", # $(Read-Host "Project Prefix"),
 [string] $userName = "domain\user" # $(Read-Host "Username")
)

function Main
{
 Create-Shortcut "$ProjectPrefix Visual Studio 2010" "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe" $userName;
 Create-Shortcut "$ProjectPrefix SQL Server Management Studio" "C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\Ssms.exe" $userName;
 Create-Shortcut "$ProjectPrefix Visual Studio Command Line" "%comspec% /k `"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat`" x86" $userName -iconTarget "%comspec%, 0";
 Create-Shortcut "$ProjectPrefix LINQPad 4" "C:\Program Files (x86)\LINQPad\LINQPad.exe" $userName;
 Create-Shortcut "$ProjectPrefix Internet Explorer" "C:\Program Files (x86)\Internet Explorer\iexplore.exe" $userName;
}

function Create-Shortcut([string] $shortcutName, [string] $target, [string] $username, [string] $iconTarget, [string] $workingDirectory="") {
 $WshShell = New-Object -comObject WScript.Shell;
 $Shortcut = $WshShell.CreateShortcut("$Home\Desktop\$shortcutName`.lnk");
 Write-Host "C:\Windows\System32\runas.exe /user:$username /netonly `"$target`"";
 $Shortcut.TargetPath = "C:\Windows\System32\runas.exe"
 $Shortcut.Arguments = "/user:$username /netonly `"$target`"";
 if ([string]::IsNullOrEmpty($iconTarget)) {
  $Shortcut.IconLocation = "$target, 0";
 } else {
  $Shortcut.IconLocation = $iconTarget;
 }
 $Shortcut.WorkingDirectory = $workingDirectory;
 $Shortcut.Save();
}

& Main;

Sunday, October 7, 2012

C# AES Encryption & Decryption Methods

I had a hard time finding a set of functions to handle AES encryption and decryption correctly so I figured I would share working ones with the world.

Suppose the application has it's secret values securely loaded. Below is not a secure way to handle this, but for the purpose of this post we will be overly transparent.
key = "PasswordP@s$w0rdPa$$w0rdP@ssw0rd";
salt = "S@lt$@ltSa|tSaltS@lt$a|t$@|tSalT";
iv = "iv1^!vi^1viV!^iv"; // length must be 16 bytes
Here is an example of it's potential use.
cipherText = clearText.Encrypt(key, salt, iv);
clearText2 = cipherText.Decrypt(key, salt, iv);
Below is the section of code that handles the encryption and decryption of the strings. Feel free to change 72 to a different number; if you change it here, you MUST change the Decrypt function to use the same number of rounds. This is used to figure out how many rounds of encryption will occur. Note that fewer rounds is faster but potentially less secure and the opposite for more rounds.
/// 
/// Encrypts text given default values
/// 
/// /// /// Password Salt value/// 16 character string/// 
public static string Encrypt(this string PlainText, string Password, string Salt, string InitialVector)
{
    return PlainText.Encrypt(Password, Salt, "SHA1", 72, InitialVector, 256);
}

/// 
/// Encrypts a string given set of parameters
/// 
/// /// /// /// /// /// /// /// 
public static string Encrypt(this string PlainText, string Password, string Salt, string HashAlgorithm, int PasswordIterations, string InitialVector, int KeySize)
{
    try
    {
        byte[] PlainTextBytes = Encoding.UTF8.GetBytes(PlainText);
        byte[] CipherTextBytes;
        using (ICryptoTransform Encryptor = CreateRijndael(Password, Salt, HashAlgorithm, PasswordIterations, InitialVector, KeySize, CryptoTransformDirection.Encrypt))
        {
            using (MemoryStream MemStream = new MemoryStream())
            {
                using (CryptoStream cryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write))
                {
                    cryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
                    cryptoStream.FlushFinalBlock();
                    CipherTextBytes = MemStream.ToArray();
                }
            }
        }
        return Convert.ToBase64String(CipherTextBytes);
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

/// 
/// Decrypts text given default values
/// 
/// /// /// Password Salt value/// 16 character string/// 
public static string Decrypt(this string CipherText, string Password, string Salt, string InitialVector)
{
    return CipherText.Decrypt(Password, Salt, "SHA1", 72, InitialVector, 256);
}

/// 
/// Decrypts a string given set of parameters
/// 
/// /// /// /// /// /// /// /// 
public static string Decrypt(this string CipherText, string Password, string Salt, string HashAlgorithm, int PasswordIterations, string InitialVector, int KeySize)
{
    try
    {
        byte[] CipherTextBytes = Convert.FromBase64String(CipherText);
        byte[] PlainTextBytes;
        int ByteCount;
        using (ICryptoTransform Decryptor = CreateRijndael(Password, Salt, HashAlgorithm, PasswordIterations, InitialVector, KeySize, CryptoTransformDirection.Decrypt))
        {
            using (MemoryStream MemStream = new MemoryStream(CipherTextBytes))
            {
                using (CryptoStream cryptoStream = new CryptoStream(MemStream, Decryptor, CryptoStreamMode.Read))
                {
                    PlainTextBytes = new byte[CipherTextBytes.Length];
                    ByteCount = cryptoStream.Read(PlainTextBytes, 0, PlainTextBytes.Length);
                }
            }
        }
        return Encoding.UTF8.GetString(PlainTextBytes, 0, ByteCount);
    }
    catch (Exception ex)
    {
        //return String.Empty;
        throw ex;
    }
}

/// 
/// Enumeration to define which direction the crypto is going
/// 
public enum CryptoTransformDirection {
    Encrypt,
    Decrypt
}

/// 
/// Gets a transform object which will perform the Encryption/Decryption
/// 
/// /// /// /// /// /// /// Encrypt/Decrypt/// 
public static ICryptoTransform CreateRijndael(string Password, string Salt, string HashAlgorithm, int PasswordIterations, string InitialVector, int KeySize, CryptoTransformDirection direction)
{
    byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
    byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
    PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
    byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
    RijndaelManaged SymmetricKey = new RijndaelManaged();
    SymmetricKey.Mode = CipherMode.CBC;
    if (direction == CryptoTransformDirection.Decrypt)
    {
        return SymmetricKey.CreateDecryptor(KeyBytes, InitialVectorBytes);
    }
    else
    {
        return SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes);
    }

}

Thursday, September 27, 2012

jQuery Plugin to Register a Function to Run on UpdatePanel Request

Short post, long title. Here is a simple jQuery plugin that makes it a little easier to add an begin/end request handler to the PageRequestManager on an ASP.NET web form.
(function ($, undefined) {
    $.registerAsyncBegin = function(callback) {
        var prm = Sys.WebForms.PageRequestManager.getInstance();
        if (prm) {
            prm.add_beginRequest(callback);
        }
        return callback;
    };
    $.registerAsyncEnd = function(callback) {
        var prm = Sys.WebForms.PageRequestManager.getInstance();
        if (prm) {
            prm.add_endRequest(callback);
        }
        return callback;
    };
})(jQuery);
This example adds a function to be executed after ever Update Panel request and runs the function. This is useful if you have a function that needs to do some initial processing as well as after a request.
($.registerAsyncEnd(SomeCallbackFunction))();

Tuesday, September 11, 2012

Add Git Bash Option to Context Menu

I found a nice article, Adding “Open in Git Bash” to the Context Menu, which gives describes the process for adding the Git Bash command to the context menu. I am not a fan of requiring a batch file, we should be able to do it without the batch file.

This is set up for the 32-bit Git install. Here is the command that we can use to not use the batch files.
"cmd.exe /c \"cd \\\"%V\\\" && \"C:\\Program Files (x86)\\Git\\bin\\sh.exe\" --login -i\""
If you want to use a different icon, use regedit.exe and navigate to Directory\shell right-click and click "new" and then "expandable string value". Name the new string "Icon" and then set the value to be the location of the icon to use. Export that key and copy the "Icon" entry and past into the appropriate locations below, then delete the "Icon" string that was just created. For a good write-up on this, check out this tutorial http://www.sevenforums.com/tutorials/21878-context-menu-add-shortcuts-icons.html.

Here is my solution:
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Classes\Directory]

[HKEY_CURRENT_USER\Software\Classes\Directory\Background]

[HKEY_CURRENT_USER\Software\Classes\Directory\Background\shell]

[HKEY_CURRENT_USER\Software\Classes\Directory\Background\shell\bash]
@="Open in Git &Bash"

[HKEY_CURRENT_USER\Software\Classes\Directory\Background\shell\bash\command]
@="cmd.exe /c \"cd \"%V\" && \"C:\\Program Files (x86)\\Git\\bin\\sh.exe\" --login -i\""
"Icon"=hex(2):43,00,3a,00,5c,00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,20,\
  00,46,00,69,00,6c,00,65,00,73,00,20,00,28,00,78,00,38,00,36,00,29,00,5c,00,\
  47,00,69,00,74,00,5c,00,65,00,74,00,63,00,5c,00,67,00,69,00,74,00,2e,00,69,\
  00,63,00,6f,00,00,00

[HKEY_CURRENT_USER\Software\Classes\Directory\shell]

[HKEY_CURRENT_USER\Software\Classes\Directory\shell\bash]
@="Open in Git &Bash"

[HKEY_CURRENT_USER\Software\Classes\Directory\shell\bash\command]
@="cmd.exe /c \"cd \"%V\" && \"C:\\Program Files (x86)\\Git\\bin\\sh.exe\" --login -i\""
"Icon"=hex(2):43,00,3a,00,5c,00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,20,\
  00,46,00,69,00,6c,00,65,00,73,00,20,00,28,00,78,00,38,00,36,00,29,00,5c,00,\
  47,00,69,00,74,00,5c,00,65,00,74,00,63,00,5c,00,67,00,69,00,74,00,2e,00,69,\
  00,63,00,6f,00,00,00
I have not figured out how to set the icon on the CMD window to the git icon instead of the basic CMD icon. It is not that important, it works without it. Perhaps one day.

Thursday, September 6, 2012

Javascript Fix Up For Internet Explorer Select Options Truncation

OK, that is a long title, but the issue has real world implications. Since there are still requirements that require support for Internet Explorer 8, it would behoove me to have an elegant pattern that fixes the limitations of IE8 and enhances the user experience.

My initial pass is adapted from http://css-tricks.com/select-cuts-off-options-in-ie-fix/. I found it some what long winded and only solved part of the problem.
function dropDownListFixup(sender, args) {
    var el,
        ua = $.browser,
        up = (sender) ? "#" + sender._updatePanelClientIDs.join(",#") : null,
        selects = $("select", up);
    if (ua.msie && Number(ua.version) < 9) {
        selects
            .each(function () {
                el = $(this);
                el.data("oWidth", el.outerWidth());
            })
            .mouseenter(function () {
                $(this).css("width", "auto");
            })
            .bind("blur change", function () {
                el = $(this);
                el.css("width", el.data("oWidth"));
            });
    }
    selects.each(function (e) {
        this.title = $(":selected", $(this)).text();
    });
}

$(function () {
    var prm = Sys.WebForms.PageRequestManager.getInstance();
    prm.add_endRequest(dropDownListFixup);
    dropDownListFixup();
    $("body").delegate("select", "change", function (e) { 
        this.title = $(":selected", $(this)).text();
    });
});
After some refactoring and extending, I came up with a solution that is slightly more compact, but provides slightly more functionality and handles drop down lists in update panels. Since even if the drop downs are in modern browsers, the text may be truncated on the closed display if the width is set too small. To fix this issue, I set the title on the drop down so that if the user hovers over the control, the tool tip will display with the value.
// Adapted from: http://css-tricks.com/select-cuts-off-options-in-ie-fix/
function dropDownListSetTitles(sender, args, e) {
    var selects = $("select", ((sender) ? "#" + sender._updatePanelClientIDs.join(",#") : null));
    selects.each(function (e) {
        var el = $(this);
        el.data("oWidth", el.outerWidth());
        setDropDownListTitle.apply(this, [e]);
    });
}
function setDropDownListTitle(e) {
    this.title = $(":selected", $(this)).text();
}
$(function () { // dropDownListFixup
    dropDownListSetTitles();
    if ($.browser.msie && Number($.browser.version) < 9) { //// mouseenter  mouseleave
        $("body").delegate("select", "focus", function () {
            $(this).css("width", "auto");
        })
        .delegate("select", "blur change", function () {
            var el = $(this);
            el.css("width", el.data("oWidth"));
        });
    }
    $("body").delegate("select", "change", setDropDownListTitle);
    Sys.WebForms.PageRequestManager.getInstance().add_endRequest(dropDownListSetTitles);
});

Thursday, August 30, 2012

Open Blogger Drafts In Windows Live Writer

Blogging is a feast or famine activity for me.  Sometimes the topics and words just flow out as they have recently.  The recent update to Blogger’s administration interface and editing to be themed more like Google+ and Docs is long deserved and an excellent enhancement.  It does miss the ability to handle the displaying of syntax highlighted code, but you can still do it if you want to always view the HTML while writing.  Sometimes I like using the web interface, sometimes I like to use an actual application.  It isn’t very obvious that you can open Blogger drafts and in fact it took me a while to figure it out.   In hind sight, it is rather easy to open your draft posts in Windows Live Writer. 

  1. Start from a normal post window
  2. Click the drop down menu to the left of the “Home” ribbon menu
  3. Click “Open recent post”
  4. Select your blog in the left pane
  5. All of the posts on the blog will display in the list area on the right

At first glance, it looked like it was only posts that were published. This however is not the case; it contains a list of ALL published, scheduled, and drafts. The application doesn’t denote the status of a post (thus my confusion), but at least it is possible to open drafts.

Saturday, August 18, 2012

Visual Studio 2010 Find/Replace with Regular Expressions

I recently encountered a situation that required that I change a nontrivial amount of strings in multiple files where the strings were predictable except for and id. The solution was using regular expressions in Visual Studio. Regular expressions in Visual Studio give to you all the power of .Net's engine plus some useful additions. I am sure there is a way to reduce the escaping, but it worked for me. Here are some examples, some of them are changed slightly from the previous example to accommodate a slightly different case. I am not going to comment on each as the examples should speak for themselves.
Find Regular Expression:
[$]\(\'input\[id[$]=\\\'{:i}\\\'\]\'\)\.focus\(\);

Matches string:
onfocus="$('input[id$=\'btnName\']').focus();"

Replace:
focusCtrl(\'\1\');

Find Regular Expression:
[$]\(\"input\[id[$]=\'{:i}\'\]\"\)\.focus\(\);

Test match string:
$(function () { $("input[id$='btnName']").focus(); });

Replace:
focusCtrl(\'\1\');

Find Regular Expression:
return process\(\'{:i}\', \'{:i}\'\);

Test match string:
return process('txtNameID', 'txtName');

Replace:
return process(\'\1\', \'\2\', \'<%= NameID %>\');

Result:
return process('txtNameID', 'txtName', '<%= NameID %>');

Find Regular Expression:
alt=\"Validation Error\" title=\"[^"]+\" src="/_layouts/images/EXCLAIM.GIF"

Test match string:
<img alt="Validation Error" title="Name is required" src="/_layouts/images/EXCLAIM.GIF" />

Replace:
onprerender="Validator_PreRender" runat="server"

Find Regular Expression:
alt=\"Validation Error\" title=\"[^"]+\" src="/_layouts/images/EXCLAIM.GIF"

Test match string:
<img alt="Validation Error" title="Name is required" src="/_layouts/images/EXCLAIM.GIF" />

Replace:
onprerender="Validator_PreRender" runat="server"

Find Regular Expression:
\<img alt=\"Validation Error\" title=\"[^"]+\" src="/_layouts/images/EXCLAIM.GIF" /\>[ ]*

Test match string:
<img alt="Validation Error" title="Name is required" src="/_layouts/images/EXCLAIM.GIF" />

Replace:
<img onprerender="Validator_PreRender" runat="server" />

Find Regular Expression:
\<span class="ms-error"\>\<img src="/_layouts/images/EXCLAIM.GIF" /\>\</span\>[ ]*

Test match string:
<img alt="Validation Error" title="Name is required" src="/_layouts/images/EXCLAIM.GIF" />

Replace:
<img alt="Validation Error" title="" src="/_layouts/images/EXCLAIM.GIF" />

Tuesday, August 14, 2012

Blogger Template Edit HTML Font Fixup

On occasion I update the HTML of my blog using bloggers built in editor and it displays using something resembling Times New Roman. It is a terrible font to view code with. I tried to see what errors were occurring and FireBug didn't reveal anything. After some digging, I openned Venkman and discovered this little gem:
Error: downloadable font: download not allowed (font-family: "Open Sans" style:normal weight:normal stretch:normal src index:1): content blocked
source: http://themes.googleusercontent.com/static/fonts/opensans/v5/u-WUoqrET9fUeobQW7jkRT8E0i7KZn-EPnyo3HZu7kw.woff
Source Code:
@font-face {   font-family: "Open Sans";   font-style: normal;   font-weight: normal;   src: local("Open Sans"), url("//themes.googleusercontent.com/static/fonts/opensans/v5/u-WUoqrET9fUeobQW7jkRT8E0i7KZn-EPnyo3HZu7kw.woff") format("woff"); }
I felt like i was hot on the trail. After a while googling and not finding any good way to fix it, I created the following GreaseMonkey script.
// ==UserScript==
// @name        Blogger Template Edit HTML Fixup
// @description Script that fixes the HTML template editor dialog's text area.  monospace font unavailable, defaulting to consolas.  Firefox gives me: Error: downloadable font: download not allowed (font-family: "Open Sans" style:normal weight:normal stretch:normal src index:1): content blocked.
// @namespace   net.intellectualponderings.BloggerTemplateEditHTMLFixup
// @include     http://draft.blogger.com/*
// @include     https://draft.blogger.com/*
// @grant       GM_addStyle
// @grant       GM_getValue
// @grant       GM_setValue
// @version     1
// ==/UserScript==

(function () {
  var cssTemplateEditHTMLFixFont = (<>).toString();
  
  //Initial Styles
  GM_addStyle( cssTemplateEditHTMLFixFont );
})();

UPDATE 2012-09-04: Added the @grant statements in the header to make the scripts work with GreaseMonkey 1.0.

Wednesday, August 8, 2012

Make Applications Work With Remote Domain Environment On Unconnected Computer

Working remotely via VPN in an environment where you are not attached to the domain is a pain if windows authentication is in use. Windows doesn't handle domain changes as gracefully as it should, especially if I was only going to be on it for a couple weeks. Fortunately, there are ways that you can remove the road blocks by creating some shortcuts and modifying the shortcuts.

Here are some examples of the shortcut targets I made: (obviously, change the domain\username to your domain credentials for the remote environment)
C:\Windows\System32\runas.exe /user:domain\username /netonly "C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\Ssms.exe"
C:\Windows\System32\runas.exe /user:domain\username /netonly "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe"
C:\Windows\System32\runas.exe /user:domain\username /netonly "%comspec% /k \"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat\" x86"
C:\Windows\System32\runas.exe /user:domain\username /netonly "C:\Program Files (x86)\Microsoft Visual SourceSafe\ssexp.exe"

In some cases you may need to run the applications as administrator. The easiest way I found to escalate privileges:
  1. Right-click on the shortcut
  2. Click Properties
  3. On the "Shortcut" tab, click "Advanced..."
  4. Check the "Run as administrator" checkbox
  5. Click OK (closes the Advanced Properties dialog)
  6. Click OK (closes the Properties dialog)
Now anytime you run that shortcut, it will run as administrator.

This method removed all of my authentication issues and I could development as usual.

Wednesday, July 25, 2012

Git-Tfs Setup And Development Workflow

I finally got around to testing the git-tfs tool. I originally looked at this several months ago and didn't have time to dive into it and WOW was I missing out.

Those That Came Before Me

There are some excellent blogs out there which have plenty of information on this tool, except they all seem to miss the a major issue that none of them address. I will cover them below and the highlights of the installation process.

The Setup

To set it up, just clone the git-tfs GitHub repository. If you use GitHub for Windows, just click the "Clone in Windows" button.

To build the solution, make sure to use the Visual Studio 2010 Command Line, or at least make sure the Visual Studio environment variables are set.
msbuild GitTfs.sln /p:Configuration=Vs2010_Debug
It appears that there is a hidden step that should immediately follow this, copying the output to the Git\bin directory. From the git-tfs directory where the solution file exists, run the following (update accordingly).
copy /Y GitTfs.Vs2010\bin\Debug\*.dll "C:\Program Files (x86)\Git\bin"
copy /Y GitTfs.Vs2010\bin\Debug\*.exe "C:\Program Files (x86)\Git\bin"
Verify that the Git\bin directory is in your path. Open a git bash shell and run:
git tfs help
If the command is found, it is ready to go. At this point a celebration may be appropriate. There is still plenty of awesomeness to come.

The Simple Development Workflow

The workflow for this is actually pretty simple. Git-tfs is only needed in 3 use cases:
  • Clone the project
  • Pulling updates from TFS
  • Pushing files to TFS
Once the project has been cloned, standard git best practices apply. All check-ins to the git repository are stored with the reposiotry.

Getting The Project Files

There are a couple ways to get files out of TFS. You can clone the entire TFS project or just part of it. Performing a quick-clone will generate a git repository with only the latest code (no history). Using clone will generate a git repository with full history. NOTE: in all examples below, omitting <Local Location> will generate the repository in the current directory.

The following is an example of the commands required:
git tfs clone http://vstfs:8080 $/Team.Project <Local Location>

git tfs quick-clone http://vstfs:8080 $/Team.Project <Local Location>
The following are examples of generating a partial repository.
git tfs clone http://vstfs:8080 $/Team.Project/ <Local Location>

git tfs quick-clone http://vstfs:8080 $/Team.Project/ <Local Location>
This can be very useful for instances where a TFS project has multiple root folders like branches, tags, and trunk. It doesn't make a lot of sense to clone the tags unless you have to have it especially if the trunk is needed.

From this point forward, all local changes can be maintained with Git Bash or GitHub for Windows until the repository needs to be updated or changes checked in.

Updating The Repository From TFS

Updating is straight forward. All merging is accomplished with Git's merging functionality and tools.
git tfs pull

Pushing to TFS

Once the changes are complete, use the following to check in the changes. Checking-in to TFS does a rebase, in that it only checks in the latest version and the other check-ins in the git repository are left as is but not persisted in TFS.
git tfs checkin -m "Add a great comment"

That

Friday, July 20, 2012

Visual Studio Package Manager Console Add Default Functions

After posting my useful one liners, I started thinking about ways to bake the functions into VS without having to copy and paste from my OneNote all the time. I remembered that powershell had profile scripts that run at the start just like most linux shells.

In order to add a profile to the Package Manager Console, create a file called NuGet_profile.ps1 in the C:\Users\\Documents\WindowsPowerShell directory.

# Copy the NuGet_profile.ps1 file to (the folder may not exist):
#
# C:\Users\<username>\Documents\WindowsPowerShell\NuGet_profile.ps1

function DebugWeb() {
 $dte.Debugger.LocalProcesses | ? { ($_.Name  -match ".*(w3wp|WebDev.WebSe.*)\.exe$") }  | % { $_.Attach() };
}

If you have project specific functions you can create separate scripts for each function and save them to the solution directory. Since the console's default directory is the solution folder, all of the scripts are at your fingertips.

As a bonus, adding the setVSVars function that I introduced in [[Powershell Unlock File In TFS Using Visual Studio Command Line Vars]], with a simple change, I can add functionality that will restore focus to the current window. I highlighted the additions and left in a couple lines (11, 12) which show other ways to access windows inside Visual Studio.

function set-VSVars() {
 $activeWindow = $dte.ActiveWindow;
 #Set Visual Studio Command Prompt environment variables`n
 pushd "c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC";
 cmd /c "vcvarsall.bat&set" | ? { $_ -match "^(?<Name>[^=]+)=(?<Value>.+)$" } | % {
     Set-Item -Force -Path "ENV:\$($matches['Name'])" -Value $matches["Value"];
 }
 popd;
 Write-Host "Visual Studio 2010 Command Prompt variables set." -ForegroundColor Yellow;
 
 #$dte.Windows | ? { $_.Caption -eq "Package Manager Console" } | % { $_.Activate(); }
 #$dte.Windows.Item("Package Manager Console").Activate();
 $activeWindow.Activate();
}

This is useful if you need access to Visual Studio Command Line functions.

Saturday, July 14, 2012

Prevent Text Box Selection And Automatically Select Different Control

I ran into an interesting issue where I needed to allow a user to select a contact in a custom SharePoint 2010 application. Preventing the selection of the text box is not as straight forward as one might hope, especially handling cross browser. Since SharePoint has the concept of a People Picker, I decided that I could leverage that pattern to increase application performance. The requirement had several parts:
  1. The result needed to only display the users name
  2. The ID of the contact needed to be saved
  3. The label needs to be able to send the value back to the server, but prevent people from editing the label
The first and third points are solved by using a text box instead of a label. The ID is saved by hiding ID text box so that it is posted back with the rest of the data. We make the solution slightly more elegant by treating the text box like modern browsers handle the file input control; clicking the text box opens the dialog. There is nothing that is specific to ASP.NET and can be easily converted to standard HTML.
<asp:TextBox ID="txtContact" onfocus="focusCtrl('btnContact', event);" runat="server"></asp:TextBox>
<asp:TextBox ID="txtContactID" style="display: none;" runat="server"></asp:TextBox>
<asp:Button ID="btnContact" OnClientClick="return contactPicker('txtContactID', 'txtContact');" Text="Select" CausesValidation="false" runat="server"></asp:Button>
<asp:RequiredFieldValidator ID="rfvContact" ControlToValidate="txtContact" ErrorMessage="Contact is required" ValidationGroup="Required" Display="Dynamic" InitialValue="" CssClass="ms-error" EnableClientScript="false" runat="server">
    <img alt="Validation Error" title="Contact is required" src="/_layouts/images/EXCLAIM.GIF" />
</asp:RequiredFieldValidator>
<script type="text/javascript">
     $(function () { focusCtrl('btnContact'); });
</script>
A couple important notes about the focusCtrl function, the code above assumes that the specified control will be initially selected, but the same function is used for the text box focus event with different outcomes. The initial run without the event won't pass an event into the function which won't trigger the control click event. Once the focus event has fired off an event is passed in and causes the controls click event to fire. I included a version of the contact picker function to show how we are populating controls on the callback.
function focusCtrl(ctlID, e) {
    var ctl = $('input[id$=\'' + ctlID + '\']');
    e = e || window.event;
    var t = (e) ? e.target || e.srcElement : null;
    if (ctl.length > 0) {
        ctl.focus();
        if (t != null) {
            ctl.click();
        }
    } else if (e || window.event) {
        $(t).blur();
        //Focus next textbox
        //$(t).next("input:not(input[type='submit']):visible").focus();
    }
    return false;
}

function contactPicker(txtID, txtLabel) {
    var options = {
        url: '../Contacts/ContactPicker.aspx',
        title: 'Contact Picker',
        allowMaximize: true,
        showClose: true,
        showMaximized: false,
        dialogReturnValueCallback: function (dialogResult, returnValue) {
                                        if (dialogResult == SP.UI.DialogResult.OK) {
                                            var ret = unescape(returnValue),
                                                name = ret.split(";#")[1];
                                            $("input[id$='" + txtName + "']").val(name);
                                            $("input[id$='" + txtID + "']").val(ret);
                                        }
                                    }
    };
    SP.UI.ModalDialog.showModalDialog(options);
    return false;
}

Tuesday, July 10, 2012

Condencing Long If Statements Where A Value Is One of Many Values

Every once in a while I come across a situation where I need to run a section of code if it is one of many different values. There are a several solutions to this situation.

The most common is the basic long form statement. This is well and good but takes a while to write and is pretty verbose. In my opinion, it is actually a bit harder to read.
$('.format-number').each(function () {
    var t = $(this);
    if (t[0].nodeName === "INPUT" || t[0].nodeName === "SELECT") {
        t.val(SomeStringFormatFunc(t.val()));
    }
    else {
        t.text($.fn.autoNumeric.Format(t.attr('id'), t.text()));
    }
});
Let's focus on the if statement. After some reworking the statement, we can use an array and the indexOf function to determine the same. It is a little shorter.
if (["INPUT", "SELECT"].indexOf(t[0].nodeName) >= 0) {
The next example exploits some, sadly rarely used, JavaScript functionality and makes it shorter yet. Using an JSON object, dictionary look up, and if statement evaluation knowledge, the following uses all of these to achieve the same result.
if (({ "INPUT": 1, "SELECT": 1 })[t[0].nodeName]) {
A potential benefit of both refactor attempts is that the array and JSON object can be refactored if those need to be reused in other places. Alternatively, if it needs to be configurable, abstracting the array or JSON object solutions cater to that need.

Friday, July 6, 2012

Powershell Unlock File In TFS Using Visual Studio Command Line Vars

I recently found a situation where someone checked out a file and locked it. That severely impedes my ability to make changes to that file. So I set out to remove the lock using Powershell, while I didn't get the native Powershell functionality I normally like, it still achieves the solution in a usable script.

The solution required that I use tf.exe, not an issue, but it is not in windows path by default. It is available if you use the Visual Studio Command Line, which is just a regular command line which runs a batch file. I thought it might be useful to have the ability to turn Powershell into a Visual Studio Command Line. I found a blog post, Replace Visual Studio Command Prompt with Powershell, that works, but I updated it to work with Visual Studio 2010 and more fully use the power of Powershell.

Set Visual Studio Command Prompt environment variables
#Set Visual Studio Command Prompt environment variables
pushd "c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC";
cmd /c "vcvarsall.bat&set" | ? { $_ -match "^(?<Name>[^=]+)=(?<Value>.+)$" } | % {
    Set-Item -Force -Path "env:\$($matches['Name'])" -Value $matches["Value"];
}
popd;
Write-Host "Visual Studio 2010 Command Prompt variables set." -ForegroundColor Yellow;
Here is my script to unlock a file in TFS even if the workspace doesn't belong to the user or on the users computer. If the command line arguments are not entered, the script will prompt for the arguments. It is a nice pattern that I may make a standard of in my scripts.
param(
  [string] $SourceControlFilePath = $(Read-Host "Source Control File Path"),
  [string] $Workspace = $(Read-Host "Workspace"),
  [string] $Username = $(Read-Host "Username")
)

& .\set-vsvars.ps1;

tf lock /lock:none $SourceControlFilePath /workspace:$Workspace`;$Username

Wednesday, June 27, 2012

Google Top Bar Rearrange UserScript for GreaseMonkey

I ran across a script on UserScripts.org called Google topbar. It doesn’t appear to be maintained any longer and doesn’t worked in FireFox 13 (not sure if it didn't work before, but that is what I tested on). So I decided that I would use it as a base for an updated script to make it display the links I most commonly use. 

// ==UserScript==
// @name          Google Top Bar Rearrange
// @namespace     com.blogspot.intellectualponderings.GoogleToolBarRearrange
// @description   Custom Google Links and Order, based on http://userscripts.org/scripts/show/18128
// @include       http://*.google.*/*
// @include       https://*.google.*/*
// ==/UserScript==

(function(undefined) {
  var shared = {
    more: ['Maps', 'Images', 'Play', 'YouTube', 'News', 'Shopping'],
    top: ['Blogger', 'Calendar', 'Reader'],
    topBarList: "//ol[@class='gbtc']",
    topBarNode: "//li[@class='gbt'][a[span[text()='{{0}}']]]",
    moreList: "//ol[@id='gbmm']",
    moreNode: "//li[@class='gbmtc'][a[text()='{{0}}']|text()='{{0}}']",
    nodeLink: "//a"
  };
  
  var node = function(path, text, context) {
    path = (text) ? path.replace(/\{\{0\}\}/g, text) : path;
    context = context || document;
    return document.evaluate(path, context, null, XPathResult.ANY_TYPE,null).iterateNext();
  },
  toMoreMenu = function(text) {
    var tag = node(shared.topBarNode, text);
    if (tag) {
      var more = node(shared.moreList);
      if (more) {
          more.appendChild(tag);
          tag.className = 'gbmtc';
      }
    }
  },
  toTopMenu = function(text) {
    var more = node(shared.moreList),
        tag = node(shared.moreNode, text, more),
        alink, moreli;
    if (tag) {
      var topBar = node(shared.topBarList);
      if (topBar) {
        moreli = topBar.lastChild;
        topBar.appendChild(tag);
        tag.className = "gbt";
        alink = node(shared.nodeLink);
        alink.className = "gbzt";
        if (moreli) {
          topBar.removeChild(moreli);
          topBar.appendChild(moreli);
        }
      }
    }
  };

  if (window.top == window.self) {
      var i;
      for (i = 0; i < shared.more.length; i++) {
        toMoreMenu(shared.more[i]);
      }
      for (i = 0; i < shared.top.length; i++) {
        toTopMenu(shared.top[i]);
      }
  }
})();

And then I decided to change it so that it would use jQuery and be a bit more complete. In fact, I didn't keep much.

// ==UserScript==
// @name          Google Top Bar Rearrange
// @namespace     com.blogspot.intellectualponderings.GoogleToolBarRearrange
// @description   Custom Google Links and Order, based on http://userscripts.org/scripts/show/18128
// @include       http://*.google.*/*
// @include       https://*.google.*/*
// @grant         GM_getValue
// @grant         GM_setValue
// @require       https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js
// ==/UserScript==


(function(undefined) {
  var context = $("#gbz ol.gbtc"),
      more = ['Maps', 'Images', 'Play', 'YouTube', 'News', 'Shopping'],
      top = ['Blogger', 'Calendar', 'Reader'];
      
  var addItem = function(url, text) {
    var item = $("<li />").addClass("gbt").append(
                  $("<a />").attr("href", url).addClass("gbzt").attr("target", "_blank").append(
                    $("<span/>").addClass("gbtb2")
                  ).append(
                      $("<span />").addClass("gbts").html(text)
                  )
                );
        $(context).find("li.gbt:last-child").before(item);
    },
    removeItem = function(text) {
    var link = $(context).find("li.gbt span:contains('" + text + "')").parent(),
        href = link.attr("href");
        link.remove();
        return href;
    },
    moveItemToMore = function(text) {
    if ($(context).find("ol.gbmcc li.gbmtc a:contains('" + text + "')").size() == 0) {
      var url = removeItem(text);
      addMoreItem(url, text);
    }
    },
    addMoreItem = function(url, text) {
    var item = $("<li />").addClass("gbmtc").append(
                  $("<a />").attr("href", url).addClass("gbmt").attr("target", "_blank").html(text));
        $(context).find("ol.gbmcc li.gbmtc:last-child").prev().before(item);
    },    
    removeMoreItem = function(text) {
    var link = $(context).find("ol.gbmcc li.gbmtc a:contains('" + text + "')"),
        href = link.attr("href");
        link.remove();
        return href;
    },
    moveItemToTop = function(text) {    
    if ($(context).find("a span.gbts:contains('" + text + "')").size() == 0) {
      var url = removeMoreItem(text);
      addItem(url, text);
    }
    };
    
  if (window.top == window.self) {
      var i;
      for (i = 0; i < more.length; i++) {
        moveItemToMore(more[i]);
      }
      for (i = 0; i < top.length; i++) {
        moveItemToTop(top[i]);
      }
  }
})();

I am much happier with this one.  Using the DOM XML functions seems so pre-jQuery.

UPDATE 2012-08-29: GreaseMonkey updated and made Google Reader not display feed items. I added some @grant lines in the header and everything works again. My discussion on Google Groups is located here.

// @grant         GM_getValue
// @grant         GM_setValue

Wednesday, June 20, 2012

MSBuild Task Build Projects With Different Properties

I built an MSBuild Script a couple months ago and stumbled across a couple interesting features that MSBuild gives you a way to control a project file's build in a fine tuned way. In my build scenario, I have 3 SharePoint 2010 projects and a WCF service to build (preferably without a lot of source). Since I want to generate the SharePoint solutions and have separate have the WCF service output placed in a different place, normally the path of least resistance would be to have two MSBuild tasks, one for the SharePoint projects and one for the WCF service. This works, but there is some refactoring that can be accomplished. Thus, the following.

Suppose a script reads in several properties we want to use on the various projects. In this example we will hardcode them in a PropertyGroup node as follows:
<PropertyGroup Label="Build options" Condition="'$(GenerateSerializationAssemblies)' == ''">
    <GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies><!-- Auto|On|Off -->
    <Configuration>Release</Configuration>
</PropertyGroup>
Now we define the ItemGroup with all of our projects. The important part each node is that we define the Targets and Properties nodes that we would otherwise define in the MSBuild task node. This example shows that we set the same Configuration setting for all projects and only set the GenerateSerializationAssemblies on the DemoWeb project and then set it to off for the other projects.
<ItemGroup>
<ProjectsToBuild Include="$(SourceFolder)\$(DemoWeb)\$(DemoWeb).csproj">
    <Targets>Package</Targets>
    <Properties>BasePackagePath=.\$(WSPOutputFolder)\;IsPackaging=True;Configuration=$(Configuration);GenerateSerializationAssemblies=$(GenerateSerializationAssemblies)</Properties>
</ProjectsToBuild>
<ProjectsToBuild Include="$(SourceFolder)\$(DemoConfiguration)\$(DemoConfiguration).csproj">
    <Targets>Package</Targets>
    <Properties>BasePackagePath=.\$(WSPOutputFolder)\;IsPackaging=True;Configuration=$(Configuration);GenerateSerializationAssemblies=Off</Properties>
</ProjectsToBuild>
<ProjectsToBuild Include="$(SourceFolder)\$(DemoBusinessData)\$(DemoBusinessData).csproj">
    <Targets>Package</Targets>
    <Properties>BasePackagePath=.\$(WSPOutputFolder)\;IsPackaging=True;Configuration=$(Configuration);GenerateSerializationAssemblies=Off</Properties>
</ProjectsToBuild>
<ProjectsToBuild Include="$(WCFSourceFolder)\$(DemoSvc)\$(DemoSvc).csproj">
    <Targets>ResolveReferences;_CopyWebApplication</Targets>
    <Properties>WebProjectOutputDir=$(WCFOutputFolder)\;OutDir=$(WCFOutputFolder)\bin\;Configuration=$(Configuration)</Properties>
</ProjectsToBuild>
</ItemGroup>
Now the projects and settings are set as needed. The compilation is done as normal, just with some of the nodes refactored. This is great, one MSBuild task, and finer grain control over each project.
<Target Name="Compile" DependsOnTargets="Clean">
    <!-- ... Other tasks ... -->
    
    <MSBuild Projects="@(ProjectsToBuild)">
        <Output ItemName="BuildOutput" TaskParameter="TargetOutputs"/>
    </MSBuild>
    <Message Text="!%0D%0A@(BuildOutput)" />

    <!-- ... Other tasks ... -->
</Target>
As it turns out, it appears that the properties/targets from the current project are applied onto the result of the other project's properties/targets (respectively applied onto the previous.

I have not found this documented previously, but it works for me. I hope it will help someone else.

Wednesday, June 6, 2012

Show and Hide VirtualBox Menu Bar and Status Bar

I use VirtualBox to host virtual machines on my laptop. I am not a fan of clutter, especially when things needlessly take up space. The menu and status bar on the virtual machine window fall into the category of unneeded clutter most of the time. After a considerable amount of digging, I found the VBoxManage command and from there built my commands from there.

There is a downside to using this, the changes are applied the next time VirtualBox starts. Which means you have to completely close VirtualBox and then start it again to toggle it. When you are finished with the bars, you have to close VirtualBox again to toggle it back. I find that I need to use it once every couple months, so the downside is not really an issue.

Here are the contents of batch files to toggle the menu bar and status bar.
C:
cd "\Program Files\Oracle\VirtualBox"
VBoxManage setextradata global GUI/Customizations noMenuBar,noStatusBar
As always, it is good to know how to undo what just occurred.
C:
cd "\Program Files\Oracle\VirtualBox"
VBoxManage setextradata global GUI/Customizations MenuBar,StatusBar

Thursday, May 24, 2012

Calculating the Years and Months Between Two Dates

I ran into an interesting problem that had a much shorter solution than I anticipated. I searched the web for simple solutions and ran across this post on msdn (http://forums.asp.net/t/1289294.aspx/1?TimeSpan+Years+and+Months). Suppose you have 2 dates and want to find the following information about the dates range:
  • Number of years
  • Number of months
  • Number of quarters
  • Number of half years
Often I will test algorithms in Powershell (or some scripting language) and then convert them in what whatever static language I need it to be in; this is one of those cases.
$d1 = [DateTime]::Parse("5/3/2022");
$d2 = [DateTime]::Parse("2/2/2012");
$d3 = [DateTime]::MinValue + ($d1 - $d2)
$d3.Year - 1 # 10
$d3.Month - 1 # 3
$d3.Day - 1 # 1
There are some assumptions that I am making in a couple calculations for my application. I only care about whole periods, so I will be using the floor function to select the correct count. I am also ignoring the days, they don't matter.
TimeSpan span = AmortSched.FirstPrincipalPymntDate - AmortSched.FirstPrincipaMatDate;
DateTime range = DateTime.MinValue + span;

int yearCount = range.Year - 1;
int monthCount = range.Month - 1;
int totalMonthCount = (yearCount * 12) + monthCount;
int totalQuarterCount = Math.Floor(totalMonthCount / 3);
int totalHalfYearsCount = Math.Floor(totalMonthCount / 6);

Thursday, May 3, 2012

Set Visual Studio 2010 Shortcuts Using Powershell

The awesome things you can do with the $dte object inside Visual Studio 2010 just keep piling up. I could not find any examples on how to do it with Powershell.

You can list the commands and their bindings with the following command. Important note... there are a LOT of commands and it will likely take several seconds to output everything.
$dte.Commands | ft -Property Name, Bindings

If you already know the command name, you can get the bindings using the following command.
$dte.Commands | ? { $_.Name -eq "View.TfsSourceControlExplorer" } | % {
    $_.Bindings;
}

Be careful to not choose a shortcut that is already in use. The following are some bindings that I find very useful. The first sets a convenient way to open the Source Control Explorer.
$dte.Commands | ? { $_.Name -eq "View.TfsSourceControlExplorer" } | % {
    $_.Bindings = "Global::Ctrl+W, Ctrl+1";
}

This opens the folder in Windows Explorer containing the current file.
$dte.Commands | ? { $_.Name -eq "File.OpenContainingFolder" } | % {
    $_.Bindings = "Global::Ctrl+Shift+Alt+O";
}

This opens the Attach to Process... dialog. I can't remember if this is the default shortcut, but here it is regardless.
$dte.Commands | ? { $_.Name -eq "Tools.AttachtoProcess" } | % {
    $_.Bindings = "Global::Ctrl+Alt+P";
}

Friday, April 27, 2012

IIS Application Pool Disable "Ping Enabled" Via Powershell

Probably one of the most frustrating features of debugging Visual Studio is that it will stop debugging an IIS process after a short amount of time and display a dialog box which says:
The web server process that was being debugged has been terminated by Internet Information Services (IIS). This can be avoided by configuring Application Pool ping settings in IIS. See help for further details.
To add insult to injury, it makes you click OK for every application pool that has timed out. In an environment where you have a 4-5 processes attached, this is an annoying waste of time.

It has been long known and pretty well documented on how to fix it in the IIS gui. It is a rather slow process and needs to be automated.

Here is my Powershell function. It will find all of the application pools that start with the specified string and disable the "Ping Enabled" field.

function Disable-Pinging($appPoolNameStartsWith = 'SharePoint - ') {
 Import-Module WebAdministration;
 sl iis:\AppPools;
 Get-ChildItem | ? { $_.Name.StartsWith($appPoolNameStartsWith); } | % { 
  $_.processModel.pingingEnabled = $false; 
 }
 Remove-Module WebAdministration;
}

Thursday, April 19, 2012

Attach Debugger To All Web Processes Using Powershell Inside Visual Studio 2010

Along time ago, when the Visual Studio 2010 Powershell Console was first released I started to play around with the new capabilities and I promptly forgot about it.

I recently ran across a need to have a faster way to attach to all the web processes on my computer. I remembered that I could use Powershell to access everything in Visual Studio 2010. A little digging later I ran across the "$dte" object and remembered that this was the key to accessing everything. Using "Get-Member" several times I quickly dug into
"$dte" and got a list of local processes and found an "Attach" function on the processes object.

Here is my one-line powershell command that attaches to all IIS processes and Development Web Server processes. Doing this doesn't force you to click the warning for attaching to processes that run as different users that normally appear when using the "Attach to Process..." dialog.

$dte.Debugger.LocalProcesses | ? { ($_.Name  -like "*w3wp.exe") -or ($_.Name  -like "*WebDev.WebSe*.exe") }  | % { $_.Attach(); }

I find it as a good practice to know how to undo what I do through scripting. I normally just hit the stop debug button in Visual Studio, but below is how to detach all the debugger from the processes. Perhaps someone will find it useful.

$dte.Debugger.DetachAll();

Monday, April 9, 2012

Using Powershell to update HOSTS file with IP Address of Server

My development requires me to work on a virtual machine, which has 2 sites hosted via host headers. Unfortunately the IP address changes once in a while when I start up the VM. The normal process goes:
  • On my vm, open cmd
  • Run ipconfig
  • Copy the ip address that starts with 192.168.1.
  • On my host machine, open notepad in administrator mode
  • Open the "HOSTS" file
  • Modify the ip addresses
  • Save

The new process with the PowerShell script:
  • Open PowerShell in Administrator mode
  • Run script
It seems like I should be able to run a script from PowerShell in Administrator mode, but I haven't figured that out yet. Perhaps there is a Windows Explorer context menu modification I can use.

The script gets the IP address of the server and gets the the IP address that I need to use. It proceeds to find and update any existing entries in the HOSTS file and then appends the updated line to the end of the file if it isn't in the HOSTS file.
#
# Modify HOSTS file with updated servers IP address
#

function Main {
 $ipAddrs = [System.Net.DNS]::GetHostAddresses("DEMO-VM-D") | ? { $_.IPAddressToString.StartsWith("192.168.1.") };
 $ipAddr = $ipAddrs.IPAddressToString;
 
 Modify-Host "dev.demo.local" $ipAddr;
 Modify-Host "dev.demo.com" $ipAddr;
}

function Modify-Host($hostDomain, $ip = "127.0.0.1") {
 $hostsPath = "$env:windir\System32\drivers\etc\hosts";
 $hosts = Get-Content $hostsPath;
 $escapedHost = $hostDomain -replace "[.]", "\.";
 $exists = $false;
 $hosts = $hosts | % {if ($_ -match "^\s*((\d{1,3}){3}.*?$escapedHost.*)")
                            {$exists = $true;Write-Host "Updating $hostDomain -> $ip"; "$ip`t$hostDomain";} else {$_}};

 if ($exists -eq $false) {
  Write-Host -NoNewline "Adding $hostDomain to the HOSTS file...";
  $hosts += "$ip`t$hostDomain";
 }
 $hosts | Out-File $hostsPath -enc ascii
 
 Write-Host "Done.";
}
& Main;

Wednesday, April 4, 2012

Start Development Web Server Using Powershell Inside Visual Studio 2010

I have a WCF service that is hosted in a Development Web Server (locally) that supports a SharePoint 2010 custom web application. It is a hassle to drive into large solutions to start the server, so I wrote a little script.

Here is the script that starts the Development Web Server (Cassini) inside Visual Studio 2010 using Powershell.

param(
 $rootpath,
 $port = 8383,
 $virtualpath = '/'
)

Kill -name WebDev.WebServer*;

& 'C:\Program Files (x86)\Common Files\microsoft shared\DevServer\10.0\WebDev.WebServer40.EXE' /path:"$rootpath" /port:$port /vpath:"$virtualpath "


Here is an easy copy and paste line that you can throw into a Powershell console or Package Manager Console inside Visual Studio 2010.

Kill -name WebDev.WebServer*;& 'C:\Program Files (x86)\Common Files\microsoft shared\DevServer\10.0\WebDev.WebServer40.EXE' /path:"C:\TFS\\Main\Source\" /port:8383 /vpath:"/";

Tuesday, March 13, 2012

Serialize a. ADO.NET DataTable to a CSV String

Here is an example of querying a stored procedure and serializing the DataTable to a comma delimited string. The bonus with this with the reuse of the connection string in the associated Entity Data Model (EDM). I originally tried to use the connection object from the EDM, but I received errors sparatically when I opened the "StoreConnection" SQLConnection object.

Code notes:
"_baseContext" is an object of type "ObjectContext" is the result from the Entity Framework context's "GetBaseContext()" function.

The BuildXmlString function is the same from used in my SQL Multiple Item Update Using XML blog post. 

public string GetDemoExport(int[] DemoItemIDs)
{
    string connectionString = ((System.Data.EntityClient.EntityConnection)_baseContext.Connection).StoreConnection.ConnectionString;
    DataTable data = new DataTable();
    using (SqlConnection conn = new SqlConnection(connectionString))
    {
        //IDbConnection conn = ((System.Data.EntityClient.EntityConnection)_baseContext.Connection).StoreConnection;
        SqlCommand cmd = new SqlCommand("GetDemoExport", (SqlConnection)conn);
        cmd.CommandType = CommandType.StoredProcedure;
        string paramRoot = "DemoItemIDs";
        cmd.Parameters.AddWithValue("@" + paramRoot, BuildXmlString<int>(paramRoot, DemoItemIDs));

        conn.Open();
        SqlDataAdapter adapter = new SqlDataAdapter(cmd);
        adapter.Fill(data);
        conn.Close();
    }

    StringBuilder buffer = new StringBuilder();

    int colCount = data.Columns.Count;
    for (int idx = 0; idx < colCount; idx++)
    {
        if (buffer.Length > 0)
        {
            buffer.Append(",");
        }
        buffer.Append(data.Columns[idx].ColumnName);
    }
    buffer.AppendLine("");
    int rowCount = data.Rows.Count;
    for (int idx = 0; idx < rowCount; idx++)
    {
        DataRow row = data.Rows[idx];
        buffer.AppendLine(string.Join(",", row.ItemArray));
    }

    return buffer.ToString();
}

Saturday, March 10, 2012

Ensure a SQL Table Has a Certain Number of Rows

Here is a useful simple script that can be modified to ensure a table has a certain number of rows. The example uses an insert statement, however I have used it to call a stored procedure that inserts rows (among other things). It is easier than copy/pasting insert/stored procedure statements.
DECLARE @Count as int
SELECT @Count = COUNT(DemoItemID) FROM DemoItems
WHILE @Count < 50
BEGIN
 INSERT INTO DemoItems ([DemoItemID])
  VALUES (@Count+1)
 SELECT @Count = COUNT(DemoItemID) FROM DemoItems
END
PRINT 'Done.'

Thursday, March 8, 2012

SQL Multiple Item Update Using XML

From time to time, I run into a situation where I need to update multiple rows on a table. I could make multiple requests to the database, but that is rather chatty. Alternatively, a comma delimited string of values could be parsed and used to get the update. Being a big fan of XQuery, I was able to pass in an xml document and load a table that could be used to join with the table to be updated. This should scale beautifully since it plays to SQL's strengths. I have looked for a way to get some metrics on the efficiency of this verses other options and could not find a way.

This is the function I used to generate XML string. I think there are ways that I can make the function a little tighter, but it does the job and is too inefficient.

public static string BuildXmlString<t>(string xmlRootName, T[] values)
{
    StringBuilder xmlString = new StringBuilder();

    xmlString.AppendFormat("<{0}>", xmlRootName);
    int count = values.Length;
    for (int idx = 0; idx < count; idx++)
    {
        xmlString.AppendFormat("<value>{0}</value>", values[idx]);
    }
    xmlString.AppendFormat("</{0}>", xmlRootName);

    return xmlString.ToString();
}
Here is the SQL that uses the XML and updates a table with a resolved parameter value.
DECLARE @status varchar(50), @demoItemIDs xml
 SET @status = ''
 
 -- Load up the table variable with the parsed XML data
 DECLARE @DemoItems TABLE (ID int) 
 INSERT INTO @DemoItems (ID) SELECT ParamValues.ID.value('.','int')
 FROM @demoItemIDs.nodes('/demoItemIDs/value') as ParamValues(ID) 

 -- Resolve status name to ID
 DECLARE @statusID int 
 SELECT TOP 1 @statusID = DemoStatusID FROM DemoStatuses ds WHERE ds.Name = @status

 IF (@statusID > 0)
 BEGIN 
  -- Update the payments with the new status
  UPDATE [Demo].[dbo].[DemoItemApprovals]
     SET DemoStatusID = @statusID
   WHERE DemoItemID IN (
    SELECT ID FROM @DemoItems d
      INNER JOIN DemoItemApprovals da
    ON    da.PaymentID = d.ID
   )
 END
 ELSE
 BEGIN
  RAISERROR (
   N'The status ''%s'' does not exist.' -- Message text.
   , 10 -- Severity,
   , 1 -- State,
   , @status -- First argument.
   );
 END

Monday, February 20, 2012

Log Manager With Extra Information Using SharePoint 2010 Unified Logging Service

Here is a wrapper class which covers the SharePoint ULS interface and logs information to the ULS. I attempted to add as much information as I could and allow for a property dictionary to be appended to the log entry.

namespace Demo.Web.Logging
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.SharePoint.Administration;
    using System.Diagnostics.Eventing;
    using System.Runtime.InteropServices;
    using System.Security.AccessControl;
    using System.Security.Principal;
    using System.Threading;
    using System.Diagnostics;

    public class LogManager
    {
        public static void Write(Exception ex, SPDiagnosticsCategory category, TraceSeverity severity)
        {
            if (ex.Data != null && !ex.Data.Contains("CallingFunction"))
            {
                ex.Data.Add("CallingFunction", System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name);
            }
            Dictionary<string, object> props = null;
            if (ex.Data != null && ex.Data.Count > 0)
            {
                props = new Dictionary<string, object>();
                foreach (string key in ex.Data)
                {
                    props.Add(key, ex.Data[key]);
                }
            }
            LoggingService.Log(ex.ToString(), props);
        }

        public static void Write(string message, Dictionary<string, object> props)
        {

            LoggingService.Log(message, props);
        }

        public static void Write(string message, Dictionary<string, object> props, TraceSeverity severity)
        {
            LoggingService.Log(message, props, severity);
        }

        public static void Write(string message, Dictionary<string, object> props, TraceSeverity severity, string categoryName)
        {
            LoggingService.Log(message, props, severity, categoryName);
        }

        public static void Write(string message, Dictionary<string, object> props, TraceSeverity severity, SPDiagnosticsCategory category)
        {
            //SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("My Category", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, ex.Message, ex.StackTrace);
            LoggingService.Log(message, props, severity, category);
        }
    }

    #region [ Internal ULS Access Implementation ]
    
    internal class LoggingService : SPDiagnosticsServiceBase
    {
        public static string DemoDiagnosticAreaName = "Demo";
        private static LoggingService _Current;
        public static LoggingService Current
        {
            get
            {
                if (_Current == null)
                {
                    _Current = new LoggingService();
                }

                return _Current;
            }
        }

        private LoggingService()
            : base("Demo Logging Service", SPFarm.Local)
        {

        }

        protected override IEnumerable<spdiagnosticsarea> ProvideAreas()
        {
            List<spdiagnosticsarea> areas = new List<spdiagnosticsarea>
            {
                new SPDiagnosticsArea(DemoDiagnosticAreaName, new List<spdiagnosticscategory>
                {
                    new SPDiagnosticsCategory("Application", TraceSeverity.Unexpected, EventSeverity.Error),
                    new SPDiagnosticsCategory("WebService", TraceSeverity.Unexpected, EventSeverity.Error),
                    new SPDiagnosticsCategory("WebConfigMod", TraceSeverity.Unexpected, EventSeverity.Error)
                })
            };

            return areas;
        }

        public static void Log(string message)
        {
            Log(message, null);
        }

        public static void Log(string message, Dictionary<string, object> props)
        {
            Log(message, props, TraceSeverity.Unexpected);
        }
        public static void Log(string message, Dictionary<string, object> props, TraceSeverity severity)
        {
            Log(message, props, TraceSeverity.Unexpected, "Application");
        }
        public static void Log(string message, Dictionary<string, object> props, TraceSeverity severity, string categoryName)
        {
            SPDiagnosticsCategory category = LoggingService.Current.Areas[DemoDiagnosticAreaName].Categories[categoryName];
            Log(message, props, severity, category);
        }
        public static void Log(string message, Dictionary<string, object> props, TraceSeverity severity, SPDiagnosticsCategory category)
        {
            if (props == null)
            {
                props = new Dictionary<string, object>();
            }
            if (!props.ContainsKey("CallingFunction"))
            {
                props.Add("CallingFunction", (new StackTrace()).GetFrame(1).GetMethod().Name);
            }
            string propSerial = "{" + string.Join(",", props.Select(
                d => string.Format("\"{0}\":\"{1}\"", d.Key, d.Value.ToString())
                ).ToArray()) + "}";

            //SPDiagnosticsCategory category = LoggingService.Current.Areas[DemoDiagnosticAreaName].Categories[categoryName];
            LoggingService.Current.WriteTrace(0, category, TraceSeverity.Unexpected, message + " ~ Properties: " + propSerial);
        }
    }

    #endregion
    }
}

Wednesday, February 15, 2012

Log Manager With Extra Information Using Enterprise Library

Here is a pattern for logging which encourages extra information to be stored with the error.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Diagnostics;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Logging.ExtraInformation;
using Microsoft.Practices.EnterpriseLibrary.Logging.Filters;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using System.Reflection;
using System.Collections;

namespace DemoServices.Logging
{
    public class LogManager
    {
        public static void Write(Exception ex, string title = "", string category = "", TraceEventType severity = TraceEventType.Error)
        {
            if (ex != null)
            {
                if (String.IsNullOrEmpty(title))
                {
                    title = System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name;
                }
                if (String.IsNullOrEmpty(category))
                {
                    category = System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name;
                }
                Dictionary<string, object> props = null;
                if (ex.Data != null && ex.Data.Count < 0)
                {
                    props = new Dictionary<string, object>();
                    foreach(string key in ex.Data) {
                        props.Add(key, ex.Data[key]);
                    }
                }
                Write(ex.ToString(), title, category, severity, props);
            }
        }

        public static void Write(string message, string title = "", string category = "", TraceEventType severity = TraceEventType.Error, IDictionary<string, object> props = null)
        {
            if (String.IsNullOrEmpty(title))
            {
                title = System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name;
            }
            if (String.IsNullOrEmpty(category))
            {
                category = System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name;
            }
            if (props == null)
            {
                props = new Dictionary<string, object>();
            }
            ManagedSecurityContextInformationProvider informationHelper = new ManagedSecurityContextInformationProvider();
            informationHelper.PopulateDictionary(props);
            DebugInformationProvider debugHelper = new DebugInformationProvider();
            debugHelper.PopulateDictionary(props);

            LogWriter logger = EnterpriseLibraryContainer.Current.GetInstance<logwriter>();
            logger.Write(message, new string[] { "Demo", category }, 20, 0, severity, title, props as Dictionary<string, object>);
        }

        //public static void Write(Exception ex, string title, string category, TraceEventType severity = TraceEventType.Error, Dictionary<string, object> props = null)
        //{
        //    if (ex != null)
        //    {
        //        Write(ex.ToString(), title, category, severity, props);
        //    }
        //}

        //public static void Write(string message, string title, string category, TraceEventType severity = TraceEventType.Error)
        //{
        //    Dictionary<string, object> props = new Dictionary<string, object>();
        //    //props.Add("CallingFunction", (new StackTrace()).GetFrame(1).GetMethod().Name);

        //    if (props == null)
        //    {
        //        props = new Dictionary<string, object>();
        //    }
        //    ManagedSecurityContextInformationProvider informationHelper = new ManagedSecurityContextInformationProvider();
        //    informationHelper.PopulateDictionary(props);
        //    DebugInformationProvider debugHelper = new DebugInformationProvider();
        //    debugHelper.PopulateDictionary(props);

        //    LogWriter logger = EnterpriseLibraryContainer.Current.GetInstance<logwriter>();
        //    logger.Write(message, new string[] { "Demo", category }, 20, 0, severity, title, props);
        //}

    }
}