Some Windows Function Magic Here...

Anything beyond the basics in using the LiveCode language. Share your handlers, functions and magic here.

Moderators: FourthWorld, heatherlaine, Klaus, kevinmiller, robinmiller

Post Reply
ThatOneGuy
Posts: 77
Joined: Fri Jan 04, 2013 9:57 am

Some Windows Function Magic Here...

Post by ThatOneGuy » Sun Feb 23, 2014 4:15 am

I'm still working on it, but I thought I might as well share what I have so far with others since this exists nowhere else online. Here's a small and very simple windows c++ executable I have been working on that allows you to lock programs to the desktop and/or modify the desktop workspace so that fullscreen windows don't cover your apps. These are both things that you cannot currently do inside of LC.

To lock a window to the desktop, send it a single argument of the window's id like "wSet 6312879" (no quotes). It will run in the background and ensure that the window doesn't move up in ordering (it might flicker when clicked sometimes though if your refresh rate is very high).

To change the workspace, send the rect of the desired new workspace as four seperate arguments on the command line. Using "wSet 50 0 1366 768" (no quotes) will reserve the space 50 pixels from the left side of the screen for a 1366x768 monitor and adjust all currently open windows to fit in the new area. Rerun it with the original values obtained from "the windowboundingrect" to revert the changes before closing.

You can try out the executable I attached or just compile the code below with the proper includes (windows, time, iostream).

Code: Select all

#include "stdafx.h"
using namespace std;

bool downLevel(HWND WindowID)
{
	if (!IsWindowVisible(WindowID))
		return false;
	if (SetWindowPos(WindowID, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOREPOSITION))
		return true;
	else
		return false;
}

bool setRect(long left, long top, long right, long bottom)
{
	RECT workarea;

	//Set up new work area size
	workarea.left = left;
	workarea.top = top;
	workarea.right = right;
	workarea.bottom = bottom;

	//Set the new work area and broadcast the change to all running applications
	if (SystemParametersInfo(SPI_SETWORKAREA, 0, &workarea, SPIF_SENDCHANGE))
		return true;
	else
		return false;
}

int main(int argc, char* argv[])
{
	if (argc == 2)
	{
		HWND tWindowID = (HWND)atoi(argv[1]);

		//Check if window can be set
		if (!IsWindowVisible(tWindowID))
		{
			cout << "ERROR: Invalid Window ID!";
			return 0;
		}
		//Lock window
		while (downLevel(tWindowID)) { Sleep(2); }
		cout << "Process Lost.";
		return 0;
	}
	else if (argc == 5)
	{
		if (!(setRect(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), atoi(argv[4]))))
			cout << "ERROR: Failed to edit system parameters.";
		return 0;
	}
	else
	{
		cout << "ERROR: Incorrect number of parameters!";
		return 0;
	}
}
If anyone can help me get these functions set up as externals to call from inside LC I would be very happy.

Have fun making things that were impossible before.
Last edited by ThatOneGuy on Sun Feb 23, 2014 5:45 pm, edited 1 time in total.

FourthWorld
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 10043
Joined: Sat Apr 08, 2006 7:05 am
Contact:

Re: Some Windows Function Magic Here...

Post by FourthWorld » Sun Feb 23, 2014 4:50 am

Well done. Thanks for sharing that.
Richard Gaskin
LiveCode development, training, and consulting services: Fourth World Systems
LiveCode Group on Facebook
LiveCode Group on LinkedIn

ThatOneGuy
Posts: 77
Joined: Fri Jan 04, 2013 9:57 am

Re: Some Windows Function Magic Here...

Post by ThatOneGuy » Sun Feb 23, 2014 6:28 am

I jumped the gun a bit earlier.
Here's another executable with a bunch more Windows API functions that might prove useful for anyone working on things that need them.

Now you can access other programs' windows as well. You'd probably never need to use a most of them, but here they are anyway. Just run the program without any arguments to see the list of functions.

It's very easy to mess things up using this (such as creating invisible windows that you can't find again or making impossible workspaces), so be careful.

I have enough functions for what I need now, so I might just stop here.

Here's the code:

Code: Select all

#include "stdafx.h"
using namespace std;

bool CALLBACK enumWindowsProc(
	__in  HWND hWnd,
	__in  LPARAM lParam
	) {
	if (!::IsIconic(hWnd)) {
		return true;
	}


	int length = ::GetWindowTextLength(hWnd);
	if (0 == length) return true;

	TCHAR* buffer;
	buffer = new TCHAR[length + 1];
	memset(buffer, 0, (length + 1) * sizeof(TCHAR));

	GetWindowText(hWnd, buffer, length + 1);
	wstring windowTitles = wstring(buffer);
	delete[] buffer;

	wcout << (long)hWnd << " " << windowTitles << std::endl;

	return true;
}

bool downLevel(HWND WindowID)
{
	if (!IsWindowVisible(WindowID))
		return false;
	if (SetWindowPos(WindowID, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOREPOSITION))
		return true;
	else
		return false;
}

bool setRect(long left, long top, long right, long bottom)
{
	RECT workarea;

	//Set up new work area size
	workarea.left = left;
	workarea.top = top;
	workarea.right = right;
	workarea.bottom = bottom;

	//Set the new work area and broadcast the change to all running applications
	if (SystemParametersInfo(SPI_SETWORKAREA, 0, &workarea, SPIF_SENDCHANGE))
		return true;
	else
		return false;
}

int main(int argc, char* argv[])
{
	ShowWindow(NULL, 0);
	if ((argc == 3) && ((string)argv[1] == "-d"))
	{
		HWND tWindowID = (HWND)(atoi(argv[2]));

		//Check if window can be set
		if (!IsWindowVisible(tWindowID))
		{
			cout << "ERROR: Invalid Window ID!";
			return 0;
		}
		//Lock window
		while (downLevel(tWindowID)) { Sleep(2); }
		cout << "Process Lost.";
		return 0;
	}
	else if ((argc == 6) && ((string)argv[1] == "-w"))
	{
		if (!(setRect(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5]))))
			cout << "ERROR: Failed to edit system parameters.";
		return 0;
	}
	else if ((argc == 2) && ((string)argv[1] == "-t"))
	{
		EnumWindows(enumWindowsProc, NULL);
		return 0;
	}
	else if ((argc == 2) && ((string)argv[1] == "-l"))
	{
		EnumWindows(enumWindowsProc, NULL);
		return 0;
	}
	else if ((argc == 3) && ((string)argv[1] == "-m"))
	{
		HWND hwnd = (HWND)(atoi(argv[2]));
		ShowWindow(hwnd, SW_MAXIMIZE);
		return 0;
	}
	else if ((argc == 3) && ((string)argv[1] == "-r"))
	{
		HWND hwnd = (HWND)(atoi(argv[2]));
		ShowWindow(hwnd, SW_RESTORE);
		return 0;
	}
	else if ((argc == 3) && ((string)argv[1] == "-f"))
	{
		HWND hwnd = (HWND)(atoi(argv[2]));
		ShowWindow(hwnd, SW_FORCEMINIMIZE);
		return 0;
	}
	else if ((argc == 3) && ((string)argv[1] == "-h"))
	{
		HWND hwnd = (HWND)(atoi(argv[2]));
		ShowWindow(hwnd, SW_HIDE);
		return 0;
	}
	else if ((argc == 3) && ((string)argv[1] == "-u"))
	{
		HWND hwnd = (HWND)(atoi(argv[2]));
		ShowWindow(hwnd, SW_SHOW);
		return 0;
	}
	else if ((argc == 3) && ((string)argv[1] == "-t"))
	{
		HWND hwnd = (HWND)(atoi(argv[2]));
		cout << std::boolalpha << (bool)IsWindow(hwnd);
		return 0;
	}
	else
	{
		cout << "\n\tUsage:\n"
			<< "\t  -w [left] [top] [right] [bottom] \n\t    Adjust fullscreen window area.\n"
			<< "\t  -l [] \n\t    Retrieve list of all minimized windows (broken).\n"
			<< "\t  -d [id] \n\t    Lock window to desktop.\n"
			<< "\t  -r [id] \n\t    Restore window.\n"
			<< "\t  -m [id] \n\t    Maximize window.\n"
			<< "\t  -f [id] \n\t    Force-minimize window.\n"
			<< "\t  -h [id] \n\t    Hide window.\n"
			<< "\t  -u [id] \n\t    Un-hide window.\n"
			<< "\t  -t [id] \n\t    Tests if id names a window.\n";
		return 0;
	}
}

ThatOneGuy
Posts: 77
Joined: Fri Jan 04, 2013 9:57 am

Re: Some Windows Function Magic Here...

Post by ThatOneGuy » Mon Feb 24, 2014 2:39 am

It's just one of those weekends when I don't have anything better to do than to play around with this... so here's some more stuff.

I've added a few more functions and set it up to be called from inside the IDE without shell calls. The ".ext" executable I added works like a small, backgrounded extension to LiveCode that can receive calls that resemble internal functions, such as the one "set rect 37819389 50 50 1300 750" that sets the rect of the external window with ID 37819389 to 50,50,1300,750.

All you need to do is open the process and leave it open as long as your program needs these functions, write the function calls to the process, and read the result back. Each result terminates in line with single "#" character, so you know when to stop reading. If you pass up this character and keep reading, then your program will freeze while it waits for more output. See the attached livecode sample to see how it works.

Here's the functions it has now:

Usage: [command] [value] [#] [#] [#] ...
get list [#]: Retrieve list of windows.
[0] - All minimized.
[1] - All other.
[2] - All.
get iswindow [id]: Test if id names a window.
get rect [id]: Retrieve rect of specified window.
set desktop [id]: Lock window to desktop.
set workspace [left] [top] [right] [bottom]: Set area for fullscreen windows.
set maximized [id]: Maximize specified window.
set minimized [id]: Minimize specified window.
set restored [id]: Restore specified window to default position.
set hidden [id]: Hide specified window.
set unhidden [id]: Unhide specified window.
set rect [left] [top] [right] [bottom]: Set rect of specified window.
help: Show this message.
quit: Close this process.

Using "get list" with anything other than 0 is returns a huge list and doesn't make much sense. So if you need to get window lists, try to avoid using 1 and 2 unless you can discern the real ones from the hidden and system ones somehow.

To set the rect of an external window, it must first be not minimized and not maximized. If you forget this, then the function will not do anything when called. You can use "set restored" to fix this.

If you use "set hidden" to make an external window invisible, make sure you keep track of the window ID you used. If you forget the ID, then you will have a very hard time unhiding the window.

If you need other things it would be very easy for me to add them in, so just ask.

Here's the code again:

Code: Select all

#include "stdafx.h"
using namespace std;

int global_cmp = NULL;
//bool global_exit_status;
//bool global_thread_locked;

BOOL CALLBACK enumWindowsProc(__in HWND hWnd, __in LPARAM lParam)
{
	if ((global_cmp == 0) && (!::IsIconic(hWnd)))
		return TRUE;
	else if ((global_cmp == 1) && (::IsIconic(hWnd)))
		return TRUE;

	int length = ::GetWindowTextLength(hWnd);
	if (0 == length) return TRUE;

	TCHAR* buffer;
	buffer = new TCHAR[length + 1];
	memset(buffer, 0, (length + 1) * sizeof(TCHAR));

	GetWindowText(hWnd, buffer, length + 1);
	wstring windowTitle = wstring(buffer);
	delete[] buffer;

	string str(windowTitle.begin(), windowTitle.end());

	cout << (long)hWnd << " " << str << std::endl;

	return TRUE;
}

bool setWRect(HWND WindowID, int x, int y, int cx, int cy)
{
	if (SetWindowPos(WindowID, HWND_TOP, x, y, cx - x, cy - y, SWP_NOACTIVATE | SWP_NOOWNERZORDER))
		return true;
	else
		return false;
}

bool downLevel(HWND WindowID)
{
	HWND ProgmanHwnd =
		::FindWindowEx(
		::FindWindowEx(
		::FindWindow(L"Progman", L"Program Manager"),
		NULL,
		L"SHELLDLL_DefView",
		L""),
		NULL,
		L"SysListView32",
		L"FolderView");
	::SetParent(WindowID, ProgmanHwnd);	

	//global_thread_locked = true;
	//while (!(global_exit_status))
	//{
		//if (!IsWindowVisible(WindowID))
			//return false;
		//if (!(SetWindowPos(WindowID, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOREPOSITION)))
			//global_exit_status = true;
		//Sleep(1);
	//}
	return false;
}

bool setRect(long left, long top, long right, long bottom)
{
	RECT workarea;

	//Set up new work area size
	workarea.left = left;
	workarea.top = top;
	workarea.right = right;
	workarea.bottom = bottom;

	//Set the new work area and broadcast the change to all running applications
	if (SystemParametersInfo(SPI_SETWORKAREA, 0, &workarea, SPIF_SENDCHANGE))
		return true;
	else
		return false;
}

int main(int argc, char* argv[])
{
	string command;
	string function;
	HWND hwnd;
	string word;
	string t;
	RECT rect;
	int args;
	ShowWindow(NULL, 0);

	int arg1, arg2, arg3, arg4, arg5, arg6;

	while (((string)command != "quit") && ((string)command != "exit"))
	{
		getline(cin, t);
		args = 0;
		istringstream iss(t);

		if (iss >> word)
		{
			command = word;
		}
		if (iss >> word)
		{
			function = word;
		}
		if (iss >> word)
		{
			arg1 = atoi(word.c_str());
			args++;
		}
		if (iss >> word)
		{
			arg2 = atoi(word.c_str());
			args++;
		}
		if (iss >> word)
		{
			arg3 = atoi(word.c_str());
			args++;
		}
		if (iss >> word)
		{
			arg4 = atoi(word.c_str());
			args++;
		}
		if (iss >> word)
		{
			arg5 = atoi(word.c_str());
			args++;
		}
		if (iss >> word)
		{
			arg6 = atoi(word.c_str());
			args++;
		}

		t = "";

		if (command == "help")
		{
			cout << " Usage:   [command] [value] [#] [#] [#] ...\n"
				<< "  get list [#]\n     Retrieve list of windows.\n"
				<< "     [0] - All minimized.\n"
				<< "     [1] - All other.\n"
				<< "     [2] - All.\n"
				<< "  get iswindow [id]\n     Test if id names a window.\n"
				<< "  get rect [id]\n     Retrieve rect of specified window.\n"
				//<< "  get release []\n     Unlock desktop thread.\n"
				<< "  set desktop [id]\n     Lock window to desktop.\n"// Locks thread.\n"
				<< "  set workspace [left] [top] [right] [bottom]\n     Set area for fullscreen windows.\n"
				<< "  set maximized [id]\n     Maximize specified window.\n"
				<< "  set minimized [id]\n     Minimize specified window.\n"
				<< "  set restored [id]\n     Restore specified window to default position.\n"
				<< "  set hidden [id]\n     Hide specified window.\n"
				<< "  set unhidden [id]\n     Unhide specified window.\n"
				<< "  set rect [left] [top] [right] [bottom]\n     Set rect of specified window.\n"
				<< "  help\n     Show this message.\n"
				<< "  quit\n     Close this process.\n"
				<< "#";
		}
		else if (command == "get")
		{
			if (function == "list")
			{
				if ((unsigned int)arg1 > 2)
				{
					cout << "ERROR: Invalid argument.\n";
				}
				else
				{
					global_cmp = (unsigned int)arg1;
					EnumWindows(enumWindowsProc, NULL);
				}
			}
			else if (function == "iswindow")
			{
				hwnd = (HWND)(arg1);
				cout << boolalpha << IsWindow(hwnd) << endl;
			}
			else if (function == "rect")
			{
				hwnd = (HWND)(arg1);
				GetWindowRect(hwnd, &rect);
				cout << rect.left << ' ' << rect.top << ' ' << rect.right << ' ' << rect.bottom << endl;
			}
			else if (function == "release")
			{
				//global_exit_status = true;
				//global_thread_locked = false;
			}
			else
			{
				cout << "ERROR: Unknown function.\n";
			}
		}
		else if (command == "set")
		{
			if (function == "desktop")
			{
				if (args == 1)
				{
					hwnd = (HWND)(arg1);
					
					if (!IsWindowVisible(hwnd))
					{
						cout << "ERROR: Invalid Window ID!\n";
					}
					else
						downLevel(hwnd);
					}
					else
						cout << "ERROR: Invalid arguments\n";
			}
			else if (function == "workspace")
			{
				if (args == 4)
				{
					if (!(setRect(arg1, arg2, arg3, arg4)))
						cout << "ERROR: Failed to edit system parameters.\n";
				}
				else
					cout << "ERROR: Invalid arguments\n";
			}
			else if (function == "maximized")
			{
				if (args == 1)
				{
					hwnd = (HWND)(arg1);
					ShowWindow(hwnd, SW_MAXIMIZE);
				}
				else
					cout << "ERROR: Invalid arguments\n";
			}
			else if (function == "minimized")
			{
				if (args == 1)
				{
					hwnd = (HWND)(arg1);
					ShowWindow(hwnd, SW_FORCEMINIMIZE);
				}
				else
					cout << "ERROR: Invalid arguments\n";
			}
			else if (function == "restored")
			{
				if (args == 1)
				{
					hwnd = (HWND)(arg1);
					ShowWindow(hwnd, SW_RESTORE);
				}
				else
					cout << "ERROR: Invalid arguments\n";
			}
			else if (function == "hidden")
			{
				if (args == 1)
				{
					hwnd = (HWND)(arg1);
					ShowWindow(hwnd, SW_HIDE);
				}
				else
					cout << "ERROR: Invalid arguments\n";
			}
			else if (function == "unhidden")
			{
				if (args == 1)
				{
					hwnd = (HWND)(arg1);
					ShowWindow(hwnd, SW_SHOW);
				}
				else
					cout << "ERROR: Invalid arguments\n";

			}
			else if (function == "rect")
			{
				if (args == 5)
				{
					hwnd = (HWND)(arg1);
					if (!(setWRect(hwnd, arg2, arg3, arg4, arg5)))
						cout << "ERROR: Failed to edit window.\n";
				}
				else
					cout << "ERROR: Invalid arguments\n";
			}
			else
			{
				cout << "ERROR: Unknown function.\n";
			}

		}
		cout << "#\n";
	}
	return 0;
}
EDIT:

I fixed the odd flickering on the "set desktop" function and removed the "release" function since there's no more multi-threading involved, but now it requires explorer.exe running for that function it to work. I think that should be OK for most people.

In a program I'm working on, I just edited the code I posted in the sample here to create a function so I can just use "put wSettings("set workspace 0 24 1366 768")" and the like to run any of these commands. It's much faster than those old executables I was using before and definitely much better than the freeware that was my only other option.

I also made the horrible mistake of using both wide and narrow range outputs. Now that is fixed as well.
Attachments
wSettings test.zip
(14.61 KiB) Downloaded 279 times
Last edited by ThatOneGuy on Mon Feb 24, 2014 6:47 am, edited 3 times in total.

Simon
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3901
Joined: Sat Mar 24, 2007 2:54 am

Re: Some Windows Function Magic Here...

Post by Simon » Mon Feb 24, 2014 3:47 am

Hi ThatOneGuy,
I was just about to post a link to this thread I remembered, it was this guy that kept going on and on about some z order function request and thought this might help him...
Then I saw who started that thread :)

Congratulation!
Great to see someone "just getting the job done".

Simon
I used to be a newbie but then I learned how to spell teh correctly and now I'm a noob!

ThatOneGuy
Posts: 77
Joined: Fri Jan 04, 2013 9:57 am

Re: Some Windows Function Magic Here...

Post by ThatOneGuy » Mon Feb 24, 2014 5:41 am

Thanks Simon.

I just figured, if I knew how to do it in c++, and I didn't know how to make an "external" for LiveCode, I might as well make an extension process instead. There's really nothing to it.

I decided to start putting my progress on here as I go because I'm sure there's someone out there who's trying/wanting to do some of the same things as me and couldn't figure it out and didn't want to ask.

I'm not sure if this will come in handy for anyone, but here's hoping.

FourthWorld
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 10043
Joined: Sat Apr 08, 2006 7:05 am
Contact:

Re: Some Windows Function Magic Here...

Post by FourthWorld » Tue Feb 25, 2014 1:41 am

It's a great contribution to the community and I appreciate your posting it.

If anyone needs a cross-platform implementation, on Linux this article by Kyle Rankin from Linux Journal explains how to use wmctril:
http://www.linuxjournal.com/magazine/ha ... top-wmctrl

Anyone have a solution for always-behind on OS X?
Richard Gaskin
LiveCode development, training, and consulting services: Fourth World Systems
LiveCode Group on Facebook
LiveCode Group on LinkedIn

paul_gr
Posts: 319
Joined: Fri Dec 08, 2006 7:38 pm

Re: Some Windows Function Magic Here...

Post by paul_gr » Thu Feb 27, 2014 7:47 pm

ThatOneGuy wrote:I didn't know how to make an "external" for LiveCode, I might as well make an extension process instead. There's really nothing to it.
Thanks for that info, very useful. Lot easier than making an external.

Paul

Post Reply