首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >仅列出'Devices and Printers‘面板中显示的设备

仅列出'Devices and Printers‘面板中显示的设备
EN

Stack Overflow用户
提问于 2017-11-14 06:24:47
回答 1查看 809关注 0票数 1

我正在用C#编写一个应用程序,它将在用户的PC上运行,我想只列出Windows“设备和打印机”控制面板中显示的设备,如显示器、键盘、鼠标、扬声器等。

我可以使用WMI来提取所有设备的列表,但是有没有一种方法可以只提取在控制面板的该部分中显示的设备,而不是整个列表?

我在网上搜索过,没有发现任何与此相关的内容,我甚至找不到设备出现在该列表中的标准。

是否可以访问该列表中显示的设备列表,如果不能,是否有可应用于仅显示这些设备的完整列表的过滤器?

提前感谢

EN

回答 1

Stack Overflow用户

发布于 2017-11-14 09:20:01

我使用p/invoke和COM interop来实现这一点,方法是枚举Microsoft.DevicesAndPrinters中的外壳项,并确保PKEY_Devices_CategoryIds包含以PrintFax开头的项进行过滤。

没有办法将必要的互操作定义归结为一个答案,但这是我用来枚举显示名称、DEVMODE名称和任何大小的图像的逻辑:

代码语言:javascript
复制
public sealed class PrinterInfo
{
    public string IdName { get; }
    public string DisplayName { get; }
    public Bitmap Image { get; }

    private PrinterInfo(string idName, string displayName, Bitmap image)
    {
        IdName = idName;
        DisplayName = displayName;
        Image = image;
    }

    public static IReadOnlyList<PrinterInfo> GetInstalledPrinterNamesAndImages(Size imageSize)
    {
        var r = new List<PrinterInfo>();

        using (var folderIdList = CreateDevicesAndPrintersIDL())
        {
            var folder = GetShellFolder(folderIdList);
            var enumerator = folder.EnumObjects(IntPtr.Zero, SHCONTF.NONFOLDERS);

            for (;;)
            {
                // If you request more than are left, actualCount is 0, so we'll do one at a time.
                var next = enumerator.Next(1, out var relativeIdList, out var actualCount);
                next.ThrowIfError();
                if (next == HResult.False || actualCount != 1) break; // printerChild is junk

                using (relativeIdList)
                using (var absoluteIdList = ILCombine(folderIdList, relativeIdList))
                {
                    var shellItem = GetShellItem(absoluteIdList);
                    var idName = GetPrinterFriendlyNameIfPrinter(shellItem);
                    if (idName != null)
                        r.Add(new PrinterInfo(idName, GetDisplayName(shellItem), GetImage(shellItem, imageSize)));
                }
            }
        }

        return r;
    }

    private static ItemIdListSafeHandle CreateDevicesAndPrintersIDL()
    {
        SHGetKnownFolderIDList(FOLDERID.ControlPanelFolder, KF_FLAG.DEFAULT, IntPtr.Zero, out var controlPanelIdList).ThrowIfError();
        using (controlPanelIdList)
        {
            GetShellFolder(controlPanelIdList).ParseDisplayName(IntPtr.Zero, null, "::{A8A91A66-3A7D-4424-8D24-04E180695C7A}", IntPtr.Zero, out var childDevicesAndPriversIdList, IntPtr.Zero);
            using (childDevicesAndPriversIdList)
                return ILCombine(controlPanelIdList, childDevicesAndPriversIdList);
        }
    }

    private static string GetPrinterFriendlyNameIfPrinter(IShellItem2 shellItem)
    {
        // Devices_PrimaryCategory returns "Printers" for printers and faxes on Windows 10 but "Printers and faxes" on Windows 7.
        using (var categoryIds = new PropVariantSafeHandle())
        {
            shellItem.GetProperty(PKEY.Devices_CategoryIds, categoryIds).ThrowIfError();
            if (!categoryIds.ToStringVector().Any(id => id.StartsWith("PrintFax", StringComparison.OrdinalIgnoreCase)))
                return null;
        }

        // The canonical or "friendly name" needed to match the devmode
        // https://blogs.msdn.microsoft.com/asklar/2009/10/21/getting-the-printer-friendly-name-from-the-device-center-shell-folder/
        // PKEY_Devices_InterfacePaths doesn't seem to ever be found, but PKEY_Devices_FriendlyName works so...
        shellItem.GetString(PKEY.Devices_FriendlyName, out var friendlyName).ThrowIfError();
        return friendlyName.ReadAndFree();
    }

    private static string GetDisplayName(IShellItem2 shellItem)
    {
        return shellItem.GetDisplayName(SIGDN.NORMALDISPLAY).ReadAndFree();
    }

    private static Bitmap GetImage(IShellItem2 shellItem, Size imageSize)
    {
        return ((IShellItemImageFactory)shellItem).GetImage(new POINT(imageSize.Width, imageSize.Height), SIIGBF.SIIGBF_BIGGERSIZEOK)
            .CopyAndFree(); // Bitmap.FromHbitmap is useless with alpha, so make a copy
    }


    private static IShellFolder GetShellFolder(ItemIdListSafeHandle itemIdList)
    {
        SHBindToObject(IntPtr.Zero, itemIdList, null, typeof(IShellFolder).GUID, out var objShellFolder).ThrowIfError();
        return (IShellFolder)objShellFolder;
    }

    private static IShellItem2 GetShellItem(ItemIdListSafeHandle itemIdList)
    {
        SHCreateItemFromIDList(itemIdList, typeof(IShellItem2).GUID, out var objShellItem).ThrowIfError();
        return (IShellItem2)objShellItem;
    }
}

(C# 7)

下面是您可以编译的完整演示:https://github.com/jnm2/example-devices-and-printers/tree/master/src

对于不需要ValueTuple的C# 6版本,请参阅https://github.com/jnm2/example-devices-and-printers/tree/master/src-csharp6

我很乐意回答任何问题。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47274577

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档