Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Code example for WcsGetDefaultColorProfile

Does anyone have a working code example showing a call to the Windows Color System function WcsGetDefaultColorProfile to get the default color profile for a specific device? It works for me when I pass null for the pDeviceName parameter, but when I try to pass the device name of a monitor, I always get back an error code of ERROR_FILE_NOT_FOUND.

I would prefer a C# example, but I'll take anything I can get. I can't find any sample code for the newer WCS profile management functions anywhere.

like image 942
Rand Scullard Avatar asked Nov 23 '12 17:11

Rand Scullard


3 Answers

I was running into the same problem, and the reason for your frustration is that the MSDN docs are incorrect (or at best misleading) about the pDeviceName parameter to WcsGetDefaultColorProfile.

The MSDN doc (http://msdn.microsoft.com/en-us/library/dd372247(v=vs.85).aspx) indicates pDeviceName refers to the "name of the device", which for display devices one would assume to be a windows display device name, such as "\.\DISPLAY1", as returned in the DeviceName parameter of the DISPLAY_DEVICE struct from EnumDisplayDevices.

In fact, what it is requiring here is the DeviceKey parameter of the monitor, and specifically the DeviceKey obtained when the EDD_GET_DEVICE_INTERFACE_NAME flag is used in EnumDisplayDevices.

So working code looks like this, assuming szDisplayDeviceName is already set to display device name you care about, e.g., "\.\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
    }
}
like image 71
Matt M Avatar answered Sep 22 '22 07:09

Matt M


Following up on the answer from Matt M above (thanks!): If you want to match what other apps do, then use the first monitor returned by EnumDisplayDevices as above.

But note that this may return an incorrect (inactive/disabled) monitor in a multi-monitor setup where one or more monitors are disabled but still connected. Of course, Microsoft doesn't document this anywhere, and so a lot of apps including big players like Photoshop are broken in that regard :(

In case you want to do the right thing, you have to call EnumDisplayDevices multiple times until you find the active monitor (flags DISPLAY_DEVICE_ACTIVE and DISPLAY_DEVICE_MULTI_DRIVER in case multiple monitors are associated to the display).

like image 43
fhoech Avatar answered Sep 20 '22 07:09

fhoech


The documentation doesn't make it obvious at all, but from the previous answers I pieced together a complete solution (in the interests of clarity there is no error checking here). Also, this will only return a default as specified by the user, if they've never set a default, you will need to use GetStandardColorSpaceProfile to get the "other" default.

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;
like image 24
Mark Ingram Avatar answered Sep 20 '22 07:09

Mark Ingram