Welcome to MSDN Blogs Sign in | Join | Help
Check out Flickr Spell - a cool little Web app that can render a word using images of alphabets available on Flickr.

If you were in the soft drink industry, what one piece of information would you absolutely covet that, if you had it, would make you believe that your success is a foregone conclusion? Maybe the recipe for Pepsi? Or how about if you were an automobile manufacturer? Perhaps the schematics for Toyota's hybrid powertrain from the Prius would make your mouth water.

Now these are hypothetical situations, of course (assuming you're not planning to pull off The Italian Job). But wait! If you're an engineering manager in the software industry, you might actually have the Recipe For A Successful Organization And Product. Joel Spolsky over at his blog describes the "Development Abstraction Layer", a means to let the key players in an organization to do what they do well, with no extraneous distractions. I quote an interesting excerpt below:
"A programmer is most productive with a quiet private office, a great computer, unlimited beverages, an ambient temperature between 68 and 72 degrees (F), no glare on the screen, a chair that's so comfortable you don't feel it, an administrator that brings them their mail and orders manuals and books, a system administrator who makes the Internet as available as oxygen, a tester to find the bugs they just can't see, a graphic designer to make their screens beautiful, a team of marketing people to make the masses want their products, a team of sales people to make sure the masses can get these products, some patient tech support saints who help customers get the product working and help the programmers understand what problems are generating the tech support calls, and about a dozen other support and administrative functions which, in a typical company, add up to about 80% of the payroll. It is not a coincidence that the Roman army had a ratio of four servants for every soldier. This was not decadence. Modern armies probably run 7:1."
This may actually sound like the idealistic dreams of a programmer in La-La land, but I think he is absolutely on to something. If nothing else, he runs a successful company where he implements all of the above.

However, while his article appears developer-centric, I would extend this to envelop other roles in engineering as well, namely, testers and program managers. Their roles are equally crucial.

My 2 paise.

This was just SO COOL, I had to write about it.

Having read portions of Sally McGhee’s book on productivity using Outlook 2003, I’ve been working hard to whittle my Inbox down to 50-odd messages from somewhere in the vicinity of 2000+. I’ve managed to maintain this goal somewhat, and I’m constantly monitoring my Inbox to ensure that I don’t go over, say, 75 messages in it at any time.

The primary method I use to achieve this is the two-minute rule: for any new email, I read enough of it to decide whether it is actionable or not. If it is actionable and the action will take no more than 2 minutes of my time, I finish the task right then, and hit Ctrl-D on that message with a loud, satisfying slam. If it is expected to take more than 2 minutes, I leave it in my inbox. All personal messages get read upon opening, and, based on their contents are either deleted (message from home owners association about upcoming meeting to discuss dark stain on east wall near elevator on the 4th floor) or moved (driving directions to buddy’s house for next Friday’s Boardgame Nite) to a Personal folder right away. Anything else gets zapped with a Shift-Delete and an evil chuckle. The upshot of following this method, then, is that my Inbox pretty much has become my Task/todo list, and my Outlook Task list is basically empty (I don’t like the cumbersome Tasks interface, but I shouldn’t  complain since they’re only at, er… version 11. I hope O12 will fix this).

Anyway, this brings me to the point of my posting: I was fooling around trying to create a new PST file to save my POP3 mail, and then realized that I can’t have my Exchange email go to one PST file and have my POP3 mail go to another – bummer. I didn’t know this until I created the second PST file, of course, and found out to my dismay that all my Outlook email landed up there beside my POP3 mail (because all accounts must use the same PST file; I don’t understand the need for this requirement. Or maybe I don’t understand the feature). In any case, I decided to do away with the second PST, and so I Shift-Deleted all the Exchange mail I thought was duplicated, from the new PST. Turns out, since this new PST had become the new default location for all my mail, all of this mail was deleted from my Exchange account as well. Wow! I realized this with a sinking feeling when I switched back to the original PST as the default, and found 0 messages in my Inbox.

My todo list had just evaporated.

That was when I had that same sinking feeling you get when you delete that last copy of your term paper, or the Russell Peters fanclub website code you hacked up overnight on your local disk.

I did have a hunch though – I’ve noticed earlier that Outlook folders have a “Process all marked headers” right click menu option. Free Image Hosting at www.ImageShack.us I’ve always assumed from seeing this that Outlook doesn’t really purge messages in a PST, but rather marks a header for later processing, for performance reasons. I looked at the PST file from which I had deleted all my Outlook mail, and its size (13+ MB) betrayed this fact as well.

So off I went down the rabbit hole looking for programs that could recover deleted email. Sure enough, I hit upon several commercial options that would tease you by showing all your deleted email, and then charge you 4 times your net worth to actually recover them. Imagine someone gave you a free Ferrari, and then charged you $250,000 for the key to the fuel tank lock. Sort of like that. Like a bad dream. Anywho, I poked around a bit more and found a Microsoft KB article that gave me some more clues – apparently it is possible to add a registry key to enable recovery of deleted emails in Outlook. Didn’t work for me, unfortunately. A bit more searching though, and I hit upon this page detailing an ingenious, free (as in $0.00, as in Buy None Get One Free!!, as in beer) method derived from techniques detailed by a fraud examiner in the High Technology Crime Investigation Association’s webpage. The method involved corrupting the PST file first, and then recovering it with scanpst.exe which ships with Outlook 2003. Apparently ScanPST reverts all objects to “not deleted” when repairing a corrupted file, but not when scanning an uncorrupted file. Kudos to the kind folks at techrepublic for putting up this tip. Thanks to Raihan Kibria for frhed, the freeware binary editor I used for this. Hooray for the public domain!

Now back to my todo list…

John Udell has implemented LibraryLookup, an awesome Javascript bookmarklet to look up a book that you're browsing on a site like Amazon, in your local library's web catalog. This is IMHO one of those neat little ideas that are easy to implement, but pure genius to come up with. I found this while researching Joel Spolsky's book "Joel on software : and on diverse and occasionally related matters that will prove of interest to software developers, designers, and managers, and to those who, whether by good fortune or ill luck, work with them in some capacity" (if the title got any longer, we'd have buffer overruns in bookstore catalogs). I'm looking forward to reading this after having devoured his eminently readable online mini-book on User Interface design (Quotable quote on the Windows Help setup "wizard" from in there:

The first problem with this dialog is that it's distracting. You are trying to find help in the help file. You do not, at that particular moment, give a hoot whether the database is small, big, customized, or chocolate-covered. In the meanwhile, this wicked, wicked dialog is giving you little pedantic lectures that it must create a list (or database).

Highly recommended read for long bus or train rides.

 

Do you dream of debugging Yukon CLR code?

 

SQL Server 2005 (the database formerly known as “Yukon”) hosts the Microsoft.Net CLR runtime in-process, allowing developers and database administrators to author stored objects (such as procedures, functions, and triggers) as well as user-defined data types (“UDTs”) using a .Net language such as C# or VisualBasic.Net. This makes for a great programmability story, since computation-intensive operations can now be written in a fraction of the time using a language such as C#, and can also be offloaded to the CLR to increase performance depending upon the type of operation(s) in question.

 

But while most of us no longer queue up before mainframes praying that our stack of 3,072 punched cards from a 14-hour coding (punching?) session isn’t “returned to sender” thanks to a missed semi-colon, Microsoft’s next database server has not decreased in complexity either. With the addition of the hosted .Net runtime, debugging all the cool C# code that developers will churn out is an important supportability issue.

 

What are the current options available for debugging CLR code in Yukon?

  1. Microsoft Visual Studio (“Whidbey”) 2005 – this is ye olde beast with kick-ass new IntelliSense, and the brand spankin’ new support for integrated TSQL and CLR debugging (“mixed-mode” debugging, if you will).
  2. See #1

Ok, so the above list is not entirely accurate, given that Borland is expected to join the fray. My point though, is that if you’d like to hit the ground running when Yukon ships this year, VS 2005 is the only option (albeit a fantastic one, what with the ability to create and debug SQL Server 2005 database projects, true mixed-mode debugging capabilities, cool stuff like integrated callstacks, stepping from TSQL into managed code and back, setting breakpoints in both SQL and CLR code, and so on).

 

However, if you are an independent software developer/database administrator or a hobbyist programmer evaluating .Net programmability in Yukon, the cost is prohibitive (unless you bought EBAY at $6).

 

 

Who Wants To Debug CLR Code For $0.00?

 

So how does a price of $0.00 (+ tax) for CLR debugging in Yukon sound to you? Sure, you won’t get an IDE (at least not yet) but you can still at least debug CLR code running inside Yukon. Enter mdbg, with its own work-in-progress GUI. Mdbg ships with the .Net framework SDK v2.0, and is a replacement for the trusty old cordbg command-line CLR debugger currently available for free with the .Net Framework v1.1. But Mdbg can debug the CLR even when its hosted within another process, and is therefore a great candidate for debugging the CLR hosted within Yukon as well. Mdbg is written purely in managed code (using COM-interop for all the hairy debugger API glue), is extensible using a neat extension mechanism, scriptable, and will be available as a free download once the Framework v2.0 ships. Note that dbgClr which is a lightweight GUI debugger based off of the Visual Studio code also ships for free alongside the SDK. More information can be found here: http://weblogs.asp.net/andypennell/archive/2005/02/21/377621.aspx. It's future beyond v2.0 of the Framework appears shaky, and there are hints that mdbg and its GUI will supercede it.

 

 

Can I Get A TSQL Debugger For The Same Price?

 

Ah, glad you asked. In fact, a prototype TSQL debugger written purely in managed code is in the works, and we are working to make it available both as a sample for TSQL debugger writers, as well as for the general (developer/dba) public. While it won’t put Visual Studio out of business, it should be handy for most straightforward TSQL connection debugging tasks. Watch this space!

 

Developer evangelist Joe Stagner "discusses security risks, vulnerabilities, and solutions from the software developer's perspective" in a series of security webcasts for developers. The idea is to provide "real-life examples and security tips and tricks" to write more secure code.

It appears there's also a drawing for a free Portable Media Center device for those moments when you're stuck for 4 minutes in the line at the post office and just need to watch 3 minutes of Seinfeld (and 1 minute of advertisements). Yes, I am being sarcastic :-)

CLR User Defined Types (UDTs) are a fantastic new addition to SQL Server 2005 (the database formerly known as Yukon). Every Yukon UDT must specify the type of serialization format it uses so that the engine knows how to roundtrip data represented by an instance of that UDT between the TSQL (unmanaged) and managed worlds. For example, conversion of a UDT instance into an NVARCHAR instance causes the UDT serialization code to be invoked.

As of Beta 2, Yukon supports two serialization formats:

  • Format.Native
  • Format.UserDefined

Yukon maintains UDT instances in the unmanaged world as a special byte stream annotated with metadata information, whereas it is maintained as an instance of a CLR type in the managed runtime.

Format.Native serialization: This is the simple form of serialization where the engine automatically performs serialization and deserialization of instances of the type. Yukon UDTs employing this form of serialization are basically CLR classes or structs in the managed world, such that every single instance of that type has the same storage requirements as the others. Therefore, fields of such UDTs are all of types whose storage requirements are known at compile-time (you could think of them as “blittable” types). For example, System.Int32 and System.Byte among others are valid for native serialization; System.String is not valid since different instances of a System.String can have different lengths. The engine computes the storage requirements for this type at type registration time (i.e. during a CREATE TYPE operation) and saves this away in internal metadata tables.

Format.UserDefined serialization: This form of serialization for a UDT requires more work on the part of the UDT author who must explicitly provide the code that will serialize instances of such a UDT since the engine has no idea what parts of which fields we have decided to persist. In other words, garbage in, garbage out. In fact, the engine requires us to implement the System.Data.Sql.IBinarySerialize interface containing the Read() and Write() methods for deserialization and serialization, respectively. While this does mean that we must write additional code to make sure our own data is roundtrippable, it is in fact a rather powerful feature – we can now create UDTs containing fields of type System.Collections.Hashtable or array types as long as we take the responsibility to correctly serialize and deserialize instances of the UDT in our implementations of Read(System.IO.BinaryReader) and Write(System.IO.BinaryWriter).

Which brings me to the topic of this posting: the importance of having correct serialization code. I was playing around with a Yukon UDT to store an address using Format.UserDefined serialization. My UDT has a public field ‘m_XmlFullAddress’ of type SqlXml to contain an XML representation of the address. The Read() and Write() methods alone are shown below for convenience:

    //******************************************************

    // IBinarySerialize interface

    //******************************************************

 

    public void Read(BinaryReader r)

    {

      m_fNotNull = r.ReadBoolean();

      if (!r.ReadBoolean()) {

        m_firstline = r.ReadString();

      }

      else {

        m_firstline = SqlString.Null;

      }

      if (!r.ReadBoolean()) {

        m_secondline = r.ReadString();

      }

      else {

        m_secondline = SqlString.Null;

      }

    }

 

    public void Write(BinaryWriter w)

    {

      w.Write(m_fNotNull);

      w.Write(m_firstline.IsNull);

      if (!m_firstline.IsNull) {

        w.Write(m_firstline.ToString());

      }

      w.Write(m_secondline.IsNull);

      if (!m_secondline.IsNull) {

        w.Write(m_secondline.ToString());

      }

    }

 

With my implementation of the Address UDT, when I attempted to set the value of ‘m_XmlFullAddress’ explicitly using the public field accessor syntax and then read it back immediately afterward, it would not persist. The TSQL code excerpt I used to do this is shown below:

declare @a Address

set @a = CONVERT(Address, '911 Fire Lane |Circuit City, XO 00000')

select @a. m_XmlFullAddress

set @a.m_XmlFullAddress = '<address/>'

select @a. m_XmlFullAddress

go

 

The second SELECT returns the same result as the first. It foxed me for a while since I was under the impression that having just set a value on the UDT variable, immediately retrieving that same value should somehow obviously work, right? I mean the engine probably maintains an instance of the Address class in the hosted CLR and would simply access the corresponding field I just set a moment ago.

Not so.

The server actually relies on the serialization code to perform any operations that modify the instance of the UDT. Setting the ‘m_XmlFullAddress’ property in the TSQL world (using the SET statement above) actually causes SQL Server to follow a series of steps (not necessarily in the exact order shown):

  1. infer that @a.m_XmlFullAddress is of type SqlXml
  2. take the NVARCHAR string supplied (the right hand side operand of the assignment), and convert it to an XML data type instance implicitly (since the XML data type maps to the managed SqlXml data type)
  3. now convert the XML data type instance from step 2 into a managed SqlXml instance using the internal conversion/mapping routines
  4. send the set of bytes representing the current value of the UDT variable @a to the managed world by invoking our deserialization method (IBinarySerialize.Read()), and instantiate this UDT in the managed runtime using its equivalent CLR type
  5. perform the assignment of the new SqlXml value to the SqlXml field of the managed instance
  6. invoke our serialization code (IBinarySerialize.Write()) to convert the modified managed UDT instance back into a byte stream representing the TSQL UDT variable world and assign it to the variable @a

Note from the steps above how our deserialization and serialization code is invoked in steps 4 & 6 respectively, even though we were performing what we thought was an innocuous and straightforward field mutation. It should now be hopefully evident that the real reason why our code did not work is because our serialization code was ignoring the SqlXml field. By leaving out m_XmlFullAddress in IBinarySerialize.Read(), its value is never read in or set during deserialization, and by leaving it out in IBinarySerialize.Write() it is never saved out during serialization.

Correcting this problem is relatively simple. The modified Read() and Write() methods are shown below for convenience:

    //******************************************************

    // IBinarySerialize interface

    //******************************************************

 

    public void Read(BinaryReader r)

    {

      m_fNotNull = r.ReadBoolean();

      if (!r.ReadBoolean()) {

        m_firstline = r.ReadString();

      }

      else {

        m_firstline = SqlString.Null;

      }

      if (!r.ReadBoolean()) {

        m_secondline = r.ReadString();

      }

      else {

        m_secondline = SqlString.Null;

      }

 

      // get the XML value

      if (!r.ReadBoolean()) {

        string xml = r.ReadString();

        m_XmlFullAddress = SqlXml_Parse(xml);

      }

      else {

        m_XmlFullAddress = SQLXML_NULL;

      }

    }

 

    public void Write(BinaryWriter w)

    {

      w.Write(m_fNotNull);

      w.Write(m_firstline.IsNull);

      if (!m_firstline.IsNull) {

        w.Write(m_firstline.ToString());

      }

      w.Write(m_secondline.IsNull);

      if (!m_secondline.IsNull) {

        w.Write(m_secondline.ToString());

      }

 

      // write the xml

      w.Write(m_XmlFullAddress.IsNull);

      if (!m_XmlFullAddress.IsNull) {

        string xml = m_XmlFullAddress.Value;

        w.Write(xml);

      }

    }

 

Here's the full C# code for the Address UDT. This is the code containing the corrections/additions necessary for correct serialization. Note that this code is provided AS-IS so don't sue me if it causes your toaster to reboot infinitely:

#region Using directives

 

using System;

using System.Data.SqlTypes; // for INullable

using System.Data.Sql;            // for IBinarySerialize, SqlUserDefinedTypeAttribute

using System.IO;

using System.Collections.Generic;

using System.Text;

using System.Xml;

 

#endregion

 

namespace Examples

{

 

       /// <summary>

       /// Address CLR User-defined type using UserDefined serialization

       /// </summary>

       [Serializable]

       [SqlUserDefinedType(Format.UserDefined, IsByteOrdered = true, MaxByteSize = 4096, IsFixedLength = false)]

       public class Address : INullable, IBinarySerialize

       {

              //******************************************************

              // Address Public Members

              //******************************************************

              public SqlXml m_XmlFullAddress;

 

              //******************************************************

              // Address Private Members

              //******************************************************

              private SqlString m_firstline;

              private SqlString m_secondline;

              private bool m_fNotNull; //false if null, default ctor makes it null

 

              //******************************************************

              // Constructors

              //******************************************************

 

              // Constructor for a null value: Address.Null

              public Address()

              {

                     m_fNotNull = false;

                     m_firstline = SqlString.Null;

                     m_secondline = SqlString.Null;

                     m_XmlFullAddress = SQLXML_NULL;

              }

 

              public Address(SqlString line1, SqlString line2)

              {

                     if (line1.IsNull && line1.IsNull) {

                           m_fNotNull = false;

                           m_firstline = SqlString.Null;

                           m_secondline = SqlString.Null;

                           m_XmlFullAddress = SQLXML_NULL;

                     }

                     else {

                           m_fNotNull = true;

                           m_firstline = line1;

                           m_secondline = line2;

                           m_XmlFullAddress = SQLXML_THIS_INSTANCE;

                     }

              }

 

              //******************************************************

              // INullable interface

              //******************************************************

 

              // INullable

              public bool IsNull

              {

                     get

                     {

                           return !m_fNotNull;

                     }

              }

 

              public static Address Null

              {

                     get

                     {

                           return new Address();

                     }

              }

 

              //******************************************************

              // Common static methods for SQL UDTs

              //******************************************************

              public override string ToString()

              {

                     if (IsNull) {

                           return "Null";

                     }

                     else {

                           return Value.ToString();

                     }

              }

 

              public static Address Parse(SqlString s)

              {

                     if (s.IsNull) {

                           return Address.Null;

                     }

 

                     String str = s.ToString();

                     String[] lines = new String[2];

                     // using || to indicate separation between address lines 1 and 2

                     // for now assume it won't appear in any address

                     lines = str.Split(new char[] { '|' });

 

                     if (lines.Length == 2) {

                           return new Address(lines[0], lines[1]);

                     }

                     else if (lines.Length == 1) {

                           return new Address(lines[0], SqlString.Null);

                     }

                     else {

                           return Address.Null;

                     }

              }

 

              //******************************************************

              // IBinarySerialize interface

              //******************************************************

 

              public void Read(BinaryReader r)

              {

                     m_fNotNull = r.ReadBoolean();

                     if (!r.ReadBoolean()) {

                           m_firstline = r.ReadString();

                     }

                     else {

                           m_firstline = SqlString.Null;

                     }

                     if (!r.ReadBoolean()) {

                           m_secondline = r.ReadString();

                     }

                     else {

                           m_secondline = SqlString.Null;

                     }

 

                     // get the XML value

                     if (!r.ReadBoolean()) {

                           string xml = r.ReadString();

                           m_XmlFullAddress = SqlXml_Parse(xml);

                     }

                     else {

                           m_XmlFullAddress = SQLXML_NULL;

                     }

              }

 

              public void Write(BinaryWriter w)

              {

                     w.Write(m_fNotNull);

                     w.Write(m_firstline.IsNull);

                     if (!m_firstline.IsNull) {

                           w.Write(m_firstline.ToString());

                     }

                     w.Write(m_secondline.IsNull);

                     if (!m_secondline.IsNull) {

                           w.Write(m_secondline.ToString());

                     }

 

                     // write the xml

                     w.Write(m_XmlFullAddress.IsNull);

                     if (!m_XmlFullAddress.IsNull) {

                           string xml = m_XmlFullAddress.Value;

                           w.Write(xml);

                     }

              }

 

              //******************************************************

              // Address Specific Fields, Properties and Methods

              //******************************************************

 

              public static SqlXml SqlXml_Parse(string s)

              {

                     // create a SqlXml instance from values[2]

                     MemoryStream ms = new MemoryStream();

                     StreamWriter sw = new StreamWriter(ms);

                     sw.Write(s);

                     sw.Flush();

                     return new SqlXml(ms);

              }

 

              public SqlXml SQLXML_THIS_INSTANCE

              {

                     get

                     {

                           return this.getAddressXml();

                     }

              }

 

              public SqlXml getAddressXml()

              {

                     StringBuilder sb = new StringBuilder(16384);

                     if (this.IsNull) {

                           sb.Append("<address/>");

                     }

                     else {

                           sb.Append("<address>");

                           sb.Append("<line id=\"1\">" + this.m_firstline + "</line>");

                           if (!this.m_secondline.IsNull) {

                                  sb.Append("<line id=\"2\">" + this.m_secondline + "</line>");

                           }

                           sb.Append("</address>");

                     }

                     return SqlXml_Parse(sb.ToString());

              }

 

              public SqlXml SQLXML_NULL

              {

                     get

                     {

                           return SqlXml_Parse("");

                     }

              }

 

              public SqlString Value

              {

                     get

                     {

                           if (m_fNotNull) {

                                  if (m_secondline.IsNull) {

                                         return m_firstline;

                                  }

                                  else {

                                         return new SqlString(m_firstline.Value + "|" + m_secondline.Value);

                                  }

                           }

                           else {

                                  return SqlString.Null;

                           }

                     }

              }

       }

}

 

Format.UserDefined is a powerful way to persist custom and/or complex data structures using Yukon UDTs. For effective use of this method of serialization however, we must be very careful with our serialization and deserialization code.

I stumbled upon what I think is a neat way to enumerate the directories that are currently in your path. Have you ever wanted to look at your current path in Windows and typed "SET PATH" only to see a mongo list of concatenated paths containing spaces in their names nailed together with semicolons that hypnotize you if you stare at it too long? Well, sure you could pipe the output of SET PATH to your favorite text parser (sed, awk, perl, csript anyone?) if you felt particularly masochistic, but here's another way using a default built-in command: "WHERE ."

So if your path looked something like this:

C:\>set path
Path=C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin;C:\Program Files\Microsoft Visual Studio.NET 2003\Common7\IDE\;C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin;C:\WINDOWS\system32;C:\WINDOWS;c:\depot;c:\dev\bin;
C:\dev\bin\cygwin\bin;c:\dev\bin\jdk1.4.1_01\bin;C:\Program Files\emacs-21.2\bin;C:\Program Files\Microsoft SQL Server\90\Tools\Binn\;C:\dev\bin\sfu30\common\;C:\PROGRA~1\CA\SHARED~1\SCANEN~1;C:\PROGRA~1\CA\ETRUST~1;C:\Program Files\Microsoft SQL Server\90\DTS\Binn\;C:\Program Files\Microsoft SQL Server\90\Tools\Binn\VSShell\Common7\IDE\;C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn;C:\CTS;c:\Program Files\WinZip

try this:

C:\>where .
.\.
C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin\.
C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\.
C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin\.
C:\WINDOWS\system32\.
C:\WINDOWS\.
c:\depot\.
c:\dev\bin\.
C:\dev\bin\cygwin\bin\.
c:\dev\bin\jdk1.4.1_01\bin\.
C:\Program Files\emacs-21.2\bin\.
C:\Program Files\Microsoft SQL Server\90\Tools\Binn\.
C:\dev\bin\sfu30\common\.
C:\PROGRA~1\CA\SHARED~1\SCANEN~1\.
C:\PROGRA~1\CA\ETRUST~1\.
C:\Program Files\Microsoft SQL Server\90\DTS\Binn\.
C:\Program Files\Microsoft SQL Server\90\Tools\Binn\VSShell\Common7\IDE\
C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn\.
C:\CTS\.
c:\Program Files\WinZip\.

WHERE.EXE ships with Windows XP Server 2003 and up [Thanks to Ilya for the correction. --Ed] as well as the Windows Resource kits since Win2K (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tools/tools/where.asp) and simply tells you where in the user path the input executable is found. Since "." is the current directory, it is found in every directory in the path, and hence the above output. How dat?

Yet
Another
Blogger
Begins
A

Dismal
Attempt
Braindumping
Bits
About

Dubiously
Obtuse
Observations

Hello World! This is Ravi Subramanian and I'm currently a software breaker in the SQL/CLR group in Microsoft. This blog is for tips, tricks and tidbits that I wish to share with you devoted readers/developers/[enter interested audience classifier here] regarding the fine and dandy new new things happening up here in Redmond, WA.

YABBA DABBA DOO!

 
Page view tracker