How do I go about instantiating a COM Object in C# by CLSID?
Asked Answered
I

4

6

Forgive me if my terminology is off, as this is somewhat uncharted territory for me.

I have a program which needs to create a FolderShortcut. Microsoft has documentation on how to create it in C++, and I'm trying to translate the directions to C#. The instructions state that the CoCreateInstance function needs to be called with CLSID_FolderShortcut as a parameter, which I infer to mean that it's instantiating a COM object. The CLSID for this object is {0AFACED1-E828-11D1-9187-B532F1E9575D}.

I've tried adding a reference to Shell32.dll from the COM tab, but the FolderShortcut object does not show up in Intellisense (maybe it's not in the typelib?). I also thought about doing a DLLImport, but, of course, that only gives me access to functions, not objects.

What do I need to do to get access to this object in .Net?

Innsbruck answered 14/3, 2013 at 19:3 Comment(3)
possible duplicate of Create Shortcut in .Net for 64-Bit Machines - Compiled as 64-bit Application OnlyEta
The question "Create Shortcut in .Net for 64-Bit Machines - Compiled as 64-bit Application Only" does not deal with the FolderShortcut class. The FolderShortcut class is not visible to the compiler in the same way as the ShellLinkObject class.Innsbruck
You should change the question title to something like 'How to create a folder shortcut' IMHO.Parity
P
7

Here is a piece of code that allows you to create a folder shortcut. The CoCreateInstance can (in general) be replaced by declaring a simple class decorated with the Guid attribute with the required CLSID, and the ComImport attribute. The new call will do the COM magic automatically. With this code, you don't even need a Shell32 reference (or you can reuse the IShellLink declaration from there if you prefer).

Usage:

static void Main(string[] args)
{
    CreateFolderShortcut(@"c:\temp", Path.GetFullPath("Shortcut to Temp"));
}

Code:

public static void CreateFolderShortcut(string path, string shortcutPath)
{
    CreateFolderShortcut(path, shortcutPath, null);
}

public static void CreateFolderShortcut(string path, string shortcutPath, string comment)
{
    if (path == null)
        throw new ArgumentNullException("path");

    IShellLink link = (IShellLink)new ShellLinkFolder();

    if (comment != null)
    {
        link.SetDescription(comment);
    }
    link.SetPath(path);

    IPersistFile file = (IPersistFile)link;
    file.Save(shortcutPath, false);
}

[ComImport]
[Guid("00021401-0000-0000-C000-000000000046")]
private class ShellLink
{
}

[ComImport]
[Guid("0AFACED1-E828-11D1-9187-B532F1E9575D")]
private class ShellLinkFolder
{
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214F9-0000-0000-C000-000000000046")]
private interface IShellLink
{
    void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out IntPtr pfd, int fFlags);
    void GetIDList(out IntPtr ppidl);
    void SetIDList(IntPtr pidl);
    void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
    void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
    void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
    void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
    void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
    void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
    void GetHotkey(out short pwHotkey);
    void SetHotkey(short wHotkey);
    void GetShowCmd(out int piShowCmd);
    void SetShowCmd(int iShowCmd);
    void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon);
    void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
    void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
    void Resolve(IntPtr hwnd, int fFlags);
    void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
Parity answered 15/3, 2013 at 9:6 Comment(0)
P
13

If you do not want to import the classes during compile time, as Simon Mourier describes it is also possible to do some late binding to the COM objects, using Activator.

If you've got the ProgID of your object, get the type using

Type comType = Type.GetTypeFromProgID("MyProg.ProgId");

otherwise you can get the type by it's CLSID:

Type comType = 
    Type.GetTypeFromCLSID(new Guid("0AFACED1-E828-11D1-9187-B532F1E9575D"));

Using this type, you are now able to create an instance of the coclass, using Activator.CreateInstance:

var instance = Activator.CreateInstance(comType);

Basicly you can now invoke methods using Type.InvokeMember. This only works if the object implements the IDispatch interface.

However for your specific example you should be able to cast the instance to System.Runtime.InteropServices.ComTypes.IPersistFile, which results in an call to QueryInterface for COM objects. Using this interface you can easily access the members of IPersistFile.

You may continue here with further reading.

Paraprofessional answered 15/3, 2013 at 9:40 Comment(4)
objects used for COM interop don't have to implement IDispatch (aka 'Automation'). IUnknown is enough.Parity
COM objects need to expose IDispatch to enable script automation. If you do not have an interop type (like IPersistFile) that proxies the interface and you want to late bind COM types (using Activator), objects need to implement IDispatch to get called using (i.e.) Type.InvokeMember.Paraprofessional
Allright but that's not the subject, your statement "the object should implement the IDispatch interface" is still wrong in the context. Nobody's talking about automation here.Parity
I've updated the answer... hope this makes things a little bit clearer :)Paraprofessional
P
7

Here is a piece of code that allows you to create a folder shortcut. The CoCreateInstance can (in general) be replaced by declaring a simple class decorated with the Guid attribute with the required CLSID, and the ComImport attribute. The new call will do the COM magic automatically. With this code, you don't even need a Shell32 reference (or you can reuse the IShellLink declaration from there if you prefer).

Usage:

static void Main(string[] args)
{
    CreateFolderShortcut(@"c:\temp", Path.GetFullPath("Shortcut to Temp"));
}

Code:

public static void CreateFolderShortcut(string path, string shortcutPath)
{
    CreateFolderShortcut(path, shortcutPath, null);
}

public static void CreateFolderShortcut(string path, string shortcutPath, string comment)
{
    if (path == null)
        throw new ArgumentNullException("path");

    IShellLink link = (IShellLink)new ShellLinkFolder();

    if (comment != null)
    {
        link.SetDescription(comment);
    }
    link.SetPath(path);

    IPersistFile file = (IPersistFile)link;
    file.Save(shortcutPath, false);
}

[ComImport]
[Guid("00021401-0000-0000-C000-000000000046")]
private class ShellLink
{
}

[ComImport]
[Guid("0AFACED1-E828-11D1-9187-B532F1E9575D")]
private class ShellLinkFolder
{
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214F9-0000-0000-C000-000000000046")]
private interface IShellLink
{
    void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out IntPtr pfd, int fFlags);
    void GetIDList(out IntPtr ppidl);
    void SetIDList(IntPtr pidl);
    void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
    void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
    void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
    void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
    void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
    void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
    void GetHotkey(out short pwHotkey);
    void SetHotkey(short wHotkey);
    void GetShowCmd(out int piShowCmd);
    void SetShowCmd(int iShowCmd);
    void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon);
    void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
    void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
    void Resolve(IntPtr hwnd, int fFlags);
    void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
Parity answered 15/3, 2013 at 9:6 Comment(0)
K
0

Did you try to add a new reference:

  1. Open Solution Explorer
  2. Expand the C# project
  3. Right click on the references node and add a new COM reference
Kuwait answered 14/3, 2013 at 19:6 Comment(0)
H
0

It sounds like that you have tried adding your COM using the "Add a new COM reference". These are the things that I'd try:

  1. What I'd do first is to make sure that your COM DLL is registered in your computer. If not then register it then try to add it again using the COM tab.

  2. Are you running on a 64bit machine? Try to also make sure that your project properties is set to AnyCPU for it to be able to read the 32bit COM.

  3. Make sure that you have the interop equivalent of the DLL that you are trying to access. It is usually named like "Interop.YourDLLName.dll". Add a reference to that DLL and that should work.

Haase answered 14/3, 2013 at 19:11 Comment(1)
I verified that the DLL is registered. I am running on a 64-bit machine; the project is set to target AnyCPU. The Interop.Shell32.dll file is created, and the reference exists. I can see some of the classes and functions defined in Shell32.dll, but not what I'm looking for.Innsbruck

© 2022 - 2024 — McMap. All rights reserved.