6

有没有人有一个工作代码示例显示调用 Windows 颜色系统函数WcsGetDefaultColorProfile以获取特定设备的默认颜色配置文件?当我为 pDeviceName 参数传递 null 时,它对我有用,但是当我尝试传递监视器的设备名称时,我总是返回错误代码 ERROR_FILE_NOT_FOUND。

我更喜欢 C# 示例,但我会尽我所能。我在任何地方都找不到较新的 WCS 配置文件管理功能的任何示例代码。

4

5 回答 5

5

我遇到了同样的问题,您感到沮丧的原因是 MSDN 文档对 WcsGetDefaultColorProfile 的 pDeviceName 参数不正确(或充其量是误导)。

MSDN 文档 ( http://msdn.microsoft.com/en-us/library/dd372247(v=vs.85).aspx ) 表明 pDeviceName 指的是“设备名称”,对于显示设备,人们会假设为 Windows 显示设备名称,例如“\.\DISPLAY1”,在 EnumDisplayDevices 的 DISPLAY_DEVICE 结构的 DeviceName 参数中返回。

其实这里需要的是显示器的DeviceKey参数,具体来说就是在EnumDisplayDevices中使用EDD_GET_DEVICE_INTERFACE_NAME标志时得到的DeviceKey。

所以工作代码看起来像这样,假设 szDisplayDeviceName 已经设置为显示您关心的设备名称,例如“\.\DISPLAY1”:

WCHAR szPath[MAX_PATH];
DISPLAY_DEVICE dd;
dd.cb = sizeof(dd);
if (EnumDisplayDevices(szDisplayDeviceName, 0, &dd, EDD_GET_DEVICE_INTERFACE_NAME))
{
    if (WcsGetDefaultColorProfile(WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER, 
          dd.DeviceKey,
          CPT_ICC,
          CPST_PERCEPTUAL,
          1,  // dwProfileID -- doesn't seem to matter what value you use here
          MAX_PATH * sizeof(WCHAR),
          szPath))
    {
        PROFILE profile;
        profile.cbDataSize = (DWORD)(wcslen(szPath) + 1) * sizeof(WCHAR);
        profile.dwType = PROFILE_FILENAME;
        profile.pProfileData = (PVOID)szPath;

        HPROFILE hProfile = OpenColorProfile(&profile,
           PROFILE_READ, FILE_SHARE_READ, OPEN_EXISTING);

        // now do something with the profile
    }
}
于 2014-12-03T02:11:58.910 回答
2

跟进上面 Matt M 的回答(谢谢!):如果您想匹配其他应用程序的功能,请使用 EnumDisplayDevices 返回的第一个监视器,如上所述。

但请注意,这可能会在一个或多个监视器被禁用但仍连接的多监视器设置中返回不正确的(非活动/禁用)监视器。当然,微软没有在任何地方记录这一点,因此包括 Photoshop 等大玩家在内的许多应用程序在这方面都被破坏了:(

如果您想做正确的事情,您必须多次调用 EnumDisplayDevices 直到找到活动监视器(标志 DISPLAY_DEVICE_ACTIVE 和 DISPLAY_DEVICE_MULTI_DRIVER,以防多个监视器与显示器相关联)。

于 2015-01-12T02:26:17.823 回答
1

文档一点也不明显,但是从以前的答案中我拼凑了一个完整的解决方案(为了清楚起见,这里没有错误检查)。此外,这只会返回用户指定的默认值,如果他们从未设置过默认值,您将需要使用GetStandardColorSpaceProfile来获取“其他”默认值。

DISPLAY_DEVICE displayDevice = {};
displayDevice.cb = sizeof(DISPLAY_DEVICE);

// First, find the primary adaptor
std::stringw adaptorName;
DWORD deviceIndex = 0;
while (::EnumDisplayDevicesW(nullptr, deviceIndex++, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME))
{
    if (displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP &&
        displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
    {
        adaptorName = displayDevice.DeviceName;
        break;
    }
}

// Second, find the first active (and attached) monitor
std::string deviceName;
deviceIndex = 0;
while (::EnumDisplayDevicesW(adaptorName, deviceIndex++, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME))
{
    if (displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE &&
        displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED)
    {
        deviceName = displayDevice.DeviceKey;
        break;
    }
}

// Third, find out whether to use the global or user profile
BOOL usePerUserProfiles = FALSE;
WcsGetUsePerUserProfiles(deviceName, CLASS_MONITOR, &usePerUserProfiles);

// Finally, get the profile name
const WCS_PROFILE_MANAGEMENT_SCOPE scope = usePerUserProfiles ? WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER : WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE;

DWORD profileNameLength = 0; // In bytes
WcsGetDefaultColorProfileSize(scope, deviceName, CPT_ICC, CPST_RGB_WORKING_SPACE, 0, &profileNameLength);

wchar_t *const profileName = new wchar_t[profileNameLength / sizeof(wchar_t)];
WcsGetDefaultColorProfile(scope, deviceName, CPT_ICC, CPST_RGB_WORKING_SPACE, 0, profileNameLength, profileName);
// Do something with your profile name
delete[] profileName;
于 2016-03-10T14:41:43.537 回答
1

尝试将 Mark 的代码翻译成 C#:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace MonitorProfileNS {
    class MonitorProfile {
        [Flags()]
        enum DisplayDeviceStateFlags : UInt32 {
            // from: http://www.pinvoke.net/default.aspx/Enums/DisplayDeviceStateFlags.html
            // equvalent to defines from: wingdi.h (c:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\um\wingdi.h)
            //#define DISPLAY_DEVICE_ATTACHED_TO_DESKTOP      0x00000001
            //#define DISPLAY_DEVICE_MULTI_DRIVER             0x00000002
            //#define DISPLAY_DEVICE_PRIMARY_DEVICE           0x00000004
            //#define DISPLAY_DEVICE_MIRRORING_DRIVER         0x00000008
            //#define DISPLAY_DEVICE_VGA_COMPATIBLE           0x00000010
            //#if (_WIN32_WINNT >= _WIN32_WINNT_WIN2K)
            //#define DISPLAY_DEVICE_REMOVABLE                0x00000020
            //#endif // (_WIN32_WINNT >= _WIN32_WINNT_WIN2K)
            //#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
            //#define DISPLAY_DEVICE_ACC_DRIVER               0x00000040
            //#endif
            //#define DISPLAY_DEVICE_MODESPRUNED              0x08000000
            //#if (_WIN32_WINNT >= _WIN32_WINNT_WIN2K)
            //#define DISPLAY_DEVICE_REMOTE                   0x04000000
            //#define DISPLAY_DEVICE_DISCONNECT               0x02000000
            //#endif
            //#define DISPLAY_DEVICE_TS_COMPATIBLE            0x00200000
            //#if (_WIN32_WINNT >= _WIN32_WINNT_LONGHORN)
            //#define DISPLAY_DEVICE_UNSAFE_MODES_ON          0x00080000
            //#endif

            ///* Child device state */
            //#if (_WIN32_WINNT >= _WIN32_WINNT_WIN2K)
            //#define DISPLAY_DEVICE_ACTIVE              0x00000001
            //#define DISPLAY_DEVICE_ATTACHED            0x00000002
            //#endif // (_WIN32_WINNT >= _WIN32_WINNT_WIN2K)
            /// <summary>The device is part of the desktop.</summary>
            AttachedToDesktop = 0x1,
            MultiDriver = 0x2,
            /// <summary>The device is part of the desktop.</summary>
            PrimaryDevice = 0x4,
            /// <summary>Represents a pseudo device used to mirror application drawing for remoting or other purposes.</summary>
            MirroringDriver = 0x8,
            /// <summary>The device is VGA compatible.</summary>
            VGACompatible = 0x10,
            /// <summary>The device is removable; it cannot be the primary display.</summary>
            Removable = 0x20,
            /// <summary>The device has more display modes than its output devices support.</summary>
            ModesPruned = 0x8000000,
            Remote = 0x4000000,
            Disconnect = 0x2000000,

            /// <summary>Child device state: DISPLAY_DEVICE_ACTIVE</summary>
            Active = 0x1,
            /// <summary>Child device state: DISPLAY_DEVICE_ATTACHED</summary>
            Attached = 0x2
        }

        enum DeviceClassFlags : UInt32 {
            // from: c:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\um\Icm.h
            /// <summary>
            ///#define CLASS_MONITOR           'mntr' 
            /// </summary>
            CLASS_MONITOR = 0x6d6e7472,

            /// <summary>
            /// #define CLASS_PRINTER           'prtr'
            /// </summary>
            CLASS_PRINTER = 0x70727472,

            /// <summary>
            /// #define CLASS_SCANNER           'scnr'
            /// </summary>
            CLASS_SCANNER = 0x73636e72
        }

        enum WCS_PROFILE_MANAGEMENT_SCOPE : UInt32 {
            // from: c:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\um\Icm.h
            WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE,
            WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER
        }

        enum COLORPROFILETYPE: UInt32 {
            // from: c:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\um\Icm.h
            CPT_ICC,
            CPT_DMP,
            CPT_CAMP,
            CPT_GMMP
        }

        enum COLORPROFILESUBTYPE: UInt32{
            // from: c:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\um\Icm.h
            // intent
            CPST_PERCEPTUAL = 0,
            CPST_RELATIVE_COLORIMETRIC = 1,
            CPST_SATURATION = 2,
            CPST_ABSOLUTE_COLORIMETRIC = 3,

            // working space
            CPST_NONE,
            CPST_RGB_WORKING_SPACE,
            CPST_CUSTOM_WORKING_SPACE,
        };

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct DISPLAY_DEVICE {
            // from: http://www.pinvoke.net/default.aspx/Structures/DISPLAY_DEVICE.html
            [MarshalAs(UnmanagedType.U4)]
            public int cb;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string DeviceName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
            public string DeviceString;
            [MarshalAs(UnmanagedType.U4)]
            public DisplayDeviceStateFlags StateFlags;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
            public string DeviceID;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
            public string DeviceKey;
        }

        const UInt32 EDD_GET_DEVICE_INTERFACE_NAME = 0x1;

        //BOOL EnumDisplayDevices(
        //  _In_ LPCTSTR         lpDevice,
        //  _In_ DWORD           iDevNum,
        //  _Out_ PDISPLAY_DEVICE lpDisplayDevice,
        //  _In_ DWORD           dwFlags
        //);
        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        private static extern UInt32 EnumDisplayDevices(string s, UInt32 iDevNum, ref DISPLAY_DEVICE displayDevice, UInt32 dwFlags);

        // from: c:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\um\Icm.h
        //BOOL WINAPI WcsGetUsePerUserProfiles(
        //  _In_ LPCWSTR pDeviceName,
        //  _In_ DWORD dwDeviceClass,
        //  _Out_ BOOL *pUsePerUserProfiles
        //);
        /// <summary>
        /// https://msdn.microsoft.com/en-us/library/windows/desktop/dd372253(v=vs.85).aspx
        /// </summary>
        /// <returns>0, if failed</returns>
        [DllImport("Mscms.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        private static extern UInt32 WcsGetUsePerUserProfiles(string deviceName, DeviceClassFlags deviceClass, out UInt32 usePerUserProfiles);

        // from: c:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\um\Icm.h
        //BOOL WINAPI WcsGetDefaultColorProfileSize(
        //  _In_ WCS_PROFILE_MANAGEMENT_SCOPE profileManagementScope,
        //  _In_opt_ PCWSTR pDeviceName,
        //  _In_ COLORPROFILETYPE cptColorProfileType,
        //  _In_ COLORPROFILESUBTYPE cpstColorProfileSubType,
        //  _In_ DWORD dwProfileID,
        //  _Out_ PDWORD pcbProfileName
        //);
        /// <summary>
        /// https://msdn.microsoft.com/en-us/library/windows/desktop/dd372249(v=vs.85).aspx
        /// </summary>
        /// <param name="cbProfileName">Size in bytes! String length is /2</param>
        /// <returns>0, if failed</returns>
        [DllImport("Mscms.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        private static extern UInt32 WcsGetDefaultColorProfileSize( WCS_PROFILE_MANAGEMENT_SCOPE scope,
            string deviceName,
            COLORPROFILETYPE colorProfileType,
            COLORPROFILESUBTYPE colorProfileSubType,
            UInt32 dwProfileID,
            out UInt32 cbProfileName
        );

        // from: c:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\um\Icm.h
        //BOOL WINAPI WcsGetDefaultColorProfile(
        //  _In_ WCS_PROFILE_MANAGEMENT_SCOPE profileManagementScope,
        //  _In_opt_ PCWSTR pDeviceName,
        //  _In_ COLORPROFILETYPE cptColorProfileType,
        //  _In_ COLORPROFILESUBTYPE cpstColorProfileSubType,
        //  _In_ DWORD dwProfileID,
        //  _In_ DWORD cbProfileName,
        //  _Out_ LPWSTR pProfileName
        //);
        /// <summary>
        /// https://msdn.microsoft.com/en-us/library/windows/desktop/dd372247(v=vs.85).aspx
        /// </summary>
        /// <returns>0, if failed</returns>
        [DllImport("Mscms.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        private static extern UInt32 WcsGetDefaultColorProfile(WCS_PROFILE_MANAGEMENT_SCOPE scope,
            string deviceName,
            COLORPROFILETYPE colorProfileType,
            COLORPROFILESUBTYPE colorProfileSubType,
            UInt32 dwProfileID,
            UInt32 cbProfileName,
            StringBuilder profileName
        );

        public static string GetMonitorProfile() {
            // c++ recommendation: http://stackoverflow.com/questions/13533754/code-example-for-wcsgetdefaultcolorprofile

            DISPLAY_DEVICE displayDevice = new DISPLAY_DEVICE();
            displayDevice.cb = Marshal.SizeOf(displayDevice);

            // First, find the primary adaptor
            string  adaptorName=null;
            UInt32 deviceIndex = 0;

            while (EnumDisplayDevices(null, deviceIndex++, ref displayDevice, EDD_GET_DEVICE_INTERFACE_NAME)!=0) {
                if ((displayDevice.StateFlags & DisplayDeviceStateFlags.AttachedToDesktop)!=0 &&  
                    (displayDevice.StateFlags & DisplayDeviceStateFlags.PrimaryDevice)!=0) {
                    adaptorName = displayDevice.DeviceName;
                    break;
                }
            }

            // Second, find the first active (and attached) monitor
            string deviceName=null;
            deviceIndex = 0;
            while (EnumDisplayDevices(adaptorName, deviceIndex++, ref displayDevice, EDD_GET_DEVICE_INTERFACE_NAME)!=0) {
                if ((displayDevice.StateFlags & DisplayDeviceStateFlags.Active) != 0 &&
                    (displayDevice.StateFlags & DisplayDeviceStateFlags.Attached) != 0) {
                    deviceName = displayDevice.DeviceKey;
                    break;
                }
            }

            // Third, find out whether to use the global or user profile
            UInt32 usePerUserProfiles = 0;
            UInt32 res = WcsGetUsePerUserProfiles(deviceName, DeviceClassFlags.CLASS_MONITOR, out usePerUserProfiles);
            if (res==0) {
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
            }

            // Finally, get the profile name
            WCS_PROFILE_MANAGEMENT_SCOPE scope = (usePerUserProfiles != 0) ?
                WCS_PROFILE_MANAGEMENT_SCOPE.WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER :
                WCS_PROFILE_MANAGEMENT_SCOPE.WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE;

            UInt32 cbProfileName = 0;   // in bytes
            res = WcsGetDefaultColorProfileSize(scope, 
                deviceName,
                COLORPROFILETYPE.CPT_ICC, 
                COLORPROFILESUBTYPE.CPST_RGB_WORKING_SPACE, 
                0, 
                out cbProfileName);
            if (res == 0) {
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
            }

            int nLengthProfileName = (int)cbProfileName / 2;    // WcsGetDefaultColor... is using LPWSTR, i.e. 2 bytes/char
            StringBuilder profileName = new StringBuilder(nLengthProfileName);
            res = WcsGetDefaultColorProfile(scope, 
                deviceName, 
                COLORPROFILETYPE.CPT_ICC, 
                COLORPROFILESUBTYPE.CPST_RGB_WORKING_SPACE, 
                0, 
                cbProfileName, 
                profileName);
            if (res == 0) {
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
            }

            return profileName.ToString();
        }

        [DllImport("Mscms.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        private static extern bool GetColorDirectory(IntPtr pMachineName, StringBuilder pBuffer, ref uint pdwSize);

        public static string GetColorDirectory() {
            // s. http://stackoverflow.com/questions/14792764/is-there-an-equivalent-to-winapi-getcolordirectory-in-net
            uint pdwSize = 260;  // MAX_PATH 
            StringBuilder sb = new StringBuilder((int)pdwSize);
            if (GetColorDirectory(IntPtr.Zero, sb, ref pdwSize)) {
                return sb.ToString();
            } else {
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
            }
        }

    }
}
于 2016-08-15T16:31:16.847 回答
0

添加以下结构:

public const uint ProfileRead = 1;

public enum FileShare : uint
{
    Read = 1,
    Write = 2,
    Delete = 4
};

public enum CreateDisposition : uint
{
    CreateNew = 1,
    CreateAlways = 2,
    OpenExisting = 3,
    OpenAlways = 4,
    TruncateExisting = 5

};

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class ProfileFilename
{
    public uint type;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string profileData;
    public uint dataSize;

    public ProfileFilename(string filename)
    {
        type = ProfileFilenameType;
        profileData = filename;
        dataSize = (uint)filename.Length * 2 + 2;
    }
};

[DllImport("mscms.dll", SetLastError = true, EntryPoint = "GetStandardColorSpaceProfileW", CallingConvention = CallingConvention.Winapi)]
static extern bool GetStandardColorSpaceProfile(    uint machineName,
                                                    LogicalColorSpace profileID,
                                                    [MarshalAs(UnmanagedType.LPTStr), In, Out] StringBuilder profileName,
                                                    ref uint size);

[DllImport("mscms.dll", SetLastError = true, EntryPoint = "OpenColorProfileW", CallingConvention = CallingConvention.Winapi)]
    static extern IntPtr OpenColorProfile(  [MarshalAs(UnmanagedType.LPStruct)] ProfileFilename profile,
                                            uint desiredAccess,
                                            FileShare shareMode,
                                            CreateDisposition creationMode);

打开默认配置文件的示例:

public openDefaultColorProfile()
{ 
    StringBuilder profileName = new StringBuilder(256);
    uint size = (uint)profileName.Capacity * 2;
    success = GetStandardColorSpaceProfile(0, LogicalColorSpace.WindowsColorSpace, profileName, ref size);

    ProfileFilename sRGBFilename = new ProfileFilename(profileName.ToString());
    IntPtr hSRGBProfile = OpenColorProfile(sRGBFilename, ProfileRead, FileShare.Read, CreateDisposition.OpenExisting);
}
于 2013-03-12T14:46:02.517 回答