I'm trying to write simple code for changing an application (notepad) windows position. Notepad should be running in background (only 1 instance).
I am using Process::GetProcessesByName( "notepad") to get the process ID,
and myProcess->MainWindowHandle to get the notepad windows handle.
Now I am trying to change window size by passing the HWND handle to SetWindowPos,
I am not sure how to correctly do it (searched the web didn't find a clear newbie-answer)
1st I tried:
HWND hwnd = myProcess->MainWindowHandle;
SetWindowPos(hwnd ,
HWND_TOP,
100,
100,
0, 0, // Ignores size arguments.
SWP_NOSIZE);
This got me
Compile error: error C2440: 'initializing' : cannot convert from 'System::IntPtr' to 'HWND'
Then I tried casting and passing as reference:
HWND hwnd = (HWND)&myProcess->MainWindowHandle;
SetWindowPos(&hwnd ,
HWND_TOP,
100,
100,
0, 0, // Ignores size arguments.
SWP_NOSIZE);
}
Error:
Compile error: error C2664: 'SetWindowPos' : cannot convert parameter 1 from 'HWND *' to 'HWND'
See below for complete code:
#include "StdAfx.h"
#include <windows.h>
#include <iostream>
#include <string>
#using <System.dll>
using namespace System;
using namespace System::Diagnostics;
int main()
{
array<Process^>^myProcesses = Process::GetProcessesByName( "notepad");
if ( myProcesses->Length == 0 )
Console::WriteLine( "Could not find notepad processes on local computer." );
Collections::IEnumerator^ myEnum = myProcesses->GetEnumerator();
while ( myEnum->MoveNext() )
{
Process^ myProcess = safe_cast<Process^>(myEnum->Current);
Console::Write( "Process Name : {0} Process ID : {1} HandleCount : {2}\n", myProcess->ProcessName, myProcess->Id, myProcess->HandleCount );
Console::Write( "Main window Title : {0} MainWindowHandle : {1}", myProcess->MainWindowTitle, myProcess->MainWindowHandle );
//HWND hwnd = myProcess->MainWindowHandle; //1st try
// SetWindowPos(hwnd ,
// HWND_TOP,
// 100,
// 100,
// 0, 0, // Ignores size arguments.
// SWP_NOSIZE);
HWND hwnd = (HWND)&myProcess->MainWindowHandle;//2nd try
SetWindowPos(&hwnd ,
HWND_TOP,
100,
100,
0, 0, // Ignores size arguments.
SWP_NOSIZE);
}
}
}
you send an adress to first param of SetWindowsPos function but this function expect a HWND value. You know in C++ :
&variable = return adress of variable where is store the value
variable = return value of variable
Visual C++ Intellisense or tooltip give you prototype.
BOOL SetWindowsPos(HWND, HWND, int, int, int, int, UINT);
EDIT : as say in comment, this line :
HWND hwnd = (HWND)&myProcess->MainWindowHandle;
is wrong, you have to do :
HWND hwnd = (HWND)myProcess->MainWindowHandle.ToPointer();
ToPointer method documentation : http://msdn.microsoft.com/fr-fr/library/system.intptr.topointer(v=vs.110).aspx
Related
Hi everyone! I've created such a window (thanks to X11/Xlib: Create "GlassPane"-Window), that is on always on top, semitransparent, and doesn't consume any events. It works fine on some distributions, for example, on ubuntu or debian (after proper set of composite manager). On some distro mouse events can't penetrate through my window (with GNOME WM), on others both mouse and keyboard events can't go through. I am still testing os dependance of my app. Could you give me any tips, why my program is os dependant, is it only wm problem? Is there other way to solve my task and create universal utility that works on all linux distro? Appreciate any help!
/* for corrent exit on termination */
#include <signal.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/shape.h>
#include <X11/extensions/Xfixes.h>
#include <stdio.h>
#include <string.h> // strncmp
#include <unistd.h> // usleep
// all required elements for drawing
struct TXLibConfig
{
Display *dpy;
Window w;
XSetWindowAttributes attr;
XGCValues gcv;
XVisualInfo vinfo;
GC gc;
XserverRegion region;
int winWidth, winHeight;
};
int must_quit = 0;
// Define the function to be called when ctrl-c (SIGINT) is sent to process
void signal_callback_handler(int signum) {
must_quit = 1;
}
int XInit(TXLibConfig *txlibPtr);
int main(int argc, char *argv[]) {
// exit by Ctrl+C and pkill
signal(SIGINT, signal_callback_handler);
signal(SIGTERM, signal_callback_handler);
TXLibConfig tXlibCfg = {0};
XInit(&tXlibCfg);
int ctr = 0;
while(1) {
XClearWindow(tXlibCfg.dpy,tXlibCfg.w);
XSetForeground(tXlibCfg.dpy, tXlibCfg.gc, 0x01808020);
XRectangle rct[] = {300, 300, 200, 200};
XFillRectangles(tXlibCfg.dpy, tXlibCfg.w, tXlibCfg.gc, rct, 1);
XSetForeground(tXlibCfg.dpy, tXlibCfg.gc, 0xf0010140);
XRectangle rectan[] = {350, 350, 10*(ctr % 10 + 1), 10*(ctr % 10 + 1)};
XFillRectangles(tXlibCfg.dpy, tXlibCfg.w, tXlibCfg.gc, rectan, 1);
XFlush(tXlibCfg.dpy);
XSync(tXlibCfg.dpy, True);
ctr++;
usleep(200000);
if (must_quit == 1) break;
}
XClearWindow(tXlibCfg.dpy,tXlibCfg.w);
XDestroyWindow(tXlibCfg.dpy, tXlibCfg.w);
XCloseDisplay(tXlibCfg.dpy);
return 0;
}
int XInit(TXLibConfig *txlibPtr)
{
txlibPtr->dpy = XOpenDisplay(NULL);
if (!txlibPtr->dpy) printf("cannot open display '%s'", XDisplayName(0));
// Get screen resolution >>>>>
int snum;
snum = DefaultScreen(txlibPtr->dpy);
txlibPtr->winWidth = DisplayWidth(txlibPtr->dpy, snum);
txlibPtr->winHeight = DisplayHeight(txlibPtr->dpy, snum);
// Get screen resolution <<<<<
XMatchVisualInfo(txlibPtr->dpy, DefaultScreen(txlibPtr->dpy), 32, TrueColor, &txlibPtr->vinfo);
txlibPtr->attr.colormap = XCreateColormap(txlibPtr->dpy, DefaultRootWindow(txlibPtr->dpy), txlibPtr->vinfo.visual, AllocNone);
txlibPtr->attr.border_pixel = 0;
txlibPtr->attr.background_pixel = 0;
txlibPtr->w = XCreateWindow(txlibPtr->dpy, DefaultRootWindow(txlibPtr->dpy), 0, 0,
txlibPtr->winWidth, txlibPtr->winHeight, 10, txlibPtr->vinfo.depth,
NoEventMask, txlibPtr->vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel, &txlibPtr->attr);
// Ignore any input for passing events to other windows >>>>>
txlibPtr->region = XFixesCreateRegion (txlibPtr->dpy, NULL, 0);
XFixesSetWindowShapeRegion (txlibPtr->dpy, txlibPtr->w, ShapeBounding, 0, 0, 0);
XFixesSetWindowShapeRegion (txlibPtr->dpy, txlibPtr->w, ShapeInput, 0, 0, txlibPtr->region);
XFixesDestroyRegion (txlibPtr->dpy, txlibPtr->region);
// Ignore any input for passing events to other windows <<<<<
txlibPtr->gcv.line_width = 1;
txlibPtr->gc = XCreateGC(txlibPtr->dpy, txlibPtr->w, GCLineWidth, &txlibPtr->gcv);
XSelectInput(txlibPtr->dpy, txlibPtr->w, ExposureMask);
long value = XInternAtom(txlibPtr->dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
XChangeProperty(txlibPtr->dpy, txlibPtr->w, XInternAtom(txlibPtr->dpy, "_NET_WM_WINDOW_TYPE", False),
6, 32, PropModeReplace, (unsigned char *) &value, 1);
XMapWindow(txlibPtr->dpy, txlibPtr->w);
XFlush(txlibPtr->dpy);
usleep(100000);
return 0;
};
I'm trying DwmGetWindowAttribute to get a window's real, physical, pixel location on screen. It works well for top-level windows. But I find that it does not work for child windows, in which case it returns just E_HANDLE (0x80070006).
However, MSDN does not state anything about such child-window limitation. So I'm in baffle.
Can any one confirm this behavior? Thank you.
This is my C++ test code (DwmBounds.cpp):
#include <stdio.h>
#include <windows.h>
#include <dwmapi.h>
int main()
{
DWORD winerr = 0;
HWND hwndTop = FindWindow(L"Notepad", NULL);
if(!hwndTop)
{
wprintf(L"Cannot find a Notepad window.\n");
return 4;
}
RECT rc = {};
HRESULT hr = DwmGetWindowAttribute(hwndTop, DWMWA_EXTENDED_FRAME_BOUNDS, &rc, sizeof(rc));
if (hr != S_OK)
{
wprintf(L"Fail to call DwmGetWindowAttribute() on Notepad window. HRESULT=0x%08X\r\n",
(DWORD)hr);
return 4;
}
wprintf(L"Notepad DWMWA_EXTENDED_FRAME_BOUNDS: LT(%d, %d) RB(%d, %d)\r\n",
rc.left, rc.top, rc.right, rc.bottom);
HWND hwndEdit = FindWindowEx(hwndTop, nullptr, L"Edit", nullptr);
if (!hwndEdit)
return 4;
hr = DwmGetWindowAttribute(hwndEdit, DWMWA_EXTENDED_FRAME_BOUNDS, &rc, sizeof(rc));
if(hr != S_OK)
{
// Always get HRESULT=0x80070006 (E_HANDLE), why?
wprintf(L"Get DWMWA_EXTENDED_FRAME_BOUNDS fails on child window(0x%08X), HRESULT=0x%08X\n",
(DWORD)hwndEdit, (DWORD)hr);
return 4;
}
wprintf(L"Editbox DWMWA_EXTENDED_FRAME_BOUNDS: LT(%d, %d) RB(%d, %d)\r\n",
rc.left, rc.top, rc.right, rc.bottom);
return 0;
}
This is its output:
The benefit of DwmGetWindowAttribute is: Whatever the setting of DPI_AWARENESS_CONTEXT on the calling thread, DwmGetWindowAttribute always reports physical screen coordinate. On the other hand, GetWindowRect just reports the "virtual" coordinates relating to calling thread's DPI-awareness-context. So, I am wondering whether I can use DwmGetWindowAttribute to get each top-level window and child window's physical coordinates.
EDIT 3
OK, so it seems like this might not be an Installer issue after all. When I make a simple batch file:
exit /b 12
and call it as
cmd /c test.cmd
echo %ERRORLEVEL%
I get "12" on Windows Server 2003 R2, but "0" on XP. I thought I had tested this simple test case many times before but apparently not.
So, I've changed the tags and title but I'm leaving the other information here as there's actually a lot of useful stuff here that is not directly related to this issue.
Thoughts?
Original below
I have a custom action written in VBScript that in turn is calling a Windows batch file (the custom action is essentially allowing the user to execute something at install time they can also run later by running the batch file - it's a convenience). The function is below:
Function MainFunction
strCustomActionData = Session.Property("CustomActionData")
strSplit = Split(strCustomActionData, ";")
strInstallDir = strSplit(0)
strPostCopyAction = strSplit(1)
strScriptLocation = strInstallDir & "\MigrationMasterProcess.cmd"
strFullCommand = """" & strScriptLocation & """ " & strPostCopyAction
Set objShell = CreateObject("WScript.Shell")
Dim objExec
Set objExec = objShell.Exec(strFullCommand)
intReturnCode = objExec.ExitCode
Set objExec = Nothing
Set objShell = Nothing
WriteMessage "Return value: " & intReturnCode
' cf. http://msdn.microsoft.com/en-us/library/windows/desktop/aa371254(v=vs.85).aspx
If (intReturnCode = 0) Then
MainFunction = 1
Else
MainFunction = 3
End If
End Function
When I run the same kind of code outside of a custom action, and the batch file returns an error code (via EXIT /B), the return value is correctly captured in intReturnCode. However, from the custom action, the exit code seems to be "lost" - I always get a 0 back (I can see this in the installer log from the WriteMessage call). It doesn't matter if I use Exec or Run on the shell, I still get back a 0. The script writes its own return code out before returning it (I can see this in the stdout stream from Exec) so I know it's not actually 0. I need that return code to properly report an error back to the installer.
Ideas?
For the record this is Windows Installer 3.0 on Windows XP SP3. The installer is in Wise so I don't have a WiX snippet or I would include it, but this is the function being called.
Also this is somewhat stripped - I've left out comments and other calls to WriteMessage as well as that function. And yes psuedo-Hungarian is evil blah blah blah.
Edit: Here is the C version of the code. It's giving the same exact issue:
#include <Windows.h>
#include <msi.h>
#include <msiquery.h>
#include <stdio.h>
#include <stdlib.h>
#include "LaunchChildProcess.h"
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) {
return TRUE;
}
UINT __stdcall RunMigrationAction(MSIHANDLE hModule) {
UINT uiStat;
DWORD dwPropertySize = MAX_PATH * 2;
TCHAR szValueBuf[MAX_PATH * 2]; // arbitrary but we know the strings won't be near that long
TCHAR *szInstallDir, *szPostCopyAction;
TCHAR *szNextToken;
TCHAR szScriptLocation[MAX_PATH * 2];
TCHAR szParameters[MAX_PATH * 2];
INT iReturnValue;
LogTaggedString(hModule, TEXT("Action Status"), TEXT("Starting"));
uiStat = MsiGetProperty(hModule, TEXT("CustomActionData"), szValueBuf, &dwPropertySize);
if (ERROR_SUCCESS != uiStat) {
LogTaggedString(hModule, TEXT("Startup"), TEXT("Failed to get custom action data"));
return ERROR_INSTALL_FAILURE;
}
LogTaggedString(hModule, TEXT("Properties given"), szValueBuf);
LogTaggedInteger(hModule, TEXT("Property length"), dwPropertySize);
if (0 == dwPropertySize) {
return ERROR_INSTALL_FAILURE;
}
LogTaggedString(hModule, TEXT("Properties given"), szValueBuf);
szInstallDir = wcstok_s(szValueBuf, TEXT(";"), &szNextToken);
szPostCopyAction = wcstok_s(NULL, TEXT(";"), &szNextToken);
LogTaggedString(hModule, TEXT("Install dir"), szInstallDir);
LogTaggedString(hModule, TEXT("Post-copy action"), szPostCopyAction);
wcscpy_s(szScriptLocation, MAX_PATH * 2, szInstallDir);
wcscat_s(szScriptLocation, MAX_PATH * 2, TEXT("\\MigrationMasterProcess.cmd"));
LogTaggedString(hModule, TEXT("Script location"), szScriptLocation);
wcscpy_s(szParameters, MAX_PATH * 2, TEXT(" /C "));
wcscat_s(szParameters, MAX_PATH * 2, szScriptLocation);
wcscat_s(szParameters, MAX_PATH * 2, TEXT(" "));
wcscat_s(szParameters, MAX_PATH * 2, szPostCopyAction);
LogTaggedString(hModule, TEXT("Parameters to cmd.exe"), szParameters);
iReturnValue = ExecuteProcess(TEXT("cmd.exe"), szParameters);
LogTaggedInteger(hModule, TEXT("Return value from command"), iReturnValue);
LogTaggedString(hModule, TEXT("Action Status"), TEXT("Finished"));
return (0 == iReturnValue) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
}
void LogTaggedInteger(MSIHANDLE hInstall, TCHAR* szTag, INT iValue) {
TCHAR szValue[15];
_itow_s(iValue, szValue, 15, 10);
LogTaggedString(hInstall, szTag, szValue);
}
void LogTaggedString(MSIHANDLE hInstall, TCHAR* szTag, TCHAR* szMessage) {
MSIHANDLE hRecord;
UINT uiStat;
//TCHAR szFullMessage[4096];
//wcscpy_s(szFullMessage, 4096, TEXT("--------------- "));
//wcscat_s(szFullMessage, 4096, szTag);
//wcscat_s(szFullMessage, 4096, TEXT(": "));
//wcscat_s(szFullMessage, 4096, szMessage);
hRecord = MsiCreateRecord(3);
uiStat = MsiRecordSetString(hRecord, 0, TEXT("--------- [1]: [2]"));
uiStat = MsiRecordSetString(hRecord, 1, szTag);
uiStat = MsiRecordSetString(hRecord, 2, szMessage);
uiStat = MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), hRecord);
MsiCloseHandle(hRecord);
return;
}
int MsiMessageBox(MSIHANDLE hInstall, TCHAR* szString, DWORD dwDlgFlags) {
PMSIHANDLE newHandle = ::MsiCreateRecord(2);
MsiRecordSetString(newHandle, 0, szString);
return (MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER + dwDlgFlags), newHandle));
}
DWORD ExecuteProcess(TCHAR *szProcess, TCHAR *szParams) {
INT iMyCounter = 0, iPos = 0;
DWORD dwReturnVal = 0;
TCHAR *sTempStr = L"";
/* CreateProcessW can modify Parameters thus we allocate needed memory */
wchar_t * pwszParam = new wchar_t[wcslen(szParams) + 1];
if (NULL == pwszParam) {
return 1;
}
wcscpy_s(pwszParam, wcslen(szParams) + 1, szParams);
/* CreateProcess API initialization */
STARTUPINFOW siStartupInfo;
PROCESS_INFORMATION piProcessInfo;
memset(&siStartupInfo, 0, sizeof(siStartupInfo));
memset(&piProcessInfo, 0, sizeof(piProcessInfo));
siStartupInfo.cb = sizeof(siStartupInfo);
if (CreateProcessW(const_cast<LPCWSTR>(szProcess),
pwszParam, 0, 0, false,
CREATE_DEFAULT_ERROR_MODE, 0, 0,
&siStartupInfo, &piProcessInfo) != false) {
/* Watch the process. */
WaitForSingleObject(piProcessInfo.hProcess, INFINITE);
if (!GetExitCodeProcess(piProcessInfo.hProcess, &dwReturnVal)) {
dwReturnVal = GetLastError();
}
} else {
/* CreateProcess failed */
dwReturnVal = GetLastError();
}
/* Free memory */
free(pwszParam);
pwszParam = NULL;
/* Release handles */
CloseHandle(piProcessInfo.hProcess);
CloseHandle(piProcessInfo.hThread);
return dwReturnVal;
}
When run on my Windows Server 2003 R2 Visual Studio 2008 box, I get the error code as expected:
--------- Return value from command: 5023
When run on my Windows XP test box, I get a 0, even though it should be an error:
--------- Return value from command: 0
Both machines have Windows Installer 3.1. XP is 3.01.4001.5512, 2003 R2 is 3.01.4000.3959.
So it's something acting different between the boxes although I have no idea what.
EDIT 2
The exact table row for the action, as generated by the Wise for Windows Installer tool, is:
"RunMigrationActionCA","1537","Calllaunchchildprocess","RunMigrationAction","0"
To test the immediate flag I added 0x800 to the type column and no change was seen in the end behavior.
To be clear - this works fine on the 2003 R2 machine. That machine is not joined to a domain, but the XP machine is. Is there anything in group policy that could cause this behavior? (Grasping at straws at this point.)
It seems to be a bug in the cmd.exe of WinXP.
The solution is to use exit 123 instead of exit /b 123 in the batch file.
If you don't wish to change existing batch files, just add a wrapper.bat:
#echo off
call %*
exit %errorlevel%
And invoke it:
system("wrapper.bat your.bat all your args")
WScript objects don't work inside custom actions Please reader more here. You could use a DLL custom action. Here is a step by step tutorial.
I want to send some keystroke to the external application, and it's work fine, but when I try to send keystroke to the child window of same external application, for some reason that doesn't work, so I need help. Let's say that we want to print clipboard text from notepad, and want to do it at one step. At code that will look like this.
#include <windows.h>
#include <stdio.h>
#include <iostream.h>
using namespace std;
int main(int argc, char* argv[]){
WinExec("notepad", 1);
Sleep(1000);
HWND handle = FindWindow("notepad",0); // it's handling as well
SetForegroundWindow(handle);
keybd_event(VK_CONTROL, 0, 0, 0); // simulate CTRL down
keybd_event(VkKeyScan('V'), 0, 0, 0);
keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
Sleep(500);
keybd_event(VK_CONTROL, 0, 0, 0); // simulate CTRL down
keybd_event(VkKeyScan('P'), 0, 0, 0);
keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0); // simulate CTRL up
Sleep(1000);
HWND handle1 = FindWindow(0, "Print"); // it wann't find "Print" window
SetForegroundWindow(handle1);
keybd_event(VK_MENU, 0, 0, 0); // simulate ALT down
keybd_event(VkKeyScan('P'), 0, 0, 0);
keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
return 0;
}
But it want to send ALT+P to "Print" window, why?
Final goal is to make little macro that send to application keystorkes (on any windows child, or parent..)
OS: WIN 7 64bit
You can probably make the existing code (sort of) work by simply removing these lines:
HWND handle1 = FindWindow(0, "Print"); // it wann't find "Print" window
SetForegroundWindow(handle1);
Remember that faked input goes to the thread which has the input focus and when you show the print dialog in Notepad, that dialog will gain the input focus. You simply do not need to set the focus, the system will do that for you.
However, the approach you are taking is incredibly brittle. I suspect that you would be far better served by using something like UI Automation.
I am using plain Win32 API (no MFC I mean) to create a simple app. I use CreateWindowEx with STATUSCLASSNAME as specified in MSDN but the handle returned is NULL. I made a call to InitCommonControlsEx as indicated but that returns FALSE! So I suspect that's the reason why the bar isn't created. What's going on? Please help.
I am on Windows 7 64 bit.
hStatusBar = ::CreateWindowExW(
0,
L"STATUSCLASSNAME",
L"",
WS_VISIBLE|WS_CHILD|WS_BORDER,
0,0,0,0, hWnd, 0, hInstance, NULL
);
There is one simple mistake in your code. STATUSCLASSNAME is not a value this is a constant from < commctrl.h >. So you code actually should looks like this:
#include <commctrl.h>
.
.
.
hStatusBar = ::CreateWindowExW(
0,
STATUSCLASSNAME,
L"",
WS_VISIBLE|WS_CHILD|WS_BORDER,
0,0,0,0, hWnd, 0, hInstance, NULL
);
I suppose you was looking into this http://msdn.microsoft.com/en-us/library/bb775491%28v=VS.85%29.aspx#STATUSCLASSNAME but as it specified, there are constants in the left column not values
if you are using W (wide) chars and functions you have to change constant name (see inside commcrtl.h). I used:
status = CreateWindowExW(0, STATUSCLASSNAMEW,
L"Spec.chars fine - Czech=Česky", WS_VISIBLE | WS_CHILD | WS_BORDER,
0, 0, 0, 0, hwnd, 0, NULL, NULL);
and is seems to be ok.