Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 376
Marc Wittmann Avatar asked Aug 04 '15 12:08

Marc Wittmann


2 Answers

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
    }
}
like image 165
Dirk Vollmar Avatar answered Nov 14 '22 21:11

Dirk Vollmar


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++;
        }
    }
like image 34
LSU.Net Avatar answered Nov 14 '22 21:11

LSU.Net