I have some Windows 10 LTSC/IoT digital signs starting the ShareX screen capture application on boot like so:
"C:\Program Files\ShareX\ShareX.exe" -silent -startautocapture
After the Autocapture fires the Destination config is set to post the image to a URL but when network outages occur the ShareX UI spawns at a higher z-index than the Four Winds digital sign software display and remains there until manually closed or minimized.
I have been over the ShareX docs but this behavior isn't mentioned and the Custom Uploader options only include specifying an error message to display. Anyone have ideas on suppression or minimizing the UI? Possibly a PowerShell trick for shifting the focus?
Per #zett42 comment I started out with this answer. Unfortunately neither ShareX nor ShareX 8.14 is apparently the correct ConsoleWindowClass.
[int]$handle = [WPIA.ConsoleUtils]::FindWindow('ConsoleWindowClass','ShareX 8.14')
In the end this answer got it working using
$handle = $fw::FindWindow([IntPtr]::Zero, 'ShareX 8.14' )
Since this has a version number in the name I will need to sort out an enumeration strategy to iterate over the open windows and do substring matching but for now this succeeds.
I am very new to PowerShell so this may be ungracefully done.
$sig = #'
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(IntPtr sClassName, String sAppName);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_MINIMIZE = 0xF020;
'#
$fw = Add-Type -Namespace WPIA -Name ConsoleUtils -MemberDefinition $sig -PassThru
$wname='ShareX 14.1' # any existing window name
[int] $handle = $fw::FindWindow([IntPtr]::Zero, $wname ) # returns the Window Handle
if ($handle -gt 0)
{
[void][WPIA.ConsoleUtils]::SendMessage($handle, [WPIA.ConsoleUtils]::WM_SYSCOMMAND, [WPIA.ConsoleUtils]::SC_MINIMIZE, 0)
}
Related
I am trying to maximize my skype window from powershell.
I use the following script...
$sig = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
Add-Type -MemberDefinition $sig -name NativeMethods -namespace Win32
$hwnd = #(Get-Process lync)[0].MainWindowHandle
# Restore window
[Win32.NativeMethods]::ShowWindowAsync($hwnd, 4)
I also tried
$hwnd = #(Get-Process -id 2560)[0].MainWindowHandle
Info
Major Minor Build Revision
----- ----- ----- --------
5 1 14409 1012
But when I run the command it doesn't maximize, just returns true. Can I maximize a Skype window from poershell?
You were close in your question statement, but you are using the wrong constant.
$SW_MAXIMIZE = 3
$sig = #'
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
'#
Add-Type -MemberDefinition $sig -Name Functions -Namespace Win32
$hWnd = (Get-Process -Name lync).MainWindowHandle
[Win32.Functions]::ShowWindow($hWnd, $SW_MAXIMIZE)
Is it possible to get process id based on it's child Window Handle in powershell?
For example, this script looking for Window Handle with titile "Warning":
$sig = #"
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(IntPtr sClassName, String sAppName);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
"#
$fw = Add-Type -Namespace Win32 -Name Funcs -MemberDefinition $sig -PassThru
$wname='Warning'
$find_window = $fw::FindWindow([IntPtr]::Zero, $wname )
Write-Host $find_window
How it is shown in Task Manager:
Program.exe
|
\Warning
Now i need to determine - what PID have this warning window. I know it is possible to implement in C# (which i don't use at all), but this should be done on powershell only.
You are looking for GetWindowThreadProcessId. According to this answer, you can call that from PowerShell with this p/invoke:
[DllImport("User32.dll")]
public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
You supply a window handle in the first argument. The process and thread IDs are returned in the second argument and return value, respectively.
I have a cmd line exe provided for me which I cannot change at and I need to write a script around, but it has a Pause in built to it and I cannot see any way to skip this pause so the rest of my script can continue.
I have tried all sorts of things, including
#echo | call program.exe
program.exe < nul
cmd /c echo y &echo.| program.exe
Jay's answer here
Variations and combinations of those
Checked the program /? to see if there's a skip pause toggle, but there is not
Appreciate any advice
You can send data to a process using interop. It's called hooking the process, and there are a few resources on it. I like this answer.
This is a little code that allows you to send message to a
backgrounded application. To send the "A" char for example, simply
call sendKeystroke(Keys.A), and don't forget to use namespace
System.windows.forms to be able to use the Keys object.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace keybound
{
class WindowHook
{
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
public static void sendKeystroke(ushort k)
{
const uint WM_KEYDOWN = 0x100;
const uint WM_SYSCOMMAND = 0x018;
const uint SC_CLOSE = 0x053;
IntPtr WindowToFind = FindWindow(null, "Untitled1 - Notepad++");
IntPtr result3 = SendMessage(WindowToFind, WM_KEYDOWN, ((IntPtr)k), (IntPtr)0);
//IntPtr result3 = SendMessage(WindowToFind, WM_KEYUP, ((IntPtr)c), (IntPtr)0);
}
}
}
You'll probably have an easier time than they did getting the application instead of searching for the process because you can start it from your application:
Process proc = new Process();
proc.StartInfo.FileName = executablePath;
proc.Start();
proc.WaitForInputIdle();
Then proc.Id will be the PID.
As an alternative, I just ran into a VB type of example that seems even simpler using the Shell function, but I haven't used it before. You'll need to add a pause in your application to wait for the prompt, but this seems cleaner to read than Interop:
Dim ProcID As Integer
' Start the Calculator application, and store the process id.
ProcID = Shell("CALC.EXE", AppWinStyle.NormalFocus)
' Activate the Calculator application.
AppActivate(ProcID)
' Send the keystrokes to the Calculator application.
My.Computer.Keyboard.SendKeys("22", True)
My.Computer.Keyboard.SendKeys("*", True)
My.Computer.Keyboard.SendKeys("44", True)
My.Computer.Keyboard.SendKeys("=", True)
' The result is 22 * 44 = 968.
If you wind up with a System.ArgumentException, it's probably because the Shell function didn't get a process ID. This is because it needs full trust. The application would work if run as administrator. I don't think you'd find an easy way around this if you can't do that since it's a security issue to have applications run each other, but I could be wrong.
My example:
PowerPoint start Excel when insert embedded workbook.
I continue to work with PowerPoint. Excel stays as hidden process.
When I close PowerPoint it says to Excel to close itself (Excel closed later then PowerPoint).
When I kill PowerPoint, Excel becomes zombie.
My add-in is loaded into Excel process.
What message(I read that COM STA objects communicate through windows messaging) I should intercept to be notified about such event? What hook should I use? Is there any mean to be notified about count of reference to COM object changes?
UPDATE
My question seems was ambiguous. I am not trying to handle case when I kill PowerPoint. It just for evidence that PowerPoint send some message to Excel when closed normally. I am trying to intercept this message for a good reason.
UPDATE2
I cannot use API - because API fails. I am trying to solve Creating class in new thread in new app domain and providing it with Office COM object in embedded mode leads to zombie process
In case if you kill 'PowerPoint' there will be no reference count changes in Excel. That is why it is not being unloaded.
I believe you may simply post WM_QUIT message to the main window of the Excel process. You may find it using "Spy++.exe" which is a tool bundled with Visual Studio. Also, it is possible, that you may acquire a class factory for some object in Excel and invoke the 'LockServer' method with fLock = FALSE. This will decrease the reference count of Excel server.
But take this as a hack, since an add-in should not influence the behavior of the host application.
None. COM on a local server does use windows messages but it uses private messages which are an implementation detail. All it really does is post messages to a hidden window meaning "you have new COM events in your apartment". The hidden window passes this to the COM library which uses (I believe) shared memory to communicate with other processes, and does the rest of the work such as finding out what the event is (call, call return etc).
In other words, there is no specific message to say "excel go away", and even if there was you couldn't rely on it becaues it is an implementation detail which can change.
When you close the PowerPoint document containing the embedded spreadsheet, PowerPoint will call IUnknown::Release() on all the objects it holds. Excel keeps an internal track of outstanding objects, and will close itself when they all go away.
If you have an add-in in the excel process, and want to know when excel closes, use the addin api to find out.
Depending on the type of Add-In there are different APIs for this. The COM add-ins have events you can register for. But that is a different question: "how can my Excel add-in get notification that an invisible Excel instance is closing".
Added Solution to actual problem/question not asked
#asd.and.Rizzo, this is occurring because you hare holding on to the objects created by Excel. This means that Excel thinks it cannot exit because someone is using the objects. Excel is correct because that somone is your add-in.
This sort of lifetime issue is typical when creating any Office or Excel add-in and is not unique to .Net add-ins.
You are wanting to solve the problem by cheating Excel's reference count. But it is in your hands: The reference count is only up because you are holding objects! If you release them the count will go down to zero with no cheating.
Solution
The best solution is to avoid sinking events and avoid holding on to any object. Depending on what your add-in is doing this may not be possible.
If you cannot, you must sink the document close events. In each, check how many documents are open. When the count reaches zero you should remove your event handlers, call Marshall.ReleaseComObject on every object you have ever held, and unload your add-in. Excel will then exit.
But please note: You must call Marshall.ReleaseComObject on every object you ever hold. If you don't, Excel will never exit. This should occur if you unload the AppDomain.
I found next (from interpreting COM interfaces as pure C structs + function and Direct X hacks). Works but does not fix problem for some reason. My be should do the same for Workbooks, Worksheets...
Declare:
typedef HRESULT STDMETHODCALLTYPE QueryInterfacePtr( REFIID, void **);
typedef ULONG STDMETHODCALLTYPE AddRefPtr( );
typedef ULONG STDMETHODCALLTYPE ReleasePtr( );
typedef ULONG STDMETHODCALLTYPE GetTypeInfoCountPtr( UINT *) ;
typedef ULONG STDMETHODCALLTYPE GetTypeInfoPtr ( UINT, LCID, ITypeInfo **) ;
typedef ULONG STDMETHODCALLTYPE GetIDsOfNamesPtr ( REFIID, LPOLESTR *,
UINT, LCID, DISPID *) ;
typedef ULONG STDMETHODCALLTYPE InvokePtr ( DISPID, REFIID,
LCID, WORD, DISPPARAMS *,
VARIANT *, EXCEPINFO *, UINT *) ;
typedef struct {
// IUnknown functions
QueryInterfacePtr * QueryInterface;
AddRefPtr * AddRef;
ReleasePtr * Release;
// IDispatch functions
GetTypeInfoCountPtr* GetTypeInfoCount;
GetTypeInfoPtr* GetTypeInfo;
GetIDsOfNamesPtr* GetIDsOfNames;
InvokePtr* Invoke;
} IDispatchInterceptor;
typedef ULONG __stdcall releasePTR(IDispatch *self);
typedef ULONG __stdcall addrefPTR(IDispatch *self);
Next I have done for Excel:
static IDispatch * application = NULL;
static releasePTR* realRelease = NULL;
static addrefPTR* realAddRef = NULL;
static ULONG wasAdd = 0;
static ULONG oldCount = 0;
HRESULT STDMETHODCALLTYPE QueryInterfaceNativeOutOfProcSrv(REFIID riid, void **ppv){
return application->QueryInterface(riid,ppv);
}
ULONG STDMETHODCALLTYPE AddRefNativeOutOfProcSrv(){
return application->AddRef();
}
ULONG STDMETHODCALLTYPE ReleaseNativeOutOfProcSrv(){
return application->Release();
}
ULONG STDMETHODCALLTYPE GetTypeInfoCountSrv( UINT * count) {
return application->GetTypeInfoCount(count);
}
ULONG STDMETHODCALLTYPE GetTypeInfoSrv ( UINT n, LCID id, ITypeInfo ** inf) {
return application->GetTypeInfo(n,id,inf);
}
ULONG STDMETHODCALLTYPE GetIDsOfNamesSrv ( REFIID a, LPOLESTR * b, UINT c, LCID d, DISPID * e) {
return application->GetIDsOfNames(a,b,c,d,e);
}
ULONG STDMETHODCALLTYPE InvokeSrv ( DISPID a, REFIID b, LCID c, WORD d, DISPPARAMS * e, VARIANT * i, EXCEPINFO * j, UINT *k) {
return application->Invoke(a,b,c,d,e,i,j,k);
}
static IDispatchInterceptor interceptor =
{QueryInterfaceNativeOutOfProcSrv,AddRefNativeOutOfProcSrv,ReleaseNativeOutOfProcSrv,
GetTypeInfoCountSrv,GetTypeInfoSrv,GetIDsOfNamesSrv,InvokeSrv
};
ULONG __stdcall release(IDispatch *self)
{
ULONG c = realRelease(self);
Log->logWrite("release %d",c);
if ( c == 1)
{
if (instance != NULL)
{
instance->OnBeginShutdown(NULL);
Log->logWrite("OnBeginShutdown %d",c);
instance->OnEmbeddedDisconnection();
Log->logWrite("OnEmbeddedDisconnection %d",c);
instance = NULL;
}
}
//if (c == 2) {
// c = realRelease(self);
// c = realRelease(self);
//}
return c;
}
ULONG __stdcall addref(IDispatch *self)
{
ULONG c = oldCount;
if (wasAdd == 0)
{
c = realAddRef(self);
oldCount = c;
wasAdd++;
}
else if (wasAdd == 1)
{
Log->logWrite("ADDREF FAKE %d",c);
wasAdd++;
}
else if (wasAdd == 2)
{
Log->logWrite("ADDREF FAKE %d",c);
wasAdd++;
}
else
{
c = realAddRef(self);
}
Log->logWrite("ADDREF %d",c);
return c;
}
void InterceptRelease( IDispatch obj){
void iunknown_vtable= (void*)((unsigned int)obj);
void* idispatch_vtable = (void*)(((unsigned int)iunknown_vtable)+8);
unsigned int* v1 = (unsigned int*)idispatch_vtable;
realRelease = (releasePTR*)*v1;
DWORD old;
VirtualProtect(v1,4,PAGE_EXECUTE_READWRITE,&old);
*v1 = (unsigned int) release;
//while(obj->Release() > 0){};
}
void InterceptAddRef( IDispatch obj){
void iunknown_vtable= (void*)((unsigned int)obj);
void* idispatch_vtable = (void*)(((unsigned int)iunknown_vtable)+4);
unsigned int* v1 = (unsigned int*)idispatch_vtable;
realAddRef = (addrefPTR*)*v1;
DWORD old;
VirtualProtect(v1,4,PAGE_EXECUTE_READWRITE,&old);
*v1 = (unsigned int) addref;
}
Apply:
IDispatch * app = Application;
InterceptRelease(app);
InterceptAddRef(app);
Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
How to turn screensaver on (windows 7) by a code (in cmd)?
Does the following meet your requirements?
start logon.scr /s
As long as the .scr is on the PATH the above command should work.
EDIT: I don't know if Windows 7 comes with logon.scr, make sure you're testing it with a .scr that is actually installed in Windows 7.
Note that I got the idea of just invoking the .scr with /s from Screensaver Sample Command Line Options:
When Windows runs your screensaver, it
launches it with one of three command
line options:
/s – Start the screensaver in full-screen mode.
/c – Show the configuration settings dialog box.
/p #### – Display a preview of the screensaver using the specified
window handle.
EDIT 2:
I did some additional searching and found that you could create lock.cmd:
#start /wait logon.scr /s & rundll32 user32.dll,LockWorkStation
Or lock.vbs:
Set objShell = CreateObject("Wscript.Shell")
' The "True" argument will make the script wait for the screensaver to exit
returnVal = objShell.Run("logon.scr", 1, True)
' Then call the lock functionality
objShell.Run "rundll32.exe user32.dll,LockWorkStation"
Neither of these answers is perfect, both reveal a flicker of the desktop after the screen saver is disabled and just prior to the workstation being locked.
It may not be possible to reproduce the system behaviour of starting the screen saver and password protecting on resume. Even the answer to Launch System Screensaver from C# Windows Form only starts the screen saver, it does not password protect on resume.
Putting together the cmd and vbs script ideas with the code from the answer to Launch System Screensaver from C# Windows Form I came up with the following:
using System;
using System.Runtime.InteropServices;
public static class LockDesktop
{
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
[DllImport("user32.dll", EntryPoint = "LockWorkStation")]
private static extern IntPtr LockWorkStation();
private const int SC_SCREENSAVE = 0xF140;
private const int WM_SYSCOMMAND = 0x0112;
public static void SetScreenSaverRunning()
{
SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, SC_SCREENSAVE, 0);
LockWorkStation();
}
public static void Main()
{
LockDesktop.SetScreenSaverRunning();
}
}
To build it, install the .NET Framework, copy and paste the above code into lock.cs, then run:
%SystemRoot%\Microsoft.NET\Framework\v3.5\csc.exe lock.cs
Put the created lock.exe in your path, after that, typing lock should engage the configured screen saver and lock your workstation.
using System;
using System.Runtime.InteropServices;
public static class LockDesktop
{
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
private const int SC_SCREENSAVE = 0xF140;
private const int WM_SYSCOMMAND = 0x0112;
public static void SetScreenSaverRunning()
{
SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, SC_SCREENSAVE, 0);
}
public static void Main()
{
LockDesktop.SetScreenSaverRunning();
}
}
This works - only downside is that u cant interact with pc for something like 7 sec, but i guess its 7's to give ppl time before making screensaver 'permanent'.
I have Windows 7. I placed the line:
#start /wait %windir%\ExtraPath\ScreenSaverName.scr /s & rundll32 user32.dll,LockWorkStation
in a batch (.bat) file, place it in a appropriate dir, and created a shortcut pointing to this, with the desired shortcut key.
In this line, \ExtraPath is the additional path under your win dir (usually this is \system32) where the screen savers are located, and ScreenSaverName.scr is the name of the desired screen saver itself.
It works perfectly.
Now I can press the shortcut keys to run the screen saver and lock the pc.
You could try Powershell Script To Start A Random Screen Saver.