mike-obrien.net Curriculum Vitae Blog Labs
Friday, January 02, 2009

While reading CLR via C# I picked up a couple of interesting tidbits about the using block that I never realized. First you can declare/instantiate multiple objects as long as they are of the same type and second you can pass in objects that have been declared outside the block. Not sure how useful these would be but interesting none the less. I think more useful would be allowing the developer to declare/instantiate multiple objects of different types.

class Program
{
    static void Main(string[] args)
    {
        Stuff stuff = new Stuff();

        using (
            Stuff stuff1 = new Stuff(),
            stuff2 = new Stuff(),
            stuff3 = stuff)
        {
        }
    }
}

class Stuff : IDisposable
{
    #region IDisposable Members

        public void Dispose() { }

    #endregion
}
.NET | .NET 3.5 | C#
Friday, January 02, 2009 10:23:04 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Thursday, October 30, 2008

The latest version of the Enterprise Library can be downloaded here. I'm happy to find the bug where the XmlLogFormatter was not escaping invalid characters has been fixed!

Thursday, October 30, 2008 2:39:12 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Tuesday, October 28, 2008

Adding custom token handlers to the LAB TextFormatter is fairly simple. The following formatter adds an "arrow" token (" --> ") to  the formatter.

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;

namespace MyLibrary.Logging.EnterpriseLibrary
{
    [ConfigurationElementType(typeof(CustomFormatterData))]
    public class CustomTextFormatter : TextFormatter
    {
        #region Private Fields

            private static readonly Dictionary<string, TokenHandler<LogEntry>> tokenHandlers =
                new Dictionary<string, LogEntry>>();

        #endregion

        #region Constructors

            static CustomTextFormatter()
            {
                tokenHandlers["arrow"] = GenericTextFormatter<LogEntry>
                    .CreateSimpleTokenHandler(" --> ");
            }

            public CustomTextFormatter(NameValueCollection parameters)
                : this(parameters.Get("template")) { }

            public CustomTextFormatter() : this(null, tokenHandlers) { }

            public CustomTextFormatter(string template) : this(template, tokenHandlers) { }

            protected CustomTextFormatter(string template, Dictionary<string, TokenHandler<LogEntry>>
                extraTokenHandlers) : base(template, extraTokenHandlers) { }

        #endregion    
    }
}

A few things to note: 1) If you want to define the formatter in configuration, you must add the CustomFormatterData attribute to the class. Don't try to add the the TextFormatterData attribute as I did (Because I did not pay close enough attention to the documentation!) and spend hours in Reflector trying to figure out why a TextFormatter object is created and not the derived type... This, by the way, has to do with the Assembler attribute on the nnnFormatterData class. This attribute references an assembler class that will instantiate the specific type (In this case the XmlLogFormatter), NOT the one specified in configuration. You could also define your own custom FormatterData and Assembler classes but the using the CustomFormatterData class is much simpler. 2) Again, if you want to define the formatter in configuration, you will need to add the constructor that accepts the NameValueCollection of configuration parameters. You can then grab the template parameter from this collection. 3) Create a static list of handlers that gets initialized once by a static constructor. There is no need to keep recreating these objects every time the formatter is created.

To define the formatter in configuration, specify the class reference like you normally would, just point to the new class. You can now use the token you defined in your class.

<formatters>
  <add 
    name="EventLogFormatter" 
    template="Timestamp{arrow}{timestamp}&#xD;&#xA;Message{arrow}{message}&#xD;&#xA; ..."
    type="MyLibrary.Logging.EnterpriseLibrary.CustomTextFormatter, MyLibrary"
     />
</formatters>

The result is as follows:

image

Now obviously you'll want to do more than add a simple token. In our next installment we will show you how to add a parameterized token. See you next time.

Tuesday, October 28, 2008 4:13:34 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Wednesday, October 22, 2008

I have been having XmlTraceListener woes... None of which are showstoppers just major time wasters! The following bug is fixed as of version 4.1! The first of which has to do the message. The message is not escaped and/or CDATA qualified. So if you have an ampersand or greater/less than sign in the message it will throw this cryptic error:

An error occurred while parsing EntityName. Line x, position y.

I ended up manually running the log entry object through the Format() method on the XmlLogFormatter class to get the raw xml and see what exactly it was complaining about. Sure enough there was an ampersand. Adding an extension method to the string class and manually escaping the LogEntry message alleviated this to some degree. Bad thing is though, the message is escaped for all listeners and you probably don't want to see HTML entities in your event log entry or email message. I think the only way around this would be to write your own xml trace listener.

namespace MyApp.Runtime.Extensions
{
    public static class String
    {
        public static string EscapeUnsafeXmlCharacters(this string value)
        {
            return value.Replace("<", "&lt;").Replace(">", "&gt;").Replace("&", "&amp;");
        }
    }
}

Issue #2 has to do with where the log file is saved. The FlatFileTraceListner actually saves the log file relative to the application root (Where the .config file is). Take a look at the RootFileNameAndEnsureTargetFolderExists() method in the FormattedTextWriterTraceListener class (The FlatFileTraceListener's base class). Before it passes the filename to it's base class constructor, it runs it through this method. The XmlTraceListener on the other hand does not follow this pattern which had me going nuts trying to figure out what I was doing wrong (Thinking that it just wasn't saving at all). I wasn't getting any errors from the XmlTraceListener (More on this below) so I started to get the feeling that perhaps it was writing to the log but just not where I expected. So I fired up Process Monitor and didn't see any log writes. Then I switched the app (It was a web application project BTW) to use the built in web server instead of IIS. Ran everything again and voilà:

image

So it was saving it somewhere else, but only when running under the built in web server. Looking at the code in reflector you can see that, unlike the FormattedTextWriterTraceListener, the XmlWriterTraceListener does not modify the path to be the application root before it passes it to it's base class constructor. So the directory ends up being that of the entry assembly. Bottom line is you have to supply an explicit path when you're using the XmlTraceListener in a web application.

This leads me to the third oddity; where it would save running under the builtin web server but not IIS. The base class, TextWriterTraceListener, calls the EnsureWriter() method (Shown below) before writing to the file. If it returns true it writes the entry, otherwise it doesn't. Notice that if there is a UnauthorizedAccessException it just returns false and then subsequently, in the calling Write method, silently skips writing. When I was using IIS as my web server (And the code was running as the Network Service account) I didn't see any exceptions and no log writes were showing up in Process Monitor. Which was very confusing! But when I switched over to using the built in web server (Which was obviously running as my interactive account) I saw the log writes. So when it was running under the Network Service account it obviously did not have permissions to save the log file to wherever it was trying to save it and no exception was raised... Very confusing behavior this. Reminds me of the advice in Jeffrey Richter's CLR Via C# book not to ever swallow exceptions! So keep that in mind, if you don't see any errors and log writes with the XmlTraceListner, it may be permissions and/or path related.

Well, hopefully this wasn't too long winded and hopefully it clears up some oddities with the XmlTraceListener.

public class TextWriterTraceListener : TraceListener
{
    public override void Write(string message)
    {
        if (this.EnsureWriter())
        {
            if (base.NeedIndent)
            {
                this.WriteIndent();
            }
            this.writer.Write(message);
        }
    }
    internal bool EnsureWriter()
    {
        bool flag = true;
        if (this.writer == null)
        {
            flag = false;
            if (this.fileName == null)
            {
                return flag;
            }
            Encoding encodingWithFallback = GetEncodingWithFallback(new UTF8Encoding(false));
            string fullPath = Path.GetFullPath(this.fileName);
            string directoryName = Path.GetDirectoryName(fullPath);
            string fileName = Path.GetFileName(fullPath);
            for (int i = 0; i < 2; i++)
            {
                try
                {
                    this.writer = new StreamWriter(fullPath, true, encodingWithFallback, 0x1000);
                    flag = true;
                    break;
                }
                catch (IOException)
                {
                    fileName = Guid.NewGuid().ToString() + fileName;
                    fullPath = Path.Combine(directoryName, fileName);
                }
                catch (UnauthorizedAccessException)
                {
                    break;
                }
                catch (Exception)
                {
                    break;
                }
            }
            if (!flag)
            {
                this.fileName = null;
            }
        }
        return flag;
    }

    // Other members removed for brevity...
}
Wednesday, October 22, 2008 3:54:38 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Wednesday, October 08, 2008

If you have a host name set in your hosts file pointing to your loop back address and you are trying to connect to your local SQL Server instance with this host name using Windows Auth (SSPI) you may receive one of the following errors depending on how you have things configured:

"Login failed for user ''. The user is not associated with a trusted SQL Server connection."

Or

Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'

There could be a number of things causing this error (here, here, here and here) but it in the specific scenario noted above it's probably that an alias is not set for the SQL Server. I'm not sure exactly why or how this works or why the above scenario would have an issue but creating an alias seems to resolve it. In fact if you specify "localhost" or the loopback address as the server name it works fine (Without the alias). Also when connecting to a remote SQL Server with a host name defined in the hosts file it works fine as well. hmmmm.... I really couldn't find any info on this specific scenario and creating the alias seems to resolve it so I finally gave up. If you have any more info on why this would happen please leave a comment. You can set the alias as follows:

  1. 1) Open the SQL Server Configuration Manager under Start|Programs| Microsoft SQL Server 20xx| Configuration Tools| SQL Server Configuration Manager.
  2. 2) Under the SQL Native Client Configuration node select Aliases.
  3. 3) Right-click and select “New Alias…”.
  4. 4) Enter the following information and press OK:
    1. Alias Name: sql.somedomain.com
    2. Port No: 1433
    3. Protocol: TCP/IP
    4. Server: 127.0.0.1

The connection string would be as follows:

server=sql.somedomain.com;database=SomeDatabase;Integrated Security=SSPI

Wednesday, October 08, 2008 8:19:05 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Saturday, July 26, 2008

I'm working on a media center application right now that I want to run kiosk style; topmost and fullscreen. This is amazingly simple to do in WPF, simply set the Topmost property to true, the WindowState property to Maximized and the WindowStyle to None.

.NET | .NET 3.5 | C# | WPF
Saturday, July 26, 2008 12:21:30 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Tuesday, June 17, 2008

Telerik has a very nice online code converter here. I've recently been converting VB.NET code to C# and this tool seems to be able to convert code that many of the others out there cannot. The only thing it seems to miss is the parens when converting indexer callers. So it doesn't convert the VB.NET myDataReader("Name") to the C# myDataReader["Name"]. Not a huge deal though.

Tuesday, June 17, 2008 6:54:22 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 

Rick Strahl's .NET Rocks presentation on AJAX and jQuery (that he mentioned in May) has been published. Check it out.

.NET | AJAX | jQuery
Tuesday, June 17, 2008 3:49:31 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Wednesday, May 21, 2008

Reachmail (the company I work for) is looking for a senior ASP.NET/C# developer. If you are a progressive developer who is a true geek, really understands OO and patterns and is looking for a change, browse to the link below to apply. We are currently located in Naperville but will be moving to downtown Chicago the 1st of July (Within walking distance of Union and Ogilvie). We really need someone who knows their stuff and doesn't require hand holding. Although we need someone who is able to work independently we don't want an independent person (IE "cowboy"); they must be a team player or they wont last long. We have a relaxed environment and a lot of growth.

The software is a permission based email marketing SAAS written primarily in classic ASP with a SQL Server 2005 back end. Parts of the system are written in VB.NET and C#. One of our goals is to get the application converted over to ASP.NET/C# and rearchitect much of the system. All of our existing .NET code has been upgraded to 3.5 and new development is obviously in 3.5 and C#.

 

.NET | C#
Wednesday, May 21, 2008 3:42:58 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Wednesday, November 14, 2007

Evidently the aspnet_compiler.exe does not care about .refresh files (From my tests and what I read on the internets). It would be nice if it did as it would reduce the steps needed to build a WSP (Web Site Project) on a build server. I ended up just manually copying the assemblies into the bin folder before the build as follows in this nant script:

<mkiisdir dirpath="..\BabelFish.ServiceHost.Web" vdirname="BabelFish.ServiceHost.Web" />

<copy todir="..\BabelFish.ServiceHost.Web\bin">
    <
fileset basedir="..\BabelFish.Services\bin">
        <
include name="*.dll" />
    </
fileset>
</
copy>

<exec program="C:\WINDOWS\Microsoft.NET\Framework\${framework.version}\aspnet_compiler.exe" useruntimeengine="true">
    <
arg value="-p" />
    <
arg value="..\BabelFish.ServiceHost.Web" />
    <
arg value="-v" />
    <
arg value="BabelFish.ServiceHost.Web" />
</
exec>

<deliisdir vdirname="BabelFish.ServiceHost.Web" />

Wednesday, November 14, 2007 6:33:25 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Friday, November 09, 2007

I have been wanting to dig into F# for quite some time and finally got an opportunity. My first stab at it implements the calculation of kinetic energy given a weight in lbs, speed in mph and output in kJ. Pretty basic, but ya gotta start some where right? :)

KineticEnergy.zip (.95 KB)

// Get info from the user
do Printf.printf "Enter the weight (LBS): "
let m = read_line()
do Printf.printf "Enter the velocity (MPH): "
let mph = read_line()

// Pound to kilogram conversion
let kg lbs = lbs / 2.2

// Miles/h to Meters/s conversion
let mps mph = (((mph * 1.6) * 1000.0) / 60.0) /60.0

// Kinetic energy calculation
let Ek m v = (0.5 * m * (v * v)) / 1000.0

// Show me the money
do Printf.printf "\nEk = %fkJ\n\n" (Ek (kg (float_of_string m)) (mps (float_of_string mph)))

// Wait! Let me see the answer...
do Printf.printf "Press enter to continue..."
let x = read_line()

Friday, November 09, 2007 9:52:37 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Tuesday, October 09, 2007

I love WiX, but I hate that you have to specify the id's and short filenames! This really slows you down when you have a lot of files to add. I have read that these may be optional in v3 and auto generated by the compiler. Until then you could use this Visual Studio addin that that allows you to randomly generate and insert id's and short filenames as well as insert some common tags with the aformentioned attributes randomly filled in. n-joy!

Visual Studio 2005

Installer: WiXHelperAddin.EXE (217.21 KB)
Source: WiXHelperSource.zip (258.91 KB)

Visual Studio 2008

Installer: WiXHelperAddin2008.EXE (217 KB)
Source: WiXHelperSource2008.zip (63.89 KB)

PS: Also, check out the GUID inserter (Which also appears in the image below...); this is very helpful for WiX development!

image

Tuesday, October 09, 2007 1:41:54 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 

I do this often enough to forget how to do it (And where I've seen how to do it!). Rick Strahl has a nice post about adding icons to custom Visual Studio addins; the about box icon and the icons that appear in menus.

http://www.west-wind.com/WebLog/posts/3862.aspx

Tuesday, October 09, 2007 12:42:59 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Tuesday, September 18, 2007

Here is a simple method to invert an HTML color:

private static string InvertHTMLColor(string htmlColor)

{

htmlColor = htmlColor.Replace("#", string.Empty).Trim();

if (string.IsNullOrEmpty(htmlColor) || htmlColor.Length != 6)

     throw new ArgumentException("Invalid HTML color.");

return string.Format("#{0}{1}{2}",

     InvertChannel(htmlColor.Substring(0, 2)),

     InvertChannel(htmlColor.Substring(2, 2)),

     InvertChannel(htmlColor.Substring(4, 2)));

}

private static string InvertChannel(string channel)

{

channel = channel.Trim();

if (string.IsNullOrEmpty(channel) || channel.Length != 2)

     throw new ArgumentException("Invalid color channel.");

int a = int.Parse(channel.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);

int b = int.Parse(channel.Substring(1, 1), System.Globalization.NumberStyles.HexNumber);

return (255 - ((16 * a) + b)).ToString("X");

}

.NET | C#
Tuesday, September 18, 2007 4:26:53 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [1]  | 
Tuesday, August 21, 2007

Attached is the source for a simple TCP proxy server. I whipped it up pretty quickly so it probably needs some refactoring if you plan to use it in production. The server class is loosely based on the Cassini implementation. You can set the listen port and forward host/port in the app config:

<configuration>

    <appSettings>

        <add key="listenPort" value="443"/>

        <add key="forwardHost" value="wush.net"/>

        <add key="forwardPort" value="443"/>

    </appSettings>

</configuration>

image

TCPProxy.zip (24.81 KB)
Tuesday, August 21, 2007 5:42:09 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Tuesday, July 17, 2007

If you are programmatically updating the value of a lookup field and it doesn't seem to update, you need to trip "dirty" flag on that field. If you don't, SP wont know that the list item needs to be updated. Basically all you do is set the field to itself and this will mark the field as dirty. Here is an example:

SPFieldLookupValueCollection lookupValues;

lookupValues = (SPFieldLookupValueCollection)listItem["MyLookupField"];

lookupValues.Add(new SPFieldLookupValue(1, "SomeLookupValue"));

// Set the lookup field back to itself to mark it dirty

listItem["MyLookupField"] = lookupValues;

listItem.Update();

Tuesday, July 17, 2007 5:35:37 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Monday, July 16, 2007

The Stopwatch Class, another great .NET 2.0 feature I keep forgetting to use so I'll blog about it (And hopefully I'll remember ;)... This is a great class for performance monitoring, instead of declaring a start time and subtracting it from the end time, etc. The following snippet checks the performance difference in doing a square root and a tangent over a number of iterations. Internally, when the stopwatch is started, the ticks of the current time are set as the start timestamp. When it is stopped the start ticks and end ticks (Current time) are subtracted and the private elapsed time field (A Long) is updated. The elapsed field is only reset when you explicitly call Reset() as it is accumulative. The public Elapsed property conveniently returns a TimeSpan.

int iterations = 10000000;

 

Stopwatch stopwatch = new Stopwatch();

stopwatch.Start();

 

// Do something...

for (int index = 0; index < iterations; index++)

{ Math.Sqrt(index); }

 

stopwatch.Stop();

 

Console.WriteLine(string.Format("Square root took {0} " +

    "milliseconds for {1} iterations.",

    stopwatch.Elapsed.Milliseconds, iterations));

 

// Do something else...

stopwatch.Reset();

stopwatch.Start();

 

for (int index = 0; index < iterations; index++)

{ Math.Tan(index); }

 

stopwatch.Stop();

 

Console.WriteLine(string.Format(@"Tangent took {0} " +

    "milliseconds for {1} iterations.",

    stopwatch.Elapsed.Milliseconds, iterations));

 

Console.ReadKey();

Monday, July 16, 2007 4:19:51 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Friday, July 13, 2007

I have seen a lot of posts on this and they all say something along the lines of 'simply go and set the path to the base vhd...'. Well, being a Virtual Server user, it wasn't as obvious to do as it is with VPC.  To do it in VS you need to...

1) Copy the VS 2008 VM to a folder in your VS2005 path and add it.

2) Copy the base VM (Which can be downloaded here) to a folder in your VS2005 path.

image

3) Under "Virtual Disks" click "Inspect", select the VS2008 vhd and click "Inspect".

image

4) The "Parent virtual hard disk(s)" property will show a link indicating that the parent drive could not be found. Click this link to set the location of the drive.

image

image

5) Once selected, click "Update parent path", start the VM and Login with administrator/P2ssw0rd.

Friday, July 13, 2007 4:24:51 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Wednesday, July 11, 2007

Someone asked me how to enable the (Fully functioning) web designer in a class library. Honestly I think using a WAP is all you need to do. It will give you a fully functioning web designer and will generate a DLL. If you already have a class library project that you want to now add web pages and user controls too just follow the instructions here (Under the section "Creating a hybrid VSeWSS/WAP Project") but instead add the following ProjectTypeGuids element below (In green). This will turn your class library project into a WAP.

<PropertyGroup>

<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>

<ProductVersion>8.0.50727</ProductVersion>

<SchemaVersion>2.0</SchemaVersion>

<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>

<ProjectGuid>{26E7042F-79CF-4782-86E2-7D1C914E164A}</ProjectGuid>

<OutputType>Library</OutputType>

<AppDesignerFolder>Properties</AppDesignerFolder>

<RootNamespace>ClassLibraryWithWebDevelopment</RootNamespace>

<AssemblyName>ClassLibraryWithWebDevelopment</AssemblyName>

</PropertyGroup>

Wednesday, July 11, 2007 5:03:30 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Friday, July 06, 2007

Attached is a simple contact generator (And source) that creates random contact data in CSV format. The data generated, although random, is pretty realistic as address and phone numbers correspond correctly and other values are sampled from a realistic set of data. The data generated appears as follows:

"Index","Id","FirstName","LastName","FullName","Username","Username2","Password","Email1","Email2","Phone1","Phone2","Fax1","Fax2","Address1","City1","State1","Zip1","Address2","City2","State2","Zip2"
"1","{c0d4079c-50c7-4214-a094-b09d51b9d143}","Raven","Huddle","Raven Huddle","rhuddle","raven.huddle","13af04ae","raven.huddle@aol.com","raven.huddle@earthlink.net","(303) 998-8084","(718) 229-7963","(303) 998-5593","(718) 229-4928","84596 Brimm BLVD","Boulder","CO","80302","36863 Noakes LN","Flushing","NY","11364"
"2","{74e80927-a654-4010-9020-7098452b4193}","Rhonda","Reitz","Rhonda Reitz","rreitz","rhonda.reitz","3282e152","rhonda.reitz@btinternet.com","rhonda.reitz@verizon.net","(907) 252-0297","(703) 242-6715","(907) 252-0163","(703) 242-9363","89079 Sterrett PL","Soldotna","AK","99669","41346 Mccauley HWY","Vienna","VA","22181"
"3","{742a3ad4-e1e8-44ed-a6a0-1654981e0c9b}","Ruth","Mcnemar","Ruth Mcnemar","rmcnemar","ruth.mcnemar","16548a78","rmcnemar@aol.com","rmcnemar@verizon.net","(305) 541-2410","(704) 892-5466","(305) 541-4633","(704) 892-3899","93562 Bruno BLVD","Miami","FL","33129","45829 Meadowview HWY","Davidson","NC","28036"
"4","{d72fb280-66cf-49a8-81bb-f5da4a096614}","Jimmy","Kizer","Jimmy Kizer","jkizer","jimmy.kizer","3528671c","jkizer@verizon.net","jimmy.kizer@earthlink.net","(704) 854-4523","(734) 797-4218","(704) 854-9102","(734) 797-8335","98044 Meadows HWY, Suite 370","Gastonia","NC","28054","50311 Pamona LN","Livonia","MI","48150"
"5","{90409c90-c967-4b1f-b66c-6b9f31043303}","Kiehl","Gillum","Kiehl Gillum","kgillum","kiehl.gillum","18fa1042","kiehl.gillum@gmail.com","kiehl.gillum@rediffmail.com","(320) 679-6636","(810) 863-2970","(320) 679-3672","(810) 863-2871","2578 Claywood BLVD","Mora","MN","55051","54794 Rockledge PKWY, Suite 945","Roseville","MI","48035"
"6","{f47a479c-d0a9-43d8-84aa-97970b0c4892}","Walletta","Spivey","Walletta Spivey","wspivey","walletta.spivey","37cdece6","wspivey@hotmail.com","wspivey@bellsouth.net","(218) 723-8749","(650) 357-1721","(218) 723-8142","(650) 357-7306","7061 Baypointe AVE","Duluth","MN","55802","59277 Mack HWY","San Mateo","CA","94403"
"7","{f2b1687a-78ce-437b-baa0-2b959ee260d5}","Chauncey","Slemp","Chauncey Slemp","cslemp","chauncey.slemp","1b9f960c","cslemp@comcast.net","cslemp@charter.net","(412) 393-0962","(916) 492-0473","(412) 393-2712","(916) 492-1842","11544 Dumesnil CIR","Pittsburgh","PA","15222","63760 Tiburon PL, Suite 814","Sacramento","CA","95814"
"8","{19189dab-e300-42d2-8580-c4216fcaafb3}","Roxanne","Bond","Roxanne Bond","rbond","roxanne.bond","3a7372b0","roxanne.bond@msn.com","roxanne.bond@aol.com","(336) 506-3075","(308) 652-9124","(336) 506-7181","(308) 652-6278","16027 Continental CIR","Burlington","NC","27215","68242 Burning BLVD","Orleans","NE","67647"
"9","{7703a831-1f56-43cd-87ea-c998fcb52682}","Elvie","Cissell","Elvie Cissell","ecissell","elvie.cissell","1e451bd6","elvie.cissell@charter.net","ecissell@cox.net","(917) 344-5189","(770) 242-7876","(917) 344-1751","(770) 242-0814","20509 Tivoli RD, Suite 491","New York","NY","10041","72725 Perlman LN","Norcross","GA","30071"
"10","{0aed5fd3-70e8-41e0-bbc4-418d122a590b}","Newman","Peters","Newman Peters","npeters","newman.peters","216c4fb","npeters@earthlink.net","npeters@hotmail.com","(719) 530-7302","(281) 944-6628","(719) 530-6221","(281) 944-5249","24992 Oak LN","Salida","CO","81201","77208 Bolsa BLVD","Houston","TX","77037"

Installer: SetupContactGenerator.EXE (833.81 KB)

Source: ContactGeneratorSource.zip (584.5 KB)

Friday, July 06, 2007 3:29:07 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Thursday, July 05, 2007

I must say, the OOB development experience in SharePoint has been less than desirable. I feel like MS has given us a number of tools but left us in the lurch figuring out how to use them, and its not always obvious how. One thing I have wanted to do is use the designer to develop web parts and deploy them with the click of a button. The VSeWSS have made the "deploy with the click of the button" part possible but the designer part has not been so obvious. How can you marry a designer experience with the VSeWSS ease of solution deployment? I'll show you how...

For Starters

The following information utilizes the Visual Studio Extensions for WSS (VSeWSS) which can be downloaded here. VSeWSS only offers a subset of the features available with solution deployment but offers "one touch" deployment OOB. Creating your own solutions manually will give you more options but may be more work than its worth (Unless you have found some nifty 3rd party tool or created your own). So the following information will strictly use what VSeWSS has to offer. VSeWSS can only run on a Windows Server 2003 box with MOSS 2007 & Visual Studio 2005 installed (Although it can be hacked to run on XP, most of the features do not work). VSeWSS may have problems if MOSS 2007 was not installed as a standalone server, so says the download notes and my experience with it. Although I have a colleague who has used it in a farm configuration and not had an issue.

Creating a blank VSeWSS Project & Web Part

Obviously you cant use the designer with a web part but you can with a Web User Control (This is the thought behind SmartParts). So lets start off by creating a project from an empty VSeWSS template.

image

Next add a new Web Part from the VSeWSS templates.

image

Now your probably thinking "Now add a Web User Control, yada yada yada", right? Wrong! If you have gone down this path before you will probably discover a couple of things; first the Web User Control is not available as a new item in a VSeWSS project. This can be worked around fairly easily by modifying the templates available to the class library project type or by creating the user control in a Web Application Project (WAP) and copy the files over, either way hacky. Second, you will discover that the designer does not behave as it does in a WAP or web site. For example your code behind and designer classes are not linked together and when you double click a control the default event code is generated in the ascx file instead of the code behind. This and a number of other quirks render this method almost as bad as just coding your entire control in the Web Part (Almost, but not quite). So how do we get the designer to work properly? .

Creating a hybrid VSeWSS/WAP Project

We need to create a hybrid VSeWSS/WAP project. This requires that you either have VS2005 SP1 installed or the WAP extensions installed (If you decided not to wait 5 hours for SP1 to install...). In order to make your VSeWSS project also a WAP project you will need to adjust the project type in the .xxproj file. To do this, right click the project we just created an select "Unload Project". It may ask you to save, press yes.

image

Once the project is unloaded, right click it again and select "Edit xxxxxxxxxxx.xxproj".

image

Once you have opened the project file you will need to add in the WAP guid into the ProjectTypeGuids element under the first PropertyGroup. This guid, {349c5851-65df-11da-9384-00065b846f21}, should be the second guid in the list (Shown in bold/green below), the first one being the VSeWSS project guid. All the guids in this element should be separated by a semicolon as shown below.

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>

    <ProductVersion>8.0.50727</ProductVersion>

    <SchemaVersion>2.0</SchemaVersion>

    <ProjectGuid>{7A3CF036-DC02-4868-8CBA-A88E75552372}</ProjectGuid>

    <ProjectTypeGuids>{9E5D3E2D-E4E2-418e-8D80-2F0DA9A94F9A};{349c5851-65df-11da-9384-00065b846f21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>

    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>

    <OutputType>Library</OutputType>

    <RootNamespace>MyGroovySharePointLibrary</RootNamespace>

    <AssemblyName>MyGroovySharePointLibrary</AssemblyName>

    <SignAssembly>true</SignAssembly>

    <AssemblyOriginatorKeyFile>Properties\Temporary.snk</AssemblyOriginatorKeyFile>

  </PropertyGroup>

 After this has been set, right click the the project again and select "Reload Project". Click yes to save when prompted.

image

Now close out of Visual Studio, save your project when prompted. Restart Visual Studio and open your project.

VSeWSS Deployment

Now your probably thinking "Now add a Web User Control, yada yada yada", right? Well, yes, you can now add a Web User Control and its designer will work properly. But we want to be able to deploy it strictly with VSeWSS and its solution builder. In order to do this we will need to install the user control with a Module, so lets do that first. Right click your project and add a new Module. I will generically call it UserControls as I will want to add more user controls to it as I add web parts.

image

Once your module is created you can delete the "sample.txt" file from the UserControls folder. Now right click the UserControls folder and click Add --> New Item and select the standard Web User Control.

image

Next open the module.xml in the UserControls folder and add a reference to your newly created user control.

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

  <Module Name="UserControls" Url="UserControls" Path="">

    <File Url="MyGroovyUserControl.ascx" />

  </Module>

</Elements>

The Url attribute in the Module element specifies the relative path on the SharePoint site where the file will be deployed to. In this case I chose a sub folder called "UserControls".

Creating the User Control and Linking the Web Part

Now, add a label to your User Control called CurrentTime (Or whatever you want to call it...). And set it, in the control load event, to the current time.

namespace MyGroovySharePointLibrary.UserControls

{

    public partial class MyGroovyUserControl : System.Web.UI.UserControl

    {

        protected void Page_Load(object sender, EventArgs e)

        {

            CurrentTime.Text = DateTime.Now.ToString();

        }

    }

}

Since the VSeWSS Solution Builder creates a solution that installs only the project assembly to the GAC (Which is lame indeed) you will need to add an assembly directive to the user control, before the control directive is defined. If you do not add it before the control directive your control will fail to load as it will not have a reference to the project assembly in the GAC.

<%@ Assembly Name="MyGroovySharePointLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9f4da00116c38ec5" %>

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MyGroovyUserControl.ascx.cs" Inherits="MyGroovySharePointLibrary.UserControls.MyGroovyUserControl" %>

<asp:Label ID="CurrentTime" runat="server" Text="Label"></asp:Label>

To obtain the public key token you can run the sn.exe utility in the SDK with the -T parameter and the path to the assembly. For example:

image

Next, override the CreateChildControls method of your web part and load the user control with the Page.LoadControl method. UPDATE: Thanks to Charles for pointing out that you also need to remove (If your not using it) the default Render(...) override. The WebPart template has this defined and by default it does not call the base implementation so ya get nothing displayed!

namespace MyGroovyWebPart

{

    [Guid("cc2a1b52-324a-4a20-bca8-0d512f8348b4")]

    public class MyGroovyWebPart :

        System.Web.UI.WebControls.WebParts.WebPart

    {

        public MyGroovyWebPart()

        {

            this.ExportMode = WebPartExportMode.All;

        }

 

        protected override void CreateChildControls()

        {

            string userControl = "/UserControls/MyGroovyUserControl.ascx";

            try

            {

                Controls.Add(this.Page.LoadControl(userControl));

            }

            catch (Exception exception)

            {

                string message = string.Format("{0} failed to load: {1}",

                    userControl,

                    exception.Message);

                Controls.Add(new LiteralControl(message));

            }

        }

    }

}

Now hit F5 and deploy!

image

As you can see, this method gives you the benefit of the designer and VSeWSS solution deployment. The full source of the sample can be downloaded here:

MyGroovySharePointLibrary.zip (417.78 KB)
Thursday, July 05, 2007 5:18:06 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [8]  | 
Tuesday, June 26, 2007

Normally I like to create a stand alone, abstract BLL assembly that can be used by multiple components (ASP.NET, WinForms, Windows Service, etc), nothing new, standard procedure. Well the VSeWSS Solution builder will only load and reference the VSeWSS project assembly in the SharePoint Solution. Pretty lame if you ask me! You would think that it would package all referenced assemblies not just the project assembly, how hard is that?? So you ether have to house all your code in the VSeWSS assembly or deploy referenced assemblies separately to the SP web front ends. If you forget about this, any code that tries to call code in the referenced assembly will throw a generic "File Not Found" exception in SP.

I sincerely hope there is a newer version of VSeWSS coming down the pike. It has the potential to be a really great tool for SP development but the limitations I keep encountering are seriously lessening its value. This is just another example of that.

Tuesday, June 26, 2007 8:42:04 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Friday, June 22, 2007

Another SharePoint anomaly... Will they ever end??? So I have had this strange issue where doing a deploy of a VSeWSS project does not seem to update the SharePoint site for certain elements like schemas. I have to retract/remove the solution manually from SP, close/reopen Visual Studio (Otherwise I would get a null ref error), then redeploy to see changes. The VSeWSS deploy process does an iisreset (Which I tried manually doing as well) but the updates do not appear without following the steps mentioned. I checked the SP database and SP hive and the elements in question were deployed... So the only thing I could think of is that these elements must cached in memory, which was baffling since an iisreset should clear that out (Right?). Well I finally got the bright idea to verify that iisreset was in fact closing out the IIS service process completely. And lo an behold it was not! After further investigation I found that the “Windows Remote Management (WS-Management)” service, which depends on IIS, was preventing a complete exit of the inetinfo process. I'm not sure what the iisreset was actually doing all along (It said it successfully reset the IIS services), possibly just recycling the AppDomain(s)? But in that case wouldn't SP release cached elements upon termination of the AppDomain(s)? Not really sure, but what I am sure of is that stopping and disabling the the WRM service allowed inetinfo to completely close when the service was restarted by iisreset. And voilà! My changes!

Incidentally I am running Virtual Server 2005 which is monitored by the "Virtual Machine Manager Agent" service. This service depends on the WRM service. So disabling the WRM service will disable your VMMA service. You might want to let your network admin know you need this disabled or it might reappear.

Friday, June 22, 2007 9:22:26 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Monday, June 18, 2007

This error almost drove me over the edge... The SharePoint properties pane in Visual Studio would show the error "An error occured trying to load the page. Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)"

image

 

After pulling much of m