views:

513

answers:

4

Hello,

I have this piece of code that works very well and gives me the path the user's start menu:

    Dim oShell As Object = CreateObject("Shell.Application")
    MsgBox(oShell.NameSpace(11).Self.Path)

This obviously uses late binding. Now say I want to do this in C#, or in VB.NET strict mode, neither of which support this kind of syntax with late binding.

Is this possible? How?

Thanks for you help!

A: 

If I remember correctly, all you have to do is to cast the object reference into the appropriate interface. If you use a COM object in .NET, you typically import the type library and then have the interfaces readily available.

OregonGhost
But I have no idea what that interface would be, actually I'm not even sure it would necessarily always be the same interface that is returned...
Laurent
If you're not sure about the interface then early binding is not for you. That's the point of early binding - the complier has to know what interface it is and can check parameters validity and generate calls. Otherwise you have to use late binding.
sharptooth
I understand that, but suppose I'm coding in C#, I can't use late binding. Is the only way of doing it in C# to find out the name of the interface?
Laurent
You should restate this question or ask another one to explicitly ask how to do this in C# so that C# experts see and answer your question.
sharptooth
This question seems related http://stackoverflow.com/questions/484214/c-early-and-late-binding
sharptooth
You can use late binding in C# using reflection (it is a little painful however)
0xA3
You can find out which interface you need by reading the documentation (no, really, all public shell interfaces are documented in Windows SDK / MSDN). Of course always the same interface is returned, because the interface implemented by the object you want to instantiate won't change. And if you just imported the type library, which is IMO the most elegant solution, you have them anyway.
OregonGhost
+2  A: 
Dim DirPath As String = _
    System.Environment.GetFolderPath(Environment.SpecialFolder.StartMenu)

See here for more.

RobS
+2  A: 

Well actually you could use reflection:

Type shellType = Type.GetTypeFromProgID("Shell.Application", true);
object shell = Activator.CreateInstance(shellType);
object folder = shellType.InvokeMember("NameSpace", BindingFlags.InvokeMethod, null, shell, new object[] { 11 });
object self = folder.GetType().InvokeMember("Self", BindingFlags.GetProperty, null, folder, new object[] { });
object path = self.GetType().InvokeMember("Path", BindingFlags.GetProperty, null, self, new object[] { });
Console.WriteLine(path);

Not the kind of code I like, but in C# 4.0 you could use the dynamic type to clean up this mess.

Darin Dimitrov
As I understood the question the OP wants to use *early* binding.
0xA3
+2  A: 

If you want to solve this the COM way you have to figure out, which COM reference to add in your VB project.

Open regedit and navigate to HKEY_CLASSES_ROOT\<class id>\CLSID, i.e.

HKEY_CLASSES_ROOT\Shell.Application\CLSID

and you will find the class id which uniquely identifies the COM component.

Under HKEY_CLASSES_ROOT\CLSID you can now look up which file is behind the COM component:

HKEY_CLASSES_ROOT\CLSID\{13709620-C279-11CE-A49E-444553540000}\InProcServer32

shows the following value:

%SystemRoot%\system32\SHELL32.dll

Now go to Visual Studio, and add a reference to this file (on the Browse tab of the Add References dialog). If you open up the projects properties, you will actually see that the nice name of the COM component added is Microsoft Shell Controls and Automation.

Once the reference is added you can use the Shell.Application object as follows:

Option Strict On

Module PrintStartMenuLocation

    Sub Main()
        Dim shell As New Shell32.Shell
        Dim folder As Shell32.Folder
        Dim folderItem As Shell32.FolderItem
        Dim startMenuPath As String

        folder = shell.NameSpace(Shell32.ShellSpecialFolderConstants.ssfSTARTMENU)
        folderItem = CType(folder.Items(0), Shell32.FolderItem)
        startMenuPath = folderItem.Path

        Console.WriteLine(startMenuPath)
    End Sub

End Module

A version in C# would look as follows:

class Program
{
    static void Main(string[] args)
    {
        Shell32.Shell shell = new Shell32.Shell();
        Shell32.Folder folder = shell.NameSpace(Shell32.ShellSpecialFolderConstants.ssfSTARTMENU);
        Shell32.FolderItem folderItem = folder.Items().Item(0) as Shell32.FolderItem;
        string startMenuPath = folderItem.Path;

        Console.WriteLine(startMenuPath);
    }
}

However, if you simply need to retrieve the location of the Start Menu folder you can do the same directly in .NET using

Dim path As String = System.Environment.GetFolderPath(Environment.SpecialFolder.StartMenu)
0xA3