I capture screens on Windows using DirectX and GDI respectively based on the Operating System (DirectX (Desktop duplication) - For Win8 & Above & GDI for Win7 and below or as a fallback in case of any error with DirectX approach).
Rarely on some machines I get both GDI and DirectX return ACCESS_DENIED error on winlogon screen.
The Desktop switches are being detected and set properly & the application is running with elevated user privileges, There is no error during desktop setting. And the same thread which is capturing the screen is calling the function that sets the current desktop to current thread. The application is not being run on any RDP sessions like the problem mentioned here & here.
I did a lot of analysis and research. I did go through the source codes of open source screen capturing applications but I did not find anything that is helpful. It already took me a week without any progress on this problem.
Here is the code for setting current desktop
if (GetCurrentDesktop(aCurrentDesktop) && _tcscmp(aCurrentDesktop, aPrevDesktop) != 0)
{
ScreenCaptureUtils::SetProcessWindowStation();
HDESK hcurrentInputDesktop = OpenInputDesktop(0, FALSE, GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
if(hcurrentInputDesktop != NULL)
{
if(!SetThreadDesktop(hcurrentInputDesktop))
{
errorCode = GetLastError();
LOG << _T("setCurrentInputDesktop: SetThreadDesktop failed. Error code: ") << errorCode;
}
else
{
errorCode = ERROR_SUCCESS;
_tcscpy_s (aPrevDesktop, MAX_PATH, aCurrentDesktop);
LOG << _T("setCurrentInputDesktop: SetThreadDesktop succeeded.");
}
hPrevDesk = hcurrentInputDesktop;
CloseDesktop(hcurrentInputDesktop);
}
else
{
errorCode = GetLastError();
LOG << _T("setCurrentInputDesktop: OpenInputDesktop failed. Error code: ") << errorCode;
}
}
else
{
LOG << _T("setCurrentInputDesktop: OpenInputDesktop failed. Error code: ") << errorCode;
}
bool GetCurrentDesktop ( TCHAR * desktopName )
{
{
HDESK hDesktop = ::OpenInputDesktop(0, FALSE, GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
if (!hDesktop)
{
return false;
}
DWORD dw = 0;
TCHAR szName[MAX_PATH];
::memset(szName, 0, sizeof(szName));
if (!::GetUserObjectInformation(hDesktop, UOI_NAME, &szName, sizeof(szName), &dw))
{
::CloseDesktop(hDesktop);
return false;
}
_tcscpy(desktopName, szName);
::CloseDesktop(hDesktop);
}
}
The code to capture the screen is similar to the following one
int screenCapture()
{
int result = -1;
int width = 1000;
int height = 700;
HDC hdcTemp, hdc;
BYTE* bitPointer;
hdc = GetDC(HWND_DESKTOP);
if (hdc != NULL)
{
hdcTemp = CreateCompatibleDC(hdc);
if (hdcTemp != NULL)
{
BITMAPINFO bitmap;
bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
bitmap.bmiHeader.biWidth = width;
bitmap.bmiHeader.biHeight = -height;
bitmap.bmiHeader.biPlanes = 1;
bitmap.bmiHeader.biBitCount = 24;
bitmap.bmiHeader.biCompression = BI_RGB;
bitmap.bmiHeader.biSizeImage = 0;
bitmap.bmiHeader.biClrUsed = 0;
bitmap.bmiHeader.biClrImportant = 0;
HBITMAP hBitmap = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)&bitPointer, NULL, NULL);
if (hBitmap != NULL)
{
HBITMAP hPrevBitmap = SelectObject(hdcTemp, hBitmap);
BitBlt(hdcTemp, 0, 0, width, height, hdc, 0, 0, SRCCOPY);
result = (int) bitPointer[0];
SelectObject(hdcTemp, hPrevBitmap);
DeleteObject(hBitmap);
}
DeleteDC(hdcTemp);
}
ReleaseDC(HWND_DESKTOP, hdc);
}
return result;
}
I'm calling GetDC with the name of the current monitor. Is this something related to calling GetDC with NULL as a parameter?. But that doesn't look like a problem as per this.
Any help would be appreciated. Thanks in advance.