Sending a struct from C++ to WPF using WM_COPYDATA - c++

I have a native C++ application that, for the time being simply needs to send its command line string and current mouse cursor coordinates to a WPF application. The message is sent and received just fine, but I cannot convert the IntPtr instance in C# to a struct.
When I try to do so, the application will either crash without exception or the line of code that converts it is skipped and the next message in the loop is received. This probably means there's a native exception occurring, but I don't know why.
Here's the C++ program. For the time being I'm ignoring the command line string and using fake cursor coordinates just to make sure things work.
#include "stdafx.h"
#include "StackProxy.h"
#include "string"
typedef std::basic_string<WCHAR, std::char_traits<WCHAR>> wstring;
struct StackRecord
{
//wchar_t CommandLine[128];
//LPTSTR CommandLine;
//wstring CommandLine;
__int32 CursorX;
__int32 CursorY;
};
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
COPYDATASTRUCT data;
ZeroMemory(&data, sizeof(COPYDATASTRUCT));
StackRecord* record = new StackRecord();
wstring cmdLine(lpCmdLine);
//record.CommandLine = cmdLine;
record->CursorX = 5;
record->CursorY = 16;
data.dwData = 12;
data.cbData = sizeof(StackRecord);
data.lpData = record;
HWND target = FindWindow(NULL, _T("Window1"));
if(target != NULL)
{
SendMessage(target, WM_COPYDATA, (WPARAM)(HWND) target, (LPARAM)(LPVOID) &data);
}
return 0;
}
And here is the part of the WPF application that receives the message. The second line inside the IF statement is skipped over, if the whole thing doesn't just crash.
public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == Interop.WM_COPYDATA)
{
var data = (Interop.CopyDataStruct)Marshal.PtrToStructure(lParam, typeof(Interop.CopyDataStruct));
var record = (Interop.StackRecord)Marshal.PtrToStructure(data.lpData, typeof(Interop.StackRecord));
MessageBox.Show(String.Format("X: {0}, Y: {1}", record.CursorX, record.CursorY));
}
return IntPtr.Zero;
}
And here are the C# definitions for the structs. I have toyed endlessly with marshalling attributes and gotten nowhere.
internal static class Interop
{
public static readonly int WM_COPYDATA = 0x4A;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CopyDataStruct
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
public struct StackRecord
{
//[MarshalAs(UnmanagedType.ByValTStr)]
//public String CommandLine;
public Int32 CursorX;
public Int32 CursorY;
}
}
Any ideas?

I am not sure what you are getting wrong necessarily without more info about your setup. I replicated the code as best I could (using WndProc in a WPF app, sending from my own win32 app) and it works fine for me. There are a few errors which will definetly crop up if you are running 64 bit applications, namely the Pack = 1 will cause the COPYDATASTRUCT to become misaligned and reading from the pointer is likely to end in pain.
It is crashing passing just the ints? Looking at your commented code passing a LPWSTR or wstring is going to cause serious issues, although that shouldn't become apparent until you unmarshal the sent data.
For what it is worth, this is snippets of my code which seem to work for me including getting the command line across.
/* C++ code */
struct StackRecord
{
wchar_t cmdline[128];
int CursorX;
int CursorY;
};
void SendCopyData(HWND hFind)
{
COPYDATASTRUCT cp;
StackRecord record;
record.CursorX = 1;
record.CursorY = -1;
_tcscpy(record.cmdline, L"Hello World!");
cp.cbData = sizeof(record);
cp.lpData = &record;
cp.dwData = 12;
SendMessage(hFind, WM_COPYDATA, NULL, (LPARAM)&cp);
}
/* C# code */
public static readonly int WM_COPYDATA = 0x4A;
[StructLayout(LayoutKind.Sequential)]
public struct CopyDataStruct
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct StackRecord
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)]
public string CommandLine;
public Int32 CursorX;
public Int32 CursorY;
}
protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_COPYDATA)
{
StackRecord record = new StackRecord();
try
{
CopyDataStruct cp = (CopyDataStruct)Marshal.PtrToStructure(lParam, typeof(CopyDataStruct));
if (cp.cbData == Marshal.SizeOf(record))
{
record = (StackRecord)Marshal.PtrToStructure(cp.lpData, typeof(StackRecord));
}
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.ToString());
}
handled = true;
}
else
{
handled = false;
}
return IntPtr.Zero;
}

I've build a couple of applications (with VC++ and VC#, respectively), addressing the "boiled-down" variant of the problem (i.e. inability to get that struct), they seem to work flawelessly, so it may really be something with your setup, as tyranid says.
Anyway, here's the code (it must be enough to just paste it into newly created WIN32 APPLICATION (for VC++) and WINDOWS FORMS APPLICATION for C# to run and test):
StackProxy.cpp
#include "stdafx.h"
#include "StackProxy.h"
#include <string>
struct StackRecord {
__int32 CursorX;
__int32 CursorY;
};
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
StackRecord record;
record.CursorX = 5;
record.CursorY = 16;
COPYDATASTRUCT data;
data.dwData = 12;
data.cbData = sizeof(StackRecord);
data.lpData = &record;
HWND target = FindWindow(NULL, _T("Window1"));
if(target != NULL)
SendMessage(target, WM_COPYDATA, (WPARAM)(HWND) target, (LPARAM)(LPVOID) &data);
return 0;
}
Form1.cs
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public struct COPYDATASTRUCT
{
public System.Int32 dwData;
public System.Int32 cbData;
public System.IntPtr lpData;
}
int WM_COPYDATA = 0x4A;
[StructLayout(LayoutKind.Sequential)]
public struct StackRecord
{
public Int32 CursorX;
public Int32 CursorY;
}
public Form1()
{
InitializeComponent();
Text = "Window1";
}
protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_COPYDATA) {
COPYDATASTRUCT cp = (COPYDATASTRUCT)Marshal.PtrToStructure(msg.LParam, typeof(COPYDATASTRUCT));
StackRecord record = (StackRecord)Marshal.PtrToStructure(cp.lpData, typeof(StackRecord));
MessageBox.Show(String.Format("X: {0}, Y: {1}, Data: {2}", record.CursorX, record.CursorY, cp.dwData));
}
base.WndProc(ref msg);
}
}
}
Hope this helps.
P.S. I've not got much knowledge of C# and (especially) interop (having interest primarily in C++ programming), but seeing no one answer [a few hours ago] just thought it would be a nice challenge to try this problem. Not to mention the bounty :)
*amn it, i'm late:))

Related

Visual Studio Extension which starts a program and attaches the debugger: How to avoid stop because of LdrpDoDebuggerBreak()?

I am trying to create a Visual Studio Extension (VS 2022) which starts a program and attaches the debugger. At this point no solution/project is loaded.
It works. However, at startup a window appears that a breakpoint has been reached:
Does anyone know a way to avoid this breakpoint?
Or a hint to an other API? Thanks.
Initial setup:
create VSIX Project
add New Item: Command
The process is launched suspended because i want to add my own breakpoints (not in example but works).
~~
static DTE dte;
public static async Task InitializeAsync(AsyncPackage package)
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
Instance = new Command1(package, commandService);
dte = await package.GetServiceAsync(typeof(DTE)) as DTE;
}
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
public struct STARTUPINFO
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[DllImport("kernel32.dll")]
public static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes,
bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment,
string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll")]
public static extern uint ResumeThread(IntPtr hThread);
private void Execute(object sender, EventArgs e)
{
STARTUPINFO si = new STARTUPINFO();
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
uint CREATE_SUSPENDED = 0x00000004;
bool success = CreateProcess(#"C:\Windows\System32\notepad.exe", null, IntPtr.Zero, IntPtr.Zero, false, CREATE_SUSPENDED, IntPtr.Zero, null, ref si, out pi);
EnvDTE.Processes processes = dte.Debugger.LocalProcesses;
foreach (EnvDTE.Process proc in processes)
{
if (proc.ProcessID == pi.dwProcessId)
{
proc.Attach();
break;
}
}
ResumeThread(pi.hThread);
}
~~
Found the solution:
public void LaunchDebugTarget(string filePath, string options, Guid engineId)
{
IVsDebugger4 debugger = (IVsDebugger4)Package.GetGlobalService(typeof(IVsDebugger));
VsDebugTargetInfo4[] debugTargets = new VsDebugTargetInfo4[1];
debugTargets[0].dlo = (uint)DEBUG_LAUNCH_OPERATION.DLO_CreateProcess;
debugTargets[0].bstrExe = filePath;
debugTargets[0].bstrOptions = options;
debugTargets[0].guidLaunchDebugEngine = engineId;
VsDebugTargetProcessInfo[] processInfo = new VsDebugTargetProcessInfo[debugTargets.Length];
debugger.LaunchDebugTargets4(1, debugTargets, processInfo);
}
Guid g = VSConstants.DebugEnginesGuids.NativeOnly;
LaunchDebugTarget(#"C:\Users\user\source\repos\ConsoleApplication19\x64\Debug\ConsoleApplication19.exe", "", g);

Attach debugger and get variable address programmatically Visual Studio

In a micro-controller programmed using C/C++ all the addresses are known before the program starts. I can use an elf file that is generated when compiling to find those with gdb, for example:
gdb "elfFile.elf" -ex "print &variableName" -ex quit
This is useful to get variables in real-time by asking the micro-controller for a variable address from an external tool that can be used to log and plot data. The tool gets the variable address from the elf file and then just asks the micro-controller to read the data at address &variableName for n bytes.
I have ported some of the micro-controller code to Windows for testing purposes and it is running correctly. I would like to add the log functionality and for this purpose I need to be able to get a variable address programmatically from a running exe file. The address of variables is not known until the program starts in Windows (perhaps the offset is known though). I am using Visual Studio Express 2017 and I intend to get addresses of the program I compile myself, not any program from outside. From inside Visual Studio I can see any variable with the debugger so I hope there's gotta be a debugger exe file I can call from outside and attach to my program and read the variable addresses in a similar fashion as I did with gdb.
Any help? Thank you
You could inject a dll with CreateRemoteProcess, and read the variable address there.
But you're looking for a program called OllyDbg.
Also, you could download and install MinGW & msys, then you can build GDB for Windows from source. But then you have to use MinGW on Windows to compile the program. (Mingw creates native Win32/Win64 programs - no need for Cygwin)
I don't have the C-Sources anymore for CreateRemoteThread, but I can give you the C# source:
// Inject(DLL_NAME, "Engine");
void Inject(string strDLLtoInject, string strEngine)
{
CheckIfDebugger();
//String pszLibFileRemote = Application.StartupPath + "\\"+ strDLLtoInject;
//String pszLibFileRemote = Application.StartupPath + "\\"+ strDLLtoInject;
//String strPathOfSharedObjectToInject = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + System.IO.Path.DirectorySeparatorChar + strDLLtoInject;
String strPathOfSharedObjectToInject = m_strTempPath + strDLLtoInject;
//System.Windows.Forms.MessageBox.Show(strPathOfSharedObjectToInject);
System.Diagnostics.Process[] TargetProcess = System.Diagnostics.Process.GetProcessesByName(strEngine);
if (TargetProcess.Length > 0)
{
System.IntPtr pTargetProcess = WinAPI.OpenProcess(WinAPI.CREATE_THREAD_ACCESS, false, TargetProcess[0].Id);
if (pTargetProcess != System.IntPtr.Zero)
{
CheckIfDebugger();
int iLoadLibraryAaddress = WinAPI.GetProcAddress(WinAPI.GetModuleHandleA("Kernel32.dll"), "LoadLibraryA");
if (iLoadLibraryAaddress != 0)
{
int iSharedObjectNameBufferSize = 1 + strPathOfSharedObjectToInject.Length;
int iSharedObjectNameAddress = WinAPI.VirtualAllocEx(pTargetProcess, 0, iSharedObjectNameBufferSize, 4096, 4);
if (iSharedObjectNameAddress != 0)
{
CheckIfDebugger();
int iReturnValue = WinAPI.WriteProcessMemory(pTargetProcess, iSharedObjectNameAddress, strPathOfSharedObjectToInject, iSharedObjectNameBufferSize, 0);
if (iReturnValue != 0)
WinAPI.CreateRemoteThread(pTargetProcess, 0, 0, iLoadLibraryAaddress, iSharedObjectNameAddress, 0, 0);
else
MsgBox("WriteProcessMemory failed.", "Error");
CheckIfDebugger();
} // End if (iSharedObjectNameAddress != null)
else
MsgBox("VirtualAllocEx failed.", "Error");
}// End if (iLoadLibraryAaddress != null)
else
MsgBox("GetProcAddress or GetModuleHandleA failed.", "Error");
CheckIfDebugger();
WinAPI.CloseHandle(pTargetProcess);
} // End if (pTargetProcess != System.IntPtr.Zero)
else
MsgBox("OpenProcess failed.", "Error");
CheckIfDebugger();
} // End if (TargetProcess.Length > 0)
else
MsgBox("GetProcessesByName failed.", "Error");
CheckIfDebugger();
} // End Sub Inject
Here's the WinAPI calls:
class WinAPI
{
public const int CREATE_THREAD_ACCESS = 0x1F0FFF;
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[System.Runtime.InteropServices.DllImport("Kernel32", EntryPoint = "GetModuleHandleA", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
public static extern int GetModuleHandleA(string lpModuleName);
[System.Runtime.InteropServices.DllImport("kernel32", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
public static extern int GetProcAddress(int hModule, string lpProcName);
[System.Runtime.InteropServices.DllImport("kernel32", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
public static extern int VirtualAllocEx(System.IntPtr hProcess, int lpAddress, int dwSize, int flAllocationType, int flProtect);
[System.Runtime.InteropServices.DllImport("kernel32", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
public static extern int WriteProcessMemory(System.IntPtr hProcess, int lpBaseAddress, string lpBuffer, int nSize, int lpNumberOfBytesWritten);
[System.Runtime.InteropServices.DllImport("kernel32", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
public static extern int CreateRemoteThread(System.IntPtr hProcess, int lpThreadAttributes, int dwStackSize, int lpStartAddress, int lpParameter, int dwCreationFlags, int lpThreadId);
[System.Runtime.InteropServices.DllImport("kernel32", EntryPoint = "CloseHandle")]
public static extern int CloseHandle(System.IntPtr hObject);
// http://www.pinvoke.net/default.aspx/kernel32/GetVersion.html
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
public static extern uint GetVersion();
[System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, ExactSpelling = true)]
internal static extern bool IsDebuggerPresent();
} // End class WinAPI

Printer port setting

Need to set the port on XPS printer. I found some example on Stackoverflow but it doesnt work.
Here is a code(Lots of trash):
LPTSTR pDeviceName = _T("Microsoft XPS Document Writer");
HANDLE phPrinter(nullptr);
PRINTER_DEFAULTS defaults;
defaults.DesiredAccess = PRINTER_ACCESS_USE;
defaults.pDatatype = 0;
PORT_INFO_3 pInfo3;;
DWORD needed;
DWORD XcvResult;
DWORD err = OpenPrinter(pDeviceName,&phPrinter,NULL);
//const BYTE* portValue = reinterpret_cast<const BYTE*>("TestPort");
PBYTE port = (PBYTE)_T("Test1");
if(err) {
int res = XcvData(phPrinter,_T("AddPort"),port,sizeof(port),NULL,0,&needed,&XcvResult);
}
else {
AfxMessageBox(_T("ERROR."),MB_OK);
}
ClosePrinter(phPrinter);
the funniest thing that this code worked just once(the first starting of XcvData func)!
Another example the same behaviour:
BOOL AddPortX(void)
{
DWORD cbneed,cbstate;
PBYTE pOutputData;
HANDLE hXcv = INVALID_HANDLE_VALUE;
PRINTER_DEFAULTS Defaults = { NULL,NULL,SERVER_ACCESS_ADMINISTER };
WCHAR pszPortName[]=L"UTReportPDFPort:";
pOutputData=(PBYTE)malloc(MAX_PATH);
if(!OpenPrinter(_T("Microsoft XPS Document Writer"),&hXcv,NULL ))
{
LPVOID lpMsgBuf;
GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), NULL,(LPTSTR) &lpMsgBuf, 0, NULL );
::MessageBox(NULL,(LPCTSTR)lpMsgBuf,_T("ERROR"),MB_OK|MB_ICONINFORMATION);
free(pOutputData);
LocalFree( lpMsgBuf );
return FALSE;
}
// False
if(!XcvData(hXcv,L"AddPort",(PBYTE)pszPortName,sizeof(pszPortName),(PBYTE)pOutputData,MAX_PATH,&cbneed,&cbstate))
{
LPVOID lpMsgBuf;
SetLastError(cbstate);
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), NULL,(LPTSTR) &lpMsgBuf, 0, NULL );
::MessageBox(NULL,(LPCTSTR)lpMsgBuf,_T("ERROR"),MB_OK|MB_ICONINFORMATION);
LocalFree( lpMsgBuf );
free(pOutputData);
}
free(pOutputData);
ClosePrinter(hXcv);
return TRUE;
}
So, how to set add printer port right, and automatically select it after adding?
Maybe, somebody knows why it works just once? I mean - the XcvData function. All next times it returns the error code 6.
The .NET solution would be good too.
public static class Winspool
{
[StructLayout(LayoutKind.Sequential)]
private class PRINTER_DEFAULTS
{
public string pDatatype;
public IntPtr pDevMode;
public int DesiredAccess;
}
[DllImport("winspool.drv", EntryPoint = "XcvDataW", SetLastError = true)]
private static extern bool XcvData(
IntPtr hXcv,
[MarshalAs(UnmanagedType.LPWStr)] string pszDataName,
IntPtr pInputData,
uint cbInputData,
IntPtr pOutputData,
uint cbOutputData,
out uint pcbOutputNeeded,
out uint pwdStatus);
[DllImport("winspool.drv", EntryPoint = "OpenPrinterA", SetLastError = true)]
private static extern int OpenPrinter(
string pPrinterName,
ref IntPtr phPrinter,
PRINTER_DEFAULTS pDefault);
[DllImport("winspool.drv", EntryPoint = "ClosePrinter")]
private static extern int ClosePrinter(IntPtr hPrinter);
public static int AddLocalPort(string portName)
{
PRINTER_DEFAULTS def = new PRINTER_DEFAULTS();
def.pDatatype = null;
def.pDevMode = IntPtr.Zero;
def.DesiredAccess = 1; //Server Access Administer
IntPtr hPrinter = IntPtr.Zero;
int n = OpenPrinter(",XcvMonitor Local Port", ref hPrinter, def);
if (n == 0)
return Marshal.GetLastWin32Error();
if (!portName.EndsWith("\0"))
portName += "\0"; // Must be a null terminated string
// Must get the size in bytes. Rememeber .NET strings are formed by 2-byte characters
uint size = (uint)(portName.Length * 2);
// Alloc memory in HGlobal to set the portName
IntPtr portPtr = Marshal.AllocHGlobal((int)size);
Marshal.Copy(portName.ToCharArray(), 0, portPtr, portName.Length);
uint needed; // Not that needed in fact...
uint xcvResult; // Will receive de result here
XcvData(hPrinter, "AddPort", portPtr, size, IntPtr.Zero, 0, out needed, out xcvResult);
ClosePrinter(hPrinter);
Marshal.FreeHGlobal(portPtr);
return (int)xcvResult;
}
}
Resource is from CodeProject
The first parameter in OpenPrinter() have to be - XcvMonitor Local Port.
Than we can use .NET management objects to select port is default.

C++ application to detach secondary monitor

I am trying to create an application to detach a secondary monitor from Windows boxes (long story).
Here is Microsoft's sample code that I used as a basis:
http://support.microsoft.com/kb/308216/en-us
Here is my code:
#include <iostream>
#include <windows.h>
void DetachDisplay()
{
BOOL FoundSecondaryDisp = FALSE;
DWORD DispNum = 0;
DISPLAY_DEVICE DisplayDevice;
LONG Result;
TCHAR szTemp[200];
int i = 0;
DEVMODE defaultMode;
// initialize DisplayDevice
ZeroMemory(&DisplayDevice, sizeof(DisplayDevice));
DisplayDevice.cb = sizeof(DisplayDevice);
// get all display devices
while (EnumDisplayDevices(NULL, DispNum, &DisplayDevice, 0))
{
ZeroMemory(&defaultMode, sizeof(DEVMODE));
defaultMode.dmSize = sizeof(DEVMODE);
if ( !EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName, ENUM_REGISTRY_SETTINGS, &defaultMode) )
OutputDebugString("Store default failed\n");
if ((DisplayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) &&
!(DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
{
DEVMODE DevMode;
ZeroMemory(&DevMode, sizeof(DevMode));
DevMode.dmSize = sizeof(DevMode);
DevMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_POSITION
| DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS ;
Result = ChangeDisplaySettingsEx((LPSTR)DisplayDevice.DeviceName, &DevMode, NULL, CDS_UPDATEREGISTRY, NULL);
//Result = ChangeDisplaySettingsEx((LPSTR)DisplayDevice.DeviceName, &DevMode, NULL, CDS_UPDATEREGISTRY, NULL);
ChangeDisplaySettingsEx (NULL, NULL, NULL, 0, NULL);
//The code below shows how to re-attach the secondary displays to the desktop
//ChangeDisplaySettingsEx((LPSTR)DisplayDevice.DeviceName, &defaultMode, NULL, CDS_UPDATEREGISTRY, NULL);
//ChangeDisplaySettingsEx((LPSTR)DisplayDevice.DeviceName, &defaultMode, NULL, CDS_UPDATEREGISTRY, NULL);
}
// Reinit DisplayDevice just to be extra clean
ZeroMemory(&DisplayDevice, sizeof(DisplayDevice));
DisplayDevice.cb = sizeof(DisplayDevice);
DispNum++;
} // end while for all display devices
}
int main()
{
DetachDisplay();
return 0;
}
However, when I compile and run it all I get is the screen to flicker as if it is changing resolutions but it doesn't actually do anything meaningful (I do notice the mouse moved...but other than that nothing).
Perhaps someone else has already created a utility to do this exact functionality, which would work just as good if it can be invoked from the command line.
Thoughts?
You can use SetDisplayConfig to do this in windows 7. The example below disables all secondary screens.
UINT32 NumPathArrayElements = 0;
UINT32 NumModeInfoArrayElements = 0;
LONG error = GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS,&NumPathArrayElements,&NumModeInfoArrayElements);
std::vector<DISPLAYCONFIG_PATH_INFO> PathInfoArray(NumPathArrayElements);
std::vector<DISPLAYCONFIG_MODE_INFO> ModeInfoArray(NumModeInfoArrayElements);
error = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS,&NumPathArrayElements, &PathInfoArray[0],&NumModeInfoArrayElements, &ModeInfoArray[0],NULL);
for(unsigned int i=0;i<PathInfoArray.size();++i){
if(PathInfoArray[i].sourceInfo.modeInfoIdx<ModeInfoArray.size()){
int modeIndex=PathInfoArray[i].sourceInfo.modeInfoIdx;
_POINTL pos=ModeInfoArray[modeIndex].sourceMode.position;
if(pos.x!=0 || pos.y!=0){
PathInfoArray[i].flags=0;
break;
}
}
}
error = SetDisplayConfig(NumPathArrayElements, &PathInfoArray[0],NumModeInfoArrayElements, &ModeInfoArray[0],(SDC_APPLY | SDC_ALLOW_CHANGES | SDC_USE_SUPPLIED_DISPLAY_CONFIG));
For a bit more info on the functions used: http://msdn.microsoft.com/en-us/library/ff539596%28v=VS.85%29.aspx
using System;
using System.Runtime.InteropServices;
namespace MyNamespace
{
public class Win32
{
public struct POINTL
{
public Int32 x;
public Int32 y;
}
public struct RECT
{
public long left;
public long top;
public long right;
public long bottom;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DISPLAY_DEVICE
{
[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;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct DEVMODE
{
public const int CCHDEVICENAME = 32;
public const int CCHFORMNAME = 32;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
[System.Runtime.InteropServices.FieldOffset(0)]
public string dmDeviceName;
[System.Runtime.InteropServices.FieldOffset(32)]
public Int16 dmSpecVersion;
[System.Runtime.InteropServices.FieldOffset(34)]
public Int16 dmDriverVersion;
[System.Runtime.InteropServices.FieldOffset(36)]
public Int16 dmSize;
[System.Runtime.InteropServices.FieldOffset(38)]
public Int16 dmDriverExtra;
[System.Runtime.InteropServices.FieldOffset(40)]
public DmFlags dmFields;
[System.Runtime.InteropServices.FieldOffset(44)]
Int16 dmOrientation;
[System.Runtime.InteropServices.FieldOffset(46)]
Int16 dmPaperSize;
[System.Runtime.InteropServices.FieldOffset(48)]
Int16 dmPaperLength;
[System.Runtime.InteropServices.FieldOffset(50)]
Int16 dmPaperWidth;
[System.Runtime.InteropServices.FieldOffset(52)]
Int16 dmScale;
[System.Runtime.InteropServices.FieldOffset(54)]
Int16 dmCopies;
[System.Runtime.InteropServices.FieldOffset(56)]
Int16 dmDefaultSource;
[System.Runtime.InteropServices.FieldOffset(58)]
Int16 dmPrintQuality;
[System.Runtime.InteropServices.FieldOffset(44)]
public POINTL dmPosition;
[System.Runtime.InteropServices.FieldOffset(52)]
public Int32 dmDisplayOrientation;
[System.Runtime.InteropServices.FieldOffset(56)]
public Int32 dmDisplayFixedOutput;
[System.Runtime.InteropServices.FieldOffset(60)]
public short dmColor; // See note below!
[System.Runtime.InteropServices.FieldOffset(62)]
public short dmDuplex; // See note below!
[System.Runtime.InteropServices.FieldOffset(64)]
public short dmYResolution;
[System.Runtime.InteropServices.FieldOffset(66)]
public short dmTTOption;
[System.Runtime.InteropServices.FieldOffset(68)]
public short dmCollate; // See note below!
[System.Runtime.InteropServices.FieldOffset(72)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
public string dmFormName;
[System.Runtime.InteropServices.FieldOffset(102)]
public Int16 dmLogPixels;
[System.Runtime.InteropServices.FieldOffset(104)]
public Int32 dmBitsPerPel;
[System.Runtime.InteropServices.FieldOffset(108)]
public Int32 dmPelsWidth;
[System.Runtime.InteropServices.FieldOffset(112)]
public Int32 dmPelsHeight;
[System.Runtime.InteropServices.FieldOffset(116)]
public Int32 dmDisplayFlags;
[System.Runtime.InteropServices.FieldOffset(116)]
public Int32 dmNup;
[System.Runtime.InteropServices.FieldOffset(120)]
public Int32 dmDisplayFrequency;
}
[Flags()]
public enum DisplayDeviceStateFlags : int
{
/// <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
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[Flags]
public enum ACCESS_MASK : uint
{
DELETE = 0x00010000,
READ_CONTROL = 0x00020000,
WRITE_DAC = 0x00040000,
WRITE_OWNER = 0x00080000,
SYNCHRONIZE = 0x00100000,
STANDARD_RIGHTS_REQUIRED = 0x000F0000,
STANDARD_RIGHTS_READ = 0x00020000,
STANDARD_RIGHTS_WRITE = 0x00020000,
STANDARD_RIGHTS_EXECUTE = 0x00020000,
STANDARD_RIGHTS_ALL = 0x001F0000,
SPECIFIC_RIGHTS_ALL = 0x0000FFFF,
ACCESS_SYSTEM_SECURITY = 0x01000000,
MAXIMUM_ALLOWED = 0x02000000,
GENERIC_READ = 0x80000000,
GENERIC_WRITE = 0x40000000,
GENERIC_EXECUTE = 0x20000000,
GENERIC_ALL = 0x10000000,
DESKTOP_READOBJECTS = 0x00000001,
DESKTOP_CREATEWINDOW = 0x00000002,
DESKTOP_CREATEMENU = 0x00000004,
DESKTOP_HOOKCONTROL = 0x00000008,
DESKTOP_JOURNALRECORD = 0x00000010,
DESKTOP_JOURNALPLAYBACK = 0x00000020,
DESKTOP_ENUMERATE = 0x00000040,
DESKTOP_WRITEOBJECTS = 0x00000080,
DESKTOP_SWITCHDESKTOP = 0x00000100,
WINSTA_ENUMDESKTOPS = 0x00000001,
WINSTA_READATTRIBUTES = 0x00000002,
WINSTA_ACCESSCLIPBOARD = 0x00000004,
WINSTA_CREATEDESKTOP = 0x00000008,
WINSTA_WRITEATTRIBUTES = 0x00000010,
WINSTA_ACCESSGLOBALATOMS = 0x00000020,
WINSTA_EXITWINDOWS = 0x00000040,
WINSTA_ENUMERATE = 0x00000100,
WINSTA_READSCREEN = 0x00000200,
WINSTA_ALL_ACCESS = 0x0000037F
}
[Flags()]
public enum ChangeDisplaySettingsFlags : uint
{
CDS_NONE = 0,
CDS_UPDATEREGISTRY = 0x00000001,
CDS_TEST = 0x00000002,
CDS_FULLSCREEN = 0x00000004,
CDS_GLOBAL = 0x00000008,
CDS_SET_PRIMARY = 0x00000010,
CDS_VIDEOPARAMETERS = 0x00000020,
CDS_ENABLE_UNSAFE_MODES = 0x00000100,
CDS_DISABLE_UNSAFE_MODES = 0x00000200,
CDS_RESET = 0x40000000,
CDS_RESET_EX = 0x20000000,
CDS_NORESET = 0x10000000
}
[Flags()]
public enum DISP_CHANGE : int
{
SUCCESSFUL = 0,
RESTART = 1,
FAILED = -1,
BADMODE = -2,
NOTUPDATED = -3,
BADFLAGS = -4,
BADPARAM = -5,
BADDUALVIEW = -6
}
[Flags()]
public enum DmFlags : int
{
DM_ORIENTATION = 0x00000001,
DM_PAPERSIZE = 0x00000002,
DM_PAPERLENGTH = 0x00000004,
DM_PAPERWIDTH = 0x00000008,
DM_SCALE = 0x00000010,
DM_POSITION = 0x00000020,
DM_NUP = 0x00000040,
DM_DISPLAYORIENTATION = 0x00000080,
DM_COPIES = 0x00000100,
DM_DEFAULTSOURCE = 0x00000200,
DM_PRINTQUALITY = 0x00000400,
DM_COLOR = 0x00000800,
DM_DUPLEX = 0x00001000,
DM_YRESOLUTION = 0x00002000,
DM_TTOPTION = 0x00004000,
DM_COLLATE = 0x00008000,
DM_FORMNAME = 0x00010000,
DM_LOGPIXELS = 0x00020000,
DM_BITSPERPEL = 0x00040000,
DM_PELSWIDTH = 0x00080000,
DM_PELSHEIGHT = 0x00100000,
DM_DISPLAYFLAGS = 0x00200000,
DM_DISPLAYFREQUENCY = 0x00400000,
DM_ICMMETHOD = 0x00800000,
DM_ICMINTENT = 0x01000000,
DM_MEDIATYPE = 0x02000000,
DM_DITHERTYPE = 0x04000000,
DM_PANNINGWIDTH = 0x08000000,
DM_PANNINGHEIGHT = 0x10000000,
DM_DISPLAYFIXEDOUTPUT = 0x20000000
}
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
[DllImport("user32.dll", EntryPoint = "CreateWindowStation", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr CreateWindowStation(
[MarshalAs(UnmanagedType.LPWStr)] string name,
[MarshalAs(UnmanagedType.U4)] uint dwFlags,
[MarshalAs(UnmanagedType.U4)] ACCESS_MASK desiredAccess,
[MarshalAs(UnmanagedType.LPStr)] ref SECURITY_ATTRIBUTES attributes
);
[DllImport("user32.dll", EntryPoint = "CreateWindowStation", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr CreateWindowStation(
[MarshalAs(UnmanagedType.LPWStr)] string name,
[MarshalAs(UnmanagedType.U4)] uint dwFlags,
[MarshalAs(UnmanagedType.U4)] ACCESS_MASK desiredAccess,
[MarshalAs(UnmanagedType.U4)] uint attributes
);
[DllImport("user32.dll", EntryPoint = "CreateDesktop", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr CreateDesktop(
[MarshalAs(UnmanagedType.LPWStr)] string desktopName,
[MarshalAs(UnmanagedType.LPWStr)] string device, // must be null.
[MarshalAs(UnmanagedType.LPWStr)] string deviceMode, // must be null,
[MarshalAs(UnmanagedType.U4)] int flags, // use 0
[MarshalAs(UnmanagedType.U4)] ACCESS_MASK accessMask,
[MarshalAs(UnmanagedType.LPStruct)] ref SECURITY_ATTRIBUTES attributes
);
[DllImport("user32.dll", EntryPoint = "CreateDesktop", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr CreateDesktop(
[MarshalAs(UnmanagedType.LPWStr)] string desktopName,
[MarshalAs(UnmanagedType.LPWStr)] string device, // must be null.
[MarshalAs(UnmanagedType.LPWStr)] string deviceMode, // must be null,
[MarshalAs(UnmanagedType.U4)] int flags, // use 0
[MarshalAs(UnmanagedType.U4)] ACCESS_MASK accessMask,
[MarshalAs(UnmanagedType.U4)] uint attributes
);
[DllImport("user32.dll", EntryPoint = "CloseDesktop", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseDesktop(IntPtr handle);
[DllImport("user32.dll")]
public static extern IntPtr OpenWindowStation(
[MarshalAs(UnmanagedType.LPWStr)]string name,
[MarshalAs(UnmanagedType.Bool)] bool fInherit,
[MarshalAs(UnmanagedType.U4)] uint desiredAccess
);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetProcessWindowStation(IntPtr hWinSta);
[DllImport("user32.dll")]
public static extern IntPtr GetProcessWindowStation();
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseWindowStation(IntPtr hWinSta);
[DllImport("user32.dll")]
public static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags);
[DllImport("user32.dll")]
public static extern int EnumDisplaySettingsEx(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode, uint dwFlags);
[DllImport("user32.dll")]
public static extern int ChangeDisplaySettingsEx(
string lpszDeviceName,
ref DEVMODE lpDevMode,
IntPtr hwnd,
ChangeDisplaySettingsFlags dwflags,
IntPtr lParam);
[DllImport("user32.dll")]
public static extern int ChangeDisplaySettingsEx(
string lpszDeviceName,
IntPtr lpDevMode,
IntPtr hwnd,
ChangeDisplaySettingsFlags dwflags,
IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
public static extern int DrawText(IntPtr hDC, string lpString, int nCount, ref RECT lpRect, uint uFormat);
[DllImport("user32.dll")]
static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
public const int ENUM_CURRENT_SETTINGS = -1;
public const int ENUM_REGISTRY_SETTINGS = -2;
public const int CWF_CREATE_ONLY = 1;
public const int DF_ALLOWOTHERACCOUNTHOOK = 1;
public const int DT_TOP = 0x00000000;
public const int DT_LEFT = 0x00000000;
public const int DT_CENTER = 0x00000001;
public const int DT_RIGHT = 0x00000002;
public const int DT_VCENTER = 0x00000004;
public const int DT_BOTTOM = 0x00000008;
public const int DT_WORDBREAK = 0x00000010;
public const int DT_SINGLELINE = 0x00000020;
public const int DT_EXPANDTABS = 0x00000040;
public const int DT_TABSTOP = 0x00000080;
public const int DT_NOCLIP = 0x00000100;
public const int DT_EXTERNALLEADING = 0x00000200;
public const int DT_CALCRECT = 0x00000400;
public const int DT_NOPREFIX = 0x00000800;
public const int DT_INTERNAL = 0x00001000;
}
public class Gdi32
{
[Flags()]
public enum DeviceCap : int
{
DRIVERVERSION = 0,
TECHNOLOGY = 2,
HORZSIZE = 4,
VERTSIZE = 6,
HORZRES = 8,
VERTRES = 10,
BITSPIXEL = 12,
PLANES = 14,
NUMBRUSHES = 16,
NUMPENS = 18,
NUMMARKERS = 20,
NUMFONTS = 22,
NUMCOLORS = 24,
PDEVICESIZE = 26,
CURVECAPS = 28,
LINECAPS = 30,
POLYGONALCAPS = 32,
TEXTCAPS = 34,
CLIPCAPS = 36,
RASTERCAPS = 38,
ASPECTX = 40,
ASPECTY = 42,
ASPECTXY = 44,
SHADEBLENDCAPS = 45,
LOGPIXELSX = 88,
LOGPIXELSY = 90,
SIZEPALETTE = 104,
NUMRESERVED = 106,
COLORRES = 108,
PHYSICALWIDTH = 110,
PHYSICALHEIGHT = 111,
PHYSICALOFFSETX = 112,
PHYSICALOFFSETY = 113,
SCALINGFACTORX = 114,
SCALINGFACTORY = 115,
VREFRESH = 116,
DESKTOPVERTRES = 117,
DESKTOPHORZRES = 118,
BLTALIGNMENT = 119
}
[DllImport("gdi32.dll")]
public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData);
[DllImport("gdi32.dll", EntryPoint = "DeleteDC")]
public static extern bool DeleteDC([In] IntPtr hdc);
[DllImport("gdi32.dll")]
public static extern int SetTextColor(IntPtr hdc, int crColor);
[DllImport("gdi32.dll")]
public static extern int SetBkColor(IntPtr hdc, int crColor);
}
public class Display
{
public static void EnumDisplayDevices()
{
uint deviceID = 0;
Win32.DISPLAY_DEVICE d = new Win32.DISPLAY_DEVICE();
d.cb = Marshal.SizeOf(d);
Win32.DEVMODE dm = GetDevMode();
while (Win32.EnumDisplayDevices(null, deviceID, ref d, 1))
{
// Print Device Information
Console.WriteLine("\nDeviceID: {5} \nDeviceName: {0} \nDeviceString: {1}\nDeviceID (GUID): {2}\nDeviceKey {3}\nStateFlags {4}\n",
d.DeviceName, d.DeviceString, d.DeviceID, d.DeviceKey, d.StateFlags, deviceID);
deviceID++;
}
}
private static Win32.DEVMODE GetDevMode()
{
Win32.DEVMODE dm = new Win32.DEVMODE();
dm.dmDeviceName = new String(new char[32]);
dm.dmFormName = new String(new char[32]);
dm.dmSize = (short)Marshal.SizeOf(dm);
return dm;
}
public static bool DetachDisplayDevice(uint deviceID)
{
bool rval = false;
Win32.DEVMODE dm = GetDevMode();
Win32.DISPLAY_DEVICE d = new Win32.DISPLAY_DEVICE();
d.cb = Marshal.SizeOf(d);
// Get the display device
if (!Win32.EnumDisplayDevices(null, deviceID, ref d, 0))
{
Console.WriteLine("Device not found!");
return false;
}
// Test that the display is actually attached to the desktop - bail if it is not
if ((d.StateFlags & Win32.DisplayDeviceStateFlags.AttachedToDesktop) == 0)
{
Console.WriteLine("Display Device {0} is not attached to this desktop!", d.DeviceName);
return false;
}
// Get current device settings
if (0 == Win32.EnumDisplaySettingsEx(d.DeviceName, Win32.ENUM_CURRENT_SETTINGS, ref dm, 0))
{
Console.WriteLine("Settings for {0} could not be enumerated!", d.DeviceName);
return false;
}
// Prepare for detach
dm.dmPelsWidth = 0;
dm.dmPelsHeight = 0;
dm.dmFields = Win32.DmFlags.DM_POSITION | Win32.DmFlags.DM_PELSWIDTH | Win32.DmFlags.DM_PELSHEIGHT;
//dm.dmFields = (int) (DmFlags.DM_POSITION);
// Test the change
int iRet = Win32.ChangeDisplaySettingsEx(d.DeviceName, ref dm, IntPtr.Zero, Win32.ChangeDisplaySettingsFlags.CDS_TEST, IntPtr.Zero);
if (iRet == (int)Win32.DISP_CHANGE.FAILED)
{
Console.WriteLine("Unable To Process Your Request.");
return false;
}
// Now do it for real
iRet = Win32.ChangeDisplaySettingsEx(
d.DeviceName,
ref dm,
IntPtr.Zero,
Win32.ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY | Win32.ChangeDisplaySettingsFlags.CDS_RESET,
IntPtr.Zero
);
//Win32.ChangeDisplaySettingsEx((string) null, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero); // Not needed for detach with CDS_RESET set.
switch (iRet)
{
case (int)Win32.DISP_CHANGE.SUCCESSFUL:
{
Console.WriteLine("Detached display: {0} \n", d.DeviceName);
rval = true;
break;
}
case (int)Win32.DISP_CHANGE.RESTART:
{
Console.WriteLine("A reboot is required for the change to take affect.\n");
break;
}
default:
{
Console.WriteLine("Failed! Return value: {0}\n", iRet);
break;
}
}
return rval;
}
public static bool AttachDisplayDevice(uint deviceID)
{
bool rval = false;
Win32.DEVMODE dm = GetDevMode();
Win32.DISPLAY_DEVICE d = new Win32.DISPLAY_DEVICE();
d.cb = Marshal.SizeOf(d);
int nWidth;
IntPtr hdc;
// Get current device context width
hdc = Win32.GetDC(IntPtr.Zero);
nWidth = Gdi32.GetDeviceCaps(hdc, (int)Gdi32.DeviceCap.HORZRES);
Win32.ReleaseDC(IntPtr.Zero, hdc);
// Get the display device
if (!Win32.EnumDisplayDevices(null, deviceID, ref d, 0))
{
Console.WriteLine("Device not found!");
return false;
}
// Test that the display is NOT actually attached to the desktop - bail if it is
if ((d.StateFlags & Win32.DisplayDeviceStateFlags.AttachedToDesktop) != 0)
{
Console.WriteLine("Display Device {0} is already attached to this desktop!", d.DeviceName);
return false;
}
// Get current device settings
if (0 == Win32.EnumDisplaySettingsEx(d.DeviceName, Win32.ENUM_REGISTRY_SETTINGS, ref dm, 0))
{
Console.WriteLine("Settings for {0} could not be enumerated!", d.DeviceName);
return false;
}
// Prepare for attach
dm.dmPosition.x += nWidth;
dm.dmFields = Win32.DmFlags.DM_POSITION;
// Test the change
int iRet = Win32.ChangeDisplaySettingsEx(d.DeviceName, ref dm, IntPtr.Zero, Win32.ChangeDisplaySettingsFlags.CDS_TEST, IntPtr.Zero);
if (iRet == (int)Win32.DISP_CHANGE.FAILED)
{
Console.WriteLine("Unable To Process Your Request.");
return false;
}
// Now do it for real
iRet = Win32.ChangeDisplaySettingsEx(
d.DeviceName,
ref dm,
IntPtr.Zero,
Win32.ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY | Win32.ChangeDisplaySettingsFlags.CDS_NORESET,
IntPtr.Zero
);
Win32.ChangeDisplaySettingsEx((string)null, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero);
switch (iRet)
{
case (int)Win32.DISP_CHANGE.SUCCESSFUL:
{
Console.WriteLine("Attached display: {0} \n", d.DeviceName);
rval = true;
break;
}
case (int)Win32.DISP_CHANGE.RESTART:
{
Console.WriteLine("A reboot is required for the change to take affect.\n");
break;
}
default:
{
Console.WriteLine("Failed! Return value: {0}\n", iRet);
break;
}
}
return rval;
}
}
}
I know this is a way old thread but I came upon it while researching my own problem: Looking for a method for displaying a non-interactive desktop on a detached secondary display device while the console session (and therefore WinSta0) is locked on Windows 7.
Answer to original question:
When detaching a display device:
DEVMODE.dmPelsWidth = 0;
DEVMODE.dmPelsHeight = 0;
DEVMODE.dmFields = DM_POSITION | DM_PELSWIDTH | DM_PELSHEIGHT;
ChangeDisplaySettingsEx(....
If you are detaching a display, the second call to ChangeDisplaySettings(Ex) is unnecessary if you specify CDS_RESET along with CDS_UPDATEREGISTRY in the original call. However, if you are attaching a display, the second call to ChangeDisplaySettings does appear to be required (or, at least, I haven't figured out a way around it).
I provide working C# code below. Here is a PowerShell script to use with it
param(
[Parameter(Mandatory=$true, Position = 0)] [string]$Function,
[Parameter(Mandatory=$false, Position = 1)] [int]$DeviceID
)
clear-host
add-type -TypeDefinition (Get-Content -Path .\Display.cs | Out-String)
Switch ($Function) {
'Enum' {[MyNamespace.Display]::EnumDisplayDevices()}
'Detach' {[MyNamespace.Display]::DetachDisplayDevice($DeviceID)}
'Attach' {[MyNamespace.Display]::AttachDisplayDevice($DeviceID)}
Default { write-host 'There is no "' $Function '" function available!' }
}
The code snippet above actually detaches secondary display device, not monitor. The same display device may contain multiple monitors. I haven't succeeded with this problem yet

How to get full path from SHBrowseForFolder function?

I'm using SHBrowseForFolder and SHGetPathFromIDList functions to get the selected folder path by user. However this method does not return the drive path of the full path. How to additionally get that information too?
Taken from this newsgroup post:
You can use SHBrowseForFolder(...), it takes BROWSEINFO as parameter;
TCHAR szDir[MAX_PATH];
BROWSEINFO bInfo;
bInfo.hwndOwner = Owner window
bInfo.pidlRoot = NULL;
bInfo.pszDisplayName = szDir; // Address of a buffer to receive the display name of the folder selected by the user
bInfo.lpszTitle = "Please, select a folder"; // Title of the dialog
bInfo.ulFlags = 0 ;
bInfo.lpfn = NULL;
bInfo.lParam = 0;
bInfo.iImage = -1;
LPITEMIDLIST lpItem = SHBrowseForFolder( &bInfo);
if( lpItem != NULL )
{
SHGetPathFromIDList(lpItem, szDir );
//......
}
SHBrowseForFolder returns the folder's PIDL and its display name, to get the full path from PIDL, call SHGetPathFromIDList
EDIT: The OP seems to be having trouble getting it to work, so here is some working C# code (you should be able to translate it to whatever language, the APIs are the same):
class SHGetPath
{
[DllImport("shell32.dll")]
static extern IntPtr SHBrowseForFolder(ref BROWSEINFO lpbi);
[DllImport("shell32.dll")]
public static extern Int32 SHGetPathFromIDList(
IntPtr pidl, StringBuilder pszPath);
public delegate int BrowseCallBackProc(IntPtr hwnd, int msg, IntPtr lp, IntPtr wp);
struct BROWSEINFO
{
public IntPtr hwndOwner;
public IntPtr pidlRoot;
public string pszDisplayName;
public string lpszTitle;
public uint ulFlags;
public BrowseCallBackProc lpfn;
public IntPtr lParam;
public int iImage;
}
public SHGetPath()
{
Console.WriteLine(SelectFolder("Hello World", "C:\\"));
}
public string SelectFolder(string caption, string initialPath)
{
StringBuilder sb = new StringBuilder(256);
IntPtr pidl = IntPtr.Zero;
BROWSEINFO bi;
bi.hwndOwner = Process.GetCurrentProcess().MainWindowHandle; ;
bi.pidlRoot = IntPtr.Zero;
bi.pszDisplayName = initialPath;
bi.lpszTitle = caption;
bi.ulFlags = 0; // BIF_NEWDIALOGSTYLE | BIF_SHAREABLE;
bi.lpfn = null; // new BrowseCallBackProc(OnBrowseEvent);
bi.lParam = IntPtr.Zero;
bi.iImage = 0;
try
{
pidl = SHBrowseForFolder(ref bi);
if (0 == SHGetPathFromIDList(pidl, sb))
{
return null;
}
}
finally
{
// Caller is responsible for freeing this memory.
Marshal.FreeCoTaskMem(pidl);
}
return sb.ToString();
}
}