Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I2C error when using the Windows Monitor Configuration Functions

I'm attempting to get/set the brightness level of the monitor through the Windows API. I've tried both the Low-Level Monitor Configuration Functions and the High-Level Monitor Configuration Functions, but they both seem to be breaking in the same place. In both cases I have no problem getting the HMONITOR handle and getting the physical monitor handle from the HMONITOR, but once I try to query the DDC/CI capabilities, I get an error saying "An error occurred while transmitting data to the device on the I2C bus."

The particular functions that cause this error are GetMonitorCapabilities for the high-level functions and GetCapabilitiesStringLength for the low-level functions. They both cause the same error.

This leads me to believe that maybe my monitor doesn't support DDC/CI, but I know my laptop's monitor brightness can be changed through the control panel, so it must be controlled through software somehow. Also I can successfully use the WMI classes in a PowerShell script to get/set the brightness as described on this page. Most things I've read suggest that most modern laptop screens do support DDC/CI.

Is there any way to find out what is causing this error or to get more information about it? I'm currently working in C++ in Visual Studio 2013 on Windows 7. I could probably use WMI in my C++ program if I can't get this current method working, but I thought I would ask here first.

Here's the code I currently have:

#include "stdafx.h"
#include <windows.h>
#include <highlevelmonitorconfigurationapi.h>
#include <lowlevelmonitorconfigurationapi.h>
#include <physicalmonitorenumerationapi.h>
#include <iostream>
#include <string>

int _tmain(int argc, _TCHAR* argv[])
{
  DWORD minBrightness, curBrightness, maxBrightness;
  HWND curWin = GetConsoleWindow();
  if (curWin == NULL) {
    std::cout << "Problem getting a handle to the window." << std::endl;
    return 1;
  }

  // Call MonitorFromWindow to get the HMONITOR handle
  HMONITOR curMon = MonitorFromWindow(curWin, MONITOR_DEFAULTTONULL);
  if (curMon == NULL) {
    std::cout << "Problem getting the display monitor" << std::endl;
    return 1;
  }

  // Call GetNumberOfPhysicalMonitorsFromHMONITOR to get the needed array size
  DWORD monitorCount;
  if (!GetNumberOfPhysicalMonitorsFromHMONITOR(curMon, &monitorCount)) {
    std::cout << "Problem getting the number of physical monitors" << std::endl;
    return 1;
  }

  // Call GetPhysicalMonitorsFromHMONITOR to get a handle to the physical monitor 
  LPPHYSICAL_MONITOR physicalMonitors = (LPPHYSICAL_MONITOR)malloc(monitorCount*sizeof(PHYSICAL_MONITOR));
  if (physicalMonitors == NULL) {
    std::cout << "Unable to malloc the physical monitor array." << std::endl;
    return 1;
  }
  if (!GetPhysicalMonitorsFromHMONITOR(curMon, monitorCount, physicalMonitors)) {
    std::cout << "Problem getting the physical monitors." << std::endl;
    return 1;
  }

  std::cout << "Num Monitors: " << monitorCount << std::endl;   // This prints '1' as expected.
  wprintf(L"%s\n", physicalMonitors[0].szPhysicalMonitorDescription);  // This prints "Generic PnP Monitor" as expected

  // Call GetMonitorCapabilities to find out which functions it supports
  DWORD monCaps;
  DWORD monColorTemps;
  // The following function call fails with the error "An error occurred while transmitting data to the device on the I2C bus."
  if (!GetMonitorCapabilities(physicalMonitors[0].hPhysicalMonitor, &monCaps, &monColorTemps)) {
    std::cout << "Problem getting the monitor's capabilities." << std::endl;
    DWORD errNum = GetLastError();
    DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
    LPVOID buffer;
    FormatMessage(flags, NULL, errNum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&buffer, 0, NULL);
    wprintf(L"%s\n", buffer);
    return 1;
  }
  // Same error when I use GetCapabilitiesStringLength(...) here.

  // More code that is currently never reached...

    return 0;
}

Edit: Also I should note that physicalMonitors[0].hPhysicalMonitor is 0, even though the monitor count and text description are valid and the GetPhysicalMonitorsFromHMONITOR function returns successfully. Any thoughts on why this might be?

like image 710
Michael Patterson Avatar asked Jun 21 '15 21:06

Michael Patterson


3 Answers

This is a "wonky hardware" problem, the I2C bus it talks about is the logical interconnect between the video adapter and the display monitor. Primarily useful for plug & play. Underlying error code is 0xC01E0582, STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA. It is generated by a the DxgkDdiI2CTransmitDataToDisplay() helper function in the video miniport driver. It is the vendor's video driver job to configure it, providing the functions that tickle the bus and to implement the IOCTL underlying GetMonitorCapabilities().

Clearly you are device driver land here, there isn't anything you can do about this failure in your C++ program. You can randomly spin the wheel of fortune by looking for a driver update from the video adapter manufacturer. But non-zero odds that the monitor is at fault here. Try another one first.

like image 94
Hans Passant Avatar answered Nov 18 '22 23:11

Hans Passant


I know its bad time to reply but i thought you should know.

The problem you are facing is because of the DDC/CI disabled on your monitor so you should go to the monitor settings and check if DDC/CI is disabled and if it is, then you have to enable it and run your code again. It would work. If you were not able to find DDC/CI option ( some of the monitor have a separate button for enabling/disabling the DDC/CI like the Benq's T71W monitor has a separate down arrow button to enable/disable DDC/CI ) then you should refer to your monitor's manual or contact the manufacturer.

Hope that helps. and sorry for late reply.

Best of luck. :)

like image 1
Ahtisham Avatar answered Nov 19 '22 00:11

Ahtisham


As I read the original question, the poster wanted to control a laptop display using DDC/CI. Laptop displays do not support DDC/CI. They provide a stripped down I2C bus sufficient to read the EDID at slave address x50, but that's it.

like image 1
Sanford Rockowitz Avatar answered Nov 19 '22 01:11

Sanford Rockowitz