Last active
August 29, 2015 14:00
-
-
Save quark-zju/11246861 to your computer and use it in GitHub Desktop.
Win32 TCP service to provide windows titles and their flashing status.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# Example ruby client of winlist-server. | |
HOST = '172.22.22.1' | |
PORT = 4015 | |
require 'socket' | |
require 'timeout' | |
def get_flashing_window_list | |
Timeout.timeout(10) do | |
TCPSocket.new(HOST, PORT).read.force_encoding(Encoding::UTF_8).lines.select{|l| l.start_with? '*'}.map{|l| l[2..-2].split(' ', 2)} rescue nil | |
end rescue nil | |
end | |
notified = {} | |
while sleep 1 | |
list = get_flashing_window_list | |
next unless list | |
# parse list | |
# list.lines.map | |
map = Hash[list] | |
map.each do |hwnd, title| | |
next if notified[hwnd] | |
notified[hwnd] = true | |
system 'notify-send', '-t', '0', '新闪动窗口', title | |
end | |
notified.delete_if {|hwnd| !map[hwnd]} | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Win32 TCP service to provide windows titles and their flashing status. | |
This file is POORLY written. Do not take it too seriously. | |
Example Usage: | |
telnet 10.0.2.2 4015 # 10.0.2.2 is vbox host | |
Trying 10.0.2.2... | |
Connected to 10.0.2.2. | |
Escape character is '^]'. | |
2016a 开始 | |
* 910b8a Alice - Bob | |
48132e Win32Project1 (正在运行) - Microsoft Visual Studio Express 2013 for Windows Desktop | |
* 260416 我的手机 | |
3d013c 无标题 - 记事本 | |
7d0b30 下载 | |
881354 C:\windows\system32\cmd.exe | |
10230 易信 | |
220408 C:\windows\system32\cmd.exe | |
240fce Program Manager | |
Should compile under VC++ 2013 express. | |
Note: '*' indicates that window is flashing. | |
Note about security: client ip whitelist is hardcoded now. | |
*/ | |
#include <WinSock2.h> | |
#include <WS2tcpip.h> | |
#include <windows.h> | |
#include <cstdio> | |
#include <cstdlib> | |
#include <iostream> | |
#include <cassert> | |
#include <ctime> | |
#include <concurrent_unordered_map.h> | |
#pragma comment(lib, "user32.lib") | |
#pragma comment(lib, "gdi32.lib") | |
#pragma comment(lib, "Ws2_32.lib") | |
#define PORT 4015 | |
static UINT WM_SHELLHOOKMESSAGE; | |
using std::cout; | |
using std::wcout; | |
using concurrency::concurrent_unordered_map; | |
concurrent_unordered_map<HWND, clock_t> flashingWindowHwnds; | |
volatile SOCKET client; | |
void BeginThread(); | |
// Enum window titles | |
BOOL CALLBACK enumWindowsCallback(HWND hwnd, LPARAM IParam) { | |
int l = GetWindowTextLengthW(hwnd) + 1; | |
// ignore invisible windows | |
LONG style = GetWindowLong(hwnd, GWL_STYLE); | |
if ((style & WS_VISIBLE) == 0) return TRUE; | |
// ignore empty title windows | |
if (l <= 1) return TRUE; | |
// Get window title | |
WCHAR *s; | |
s = (WCHAR *)calloc(l, sizeof(WCHAR)); | |
l = GetWindowTextW(hwnd, s, l); | |
s[l] = 0; | |
wcout << s << L"\n"; | |
// WCHAR -> UTF8 | |
char *utf8Buf; | |
utf8Buf = (char *)calloc(1, l * 4 + 4); | |
WideCharToMultiByte( | |
CP_UTF8, /* CodePage */ | |
0, /* dwFlags */ | |
s, /* lpWideCharStr */ | |
-1, /* cchWideChar (null terminated) */ | |
utf8Buf, /* lpMultiByteStr */ | |
l * 4 + 3, /* cbMultiByte */ | |
NULL, | |
NULL | |
); | |
// Send window hwnd, title, flashing status to client | |
size_t resultBufSize = l * 4 + 128; | |
char *resultBuf = (char *)calloc(1, resultBufSize); | |
int flash = (flashingWindowHwnds.count(hwnd) > 0 && flashingWindowHwnds[hwnd]); | |
_snprintf(resultBuf, resultBufSize, "%c %x %s\n", flash ? '*' : ' ', hwnd, utf8Buf); | |
send(client, resultBuf, strlen(resultBuf), 0); | |
free(utf8Buf); | |
free(s); | |
free(resultBuf); | |
return TRUE; | |
} | |
void startEnumWindows() { | |
EnumWindows(enumWindowsCallback, 0); | |
} | |
// http://www.howsoftworks.net/windows/user-interface/registershellhookwindow.html | |
void SetShellHook(HWND hwndHook) | |
{ | |
WM_SHELLHOOKMESSAGE = RegisterWindowMessage(TEXT("SHELLHOOK")); | |
assert(WM_SHELLHOOKMESSAGE); | |
BOOL ret = RegisterShellHookWindow(hwndHook); | |
assert(ret); | |
} | |
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) | |
{ | |
HWND clientHwnd = (HWND) lParam; | |
if (uMsg == WM_SHELLHOOKMESSAGE) | |
{ | |
switch (wParam) | |
{ | |
case HSHELL_FLASH: | |
flashingWindowHwnds[clientHwnd] = clock(); | |
break; | |
case HSHELL_WINDOWACTIVATED: | |
if (flashingWindowHwnds.count(clientHwnd)) { | |
clock_t lastFlashAt = flashingWindowHwnds[clientHwnd]; | |
// Some program will active their window as soon as flashing. Distinct it from maually activing by checking time. | |
if (lastFlashAt > 0 && clock() - lastFlashAt >= 500) { | |
flashingWindowHwnds[clientHwnd] = 0; | |
} | |
} | |
break; | |
case HSHELL_WINDOWDESTROYED: | |
if (flashingWindowHwnds.count(clientHwnd) && flashingWindowHwnds[clientHwnd]) { | |
flashingWindowHwnds[clientHwnd] = 0; | |
} | |
break; | |
case HSHELL_WINDOWCREATED: | |
break; | |
default: | |
break; | |
} | |
return 0; | |
} | |
return DefWindowProc(hwnd, uMsg, wParam, lParam); | |
} | |
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) | |
{ | |
static TCHAR szAppName[] = TEXT("WindowListService"); | |
MSG msg; | |
WNDCLASS wndclass; | |
BOOL bRet; | |
wndclass.style = CS_HREDRAW | CS_VREDRAW; | |
wndclass.lpfnWndProc = WndProc; | |
wndclass.cbClsExtra = 0; | |
wndclass.cbWndExtra = 0; | |
wndclass.hInstance = hInstance; | |
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); | |
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); | |
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); | |
wndclass.lpszMenuName = NULL; | |
wndclass.lpszClassName = szAppName; | |
bRet = RegisterClass(&wndclass); | |
assert(bRet); | |
HWND hWnd = CreateWindowExA(WS_EX_TOPMOST, | |
"WindowListService", | |
"WindowListService", | |
WS_OVERLAPPEDWINDOW, | |
CW_USEDEFAULT, | |
CW_USEDEFAULT, | |
1, 1, | |
NULL, NULL, hInstance, NULL); | |
assert(hWnd); | |
SetShellHook(hWnd); | |
BeginThread(); | |
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) | |
{ | |
if (bRet == -1) | |
{ | |
break; | |
} | |
TranslateMessage(&msg); | |
DispatchMessage(&msg); | |
} | |
return msg.wParam; | |
} | |
// http://www.codeproject.com/Articles/1891/Beginning-Winsock-Programming-Simple-TCP-server | |
DWORD WINAPI ServerThread(LPVOID pParam) | |
{ | |
//A SOCKET is simply a typedef for an unsigned int. | |
//In Unix, socket handles were just about same as file | |
//handles which were again unsigned ints. | |
//Since this cannot be entirely true under Windows | |
//a new data type called SOCKET was defined. | |
SOCKET server; | |
//WSADATA is a struct that is filled up by the call | |
//to WSAStartup | |
WSADATA wsaData; | |
//The sockaddr_in specifies the address of the socket | |
//for TCP/IP sockets. Other protocols use similar structures. | |
sockaddr_in local; | |
//WSAStartup initializes the program for calling WinSock. | |
//The first parameter specifies the highest version of the | |
//WinSock specification, the program is allowed to use. | |
int wsaret = WSAStartup(0x101, &wsaData); | |
//WSAStartup returns zero on success. | |
//If it fails we exit. | |
assert(wsaret == 0); | |
//Now we populate the sockaddr_in structure | |
local.sin_family = AF_INET; //Address family | |
local.sin_addr.s_addr = INADDR_ANY; //Wild card IP address | |
local.sin_port = htons((u_short)PORT); //port to use | |
//the socket function creates our SOCKET | |
server = socket(AF_INET, SOCK_STREAM, 0); | |
//If the socket() function fails we exit | |
assert(server != INVALID_SOCKET); | |
//bind links the socket we just created with the sockaddr_in | |
//structure. Basically it connects the socket with | |
//the local address and a specified port. | |
//If it returns non-zero quit, as this indicates error | |
int ret = bind(server, (sockaddr*)&local, sizeof(local)); | |
assert(ret == 0); | |
//listen instructs the socket to listen for incoming | |
//connections from clients. The second arg is the backlog | |
ret = listen(server, 10); | |
assert(ret == 0); | |
//we will need variables to hold the client socket. | |
//thus we declare them here. | |
sockaddr_in from; | |
int fromlen = sizeof(from); | |
while (true)//we are looping endlessly | |
{ | |
//accept() will accept an incoming | |
//client connection | |
client = accept(server, (struct sockaddr*)&from, &fromlen); | |
char * clientIp = inet_ntoa(from.sin_addr); | |
if (strcmp(clientIp, "172.22.22.2") == 0 || strcmp(clientIp, "127.0.0.1") == 0) { | |
startEnumWindows(); | |
} | |
//close the client socket | |
closesocket(client); | |
} | |
//closesocket() closes the socket and releases the socket descriptor | |
closesocket(server); | |
//originally this function probably had some use | |
//currently this is just for backward compatibility | |
//but it is safer to call it as I still believe some | |
//implementations use this to terminate use of WS2_32.DLL | |
WSACleanup(); | |
return 0; | |
} | |
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682516%28v=vs.85%29.aspx | |
void BeginThread() | |
{ | |
DWORD dwThreadId; | |
HANDLE hThread; | |
hThread = CreateThread( | |
NULL, // default security attributes | |
0, // use default stack size | |
ServerThread, // thread function name | |
NULL, // argument to thread function | |
0, // use default creation flags | |
&dwThreadId); // returns the thread identifier | |
assert(hThread); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment