Click here to Skip to main content
15,891,687 members
Articles / Programming Languages / C#
Article

Creating Shell Links (Shortcuts) in .NET Programs Using WSH

Rate me:
Please Sign up or sign in to vote.
4.83/5 (33 votes)
3 Apr 20033 min read 362.9K   9.2K   89   48
It's easy to create shortcuts using the Windows Script Host Object Model

Sample Image - maximum width is 600 pixels

Introduction

It should be a simple matter to programatically create a shortcut on the desktop that links to a particular file. Unfortunately, as far as I know, the .NET Framework does not support this directly. However, by using the Windows Scripting Host Object Model, it takes only a few lines of code to create a shortcut anywhere within the file system. The purpose of this article is to show how to do this and to provide a class with methods that make it even simpler. The demonstration program uses this class to test for the existence of shortcuts and create or delete them with the click of a checkbox.

Shell Links

A standard feature of the Windows operating system is the ability to create a file that provides a link to any other file within the Windows file system. These so-called "Shortcuts" or "Shell Links" are used in many ways. Examples include the shortcuts that appear as icons on the Windows Desktop, the Start menu, the Quick Launch area within the Task Bar, and the "Send To" menu that appears when right-clicking a file in Explorer. These shortcuts are easy to create manually in the Windows GUI, but within a .NET program they typically require the programmer to use Interop.

Windows Scripting Host

The Windows Scripting Host (WSH) enables a number of file system and network operations to be performed from a script file. Fortunately, it is very simple to directly program the WSH in a .NET program by including a reference to the WSH runtime library (IWshRuntimeLibrary). To do this within the Visual Studio .NET IDE, do the following: after creating a new project, right-click on the project name within the Solution Explorer, select "Add Reference", select the "COM" tab, find and select the "Windows Script Host Object Model" in the listbox, click "Select", and then click "OK". Next, include a reference to the library, for example, within a C# file use the following:

C#
using IWshRuntimeLibrary;

Once this groundwork is done, it is nearly trivial to create a Shell Link. 

Creating a Shell Link

The following code is all that is necessary to create a Shell Link using the Windows Scripting Host.

C#
WshShell shell = new WshShell();
IWshShortcut link = (IWshShortcut)shell.CreateShortcut(LinkPathName);
link.TargetPath=TargetPathName;
link.Save();

The IWshShortcut object has a number of properties that allow the programmer to set things like the target path, the icon, the file name, the arguments, etc. You can explore these within Visual Studio using the Object Browser or Intellisense.

The Link Class

The demonstration program includes a class named Link that encapsulates static methods to test for the presence of a shortcut and to create or delete one if necessary. For example, to test for the presence of a desktop shortcut named "Shell Link", use the following code:

C#
DesktopFolder=Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
if(Link.Exists(DesktopFolder, "Shell Link"))DoSomething();

 

To create or delete a desktop shortcut named "Shell Link" that points to a file whose path is given by TargetPathName, use the following code:

C#
DesktopFolder=Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
Link.Update(DesktopFolder, TargetPathName, "Shell Link", Create);

This will create a link if Create is true and delete it if Create is false. If no change is needed, the code will do nothing.

Conclusion

That's all there is to it! It is simple to incorporate WSH in a .NET program. Creating Shell Links is one good reason to do so. You may find other uses for WSH in your programs such as mapping network drives, setting up printer connections, determining the free space on a disk, or any other tasks that can be automated with the Windows Scripting Host.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Researcher
United States United States
Ultrapico Website: http://www.ultrapico.com

Download Expresso 3.0, the latest version of the award-winning regular expression development tool.

Comments and Discussions

 
NewsMC++ Pin
Anonymous26-Aug-05 15:03
Anonymous26-Aug-05 15:03 
GeneralRe: MC++ Pin
dborgohain3-Feb-06 9:48
dborgohain3-Feb-06 9:48 
GeneralRe: MS VC++ .NET Pin
Alan Hsieh29-Mar-06 11:43
Alan Hsieh29-Mar-06 11:43 
GeneralOS Pin
thiaguera1-Feb-05 1:54
thiaguera1-Feb-05 1:54 
QuestionCan we use WSH to find highlighted icons on desktop? Pin
u999nik1-Oct-04 8:06
u999nik1-Oct-04 8:06 
QuestionCan this be done without a DLL being created? Pin
Jordan Bradford20-Sep-04 14:30
Jordan Bradford20-Sep-04 14:30 
AnswerRe: Can this be done without a DLL being created? Pin
Jim Hollenhorst20-Sep-04 17:29
Jim Hollenhorst20-Sep-04 17:29 
AnswerRe: Can this be done without a DLL being created? Pin
Anonymous20-Sep-04 19:30
Anonymous20-Sep-04 19:30 
Jordan,
I don't know if this will help or not but I recently came accross an example of how an assembly may be loaded from an embedded resource instead of from disk, I've never tried it nor did I write it.

C#
/*========================================================================

 

  File:    Embed.cs

 

  Summary: This file shows an example of how an assembly may be loaded from

  an embedded resource instead of from disk.

  

  Instructions for use:

  1.  Include the Embed.cs file in the project or assembly in which you wish to

  'embed' an assembly.

  2.  As early in the startup code as possible ( preferably in Main() ), make 

  a call to Dennany.Embed.Init().  This will register Embed.LoadComponentAssembly()

  as the callback method for the AssemblyResolve event.  

  3.  Add the 'dynamic' assembly to your project as an Embedded Resource.

  This may be done from the menu:

  Project -> Add Existing Item (Browse to appropriate .dll file here)

  Make certain that you set the properties of the .dll to "Embedded Resource"!

  (This may be done from the Solution Explorer by right-clicking on the .dll file,

  selecting properties, and setting the "Build Action" to "Embedded Resource.")

  

  Special Note:  Because of the way the CLR loads assemblies, you may not reference

  a type in one of these embedded assemblies before the call to Embed.Init().  

  You may also not reference a type in one of these embedded assemblies in the same method

  that the call to Embed.Init() is made.

  



  

Copyright (c) 2003, Jerry Dennany

========================================================================*/

 

using System;

using System.Reflection;

using System.Reflection.Emit;

using System.Resources;

using System.IO;

using System.Diagnostics;

 

namespace Dennany {

      public sealed class Embed {

            

            // this class is static, and should not be instantiated.

            private Embed() {}

 

 

            // Init is the only public callable method of this class.  It should be called once

            // (and only once!)

            // to register the LoadComponentAssembly method as the event handler to call should the 

            // AssemblyResolve event get fired.

            public static void Init(){

                  ResolveEventHandler loadAssembly = new ResolveEventHandler(LoadComponentAssembly);

                  AppDomain.CurrentDomain.AssemblyResolve += loadAssembly;

            }

 

            // This method is called when an assembly fails to load via probing path, GAC, etc.

            // Note that we return null to the CLR if we have no assembly to load.  This is 

            // expected behavior, so that the CLR may notify the rest of the application that

            // it could not load the assembly in question.

            static Assembly LoadComponentAssembly(Object sender, ResolveEventArgs args) { 

            

                  // We'll use this reference fairly often in the future...

                  Assembly assembly = Assembly.GetExecutingAssembly();

                  

                  // Get the requested assembly's simple name (no namespace info or file extension)

                  string simpleName = args.Name.Substring(0, args.Name.IndexOf(',') );

                   

                  string dllImageResourceName = getResourceLibName( simpleName, assembly );

                  

                  return streamFromResource(dllImageResourceName, assembly);

                        

            }

 

            private static string getResourceLibName(string simpleLibName){

                  return getResourceLibName( simpleLibName, Assembly.GetExecutingAssembly() );

            }

 

            // We will go through the list of resources in the assembly and using the 

            // simpleLibName, we will find if the dll resource is embedded in the assembly

            // Note that we return null on purpose if we didn't find anything.

            // This is because we also want to return null to the CLR if we have no assembly to load.

            private static string getResourceLibName(string simpleLibName, Assembly assembly) {

                  if ( simpleLibName == null || assembly == null ) return null;

 

                  simpleLibName += ".dll"; // assume that the file ends in this extension.

                  string dllImageResourceName = null;

                  

                  // We will iterate through the list of resources in this assembly,

                  // looking for the name of the assembly that failed to load from disk

                  foreach (string resourceName in assembly.GetManifestResourceNames()) {

                        if (resourceName.Length < simpleLibName.Length) continue;

                              

                        // if the simpleName and resourceName end the same (we drop namespace info here),

                        // then this should be the embedded assembly that we are looking for.

                        if (String.Compare(simpleLibName,

                              0,

                              resourceName,

                              (resourceName.Length - simpleLibName.Length),

                              simpleLibName.Length,

                              true) == 0 ) {

                              

                              dllImageResourceName = resourceName;

                        }

                  }           

                  return dllImageResourceName;  

            }

 

            private static Assembly streamFromResource(string dllImageResourceName){

                  return streamFromResource(dllImageResourceName, Assembly.GetExecutingAssembly() );

            }

 

            // this is the 'workhorse' of the class.  Once we've got a resource name in the assembly,

            // we stream the resource to a byte array, and load the Assembly from the byte array.

            

            private static Assembly streamFromResource(string dllImageResourceName, Assembly assembly){

                  if ( dllImageResourceName == null || assembly == null ) return null;

                  

                  Stream imageStream;

                  imageStream = assembly.GetManifestResourceStream(dllImageResourceName);

                  long bytestreamMaxLength = imageStream.Length;

            

                  byte[] buffer = new byte[bytestreamMaxLength];

                  imageStream.Read(buffer,0,(int)bytestreamMaxLength);

                  

                  return  AssemblyBuilder.Load(buffer);

            }

      }

}
// EOF

GeneralNice, but here is a better way... Pin
Anonymous4-Apr-03 2:31
Anonymous4-Apr-03 2:31 
GeneralRe: Nice, but here is a better way... Pin
Jim Hollenhorst4-Apr-03 4:19
Jim Hollenhorst4-Apr-03 4:19 
GeneralRe: Nice, but here is a better way... Pin
Dynamite Jones6-Apr-03 7:16
Dynamite Jones6-Apr-03 7:16 
GeneralRe: Nice, but here is a better way... Pin
mlavenne22-Oct-04 9:29
mlavenne22-Oct-04 9:29 
GeneralRe: Nice, but here is a better way... Pin
strabu14-Jul-05 3:27
strabu14-Jul-05 3:27 
GeneralRe: Nice, but here is a better way... Pin
papadi1-Apr-07 20:55
papadi1-Apr-07 20:55 
GeneralRe: Nice, but here is a better way... [modified] Pin
RichardCollins222-Oct-07 1:34
RichardCollins222-Oct-07 1:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.