如何确定文件是否可执行?

时间:2010-09-12 05:38:41

标签: c#

我正在编写一个程序((部分工作)执行其他程序。我希望能够使用Process.Start运行尽可能多的类型的程序(用不同的语言编写)。所以,我想我应该:

  1. 打开文件
  2. 阅读第一行
  3. 检查它是否以#!
  4. 开头
  5. 如果是,请使用#!之后的程序执行,并将文件名作为参数传递
  6. 如果未找到#!,请根据已知程序的字典(例如.py -> python)检查文件扩展名,然后执行该程序
  7. 否则,只需尝试执行该文件并捕获任何错误
  8. 但是,我认为实际检查文件是否可执行是否更容易/更有效,如果是,请跳转到6.有没有办法做到这一点?

6 个答案:

答案 0 :(得分:6)

执行此操作的唯一方法是对Win32 API使用P / Invoke调用。您需要使用SHGetFileInfo方法,然后解压缩返回值:

[Flags]
internal enum SHGFI : uint
{
    ADDOVERLAYS = 0x20,
    ATTR_SPECIFIED = 0x20000,
    ATTRIBUTES = 0x800,
    DISPLAYNAME = 0x200,
    EXETYPE = 0x2000,
    ICON = 0x100,
    ICONLOCATION = 0x1000,
    LARGEICON = 0,
    LINKOVERLAY = 0x8000,
    OPENICON = 2,
    OVERLAYINDEX = 0x40,
    PIDL = 8,
    SELECTED = 0x10000,
    SHELLICONSIZE = 4,
    SMALLICON = 1,
    SYSICONINDEX = 0x4000,
    TYPENAME = 0x400,
    USEFILEATTRIBUTES = 0x10
}

/// <summary>
/// This structure contains information about a file object.
/// </summary>
/// <remarks>
/// This structure is used with the SHGetFileInfo function.
/// </remarks>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct SHFILEINFO
{
    /// <summary>
    /// Handle to the icon that represents the file. 
    /// </summary>
    internal IntPtr hIcon;

    /// <summary>
    /// Index of the icon image within the system image list.
    /// </summary>
    internal int iIcon;

    /// <summary>
    /// Specifies the attributes of the file object.
    /// </summary>
    internal SFGAO dwAttributes;

    /// <summary>
    /// Null-terminated string that contains the name of the file as it 
    /// appears in the Windows shell, or the path and name of the file that
    /// contains the icon representing the file.
    /// </summary>
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.MAX_PATH)]
    internal string szDisplayName;

    /// <summary>
    /// Null-terminated string that describes the type of file. 
    /// </summary>
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
    internal string szTypeName;        
}

/// <summary>
/// Specifies the executable file type.
/// </summary>
public enum ExecutableType : int
{
    /// <summary>
    /// The file executable type is not able to be determined.
    /// </summary>
    Unknown = 0,

    /// <summary>
    /// The file is an MS-DOS .exe, .com, or .bat file.
    /// </summary>
    DOS,

    /// <summary>
    /// The file is a Microsoft Win32®-based console application.
    /// </summary>
    Win32Console,

    /// <summary>
    /// The file is a Windows application.
    /// </summary>
    Windows,        
}

// Retrieves information about an object in the file system,
// such as a file, a folder, a directory, or a drive root.
[DllImport("shell32",
    EntryPoint = "SHGetFileInfo",
    ExactSpelling = false,
    CharSet = CharSet.Auto,
    SetLastError = true)]
internal static extern IntPtr SHGetFileInfo(
    string pszPath,
    FileAttributes dwFileAttributes,
    ref SHFILEINFO sfi,
    int cbFileInfo,
    SHGFI uFlags);

[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
private ExecutableType IsExecutable(string fileName)
{
    if (fileName == null)
    {
        throw new ArgumentNullException("fileName");
    }

    ExecutableType executableType = ExecutableType.Unknown;

    if (File.Exists(fileName)
    {
        // Try to fill the same SHFILEINFO struct for the exe type. The returned pointer contains the encoded 
        // executable type data.
        ptr = IntPtr.Zero;
        ptr = SHGetFileInfo(fileName, FileAttributes.Normal, ref this.shellFileInfo, Marshal.SizeOf(typeof(SHFILEINFO)), SHGFI.EXETYPE);

        // We need to split the returned pointer up into the high and low order words. These are important
        // because they help distinguish some of the types. The possible values are:
        //
        // Value                                            Meaning
        // ----------------------------------------------------------------------------------------------
        // 0                                                Nonexecutable file or an error condition. 
        // LOWORD = NE or PE and HIWORD = Windows version   Microsoft Windows application.
        // LOWORD = MZ and HIWORD = 0                       Windows 95, Windows 98: Microsoft MS-DOS .exe, .com, or .bat file
        //                                                  Microsoft Windows NT, Windows 2000, Windows XP: MS-DOS .exe or .com file 
        // LOWORD = PE and HIWORD = 0                       Windows 95, Windows 98: Microsoft Win32 console application 
        //                                                  Windows NT, Windows 2000, Windows XP: Win32 console application or .bat file 
        // MZ = 0x5A4D - DOS signature.
        // NE = 0x454E - OS/2 signature.
        // LE = 0x454C - OS/2 LE or VXD signature.
        // PE = 0x4550 - Win32/NT signature.

        int wparam = ptr.ToInt32();
        int loWord = wparam & 0xffff;
        int hiWord = wparam >> 16;

        if (wparam == 0)
        {
            executableType = ExecutableType.Unknown;
        }
        else
        {
            if (hiWord == 0x0000)
            {
                if (loWord == 0x5A4D)
                {
                    // The file is an MS-DOS .exe, .com, or .bat
                    executableType = ExecutableType.DOS;
                }
                else if (loWord == 0x4550)
                {
                    executableType = ExecutableType.Win32Console;
                }
            }
            else
            {
                if (loWord == 0x454E || loWord == 0x4550)
                {
                    executableType = ExecutableType.Windows;
                }
                else if (loWord == 0x454C)
                {
                    executableType = ExecutableType.Windows;
                }
            }
        }
    }

    return executableType;
} 

(这应该可行,但是从较大的库中提取,因此可能会出现一些小问题。但是,它应该足够完整,可以让你在那里大部分时间。)

答案 1 :(得分:2)

在Windows中,没有真正的“可执行”概念,就像* NIX系统中存在的特定权限一样。

您有两种选择。第一个,就像saurabh在我之前建议的那样,是依靠系统来关联文件扩展名和要执行的命令。这种方法(使用Process.Start)有许多优点 - 它为用户留下了关联的能力,就像让用户选择正确的方式来运行各种文件类型一样。

第二个选项是模仿Windows文件关联过程,通过从扩展名到可以运行文件的命令的字典,并在需要时回退到检查文件的第一行。这样做的好处是你可以设置关联,但它也需要不断修改和维护,除了失去用户方面的灵活性 - 这可能是一件好事或坏事。 p>

答案 2 :(得分:1)

如果你使用.net而不是Process.Start为你做很多事情。

如果你传递一个exe,它将运行exe。

如果您传递word文档,它将打开word文档

可能更多

答案 3 :(得分:1)

您应该能够从第5步开始。即首先检查文件扩展名。 Windows适用于文件扩展名。如果没有它们,你就无能为力。

如果您将扩展程序识别为可执行文件,则可以将其传递给Process.Start或打开该文件,并找出应将其传递给哪个可执行文件。我也会寻找与ShellExecute等效的.net,因为我并不是100%确信它是Process.Start。 (虽然过去5年我没有做过多.net / c#编码,所以我在这里错了。)

答案 4 :(得分:1)

由于您提到Linux,您可以考虑使用'file'命令。我相信gnuwin32有一个Windows命令的端口。当然,这意味着解析'file'返回的输出(文件MIME类型,如“application / x-executable”)。因此,根据您希望能够识别的可执行文件的数量,这可能不是最简单的解决方案。

[编辑:添加示例输出]

  

file.exe d:\ Downloads \ tabview.py   
  d:\下载\ tabview.py; / usr / local / bin / python脚本文本可执行文件   

  file.exe d:\ Downloads \ tabview.txt   
  d:\下载\ tabview.txt; / usr / local / bin / python脚本文本可执行文件&gt;   

  file.exe d:\ Downloads \ 7zbv14ww.exe   
  d:\下载\ 7zbv14ww.exe;用于MS Windows(GUI)的PE32可执行程序Intel 80386 32-   它   

  file.exe -b d:\ Downloads \ AlbumArtSmall.jpg   
  JPEG图像数据,JFIF标准1.01

答案 5 :(得分:1)

使用Scott的答案中的一些信息,我编写了自己的方法,该方法返回指定文件类型的Enum值。我注意到他的解决方案是,即使文件不存在,它也会返回'Unknown'。此外,我简化了几个if条件,并在没有满足任何条件的情况下添加了附加条件。

ShellFileGetInfo类

public static class ShellFileGetInfo
{
    [DllImport("shell32.dll")]
    public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);

    [StructLayout(LayoutKind.Sequential)]
    public struct SHFILEINFO
    {
        public IntPtr hIcon;
        public IntPtr iIcon;
        public uint dwAttributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szDisplayName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string szTypeName;
    };

    // Enum for return of the file type
    public enum ShellFileType
    {
        FileNotFound,
        Unknown,
        Dos,
        Windows,
        Console
    }

    // Apply the appropriate overlays to the file's icon. The SHGFI_ICON flag must also be set.
    public const uint SHGFI_ADDOVERLAYS = 0x000000020;

    // Modify SHGFI_ATTRIBUTES to indicate that the dwAttributes member of the SHFILEINFO structure at psfi contains the specific attributes that are desired. These attributes are passed to IShellFolder::GetAttributesOf. If this flag is not specified, 0xFFFFFFFF is passed to IShellFolder::GetAttributesOf, requesting all attributes. This flag cannot be specified with the SHGFI_ICON flag.
    public const uint SHGFI_ATTR_SPECIFIED = 0x000020000;

    // Retrieve the item attributes. The attributes are copied to the dwAttributes member of the structure specified in the psfi parameter. These are the same attributes that are obtained from IShellFolder::GetAttributesOf.
    public const uint SHGFI_ATTRIBUTES = 0x000000800;

    // Retrieve the display name for the file, which is the name as it appears in Windows Explorer. The name is copied to the szDisplayName member of the structure specified in psfi. The returned display name uses the long file name, if there is one, rather than the 8.3 form of the file name. Note that the display name can be affected by settings such as whether extensions are shown.
    public const uint SHGFI_DISPLAYNAME = 0x000000200;

    // Retrieve the type of the executable file if pszPath identifies an executable file. The information is packed into the return value. This flag cannot be specified with any other flags.
    public const uint SHGFI_EXETYPE = 0x000002000;

    // Retrieve the handle to the icon that represents the file and the index of the icon within the system image list. The handle is copied to the hIcon member of the structure specified by psfi, and the index is copied to the iIcon member.
    public const uint SHGFI_ICON = 0x000000100;

    // Retrieve the name of the file that contains the icon representing the file specified by pszPath, as returned by the IExtractIcon::GetIconLocation method of the file's icon handler. Also retrieve the icon index within that file. The name of the file containing the icon is copied to the szDisplayName member of the structure specified by psfi. The icon's index is copied to that structure's iIcon member.
    public const uint SHGFI_ICONLOCATION = 0x000001000;

    // Modify SHGFI_ICON, causing the function to retrieve the file's large icon. The SHGFI_ICON flag must also be set.
    public const uint SHGFI_LARGEICON = 0x000000000;

    // Modify SHGFI_ICON, causing the function to add the link overlay to the file's icon. The SHGFI_ICON flag must also be set.
    public const uint SHGFI_LINKOVERLAY = 0x000008000;

    // Modify SHGFI_ICON, causing the function to retrieve the file's open icon. Also used to modify SHGFI_SYSICONINDEX, causing the function to return the handle to the system image list that contains the file's small open icon. A container object displays an open icon to indicate that the container is open. The SHGFI_ICON and/or SHGFI_SYSICONINDEX flag must also be set.
    public const uint SHGFI_OPENICON = 0x000000002;

    // Version 5.0. Return the index of the overlay icon. The value of the overlay index is returned in the upper eight bits of the iIcon member of the structure specified by psfi. This flag requires that the SHGFI_ICON be set as well.
    public const uint SHGFI_OVERLAYINDEX = 0x000000040;

    // Indicate that pszPath is the address of an ITEMIDLIST structure rather than a path name.
    public const uint SHGFI_PIDL = 0x000000008;

    // Modify SHGFI_ICON, causing the function to blend the file's icon with the system highlight color. The SHGFI_ICON flag must also be set.
    public const uint SHGFI_SELECTED = 0x000010000;

    // Modify SHGFI_ICON, causing the function to retrieve a Shell-sized icon. If this flag is not specified the function sizes the icon according to the system metric values. The SHGFI_ICON flag must also be set.
    public const uint SHGFI_SHELLICONSIZE = 0x000000004;

    // Modify SHGFI_ICON, causing the function to retrieve the file's small icon. Also used to modify SHGFI_SYSICONINDEX, causing the function to return the handle to the system image list that contains small icon images. The SHGFI_ICON and/or SHGFI_SYSICONINDEX flag must also be set.
    public const uint SHGFI_SMALLICON = 0x000000001;

    // Retrieve the index of a system image list icon. If successful, the index is copied to the iIcon member of psfi. The return value is a handle to the system image list. Only those images whose indices are successfully copied to iIcon are valid. Attempting to access other images in the system image list will result in undefined behavior.
    public const uint SHGFI_SYSICONINDEX = 0x000004000;

    // Retrieve the string that describes the file's type. The string is copied to the szTypeName member of the structure specified in psfi.
    public const uint SHGFI_TYPENAME = 0x000000400;

    // Indicates that the function should not attempt to access the file specified by pszPath. Rather, it should act as if the file specified by pszPath exists with the file attributes passed in dwFileAttributes. This flag cannot be combined with the SHGFI_ATTRIBUTES, SHGFI_EXETYPE, or SHGFI_PIDL flags.
    public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;
}

获取文件类型的方法(exe类型)

public static ShellFileGetInfo.ShellFileType GetExeType(string file)
{
    ShellFileGetInfo.ShellFileType type = ShellFileGetInfo.ShellFileType.FileNotFound;
    if (File.Exists(file))
    {
        ShellFileGetInfo.SHFILEINFO shinfo = new ShellFileGetInfo.SHFILEINFO();
        IntPtr ptr = ShellFileGetInfo.SHGetFileInfo(file, 128, ref shinfo, (uint)Marshal.SizeOf(shinfo), ShellFileGetInfo.SHGFI_EXETYPE);
        int wparam = ptr.ToInt32();
        int loWord = wparam & 0xffff;
        int hiWord = wparam >> 16;

        type = ShellFileGetInfo.ShellFileType.Unknown;

        if(wparam != 0)
        {
            if (hiWord == 0x0000 && loWord == 0x5a4d)
            {
                type = ShellFileGetInfo.ShellFileType.Dos;
            }
            else if (hiWord == 0x0000 && loWord == 0x4550)
            {
                type = ShellFileGetInfo.ShellFileType.Console;
            }
            else if ((hiWord != 0x0000) && (loWord == 0x454E || loWord == 0x4550 || loWord == 0x454C))
            {
                type = ShellFileGetInfo.ShellFileType.Windows;
            }
        }
    }
    return type;
}

方法的用法

switch(GetExeType( file )) {
    case ShellFileGetInfo.ShellFileType.Unknown:
        System.Diagnostics.Debug.WriteLine( "Unknown: " + file );
        break;
    case ShellFileGetInfo.ShellFileType.Dos:
        System.Diagnostics.Debug.WriteLine( "DOS: " + file );
        break;
    case ShellFileGetInfo.ShellFileType.Windows:
        System.Diagnostics.Debug.WriteLine( "Windows: " + file );
        break;
    case ShellFileGetInfo.ShellFileType.Console:
        System.Diagnostics.Debug.WriteLine( "Console: " + file );
        break;
    case ShellFileGetInfo.ShellFileType.FileNotFound:
        System.Diagnostics.Debug.WriteLine( "Missing: " + file );
        break;
}