Get List of available Verbs (file association) to use with ProcessStartInfo in c#

10,102

Solution 1

From MSDN: https://msdn.microsoft.com/en-us/library/65kczb9y(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-2

 startInfo = new ProcessStartInfo(fileName);

    if (File.Exists(fileName))
    {
        i = 0;
        foreach (String verb in startInfo.Verbs)
        {
            // Display the possible verbs.
            Console.WriteLine("  {0}. {1}", i.ToString(), verb);
            i++;
        }
    }

Solution 2

The ProcessStartInfo.Verbs property is somewhat broken as it does not consider the way how newer versions of Windows (Windows 8 and above afaik) retrieve the registered application. The property only checks the verbs that are registered for the ProgId defined under HKCR\.ext (as can be seen in the reference source) and does not consider other places such as below the Registry key HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ext or some other places, e.g. defined via Policy.

Getting the registered verbs

The best way is to not rely on checking the Registry directly (as done by the ProcessStartInfo class), but to use the appropriate Windows API function AssocQueryString to retrieve the associated ProgId:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32;

class Program
{
    private static void Main(string[] args)
    {
        string fileName = @"E:\Pictures\Sample.jpg";
        string progId = AssocQueryString(AssocStr.ASSOCSTR_PROGID, fileName);

        var verbs = GetVerbsByProgId(progId);

        if (!verbs.Contains("printto"))
        {
            throw new Exception("PrintTo is not supported!");
        }

    }

    private static string[] GetVerbsByProgId(string progId)
    {
        var verbs = new List<string>();

        if (!string.IsNullOrEmpty(progId))
        {
            using (var key = Registry.ClassesRoot.OpenSubKey(progId + "\\shell"))
            {
                if (key != null)
                {
                    var names = key.GetSubKeyNames();
                    verbs.AddRange(
                        names.Where(
                            name => 
                                string.Compare(
                                    name, 
                                    "new", 
                                    StringComparison.OrdinalIgnoreCase) 
                                != 0));
                }
            }
        }

        return verbs.ToArray();
    }

    private static string AssocQueryString(AssocStr association, string extension)
    {
        uint length = 0;
        uint ret = AssocQueryString(
            AssocF.ASSOCF_NONE, association, extension, "printto", null, ref length);
        if (ret != 1) //expected S_FALSE
        {
            throw new Win32Exception();
        }

        var sb = new StringBuilder((int)length);
        ret = AssocQueryString(
            AssocF.ASSOCF_NONE, association, extension, null, sb, ref length);
        if (ret != 0) //expected S_OK
        {
            throw new Win32Exception();
        }

        return sb.ToString();
    }

    [DllImport("Shlwapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern uint AssocQueryString(
        AssocF flags,
        AssocStr str,
        string pszAssoc,
        string pszExtra,
        [Out] StringBuilder pszOut,
        ref uint pcchOut);

    [Flags]
    private enum AssocF : uint
    {
        ASSOCF_NONE = 0x00000000,
        ASSOCF_INIT_NOREMAPCLSID = 0x00000001,
        ASSOCF_INIT_BYEXENAME = 0x00000002,
        ASSOCF_OPEN_BYEXENAME = 0x00000002,
        ASSOCF_INIT_DEFAULTTOSTAR = 0x00000004,
        ASSOCF_INIT_DEFAULTTOFOLDER = 0x00000008,
        ASSOCF_NOUSERSETTINGS = 0x00000010,
        ASSOCF_NOTRUNCATE = 0x00000020,
        ASSOCF_VERIFY = 0x00000040,
        ASSOCF_REMAPRUNDLL = 0x00000080,
        ASSOCF_NOFIXUPS = 0x00000100,
        ASSOCF_IGNOREBASECLASS = 0x00000200,
        ASSOCF_INIT_IGNOREUNKNOWN = 0x00000400,
        ASSOCF_INIT_FIXED_PROGID = 0x00000800,
        ASSOCF_IS_PROTOCOL = 0x00001000,
        ASSOCF_INIT_FOR_FILE = 0x00002000
    }

    private enum AssocStr
    {
        ASSOCSTR_COMMAND = 1,
        ASSOCSTR_EXECUTABLE,
        ASSOCSTR_FRIENDLYDOCNAME,
        ASSOCSTR_FRIENDLYAPPNAME,
        ASSOCSTR_NOOPEN,
        ASSOCSTR_SHELLNEWVALUE,
        ASSOCSTR_DDECOMMAND,
        ASSOCSTR_DDEIFEXEC,
        ASSOCSTR_DDEAPPLICATION,
        ASSOCSTR_DDETOPIC,
        ASSOCSTR_INFOTIP,
        ASSOCSTR_QUICKTIP,
        ASSOCSTR_TILEINFO,
        ASSOCSTR_CONTENTTYPE,
        ASSOCSTR_DEFAULTICON,
        ASSOCSTR_SHELLEXTENSION,
        ASSOCSTR_DROPTARGET,
        ASSOCSTR_DELEGATEEXECUTE,
        ASSOCSTR_SUPPORTED_URI_PROTOCOLS,
        ASSOCSTR_PROGID,
        ASSOCSTR_APPID,
        ASSOCSTR_APPPUBLISHER,
        ASSOCSTR_APPICONREFERENCE,
        ASSOCSTR_MAX
    }
}
Share:
10,102

Related videos on Youtube

Marc Wittmann
Author by

Marc Wittmann

SOreadytohelp Fan of community driven knowledge exchange, loves travelling

Updated on September 15, 2022

Comments

  • Marc Wittmann
    Marc Wittmann over 1 year

    I am trying to open and print files with the ProcessStartInfo class. (File can be anything but let`s assume it is a PDF File)

      ProcessStartInfo pi = new ProcessStartInfo(file);
      pi.Arguments = Path.GetFileName(file);
      pi.WorkingDirectory = Path.GetDirectoryName(file);
      pi.Verb = "OPEN";  
      Process.Start(pi);
    

    this works well for the pi.Verb = "OPEN";. Some applications register themselves also with the verb "PRINT" but only some do. In my case (Windows PDF Viewer) I get an exception when trying to execute with the pi.Verb = "PRINT";

    Is there a way to see all the verbs available for a specific type in C# at runtime?

    Thx a lot

  • Marc Wittmann
    Marc Wittmann almost 9 years
    Oh my, really didn`t think they would make it that easy to use. Thx a lot