I have a strange Requierment for a Task. In gernerall terms, i need to prepare Windows PCs for Presentations with a PowerShell Script and one of the Requierments is to activate the "hightlight pointer position if ctrl is hit" function in the extended pointer settings.
My first idea was to change the reg-key but if that even works, i think it would be linked to a PC-Restart. I allready tryed to change the Value of the key after finding the diffrenece in the on and off state of the function but i cant force it to "refresh the setting" or it simply does not work.
Any ideas, workaround or the like would be appriciated.
The function is controlled by the Reg-Key HKCU:\Control Panel\Desktop - UserPrefernecesMask
The 'Mouse Sonar' function can be enabled or disabled using the SystemParametersInfo function inside user32.dll.
To use that in PowerShell, add a bit of CSharp:
Add-Type -TypeDefinition #'
using System.Runtime.InteropServices;
public class MouseSonar {
public const uint SPI_SETMOUSESONAR = 0x101D;
public const uint SPIF_UPDATEINIFILE = 0x01;
public const uint SPIF_SENDCHANGE = 0x02;
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo (uint uAction, uint uParam, bool lpvParam, uint fuWinIni);
public static void SetSonar (bool Enable) {
SystemParametersInfo(SPI_SETMOUSESONAR, 0, Enable, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
}
'#
and use it like this:
# turn Mouse Sonar on
[MouseSonar]::SetSonar($true)
# to switch Mouse Sonar off again:
[MouseSonar]::SetSonar($false)
No need to restart the computer, this works immediately.
Related
Some context:
I have an application that opens the first webcam device on Windows 10 64bit (whatever index 0 is during enumeration of devices) and does some processing on the frames. The application's source code is not accessible.
Question:
I need to make this application to work with two webcams at the same time. I thought maybe there is a way to do the following:
hide webcam 2
run application (picks up webcam 1)
hide webcam 1, unhide webcam 2
run application (picks up webcam 2)
Is there a way to do this without interrupting camera's operation? Note that both applications are running at the same time so hard-disabling a camera is not an option. Calling either a Win32 api or doing this in PowerShell is acceptable.
Thanks!
Thanks to comments on my original question, I managed to solve my problem by hooking into CM_Get_Device_Interface_List_ExW Win32 API call.
I had to verify what API is being called, so I used and API tracer tool (API monitor v2 64bit). Debugger should work too but for some reason my VS debugger did not show me any symbols (possibly missing pdbs).
The original process I tried to hook into is written in C# so I hooked into the call via an injected C# DLL containing EasyHook. Here is my code snippet (actual injection code left out):
using System;
using System.Runtime.InteropServices;
using EasyHook;
public class HookDevices : IEntryPoint
{
LocalHook FunctionLocalHook;
// construct this to hook into calls
HookDevices()
{
try
{
FunctionLocalHook = LocalHook.Create(
LocalHook.GetProcAddress("CfgMgr32.dll", "CM_Get_Device_Interface_List_ExW"),
new FunctionHookDelegate(CM_Get_Device_Interface_List_Ex_Hooked),
this);
FunctionLocalHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
}
catch (Exception ExtInfo)
{
Debug.LogException(ExtInfo);
return;
}
}
[UnmanagedFunctionPointer(CallingConvention.StdCall,
CharSet = CharSet.Unicode,
SetLastError = true)]
delegate uint FunctionHookDelegate(
ref Guid interfaceClassGuid,
string deviceID,
IntPtr buffer,
uint bufferLength,
uint flags,
IntPtr hMachine);
[DllImport("CfgMgr32.dll",
CharSet = CharSet.Unicode,
SetLastError = true,
CallingConvention = CallingConvention.StdCall)]
static extern uint CM_Get_Device_Interface_List_ExW(
ref Guid interfaceClassGuid,
string deviceID,
IntPtr buffer,
uint bufferLength,
uint flags,
IntPtr hMachine);
// this is where we are intercepting all API accesses!
static uint CM_Get_Device_Interface_List_Ex_Hooked(
ref Guid interfaceClassGuid,
string deviceID,
IntPtr buffer,
uint bufferLength,
uint flags,
IntPtr hMachine)
{
// pass-through original API
uint ret = CM_Get_Device_Interface_List_ExW(
ref interfaceClassGuid,
deviceID,
buffer,
bufferLength,
flags,
hMachine);
// do custom logic here and re-arrange "buffer"
return ret;
}
}
I am running a powershell script to change the background to a certain set of colors. I would like to do this without rebooting, but unfortunately cannot get the changes to take effect immediately on a windows 7/8 platform. I've found many solutions online but I cannot find one that works for me. I think it may have something to do with setting the SystemParametersInfo, but I don't know for sure. I've seen a few solutions and tried them out for myself, but I can't get them to work either. The registry keys update just find but the changes don't take effect until after I reboot. Below is what I have so far, if anyone seeing anything I could do different I would appreciate the help!
backgroundtest.ps1
Add-Type #"
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace Background
{
public class Setter {
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo(int uAction, int uParm, string lpvParam, int fuWinIni);
public const int UpdateIniFile = 0x01;
public const int SendWinIniChange = 0x02;
public const int SetDesktopBackground = 20; <# following examples online to set parameters #>
public static void SetBackground() {
SystemParametersInfo(SetDesktopBackground, 0, "", UpdateIniFile | SendWinIniChange);
RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true);
key.SetValue(#"WallPaper", 0); <#remove wallpaper#>
RegistryKey key2 = Registry.CurrentUser.OpenSubKey("Control Panel\\Colors", true);
key2.SetValue(#"Background", "0 118 163"); <#set background to new color>
key.Close();
key2.Close();
}
}
}
"#
[Background.Setter]::SetBackground()
The documented way to change the system colours is the SetSysColors function.
This sends the WM_SYSCOLORCHANGE message to all top-level windows to notify them of the change.
I've updated your class to clear the background and set the colour to purple. It would need copying into your PowerShell stuff. Note that the way I've declared SetSysColors you can only change one colour at a time.
public class Setter {
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo(int uAction, int uParm, string lpvParam, int fuWinIni);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SetSysColors(int count, [In] ref int index, [In] ref int colour);
public const int UpdateIniFile = 0x01;
public const int SendWinIniChange = 0x02;
public const int SetDesktopBackground = 20;
public const int COLOR_BACKGROUND = 1;
public static void SetBackground() {
SystemParametersInfo(SetDesktopBackground, 0, "", UpdateIniFile | SendWinIniChange);
int index = COLOR_BACKGROUND;
int colour = 0xFF00FF;
SetSysColors(1, ref index, ref colour);
}
}
Yesterday was my first Powershell experience and I was pretty lost with what I needed to do. In order to change the desktop background to a solid color, you first need to remove the wall paper and then you can use the SetSysColors function to immediately change the desktop background. This link helped me out tremendously. http://gallery.technet.microsoft.com/scriptcenter/Change-window-borderdesktop-609a6fb2
Hopefully this helps someone the same way it helped me.
Updated Code
$code = #'
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace Background
{
public class Setter {
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo(int uAction, int uParm, string lpvParam, int fuWinIni);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError =true)]
private static extern int SetSysColors(int cElements, int[] lpaElements, int[] lpRgbValues);
public const int UpdateIniFile = 0x01;
public const int SendWinIniChange = 0x02;
public const int SetDesktopBackground = 0x0014;
public const int COLOR_DESKTOP = 1;
public int[] first = {COLOR_DESKTOP};
public static void RemoveWallPaper() {
SystemParametersInfo( SetDesktopBackground, 0, "", SendWinIniChange | UpdateIniFile );
RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true);
key.SetValue(#"WallPaper", 0);
key.Close();
}
public static void SetBackground(byte r, byte g, byte b) {
RemoveWallPaper();
System.Drawing.Color color= System.Drawing.Color.FromArgb(r,g,b);
int[] elements = {COLOR_DESKTOP};
int[] colors = { System.Drawing.ColorTranslator.ToWin32(color) };
SetSysColors(elements.Length, elements, colors);
RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel\\Colors", true);
key.SetValue(#"Background", string.Format("{0} {1} {2}", color.R, color.G, color.B));
key.Close();
}
}
}
'#
Add-Type -TypeDefinition $code -ReferencedAssemblies System.Drawing.dll -PassThru
Function Set-OSCDesktopColor
{
<# Powershell function to remove desktop background and set background to colors we want #>
Process
{
[Background.Setter]::SetBackground(0,118,163)
return
}
}
Set-OSCDesktopColor
I grabbed Wheatfairies code, updated it and published in (with Attribution) to the PowerShell Gallery at:
https://www.powershellgallery.com/packages/Set-DesktopBackGround/1.0.0.0/DisplayScript
So now everyone can just type:
Install-Script -Name Set-DesktopBackGround
to get it.
Cheers!
is it possible to simulate Click on a process without actually clicking on it?
e.g. I wanna Click on a running Calculator with mouse stand still. is this possible?
If you are just trying to click a button within a fairly typical labels, fields, and buttons application, you can use a little bit of P/Invoke to use FindWindow and SendMessage to the control.
If you are not already familiar with Spy++, now is the time to start!
It is packaged with Visual Studio 2012 RC in: C:\Program Files\Microsoft Visual Studio 11.0\Common7\Tools. It should be similarly found for other versions.
Try this as Console C# application:
class Program
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
private const uint BM_CLICK = 0x00F5;
static void Main(string[] args)
{
// Get the handle of the window
var windowHandle = FindWindow((string)null, "Form1");
// Get button handle
var buttonHandle = FindWindowEx(windowHandle, IntPtr.Zero, (string)null, "A Huge Button");
// Send click to the button
SendMessage(buttonHandle, BM_CLICK, 0, 0);
}
}
This gets the handle to the window captioned "Form1". Using that handle, it gets a handle to the Button within the Window. Then sends a message of type "BM_CLICK" with no useful params to the button control.
I used a test WinForms app as my target. A single button and some code behind to increment a counter.
You should see the counter increment when you run the P/Invoke console application. However, you will not see the button animate.
You could also use the Spy++ Message Logger function. I recommend a filter to BM_CLICK and maybe WM_LBUTTONDOWN/WM_LBUTTONUP (what a manual click will give you).
Hope that helps!
e.g, Is the user playing a movie full screen, or looking at powerpoint in full screen mode?
I could have sworn I saw a IsFullScreenInteractive API before, but can't find it now
Here's how I've solved this problem:
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Test
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(IsForegroundWwindowFullScreen());
}
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetSystemMetrics(int smIndex);
public const int SM_CXSCREEN = 0;
public const int SM_CYSCREEN = 1;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hWnd, out W32RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct W32RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
public static bool IsForegroundWwindowFullScreen()
{
int scrX = GetSystemMetrics(SM_CXSCREEN),
scrY = GetSystemMetrics(SM_CYSCREEN);
IntPtr handle = GetForegroundWindow();
if (handle == IntPtr.Zero) return false;
W32RECT wRect;
if (!GetWindowRect(handle, out wRect)) return false;
return scrX == (wRect.Right - wRect.Left) && scrY == (wRect.Bottom - wRect.Top);
}
}
}
Vista indeed has an API pretty much exactly for this purpose - it's called SHQueryUserNotificationState.
Use GetForegroundWindow to get a handle to the window the user is working with. GetClientRect will give the dimensions of the active part of the window sans borders; use ClientToScreen to convert the rectangle to monitor coordinates.
Call MonitorFromRect or MonitorFromWindow to get the monitor that the window is in. Use GetMonitorInfo to get the coordinates of the monitor.
Compare the two rectangles - if the window rectangle completely covers the monitor rectangle, it's a full screen window.
The preferred way of detecting the state of a window is by calling GetWindowPlacement. If you do that in conjunction with GetForegroundWindow, you can easily check if the user sees a fullscreen window or not.
I can display and select a single file in windows explorer like this:
explorer.exe /select, "c:\path\to\file.txt"
However, I can't work out how to select more than one file. None of the permutations of select I've tried work.
Note: I looked at these pages for docs, neither helped.
https://support.microsoft.com/kb/314853
http://web.archive.org/web/20100716112458/http://www.infocellar.com:80/Win98/explorer-switches.htm
This should be possible with the shell function SHOpenFolderAndSelectItems
EDIT
Here is some sample code showing how to use the function in C/C++, without error checking:
//Directory to open
ITEMIDLIST *dir = ILCreateFromPath(_T("C:\\"));
//Items in directory to select
ITEMIDLIST *item1 = ILCreateFromPath(_T("C:\\Program Files\\"));
ITEMIDLIST *item2 = ILCreateFromPath(_T("C:\\Windows\\"));
const ITEMIDLIST* selection[] = {item1,item2};
UINT count = sizeof(selection) / sizeof(ITEMIDLIST);
//Perform selection
SHOpenFolderAndSelectItems(dir, count, selection, 0);
//Free resources
ILFree(dir);
ILFree(item1);
ILFree(item2);
The true way of selecting multiple files in Explorer is the next
Unmanaged code looks like this (compiled from China code posts with fixing its bugs)
static class NativeMethods
{
[DllImport("shell32.dll", ExactSpelling = true)]
public static extern int SHOpenFolderAndSelectItems(
IntPtr pidlFolder,
uint cidl,
[In, MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl,
uint dwFlags);
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr ILCreateFromPath([MarshalAs(UnmanagedType.LPTStr)] string pszPath);
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214F9-0000-0000-C000-000000000046")]
public interface IShellLinkW
{
[PreserveSig]
int GetPath(StringBuilder pszFile, int cch, [In, Out] ref WIN32_FIND_DATAW pfd, uint fFlags);
[PreserveSig]
int GetIDList([Out] out IntPtr ppidl);
[PreserveSig]
int SetIDList([In] ref IntPtr pidl);
[PreserveSig]
int GetDescription(StringBuilder pszName, int cch);
[PreserveSig]
int SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
[PreserveSig]
int GetWorkingDirectory(StringBuilder pszDir, int cch);
[PreserveSig]
int SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
[PreserveSig]
int GetArguments(StringBuilder pszArgs, int cch);
[PreserveSig]
int SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
[PreserveSig]
int GetHotkey([Out] out ushort pwHotkey);
[PreserveSig]
int SetHotkey(ushort wHotkey);
[PreserveSig]
int GetShowCmd([Out] out int piShowCmd);
[PreserveSig]
int SetShowCmd(int iShowCmd);
[PreserveSig]
int GetIconLocation(StringBuilder pszIconPath, int cch, [Out] out int piIcon);
[PreserveSig]
int SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
[PreserveSig]
int SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved);
[PreserveSig]
int Resolve(IntPtr hwnd, uint fFlags);
[PreserveSig]
int SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
[Serializable, StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode), BestFitMapping(false)]
public struct WIN32_FIND_DATAW
{
public uint dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
public static void OpenFolderAndSelectFiles(string folder, params string[] filesToSelect)
{
IntPtr dir = ILCreateFromPath(folder);
var filesToSelectIntPtrs = new IntPtr[filesToSelect.Length];
for (int i = 0; i < filesToSelect.Length; i++)
{
filesToSelectIntPtrs[i] = ILCreateFromPath(filesToSelect[i]);
}
SHOpenFolderAndSelectItems(dir, (uint) filesToSelect.Length, filesToSelectIntPtrs, 0);
ReleaseComObject(dir);
ReleaseComObject(filesToSelectIntPtrs);
}
private static void ReleaseComObject(params object[] comObjs)
{
foreach (object obj in comObjs)
{
if (obj != null && Marshal.IsComObject(obj))
Marshal.ReleaseComObject(obj);
}
}
}
it cannot be done through explorer.exe
Depending on what you actually want to accomplish you may be able to do it with AutoHotKey. It is an amazing free tool for automating things you normally can't do. It should come with Windows. This script will select your file and highlight the next two files below it when you hit F12.
F12::
run explorer.exe /select`, "c:\path\to\file.txt"
SendInput {Shift Down}{Down}{Down}{Shift Up}
return
It is also possible to just put those two middle lines in a text file and then pass it is a parm to autohotkey.exe. They have an option to compile the script also, which would make it a standalone exe that you could call. Works great with a great help file.
#Orion, It is possible to use autohotkey from C#. You can make an autohotkey script into a standalone executable (about 400k) that can be launched by your C# app (just the way you are launching explorer). You can also pass it command line parameters. It does not have any runtime requirements.
There are COM Automation LateBinding IDispatch interfaces, these are easy to use from PowerShell, Visual Basic.NET and C#, some sample code:
$shell = New-Object -ComObject Shell.Application
function SelectFiles($filesToSelect)
{
foreach ($fileToSelect in $filesToSelect)
{
foreach ($window in $shell.Windows())
{
foreach ($folderItem in $window.Document.Folder.Items())
{
if ($folderItem.Path -eq $fileToSelect)
{
$window.Document.SelectItem($folderItem, 1 + 8)
}
}
}
}
}
-
Option Strict Off
Imports Microsoft.VisualBasic
Public Class ExplorerHelp
Shared ShellApp As Object = CreateObject("Shell.Application")
Shared Sub SelectFile(filepath As String)
For Each i In ShellApp.Windows
For Each i2 In i.Document.Folder.Items()
If i2.Path = filepath Then
i.Document.SelectItem(i2, 1 + 8)
Exit Sub
End If
Next
Next
End Sub
End Class
https://learn.microsoft.com/en-us/windows/win32/shell/shellfolderview-selectitem
This is one of those questions where it may be good to consider what you're trying to achieve, and whether there's a better method.
To add some more context -
Our company develops a C# client application, which allows users to load files and do stuff with them, kind of like how iTunes manages your MP3 files without showing you the actual file on disk.
It's useful to select a file in the application, and do a 'Show me this file in Windows Explorer` command - this is what I'm trying to achieve, and have done so for single files.
We have a ListView which allows users to select multiple files within the application, and move/delete/etc them. It would be nice to have this 'show me this file in windows' command work for multiple selected files - at least if all the source files are in the same directory, but if it's not possible then it's not a major feature.
I suppose you can use FindWindowEx to get the SysListView32 of Windows Explorer, then use SendMessage with LVM_SETITEMSTATE to select the items. The difficulty being to know the position of the items... Perhaps LVM_FINDITEM can be used for this.
Grr i would like to do this as well. Media Player does it when you select 2+ files and right click and do "open file location" but not exactly sure how (nor do i really feel like spending the time w/ procmon to figure it out).