Please point your feed reader to http://www.kindohm.com for my latests posts and updates.
Here's a quick script for enumerating all of the available site definitions, their configurations, and any site templates installed on a SharePoint Server. If you're creating sites programmatically, this comes in handy as you'll likely rely on these values in a configuration document somewhere:
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$url = "http://MySite"
$site= new-Object Microsoft.SharePoint.SPSite($url )
$web= $site.OpenWeb()
$loc= [System.Int32]::Parse(1033)
$templates= $site.GetWebTemplates($loc)
foreach ($child in $templates)
{
write-host $child.Name $child.Title $child.ID
}
write-host done
When implementing a custom Feature in SharePoint 2007, you may rely on custom configuration data (in a custom configuration section) stored in a SharePoint site's web.config file. When activating and deactivating your feature through the SharePoint site settings user interface, this works just fine. However when using the STSADM.EXE command-line tool to activate and deactivate features, there is no web context and the tool does not run within the context of a SharePoint site. Thus if your feature relies on custom configuration data in a config file, the configuration data will not be available when using STSADM.EXE.
You can address this problem by creating (or modifying) a file named STSADM.EXE.config that resides in the same folder as STSADM.EXE (c:\program files\common files\microsoft shared\web server extensions\12\bin). This file is just an application configuration file (e.g. app.config), and you can add custom configuration data to it like any other web.config or app.config file. Keep in mind that this is a server-wide config file. Any customizations or changes you make will affect all uses of STSADM.EXE on all sites on the server.
Here's a quick little PowerShell script you can use in conjunction with Out-Zip to zip up a folder on your hard drive and email it:
$sender = sender@host.com
$recipient = recipient@host.com
$server = mail.host.com
$targetFolder = c:\MyFolder
$file = c:\MyZipFile.zip
if ( [System.IO.File]::Exists($file) )
{
remove-item -force $file
}
gi $targetFolder | out-zip $file $_
$subject = "Sending a File " + [System.DateTime]::Now
$body = "I'm sending a file!"
$msg = new-object System.Net.Mail.MailMessage $sender, $recipient, $subject, $body
$attachment = new-object System.Net.Mail.Attachment $file
$msg.Attachments.Add($attachment)
$client = new-object System.Net.Mail.SmtpClient $server
$client.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
$client.Send($msg)
This MSDN article on creating custom multi-column field classes in WSS lists incorrectly specifies the string delimiter used to construct a new SPFieldMultiColumnValue object. Microsoft says that the delimiter is "#;" when in fact it is ";#" (the semi-colon and pound sign are swapped). I discovered this while writing unit tests and referring to the article.
I have to cut Microsoft some slack because the documentation is "pre-release". It's one of the pains of developing against Beta software.
Hanselman recently posted about the "upper limit" of the number of projects in VS 2005. It's a good post because it's true that too many projects just bog down Visual Studio. Consider refactoring your VS .Net solution to decrease the number of projects if you're having performance problems.
I'm currently working with a VS 2003 project where there are 26 projects. We really don't need 26 projects. But for right now, the size is borderline unmanageable. VS 2003 crashes often and it takes a long time for the solution to load.
What makes matters worse (as Hanselman also notes) is the JetBrains ReSharper add-in. ReSharper by itself is an awesome tool, but not so much with very large VS 2003/2005 solutions. ReSharper constantly searches your projects as you type and perform tasks in Visual Studio, so the more projects you have the slower it goes.
Still even worse is that ReSharper will re-enable itself if you disable it as an add-in. Normally in Visual Studio, you can press the Ctrl+Space keyboard combination to enable Intellisense/auto-complete. This key combination will enable ReSharper if it is disabled. This is at least true for VS 2003.
I've resorted to completely uninstalling ReSharper for VS 2003 from my system. I use the Ctrl+Space combo pretty often by habit, and I don't want ReSharper enabled. My only option was to completely get rid of it until I move on to another project.
In the meantime, my team will look in to shrinking the number of projects in our solution to make it more manageable. Then we'll reap the benefits of a leaner solution and add-ins like ReSharper.
I came across this Powershell blog post about adding folders and files to a zip archive, but it wasn't exactly 100% usable out of the box. Here's a slight re-work of the script that will have you zipping files from a prompt all day and all night:
########################################################
#
# out-zip.ps1
#
# Usage:
#
# To zip up some files:
# ls c:\source\*.txt | out-zip c:\target\archive.zip $_
#
# To zip up a folder:
# gi c:\source | out-zip c:\target\archive.zip $_
########################################################
$path = $args[0]
$files = $input
if (-not $path.EndsWith('.zip')) {$path += '.zip'}
if (-not (test-path $path)) {
set-content $path ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
}
$ZipFile = (new-object -com shell.application).NameSpace($path)
$files | foreach {$zipfile.CopyHere($_.fullname)}
Pair programming is an important part of modern-day, agile software development processes. It helps developers write better software. Pair programming is a good thing.
However, I usually can't stand watching someone else write code, or watch someone else do anything on a keyboard for that matter. It's like getting teeth pulled. No, it's like getting teeth pulled while walking on hot coals and self-inflicting paper cuts.
A few of us at work are reading The Pragmatic Programmer, which stresses knowing how to squeeze every drop of coding power out of a text editor. A developer's text editor (or IDE) is to code as a woodworker's saw is to wood. I couldn't agree more. The better you know how to work the keyboard the faster (and easier) you will be able to write code. And I'm not even talking about editors like VI and Emacs - I'm even just talking about basic Notepad operations (although I hope you're not using Notepad to write your apps).
So, here are some basics (at least on a Windows OS) that will leave your mouse-wielding co-workers in the dust, and will make me pleased to pair-program with you:
- Ctrl+X, Ctrl+C, Ctrl+V Cut, Copy and Paste
95% of developer already know these, but they save you the hassle of choosing the cut, copy, and paste commands from a toolbar or menu.
- Ctrl+[right/left arrow] Move word to word
This will move your cursor to the next "non-alphanumeric" character in a file. Meaning, if you have spaces between words it will stop at each space. Handy for moving quickly down a line of text rather than just the arrow keys alone.
- Shift+[arrow keys] Select text
Allows you to select (e.g. highlight) text without using the mouse. Use the left and right arrows to select adjacent text, or the up and down arrows to select entire lines of text.
- Ctrl+Shift+[right/left arrow] Select text word to word
This is like the Ctrl+[right/left arrow] and Shift+[arrow keys] commands combined together. You can select words while moving your cursor rather than single characters at a time.
- Home and End keys Go to the beginning or the end of a line
These are arguably two of the least-utilized keys by developers.
- Ctrl+Home and Ctrl+End Go to the beginning or the end of a document
It's like the Home and End key behavior on steroids.
- Ctrl+s Saves the file
I'm surprised at how many people still use the mouse to move their pointer to the Save button a toolbar. This shortcut will prolong carpal tunnel syndrome by 0.01% each time you use it. All major Windows text editors support this.
- Alt, with arrows Navigates menu bars
You should know keyboard bindings for commands specific to your favorite text editor. For example: closing the text editor, code indenting/formatting, etc. But if you don't, you can use alt key to enable keyboard command of the menu bar and then use the arrow keys to navigate it.
Once you've gotten the hang of these, using them in combination with each other gives you more power. For example, if you want to delete the line that your cursor is on, use the End key, then Shift+[down arrow] to select the line, then delete.
As I already mentioned, you'll need to go beyond the basic text editing commands to harness all the power of your text editor. Get to know the keyboard bindings that your editor supports to complete certain tasks and operations. You should also be able to set up custom bindings to perform commands. For example, if you really want to use the Ctrl+U, Shift+X sequence to auto-indent and format your code, then you should be able to set up that binding in your editor. If you can't do that with your text editor or IDE, then you might want to try a different one. Being able to set up a Rebuild All command in Visual Studio .Net from the keyboard is one of my first tasks after a fresh install.
Take all this only as a suggestion. I don't think you have to use keyboard shortcuts to be a good developer, and there's probably somebody out there who is faster than me by using a mouse. However I've never met such a person, and their right wrist is probably throbbing right about now. You need to do what is comfortable to you so that you like your development environment and can get your job done. But if I'm pair programming with you and you see me falling asleep as you use the mouse to select some text, now you'll know why.
A while back I installed Office 2007 Beta 2, but today I wanted to uninstall it as there were a few quirks of Outlook 2007 that were starting to bug me. Back when I installed Office 2007 Beta 2, I installed it over Office 2003. After uninstalling Office 2007 I attempted to open Outlook 2003 and received this error message:
Cannot start Microsoft Outlook. MAPI32.DLL is
corrupt or the wrong version. This could have
been caused by installing other messaging system.
Please reinstall Outlook.
I repaired the Office 2003 installation but that did not help. I also reinstalled Office 2003 but that did not help either.
Then I came across this forum post that had something that worked (the solution is the last reply in the post). Mapi32.dll is obviously located at c:\windows\system32\Mapi32.dll right? Well, yes, but unfortunately the error message is not specifying the correct file name of the dll in question.
The actual dll that is causing the problem is MSMAPI32.dll, which is located at C:\Program Files\Common Files\System\MSMAPI\1033. Simply rename this file to something else (e.g. MSMAPI32_OLD.dll), reinstall Office 2003, and Outlook 2003 should work perfectly after that.
I'm not sure if this solution will have any effect on other permutations of how Office 2003 and Office 2007 are installed. e.g. if you get the error about MAPI32.dll if you uninstalled Office 2003 before installing Office 2007 Beta, I don't know if this solution will help you.
I recently maintained an ASP .Net application where Xml Serialization was used
to read and persist configuration data during page requests. When site traffic
increased, users encountered System.IO.IOExceptions with the
message "The process cannot access the file "c:\SomeFolder\SomeFile.xml"
because it is being used by another process. The exception was not always
encountered 100% of the time. The problem could be easily reproduced by opening
two browser windows, opening the page in each browser, and quickly refreshing
each browser side by side.
I was able to narrow down the problem to a method in a class where
deserialization takes place on each page request. The following code was used:
XmlSerializer serializer = new XmlSerializer(typeof(Foo));
TextReader reader = new StreamReader(serializedFooPath);
myFoo = (Foo)serializer.Deserialize(reader);
reader.Close();
Basically the TextReader locks the file until it is done using
it. You can easily avoid locking a file by changing how it is
accessed. Simply introduce a FileStream object that can control
how the file is accessed rather than allowing the TextReader to
control it for you. The FileStream object can accomplish this by
using the FileShare.Read enumeration value:
XmlSerializer serializer = new XmlSerializer(typeof(Foo));
FileStream fs = new FileStream(serializedFooPath, FileMode.Open, FileAccess.Read, FileShare.Read);
TextReader reader = new StreamReader(fs);
myFoo = (Foo)serializer.Deserialize(reader);
reader.Close();
fs.Close();
According to MSDN, any request to open the file will fail until the file is
closed if FileShare.Read is not specified [1].
Making this change eliminated the problem and the IOException no longer
occurred. It was an easy quick fix - perhaps a better deserialization strategy would come in handy but this fix was able to fix the bug quickly. Thanks to Jake for
helping out by supplying the MSDN reference.
[1] Refer to
http://msdn2.microsoft.com/en-us/library/system.io.fileshare.aspx for
full documentation of the FileShare enumeration and
http://msdn2.microsoft.com/en-us/library/system.io.filestream.aspx for
full documentation of the FileStream class.
The ASP .Net web caching API is a powerful and useful tool at your disposal. Here
are a few tips and suggestions that will help make your code more flexible and
maintainable when using the caching API:
- Define unique keys for cached items in your domain
- Store expiration durations or times in a configuration file (web.config)
- Include a configuration setting for enabling and disabling caching
- Encapsulate caching behavior in a class
Define Unique Keys
You can get or insert items from and into the cache using a key. A key is a String
value that uniquely identifies an item stored in the cache. Normally you will want
to use a key based on the unique identifier of some domain object you are working
with. For example, you may have a Customer class with an Integer
property named CustomerId that uniquely identifies the customer in
your domain. Using the CustomerId as the basis for the cache key would
be a good choice. However, the unique identifier of the domain object alone is not
a good, complete choice for the cache key. Consider this example:
//Bad example. Non-unique keys are used.
Customer customer = GetCustomerById(100);
Employee employee = GetEmployeeById(100);
double customerValue = 10;
double employeeValue = 100;
Cache.Insert(customer.CustomerID.ToString(), customerValue);
Cache.Insert(employee.EmployeeID.ToString(), employeeValue);
The same cache key ("100") is being used to store two different values for two different types of objects,
and the employee's value will overwrite the customer's value. In real life you wouldn't do anything like the
example above on purpose, but when dealing with the cache across many web forms or web controls in a
web application, you may not know or remember what other keys are being used to cache values.
A better approach would be to qualify the cache key with something that describes the data being stored:
//Better example. Unique keys are used.
Customer customer = GetCustomerById(100);
Employee employee = GetEmployeeById(100);
double customerValue = 10;
double employeeValue = 100;
Cache.Insert("Customer" + customer.CustomerID.ToString(), customerValue);
Cache.Insert("Employee" + employee.EmployeeID.ToString(), employeeValue);
Make Sliding and Absolute Expirations Configurable
Don't hard-code expiration times in your caching code. Someone (either you or a user) will probably
change their mind in the future about how long to store data in the cache. Use a customer configuration
section or the AppSettings section in web.config to store cache expiration values, as this
will allow you to change cache times without re-compiling the application.
The web.config file could use an appSettings section as simple as the
example below to configure the number of minutes to store an item in the cache:
<appSettings>
<add key="customerYtdSalesCacheMinutes" value="30"/>
</appSettings>
Insert items into the cache using the app setting value:
...
//calculate the absolute expiration DateTime
DateTime absoluteExp =
DateTime.Now.AddMinutes(YtdSalesCacheMinutes);
//Use a sliding expiration of zero
TimeSpan slidingExp = TimeSpan.Zero;
Cache.Insert(
someKey, someValue, null, absoluteExp, slidingExp);
...
public int YtdSalesCacheMinutes
{
get
{
return Convert.ToInt32(
ConfigurationManager.AppSettings["customerYtdSalesCacheMinutes"]);
}
}
Enable and Disable Caching Through Configuration
There may be times when you'll want to disable caching within your app (e.g. during development or debugging).
Use a configuration setting and add some simple logic to your code to handle this:
<!-- web.config -->
<appSettings>
<add key="customerYtdSalesCacheEnabled" value="true"/>
</appSettings>
// C# code //
...
if (YtdSalesCacheEnabled)
Cache.Insert(key, myValue, null, absoluteExpiration, slidingExpiration, CacheItemPriority.Normal, null);
...
public bool YtdSalesCacheEnabled
{
get
{
string setting =
ConfigurationManager.AppSettings["customerYtdSalesCacheEnabled"];
if (string.Compare(setting, "true", true) == 0)
return true;
return false;
}
}
Encapsulate Caching Behavior in a Class
Insert and obtain cached values to and from the ASP .Net cache with
a class designated for that behavior. This will provide the full benefit of an
object-oriented approach. The most obvious and immediate benefits are that caching can
be re-used from anywhere within the application, cache keys can be generated
consistently, and configuration settings can be obtained in a single place. The design
of such a class could take on many different forms to suit the needs of different
applications, but here is one example:
using System;
using System.Web.Caching;
using System.Configuration;
namespace MJH.Caching
{
public class CustomerCacheManager
{
private const string CUST_YTD_SALES_KEY = "CustomerYTDSales-{0}";
/// <summary>
/// Obtains a customer's YTD sales value.
/// </summary>
/// <param name="cache"></param>
/// <param name="customer"></param>
/// <returns></returns>
public double GetCustomerYtdSales(Cache cache, Customer customer)
{
//get the cache key
string key = GetCustYtdKey(customer.CustomerID);
//return the value from the cache if it exists
if (cache[key] != null)
return (double)cache[key];
//calculate the (time consuming) YTD sales value
double ytdSalesValue = customer.GetYtdSalesValue();
//if caching is enabled, store the calculated
//value in the cache
if (YtdSalesCacheEnabled)
{
DateTime absoluteExp =
DateTime.Now.AddMinutes(YtdSalesCacheMinutes);
TimeSpan slidingExp = TimeSpan.Zero;
cache.Insert(
key, //item key
ytdSalesValue, //item value
null, //dependencies
absoluteExp, //absolute expiration
slidingExp); //sliding expiration
}
return ytdSalesValue;
}
/// <summary>
/// Generates a cache key for a customer
/// YTD sales value.
/// </summary>
/// <param name="customerId"></param>
/// <returns></returns>
private string GetCustYtdKey(int customerId)
{
return string.Format(CUST_YTD_SALES_KEY, customerId.ToString());
}
/// <summary>
/// Gets the nubmer of minutes that YTD sales
/// values should be cached.
/// </summary>
public int YtdSalesCacheMinutes
{
get {
return Convert.ToInt32(
ConfigurationManager.AppSettings["customerYtdSalesCacheMinutes"]);
}
}
/// <summary>
/// Gets whether caching is enabled.
/// </summary>
public bool YtdSalesCacheEnabled
{
get
{
string setting = ConfigurationManager.AppSettings["customerYtdSalesCacheEnabled"];
if (string.Compare(setting, "true", true) == 0)
return true;
return false;
}
}
}
}
Make sure you check out SeeWindowsVista.com to see a lot of cool things you can do with WPF. Lots of examples of 2D and 3D, including the earlier demos/proof-of-concepts (e.g. NASCAR, North Face, etc).
"Tell me about yourself".
This is the first item I ask of interview candidates, and I'm suprised at how often it completely catches them by suprise or how they don't know how to answer the question. I've gotten responses from "what do you mean" to an entire detailed work history.
It is a tough question, but I think candidates have to know how to answer it. It's their 60-second chance to tell me what's important and relevant about their personality and their professional work history. It forces the candidate to have their thoughts organized and to be concise. Unfortunately most of the time their response results in train wreck of technical project details.
I don't expect a response that's perfect, but I'm looking to hear the candidate tell me about:
- Professional interests. e.g. hard-core programmer, .Net development, WinFX/WPF, Ajax, Agile Development, and Perl.
- Personality traits. e.g. enjoy working with people, have fun at work while being professional, refuse to work with end users.
- Work history. e.g. I was a Code Monkey and developed a Widget and a Thingamabob at Acme. I led a team of developers at Intertrode and successfully rolled out 4 large web apps.
- A few personal things. e.g. camping, hiking, playing drums, and watching kung fu
This topic in an interview isn't a make-or-break item, but it sets the stage for the rest of the interview. It gives the candidate a chance to shine and to let me know that they're serious about wanting to talk with me, about wanting the job, and that they've put some thought into what they want to tell me.
Someone in the Inetium office is messing with me. One day last week, and again yesterday, my Windows desktop wallpaper was automatically changed to this image:

I went to my Desktop properties to change it back to something else, but my wallpaper controls were disabled. What's also weird is that the image is "doctored". Most of the image is real - that is really me behind a goalie prop at the XCel Energy Center - but that flaming flag thing is not a part of the original image. Thus my research began...
- In the registry, my desktop wallpaper was pointing to an image named "hods.jpg" out on a share of our domain controller.
- I changed the registry value, but when I rebooted the setting was overwritten.
- Very few people in the office call me "hods". I have a lousy poker face, but did the best I could yesterday to try and snuff out the person that did this. Nobody fessed up.
- I talked to a couple of our domain admins and they couldn't figure out how the setting was being written, nor could they identify the the individual who put the image out on the share. The owner was only shown as "Administrator".
- I checked out the other profile folders under my Documents and Settings folder, and there was only one other one besides myself. That folder is the name of one of our old domain admins and it was created back in January 2005. I didn't see any evidence in there that he had logged in recently.
- I verified in my startup settings (using msconfig) that no special batch file or script is being run on startup.
- In the meantime I've eliminated all shared folders and have removed all known "extra" permissions to folders on my machine.
- I now have successful security events logged to my security log, so I can now find out if someone else is logging in to my machine.
Late yesterday after rebooting another time, my wallpaper settings were unlocked and I could change the wallpaper. I was never able to track down the perpetrator.
What's a bit annoying about all this is that I do demos and use my laptop with client users pretty often, so to unexpectedly see that image stretched across my screen when I log in at a client is a little awkward. But overall, I'd rather find out who is doing it than solve the problem. I guess I'll just see if the problem happens again.
I haven't posted in a while because things have been pretty busy in the office. I haven't had much time at all to continue my WPF research, but there are some other interesting things happening that I hope to write about in the near future.
VS .Net Solution Re-structuring - My development team that works on .Net apps for one of our clients is looking into restructuring the primary Visual Studio .Net solution that holds most of the app code. The way it is currently structured makes it difficult to develop, maintain, and deploy the applications. There are 24 projects in the solution, including web applications and shared assemblies. A new strategy for versioning and deployment is also in the works.
Not-so-shared web controls - I now have ownership of a common web controls project that is a part of the bigger solution named above. It was developed using web user controls but is being deployed for use by multiple web applications. Some trickery with virtual directories is used in an attempt to try and make these web controls "shared", but because of how ASP .Net caches assemblies, it causes many problems during deployment over the different web applications. Ideally I'd like to convert this project into a custom web server control library so that they can be truly shared, but there are over 75 web user controls that would need to be converted. I'm not sure if it will make sense to literally convert everything to custom web server controls, but what we're doing now doesn't work so well either.
Perhaps the biggest challenge will be to justify the time needed to work on these issues. Arguably, fixing these issues will result in much higher development productivity and smoother deployments in the future. Short term, the gains may not be noticeable - and will likely slow down any immediate development. Building up the case to work on these issues shouldn't be too difficult, but it will be necessary in order to justify working on them.
More Posts
Next page »