Пример кода для WcsGetDefaultColorProfile

У кого-нибудь есть пример рабочего кода, показывающий вызов функции Windows Color System WcsGetDefaultColorProfile для получения цветового профиля по умолчанию для конкретного устройства? У меня это работает, когда я передаю null для параметра pDeviceName, но когда я пытаюсь передать имя устройства монитора, я всегда получаю код ошибки ERROR_FILE_NOT_FOUND.

Я бы предпочел пример C #, но я возьму все, что смогу. Я нигде не могу найти пример кода для новых функций управления профилями WCS.


person Rand Scullard    schedule 23.11.2012    source источник
comment
Есть новости по этому поводу? У меня такое впечатление, что это не работает.   -  person darklon    schedule 21.03.2013
comment
@darklon Это работает, см. Ниже.   -  person Mark Ingram    schedule 10.03.2016


Ответы (5)


Я столкнулся с той же проблемой, и причина вашего разочарования в том, что документы MSDN неверны (или, в лучшем случае, вводят в заблуждение) в отношении параметра pDeviceName для WcsGetDefaultColorProfile.

Документ MSDN (http://msdn.microsoft.com/en-us/library/dd372247(v=vs.85).aspx) указывает, что pDeviceName относится к «имени устройства», которое для устройств отображения можно было бы предположить как имя устройства отображения Windows, например "\.\DISPLAY1", возвращаемое в параметре DeviceName структуры DISPLAY_DEVICE из EnumDisplayDevices.

На самом деле здесь требуется параметр DeviceKey монитора, а именно DeviceKey, полученный при использовании флага EDD_GET_DEVICE_INTERFACE_NAME в EnumDisplayDevices.

Таким образом, рабочий код выглядит так, если предположить, что 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
    }
}
person Matt M    schedule 03.12.2014

Следуя ответу Мэтта М выше (спасибо!): если вы хотите соответствовать тому, что делают другие приложения, используйте первый монитор, возвращаемый EnumDisplayDevices, как указано выше.

Но обратите внимание, что это может вернуть неверный (неактивный/отключенный) монитор в настройке с несколькими мониторами, когда один или несколько мониторов отключены, но все еще подключены. Конечно, Microsoft нигде не документирует это, и поэтому многие приложения, включая крупных игроков, таких как Photoshop, не работают в этом отношении :(

Если вы хотите поступить правильно, вам придется вызывать EnumDisplayDevices несколько раз, пока не найдете активный монитор (флаги DISPLAY_DEVICE_ACTIVE и DISPLAY_DEVICE_MULTI_DRIVER, если с дисплеем связано несколько мониторов).

person fhoech    schedule 12.01.2015

Документация вообще не делает это очевидным, но из предыдущих ответов я собрал полное решение (в интересах ясности здесь нет проверки ошибок). Кроме того, это вернет только значение по умолчанию, указанное пользователем, если они никогда не устанавливали значение по умолчанию, вам нужно будет использовать 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;
person Mark Ingram    schedule 10.03.2016

Попытался перевести код Марка на С#:

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());
            }
        }

    }
}
person ironed    schedule 15.08.2016

Добавьте следующие структуры:

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);
}
person Maxim    schedule 12.03.2013
comment
Максим, пожалуйста, посмотрите мой оригинальный вопрос. Нигде в вашем ответе вы не используете WcsGetDefaultColorProfile API, и ваш ответ не демонстрирует, как получить профиль для конкретного устройства. Я что-то упускаю? - person Rand Scullard; 17.03.2013