Add local Printer port with VBS - vbscript

first of all i'm sorry for my english.
I've one question about windows WMI and how to add a local port to shared printer. I've this script:
Set objWMIService = GetObject("winmgmts:")
Set objNewPort = objWMIService.Get _
("Win32_TCPIPPrinterPort").SpawnInstance_
objNewPort.Name = "Ricoh3300C"
objNewPort.Protocol = 2
objNewPort.HostAddress = "XXX.XXX.X.XXX"
objNewPort.PortNumber = "9100"
objNewPort.SNMPEnabled = False
objNewPort.Put_
With this i can add a printer with IP address but i want to add a printer in samba server with an address like "\\XXX.XXX.X.XXX\printerColor". I've lost a lot of time in google trying to find an script and all that i've seen is for TCPIP ports. I wan't to do it but in local port.
I've tried to use this script with prnadmin.dll and no luck.
function PortAdd(strPort, portType)
on error resume next
dim oMaster
dim oPort
dim iResult
set oMaster = CreateObject("PrintMaster.PrintMaster.1")
set oPort = CreateObject("Port.Port.1")
iResult = kErrorFailure
oPort.PortName = strPort
oPort.PortType = portType
oMaster.PortAdd oPort
if Err = 0 then
iResult = kErrorSuccess
else
wscript.echo "Error: 0x" & Hex(Err.Number) & ". " & Err.Description
end if
PortAdd = iResult
end function
I get this error:
Error: 0x1A8. Se requiere un objeto
in english is like
Error: 0x1A8. An object is required
How can i fix that error or what script can i use to add a local port?. Thanks in advance.
I forgot to say that i want to do it with normal user without admin access. The first script works fine in that users but is for TCPIP.

Consider using XcvData, e.g.
private static void AddPort(string portName)
{
var def = new PRINTER_DEFAULTS();
def.pDatatype = null;
def.pDevMode = IntPtr.Zero;
def.DesiredAccess = 1; //Server Access Administrator
IntPtr hPrinter = IntPtr.Zero;
int n = OpenPrinter(",XcvMonitor Local Port", ref hPrinter, def);
if (n == 0)
throw new Exception("Local Port monitor has not been opened.");
if (!portName.EndsWith("\0"))
portName += "\0";
// .NET strings are formed by 2-byte characters
var size = (uint) (portName.Length*2);
IntPtr portPtr = Marshal.AllocHGlobal((int) size);
Marshal.Copy(portName.ToCharArray(), 0, portPtr, portName.Length);
uint needed, xcvResult;
XcvData(hPrinter, "AddPort", portPtr, size, IntPtr.Zero, 0, out needed, out xcvResult);
ClosePrinter(hPrinter);
Marshal.FreeHGlobal(portPtr);
}
[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);

Related

Windows API C++ | Problem with GetOpenFileName / OPENFILENAME

I'm new to C++
I need to return the path to the file that the user has selected. For this i use winapi -> OPENFILENAME. When i try to return file path, i get only 1 character ("C" for disk C).
My code:
LPWSTR fileBuffer = new wchar_t[256];
OPENFILENAME ofn = { 0 };
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwnd;
ofn.lpstrFile = fileBuffer;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = 256;
ofn.lpstrFilter = NULL;
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if (GetOpenFileName(&ofn) == TRUE)
{
printf("File name: %s\n", ofn.lpstrFile);
}
i tried to increase the buffer size, return the value of ofn.lpstrFile. The first does not change anything, the second returns a int value of disk letter
You are using wchar_t (needed by OPENFILENAME) which is not the same as char (as expected with printf()) if you are using wide APIs on Windows.
Use wprintf(), or you can use %S (capital) to tell printf() to print wide characters.

Error 65535 from CommDlgExtendedError after calling GetFileSaveName

So, I'm calling GetFileSaveName from some C code. The C code is called from PowerBuilder, but I really don't think that is relevant.
On just one user's computer currently (Windows 10, updates done), it returns 0, then CommDlgExtendedError return 65535 (CDERR_DIALOGFAILURE), which the docs say means "The dialog box could not be created. The common dialog box function's call to the DialogBox function failed. For example, this error occurs if the common dialog box call specifies an invalid window handle."
I know that the common dialogs aren't completely broken on her PC - I can pull up a File Open or Save dialog in Notepad, for instance.
I had a bug in my code giving the same error number in the past, where in the OPENFILENAME structure I passed in to GetOpenFileName I had set the hWndOwner variable to a handle to a window that no longer exists, but in this case it is being set to 0, so that's not the problem.
The same code is working for thousands of other users of our software! Any bright ideas what could be going on? Thanks.
OK, people have asked me to post my code initializing the OpenFileName structure. It's PowerScript (PowerBuilder's coding language) but should be pretty comprehensible).
OPENFILENAME iOFN
CONSTANT long CHARSIZE = 2
aul_flags = OFN_HIDEREADONLY + OFN_EXPLORER +
OFN_FILEMUSTEXIST+ OFN_NOCHANGEDIR + OFN_DONTADDTORECENT
RtlZeroMemory(iOFN, ll_sizeof)
// initialize structure
iOFN.lStructSize = ll_sizeof
iOFN.nFilterIndex = 1
iOFN.nMaxFile = MAX_LENGTH
iOFN.hWndOwner = il_hWnd
iOFN.Flags = aul_flags + OFN_ENABLESIZING /* needed when using hook procedure */
// allocate memory and copy title
ll_length = Len(as_title) * CHARSIZE
iOFN.lpstrTitle = LocalAlloc(LMEM_ZEROINIT, ll_length + 2)
RtlMoveMemory(iOFN.lpstrTitle, as_title, ll_length)
// allocate memory and copy filter
this.of_Parse(",", as_filter, ls_filter)
li_max = UpperBound(ls_filter) /* count of 1-based array elements */
For li_cnt = 1 To li_max
ll_length = this.of_StringToChar(Trim(ls_filter[li_cnt]), lc_filter)
Next
ll_length = UpperBound(lc_filter) * CHARSIZE
iOFN.lpstrFilter = LocalAlloc(LMEM_ZEROINIT, ll_length)
RtlMoveMemory(iOFN.lpstrFilter, lc_filter, ll_length)
// allocate memory and copy default extension (if given)
If as_extension <> "" Then
ll_length = Len(as_extension) * CHARSIZE
iOFN.lpstrDefExt = LocalAlloc(LMEM_ZEROINIT, ll_length)
RtlMoveMemory(iOFN.lpstrDefExt, as_extension, ll_length)
End If
// allocate memory and copy initialdir (if given)
If as_initdir <> "" Then
ll_length = Len(as_initdir) * CHARSIZE
iOFN.lpstrInitialDir = LocalAlloc(LMEM_ZEROINIT, ll_length)
RtlMoveMemory(iOFN.lpstrInitialDir, as_initdir, ll_length)
End If
// allocate memory for returned data
lc_pathname = Space(MAX_LENGTH)
iOFN.nMaxFile = MAX_LENGTH
iOFN.lpstrFile = LocalAlloc(LMEM_ZEROINIT, MAX_LENGTH)
If as_initialfile <> "" Then
ll_length = Len(as_initialfile) * CHARSIZE
RtlMoveMemory(iOFN.lpstrFile, as_initialfile, ll_length)
End If
// display dialog box
lb_return = GetOpenFileName(iOFN)
I recommend you to check if there is any problem with the OPENFILENAME parameter configuration. There are many parameters in OPENFILENAME that need to be configured before GetSaveFileName can be used. Maybe there is something wrong.
TCHAR szFilename[MAX_PATH] = TEXT("");
BOOL bResult = FALSE;
DWORD dwError = NOERROR;
OPENFILENAME ofn = { 0 };
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.lpstrFilter = TEXT("All Files\0*.*\0\0");
ofn.lpstrFile = szFilename;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER |
OFN_ENABLEHOOK |
OFN_HIDEREADONLY |
OFN_NOCHANGEDIR |
OFN_PATHMUSTEXIST;
bResult = GetSaveFileName(&ofn);
if (bResult == FALSE)
{
dwError = CommDlgExtendedError();
return dwError;
}

Open Internet Explorer using Visual Basic and re size the ie window

Hey I have been stuck on this bug for a long time I hope someone can help,
so in my .NET program when you press a button a timer starts which opens up multiple Internet Explorer windows but the problem is that I want each window opened to be a different size which can be done by adding randomness to the size. But I am not sure how to do that.
PLEASE HELP!!!
this what i have so far
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Process.Start("C:\Program Files (x86)\Internet Explorer\iexplore.exe", "www.google.com")
End Sub
Your sample code Timer1_Tick is in .Net
But, if you are looking for VBA solution , try something like this
Sub IE()
Dim oIE As Object
Set oIE = CreateObject("InternetExplorer.Application")
oIE.navigate2 "www.google.com"
oIE.Height = CInt(Int((1000 * Rnd()) + 1))
oIE.Width = CInt(Int((1000 * Rnd()) + 1))
oIE.Visible = True
End Sub
This link might help : http://msdn.microsoft.com/en-us/library/aa752084(v=vs.85).aspx
This can be achieved by using interop and USER32.dll
Let me give u an example in c#.
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
public IntPtr win_handle;
void somefunction(){
string ie_win_class = "IEFrame";
string ie_win_nm = "New Tab - Windows Internet Explorer";
win_handle = FindWindow(win_class, win_name);
MoveWindow(win_handle, 600, 600, 600, 600, True);
}
sorry, i don't have experience with VB. I hope this helped you.
links-
MoveWindow function
COM Interop (Visual Basic)

how can I find out which user send the print command to windows/windows server programmatically?

I have a windows network (peer-2-peer) as well as Active Directory and I need to log the name of users who send any kind of print to the server.
I want to write a program to log their username and/or their respective IP and I'm familiar with c#.net and c++ but I haven't found any clue regarding how to solve my problem.
is there any sorts of way to catch their name by the help of WMI or should dirty my hand with APIs(but which API I don't have any idea)?
regards.
Those features are exposed under the Spooler API.
EnumJobs will enumerate all the current jobs for a given printer. It will return a JOB_INFO_1 struct, which includes the username associated with a given print job:
typedef struct _JOB_INFO_1 {
DWORD JobId;
LPTSTR pPrinterName;
LPTSTR pMachineName;
LPTSTR pUserName;
LPTSTR pDocument;
LPTSTR pDatatype;
LPTSTR pStatus;
DWORD Status;
DWORD Priority;
DWORD Position;
DWORD TotalPages;
DWORD PagesPrinted;
SYSTEMTIME Submitted;
}JOB_INFO_1, *PJOB_INFO_1;
If you'd prefer WMI, you can use wmic.exe with the /node switch (or your preferred variation) and the Win32_PrintJob class. Roughly:
c:\> wmic /node 10.0.0.1
wmic> SELECT * FROM Win32_PrintJob
...will return a struct with all the print job information for the selected server. You can filter as you wish with the WHERE clause.
I would go with using WMI. That gives you the ability to query printer batches of printers associated with your system as well as pull all supporting properties. It's as simple as...
System.Management.ObjectQuery oq = new System.Management.ObjectQuery("SELECT * FROM Win32_PrintJob");
...creating an WMI object searcher and enumerating through the results.
Here's an example:
WMI query printers
I've used this in the past and if it doesn't have all of what you need, it should at least take care of monitoring the print queues.
http://www.merrioncomputing.com
http://www.merrioncomputing.com/Download/PrintQueueWatch/PrinterQueueWatchLicensing.htm
Source code link (from the OP's comment):
http://www.codeproject.com/KB/printing/printwatchvbnet.aspx
Find out which user has sent print job using C++ in Windows.
#include <WinSpool.h>
wstring GetUserNameFromPrintJob(wstring m_strFriendlyName)
{
wstring strDocName = L"";
wstring strMachineName = L"";
wstring strUserName = L"";
HANDLE hPrinter ;
if ( OpenPrinter(const_cast<LPWSTR>(m_strFriendlyName.c_str()), &hPrinter, NULL) == 0 )
{
/*OpenPrinter call failed*/
}
DWORD dwBufsize = 0;
PRINTER_INFO_2* pinfo = 0;
GetPrinter(hPrinter, 2,(LPBYTE)pinfo, dwBufsize, &dwBufsize); //Get dwBufsize
PRINTER_INFO_2* pinfo2 = (PRINTER_INFO_2*)malloc(dwBufsize); //Allocate with dwBufsize
GetPrinter(hPrinter, 2,(LPBYTE)pinfo2, dwBufsize, &dwBufsize);
DWORD numJobs = pinfo2->cJobs;
free(pinfo2);
JOB_INFO_1 *pJobInfo = 0;
DWORD bytesNeeded = 0, jobsReturned = 0;
//Get info about jobs in queue.
EnumJobs(hPrinter, 0, numJobs, 1, (LPBYTE)pJobInfo, 0,&bytesNeeded,&jobsReturned);
pJobInfo = (JOB_INFO_1*) malloc(bytesNeeded);
EnumJobs(hPrinter, 0, numJobs, 1, (LPBYTE)pJobInfo, bytesNeeded, &bytesNeeded, &jobsReturned);
JOB_INFO_1 *pJobInfoInitial = pJobInfo;
for(unsigned short count = 0; count < jobsReturned; count++)
{
if (pJobInfo != NULL)
{
strUserName = pJobInfo->pUserName //username
strMachineName = pJobInfo->pMachineName; //machine name
strDocName = pJobInfo->pDocument; // Document name
DWORD dw = pJobInfo->Status;
}
pJobInfo++;
}
free(pJobInfoInitial);
ClosePrinter( hPrinter );
return strUserName ;
}

"Extend my Windows desktop onto this monitor" programmatically

I would like to be able to set "Extend my Windows desktop onto this monitor" via code. A PowerShell script would be ideal. WMI seems the way forward but I have zero knowledge in WMI.
Windows 7, 8 and 10 are supposed to come with a small program that does exactly this: displayswitch.exe. This page lists the following parameters:
displayswitch.exe /internal Disconnect projector (same as "Show only on 1" from the Display Properties dialog)
displayswitch.exe /clone Duplicate screen
displayswitch.exe /extend Extend screen
displayswitch.exe /external Projector only (disconnect local) (same as "Show only on 2" from the Display Properties dialog)
For a one-click solution to the problem posed, simply create a *.bat-file containing the single line
call displayswitch.exe /extend
and save it to your desktop.
[I tested this on Windows 8.1, and it has been confirmed to work on Windows 10.]
I've made a cleaner version that does not use sendkeys.
public class DisplayHelper
{
[DllImport("user32.dll")]
static extern DISP_CHANGE ChangeDisplaySettings(uint lpDevMode, uint dwflags);
[DllImport("user32.dll")]
static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags);
enum DISP_CHANGE : int
{
Successful = 0,
Restart = 1,
Failed = -1,
BadMode = -2,
NotUpdated = -3,
BadFlags = -4,
BadParam = -5,
BadDualView = -1
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
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;
}
[Flags()]
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 = 0x16,
/// <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
}
public static void EnableSecondaryDisplay()
{
var secondaryIndex = 1;
var secondary = GetDisplayDevice(secondaryIndex);
var id = secondary.DeviceKey.Split('\\')[7];
using (var key = Registry.CurrentConfig.OpenSubKey(string.Format(#"System\CurrentControlSet\Control\VIDEO\{0}", id), true))
{
using (var subkey = key.CreateSubKey("000" + secondaryIndex))
{
subkey.SetValue("Attach.ToDesktop", 1, RegistryValueKind.DWord);
subkey.SetValue("Attach.RelativeX", 1024, RegistryValueKind.DWord);
subkey.SetValue("DefaultSettings.XResolution", 1024, RegistryValueKind.DWord);
subkey.SetValue("DefaultSettings.YResolution", 768, RegistryValueKind.DWord);
subkey.SetValue("DefaultSettings.BitsPerPel", 32, RegistryValueKind.DWord);
}
}
ChangeDisplaySettings(0, 0);
}
private static DISPLAY_DEVICE GetDisplayDevice(int id)
{
var d = new DISPLAY_DEVICE();
d.cb = Marshal.SizeOf(d);
if (!EnumDisplayDevices(null, (uint)id, ref d, 0))
throw new NotSupportedException("Could not find a monitor with id " + id);
return d;
}
}
I have only tested this on a newly installed computer.
This sort of operation is not directly accessible from PowerShell in the sense that there is not a .NET interface to these settings. A lot of core OS stuff is unmanaged code which can only be manipulated via win32 API calls. While you may be on to something with WMI, I searched for a while and wasn't able to find a satisfactory WMI class which is able to manipulate this setting.
The next step would be to modify the registry directly. It looks like the setting lies under HKLM:\system\CurrentControlSet\control\video--somewhere. I believe it's the one called "Attach.ToDesktop".
This is a partial solution, so I'm marking as community wiki answer.
I'm not certain this is the right registry key, and I don't have a system on which I can test multi-monitor at the moment. The purpose of this is to determine which is the primary controller, and then it outputs the value of the Attach.ToDesktop key.
param (
$ControllerName = "$( throw 'ControllerName is a mandatory parameter' )"
)
$regPath = "HKLM:\system\CurrentControlSet\control\video"
$devDescStr = "Device Description"
Set-Location -path $regPath
$regSubKey = Get-ChildItem -recurse -include 0000
$devDescProperty = $regSubKey | Get-ItemProperty -name $devDescStr -erroraction SilentlyContinue
$priDescProperty = $devDescProperty | Where-Object { $_.$devDescStr -match $ControllerName }
Set-Location -path $priDescProperty.PSPath
Get-ItemProperty -path . -name "Attach.ToDesktop"
One first possible solution is... through the GUI (but without user interaction)
VB script (also described here but in Autoit language):
Option Explicit
Dim WshShell, Dummy, Splash
On Error Resume Next
Set WshShell = WScript.CreateObject("WScript.Shell")
'Main
Call DoIt
WScript.Quit
Sub DoIt
wshshell.Run("%systemroot%\system32\control.exe desk.cpl,#0,3")
' Give Display Properties time to load
WScript.Sleep 1000
WshShell.SendKeys "2"
WScript.Sleep 10
WshShell.SendKeys "%E"
WScript.Sleep 500
WshShell.SendKeys "%A"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{ENTER}"
End Sub 'DoIt
In Autoit, that would be:
;
; — toggle-screen.au3
;
; exec cpanel app `display settings`
Run(”C:\WINDOWS\system32\control.exe desk.cpl,#0,3?”)
; wait for window to be active
WinWaitActive(”Display Settings”)
; select 2nd display
Send(”{TAB}”)
Send(”{DOWN}”)
; work back to the ‘extend desktop’ control
Send(”+{TAB}”)
Send(”+{TAB}”)
Send(”+{TAB}”)
Send(”+{TAB}”)
Send(”+{TAB}”)
Send(”+{TAB}”)
Send(”+{TAB}”)
Send(”+{TAB}”)
Send(”+{TAB}”)
; toggle ‘extend desktop’ control and apply
Send(”{SPACE}”)
Send(”{ENTER}”)
; wait for window to be active
WinWaitActive(”Display Settings”)
; accept
Send(”{TAB}”)
Send(”{ENTER}”)
;
; — E.O.F.
;
2 lines in autohotkey
2nd display on:
RunWait C:\Windows\System32\DisplaySwitch.exe /extend
2nd display off:
RunWait C:\Windows\System32\DisplaySwitch.exe /internal
-
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
#Persistent
Any1stKeyUWantToTurnOn::RunWait C:\Windows\System32\DisplaySwitch.exe /extend
Any2stKeyUWantToTurnOff::RunWait C:\Windows\System32\DisplaySwitch.exe /internal
or
You can check and try out my tool on github / BNK3R-Boy / DisplaySwitch. I published it right now.
Here's another solution, in C# (via how to set primary monitor for Windows-7, in C#):
[Flags]
public enum SetDisplayConfigFlags : uint
{
SDC_TOPOLOGY_INTERNAL = 0x00000001,
SDC_TOPOLOGY_CLONE = 0x00000002,
SDC_TOPOLOGY_EXTEND = 0x00000004,
SDC_TOPOLOGY_EXTERNAL = 0x00000008,
SDC_APPLY = 0x00000080
}
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern long SetDisplayConfig(uint numPathArrayElements,
IntPtr pathArray, uint numModeArrayElements, IntPtr modeArray, SetDisplayConfigFlags flags);
static void CloneDisplays() {
SetDisplayConfig(0, IntPtr.Zero, 0, IntPtr.Zero, SetDisplayConfigFlags.SDC_TOPOLOGY_CLONE | SetDisplayConfigFlags.SDC_APPLY);
}
static void ExtendDisplays() {
SetDisplayConfig(0, IntPtr.Zero, 0, IntPtr.Zero, SetDisplayConfigFlags.SDC_TOPOLOGY_EXTEND | SetDisplayConfigFlags.SDC_APPLY);
}
static void ExternalDisplay() {
SetDisplayConfig(0, IntPtr.Zero, 0, IntPtr.Zero, SetDisplayConfigFlags.SDC_TOPOLOGY_EXTERNAL | SetDisplayConfigFlags.SDC_APPLY);
}
static void InternalDisplay() {
SetDisplayConfig(0, IntPtr.Zero, 0, IntPtr.Zero, SetDisplayConfigFlags.SDC_TOPOLOGY_INTERNAL | SetDisplayConfigFlags.SDC_APPLY);
}
Here is my AutoIt-Script for switching monitors as my ATI graphics card doesn't allow me to have 3 monitors active at the same time. I have 2 monitors attached and a TV. This script is doing what VonC's script does but in a more effective and faster way.
Run("C:\WINDOWS\system32\control.exe desk.cpl", "C:\Windows\system32\")
WinWait("Screen Resolution")
ControlCommand("Screen Resolution", "", "ComboBox1", "SetCurrentSelection", "SAMSUNG")
if (ControlCommand("Screen Resolution", "", "ComboBox3", "GetCurrentSelection", "") = "Disconnect this display") Then
ControlCommand("Screen Resolution", "", "ComboBox1", "SetCurrentSelection", "2")
ControlCommand("Screen Resolution", "", "ComboBox3", "SetCurrentSelection", "3")
ControlCommand("Screen Resolution", "", "ComboBox1", "SetCurrentSelection", "0")
ControlCommand("Screen Resolution", "", "ComboBox3", "SetCurrentSelection", "1")
ControlClick("Screen Resolution", "", "Button4")
WinWait("Display Settings")
ControlClick("Display Settings", "", "Button1")
Else
ControlCommand("Screen Resolution", "", "ComboBox3", "SetCurrentSelection", "3")
ControlCommand("Screen Resolution", "", "ComboBox1", "SetCurrentSelection", "2")
ControlCommand("Screen Resolution", "", "ComboBox3", "SetCurrentSelection", "1")
ControlClick("Screen Resolution", "", "Button4")
WinWait("Display Settings")
ControlClick("Display Settings", "", "Button1")
EndIf
Just replace "SAMSUNG" with your third monitors/tvs name and you're all set!
As you surely know you can convert it to an executable which runs on any machine even without AutoIt installed.
I had to made some small modifications to get VonC's script to work on my machine. It is now a little more generic.
;
; — toggle-screen2.au3
;
#include <WinAPI.au3>
; exec cpanel app `display settings`
Run(_WinAPI_ExpandEnvironmentStrings("%windir%") & "\system32\control.exe desk.cpl,#0,3?")
; wait for window to be active
WinWaitActive("Display Properties")
; select 2nd display
Send("!d")
Send("{DOWN}")
; toggle the ‘extend desktop’ checkbox
Send("!e")
; close the dialog
Send("{ENTER}")
windows key + P button will do the same thing

Resources