How to disable a secondary monitor (with ChangeDisplaySettingsEx)? - c++

I'm trying to follow the instructions on MSDN given here to disable a secondary monitor.
I'm trying to use specifically this set of functions to allow compatibility with older versions of Windows.
However, I can't manage to disable a monitor. I'm running and testing this on Windows 7 x64.
All I get is a flickering screen. The code definitely detects the monitor properly - I managed to change resolution and view it's display modes easily.
Here are (parts) of my code - I tried a lot of variations on the fields for DEVMODE
DEVMODE deleteScreenMode;
ZeroMemory(&deleteScreenMode, sizeof(DEVMODE));
deleteScreenMode.dmSize = sizeof(DEVMODE);
deleteScreenMode.dmDriverExtra = 0;
deleteScreenMode.dmFields = DM_POSITION | DM_PELSHEIGHT | DM_PELSWIDTH;
deleteScreenMode.dmPelsWidth = 0;
deleteScreenMode.dmPelsHeight = 0;
POINTL delete;
deleteion.x=0;
deleteion.y=0;
deleteScreenMode.dmPosition = deleteion;
LONG result = ChangeDisplaySettingsEx(devName,
&deleteScreenMode,
NULL,
CDS_UPDATEREGISTRY,
NULL);
Does anyone have experience with this? Thanks

I've decided to advance into a different problem - setting a primary display - and by pure luck I've stumbled into the solution.
There are 2 conditions to disable a monitor that aren't specified anywhere:
1) You can't disable the monitor dynamically - you must use CDS_UPDATEREGISTRY to write it into the registry.
2) More importantly, for some weird reason, you must first store the change in the registry (with or without CDS_NORESET, it doesn't matter), and then use again ChangeDisplaySettingsEx with NULL values to make the changes happen. This might have something to do both monitors connected to the same display device, I'm not sure...
Anyway here is the code that worked for me:
result = ChangeDisplaySettingsEx(devName, &deleteScreenMode,
NULL,
CDS_UPDATEREGISTRY | CDS_NORESET ,
NULL);
ChangeDisplaySettingsEx (NULL, NULL, NULL, NULL, NULL);
Hope it'll help someone somewhere someday.

A similar solution is hinted at here:
http://support.microsoft.com/kb/308216
This works for attaching screens. However, even armed with that knowledge, the ChangeDisplaySettingsEx documentation on how to detach a screen is also wrong about the DevMode fields that need to be set. As you noticed, you have to set not only DM_POSITION, but also DM_PELSHEIGHT | DM_PELSWIDTH.
In Windows 7 there's a new SetDisplayConfig API, but I have no personal experience with it yet. Hopefully it's better documented!

Related

WMI Query for SQL Server FilestreamSettings

Based on what I can find on the internet this doesn't seem to be something a lot of people do but I'm pretty stuck so I'm going to put it out here. I'm using WMI in C++ to try to manipulate SQL Server settings. I have the following code that doesn't return a result from my WMI query and I'm at a loss as to why:
hr = pLoc->ConnectServer(CComBSTR(L"root\\Microsoft\\SqlServer\\ComputerManagement10"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags
0, // Authority (e.g. Kerberos)
0, // Context object
&pSvc);
// ----- Check for success and set proxy blanket here -----
IEnumWbemClassObject* pClassEnum = 0;
hr = pSvc->ExecQuery(_bstr_t("WQL"), _bstr_t("SELECT * FROM FilestreamSettings"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pClassEnum);
if (SUCCEEDED(hr) && pClassEnum)
{
ULONG uReturn = 0;
while (pClassEnum && !myInstanceFound)
{
hr = pClassEnum->Next(WBEM_INFINITE, 1, &pObjInstance, &uReturn);
if (0 == uReturn || !pObjInstance)
{
break;
}
// Get the value of the InstanceName property - the SQL Server instance name
CComVariant vtProp;
hr = pObjInstance->Get(L"InstanceName", 0, &vtProp, 0, 0);
if (SUCCEEDED(hr) && (VT_BSTR) == vtProp.vt)
{
if (vtProp.bstrVal == _bstr_t('MyInstance'))
{
myInstanceFound = true;
}
}
}
.
.
.
}
The ExecQuery command succeeds. The pClassEnum enumerator object is not null, so the while loop executes. The call to 'Next', however, does not return an object (pObjectInstance is null) and &uReturn is 0 (which, as I understand it means that the call to 'Next' returned 0 results). However, if I run the same query in the wbemtest tool, I get two results (which is correct, as I have 2 SQL Server instances on this machine). I have limited C++ skills and this is my first time with WMI. Not only do I not see what's wrong here, I'm not even sure what else to try. The few code samples I've seen suggest this code should be correct. Any help would be greatly appreciated!
Thanks,
Dennis
Update: The call to Next() actually returns S_FALSE. Which, if I'm reading the docs correctly, mostly just confirms the issue of not getting a result. Next() returns S_FALSE if there are less than the number of requested results (in my case, less than 1 - or in other words, 0).
Update #2: This same code does work on my laptop (well, the Next() call does anyway). Differences are: Does work on my laptop - Win 10, Sql Server 2019 (have to change namespace to be ComputerManagement15 instead of 10), FileStream already enabled. Does not work - Win 7 VM, Sql Server 2008, FileStream not enabled. A query using Wbemtest tool gets the correct data in both cases. Just thought I'd post in case this helps.
FYI, in case anyone stumbles across this: I didn't technically solve this, in that I never got my C++ code to work. I wrote some C# code using SQL Server Management Objects (basically a wrapper over WMI) and made it into a COM server that I could call from C++. Even this didn't work directly because my C# COM server kept getting an "Access Denied" even if I ran the C++ COM client application as Administrator. What eventually worked was to extract the SSMO code out into its own C# console app which I then ran from my C# COM server as its own process using the "run as" verb so it would run as Administrator. This finally managed to enable Filestream on my SQL Server instance. It's possible there was a better/easier way to get this done but I found something that worked (although it was pretty kludgy). So if there's a chance this helps anyone else, I'm putting it out there.

Auto-generating HTML file & trying to open it in IE sometimes shows a blank page

I have a somewhat strange behavior associated with Internet Explorer (IE). My goal is to generate a report as HTML markup and display it to a user via the web browser. (Since my software is used in our corporate environment and most of those have IE web browsers by default, I observed this issue only with IE.)
The software is written as an MFC dialog-based app, that is run daily (at 7:30 am) from a repeating task scheduler task. (The task is set up to run only when a user is logged on, which is all the time. It's a single user account system, and that user is never logged out. Those machines are also powered on all the time.)
Upon startup my software generates the HTML markup, saves it in a temp file and then has IE display it to the user. That's it. (The goal is basically to automate this report and display it on the screen when the person begins their day.)
So I used basically the following code:
TCHAR buffTempFldr[MAX_PATH] = {0};
::GetTempPath(MAX_PATH, buffTempFldr);
StringCchCat(buffTempFldr, MAX_PATH, L"Log Report.htm");
//strHtml = CStringA with HTML markup of report in 8-bit ASCII format
HANDLE hFile = ::CreateFile(buffTempFldr, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
CREATE_ALWAYS, 0, NULL);
if(hFile != INVALID_HANDLE_VALUE)
{
DWORD dwcbWrtn;
::WriteFile(hFile, (BYTE *)strHtml.GetString(), strHtml.GetLength(), &dwcbWrtn, NULL);
::CloseHandle(hFile);
BOOL bInitted = SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
//Show in Web Browser (IE by default)
int nRetSH = (int)ShellExecute(NULL, L"open", buffTempFldr, NULL, NULL, SW_SHOWNORMAL);
if(nRetSH > 32)
{
//Success
}
if(!bInitted)
CoUninitialize();
}
The issue is that this works most of the time, but then maybe 10% of the time a user gets just an empty (blank) HTML page. And only after they hit refresh the page shows the actual report.
Obviously this is confusing for the users, so I'm trying to see why this is happening?
PS. The computers that this is done on are running Windows 7 Professional, and IE v.11, with regularly installed updates.
I might have gotten to the bottom of it.
This looks like a bug in the IE 11 rendering engine. (Interestingly enough, I ran it on a machine with IE 8 and it worked fine.) I discovered this by accident. When IE shows a blank page, if I just slightly resize its window, my HTML markup is then drawn in an instant. This also seems to happen only when I run my page thru IE with the screen being off in the power saving mode (although maybe running screensaver does it too -- I haven't gone all the way to test it.)
So at the moment I chose to use the following "hack" that wakes the screen before I call my ShellExecute method:
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)-1);
::Sleep(500);
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
ShellExecute(NULL, NULL, strSaveFileTo, NULL, NULL, SW_SHOWNORMAL);
But I would strongly recommend against using it, since it creates a race condition, or in other words, there's no way of knowing what happens first, the screen being woken up or IE page being displayed.
As a working solution though one may consider using a different web browser, such as Google Chrome or Firefox that won't have this issue.

How to create a partition without Windows assigning a drive letter?

I am trying to initialize and partition an attached virtual hard disk through the Windows API. I have been successful using DeviceIoControl() to do so, however whenever I apply the desired drive layout Windows is automatically assigning a drive letter to the partition and popping up an annoying "Would you like to format?" dialog.
My intent is to handle the formatting and mounting of this partition later in the program, but I'm not sure how to stop this behavior. I have tried setting RecognizedPartition to FALSE, but this seems to have no effect.
Relevant code:
Layout.PartitionStyle = PARTITION_STYLE_MBR;
Layout.PartitionCount = 4;
Layout.Mbr.Signature = MY_DISK_MBR_SIGNATURE;
Layout.PartitionEntry[0].PartitionStyle = PARTITION_STYLE_MBR;
Layout.PartitionEntry[0].PartitionNumber = 1;
Layout.PartitionEntry[0].StartingOffset.QuadPart = MY_DISK_OFFSET;
Layout.PartitionEntry[0].PartitionLength.QuadPart =
(Geom.DiskSize.QuadPart - MY_DISK_OFFSET);
Layout.PartitionEntry[0].Mbr.PartitionType = PARTITION_IFS;
Layout.PartitionEntry[0].Mbr.BootIndicator = FALSE;
Layout.PartitionEntry[0].Mbr.RecognizedPartition = FALSE;
Layout.PartitionEntry[0].Mbr.HiddenSectors =
(MY_DISK_OFFSET / Geom.Geometry.BytesPerSector);
for (int i = 0; i < 4; i++)
{
Layout.PartitionEntry[i].RewritePartition = TRUE;
}
if (!DeviceIoControl(hDisk, IOCTL_DISK_SET_DRIVE_LAYOUT_EX,
Layout, dwLayoutSz, NULL, 0, &dwReturn, NULL))
{
// Handle error
}
DeviceIoControl(hDisk, IOCTL_DISK_UPDATE_PROPERTIES,
NULL, 0, NULL, 0, &dwReturn, NULL);
What can I do to prevent automatic drive letter assignment?
The only reliable way I could find to work around this issue was to stop the "Shell Hardware Detection" service while the volume was created and formatted. However, this approach is so unapologetically silly that I refused to put it into code.
Another "hackish" option is to have the service start up and then immediately spawn itself (or a "worker" executable) in a hidden window via CreateProcess() with the CREATE_NO_WINDOW flag.
Since this software runs as a system service and I'd rather not complicate the code for something that only happens once or twice over the lifetime of the system, I've just had to accept that sometimes there will occasionally be an Interactive Services Detection window pop up for a few moments while creating the partitions.
If anyone discovers a good method for preventing the format prompt while programmatically creating and formatting a drive, I'll happily change the accepted answer (and owe you a beer).
It's been awhile since I've used this API, but from memory you can't. But it's doesn't stop you from removing the drive letter assignment after the fact.
I'm not sure if it will stop the format prompt tho, all the times that I have done this the partition has already been formatted correctly before I do the disk layout update.
I just solved this problem, by waiting for several seconds for the drive to be available and then directly issue a format action. See my answer here.
Rufus has an interesting workaround: it installs a window event hook that detects the "do you want to format this drive?" prompts and immediately closes them. See source code here.
It then goes on to arrange to mount only the partitions it cares about, but that's orthogonal.

InternetDial with INTERNET_AUTODIAL_FORCE_UNATTENDED still shows an error dialog?

I have a piece of software running on a remote device which calls InternetDial to establish a connection and report information.
Most of the time this works fine, but occasionally, some sort of error will occurr (bad signal etc.) and despite calling the function with INTERNET_AUTODIAL_FORCE_UNATTENDED, windows still appears to pop up dialogs when it fails to connect.
Rather annoyingly though, these dialogs are modal so the code just locks up.
I've tried just about every combination of options on the network configuration and ways to call the InternetDial routine, but none of them stop that dialog from appearing:
m_DResult = InternetDial(NULL, m_staticModemProfile, INTERNET_AUTODIAL_FORCE_UNATTENDED, (LPDWORD) &m_cnx, NULL);
m_DResult = InternetDial(NULL, NULL, INTERNET_AUTODIAL_FORCE_UNATTENDED, (LPDWORD) &m_cnx, NULL);
m_DResult = InternetDial(NULL, NULL, INTERNET_DIAL_UNATTENDED, (LPDWORD) &m_cnx, NULL);
m_DResult = InternetDial(NULL, m_staticModemProfile, INTERNET_DIAL_UNATTENDED, (LPDWORD) &m_cnx, NULL);
Is there a way round this or another way I can connect? I'm developing on Vista and XP Embedded if that makes a difference.
I just encountered this problem today and this code below fixed my problem. Use InternetAutodial rather than InternetDial
BOOL result = InternetAutodial(INTERNET_AUTODIAL_FORCE_UNATTENDED, NULL);

Edit Registry Values

I want to change the registry values on the pocketPC. I ran the following code:
if(enabled)
{
dwData = 120;
}
if(RegSetValueEx(HKEY_LOCAL_MACHINE, _T("System\\CurrentControlSet\\Control\\Power\\Timeouts\\BattSuspendTimeout"), 0, REG_DWORD, (LPBYTE)&dwData, sizeof(DWORD)))
{
return FALSE;
}
but it doesn't shange the registry entry. Does anyone know how to set registry key values with c++?
Thanks!
There are a two problems with what you are doing:
1: RegSetValueEx does not take a path, only a valuename. So you need to open the key path first.
e.g.
HKEY key;
if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\Power\\Timeouts", 0, 0, &key))
{
if(RegSetValueEx(key, _T("BattSuspendTimeout"), 0, REG_DWORD, (LPBYTE)&dwData, sizeof(DWORD)))
{
RegCloseKey(key);
return FALSE;
}
RegCloseKey(key);
}
2: That area of the registry requires Privileged code signing to work on all Windows Mobile devices. You can get away with it on most current touch-screen windows mobile devices if the user says "yes" to the unknown publisher question when the application is first run or installed. If you get a "Access Denied" error on the set, then you really need to be Privileged code signed for the set to work.
RegSetValueEx returns a descriptive error code. You can get a human-readable message out of this error code using FormatMessage and possibly via the Error Lookup tool, or the #ERR facility in VS. The code you have looks correct so see what the error message tells you.
How are you verifying the change? Keep in mind that making this change will not be reflected automatically in the device behavior and it probably won't show up in the Control Panel either (depends on if the CPL has already been loaded or not). The shell is unaware that you made the change and it doesn't poll the value - you have to tell it to go out and re-read. How to do it is documented in MSDN (basically you set a named system event).
Check out [VORegistry][1], it makes working with the registry so much easier.
[1]: http://www.voscorp.com/products/developer/winmobile/voregistry/index.htm VORegistry
Assuming that your looking with RegEdit, did you refresh (F5) the registry view?