I am trying to write a python routine to save and restore desktop icon positions. I am using 32-bit python 2.7 on Windows 7 x64. Using information from here (stack exchange), I am able to read the icon names and positions from the foreign process list view that Windows uses to store this info, but I fail when using LVM_SETITEMPOSITION to set new positions (or restore the positions). All of the icons end up in the same exact spot on the desktop. 'Auto arrange' and 'align to grid' are off. The relevant code is towards the bottom. WARNING: if you run this code all of your icons will be in a pile :(
import ctypes
class LVITEMW(ctypes.Structure):
_fields_ = [
('mask', ctypes.c_uint32),
('iItem', ctypes.c_int32),
('iSubItem', ctypes.c_int32),
('state', ctypes.c_uint32),
('stateMask', ctypes.c_uint32),
('pszText', ctypes.c_uint64),
('cchTextMax', ctypes.c_int32),
('iImage', ctypes.c_int32),
('lParam', ctypes.c_uint64), # On 32 bit should be c_long
('iIndent',ctypes.c_int32),
('iGroupId', ctypes.c_int32),
('cColumns', ctypes.c_uint32),
('puColumns', ctypes.c_uint64),
('piColFmt', ctypes.c_int64),
('iGroup', ctypes.c_int32),
]
class POINT(ctypes.Structure):
_fields_ = [('x', ctypes.c_int), ('y', ctypes.c_int)]
def icon_save_restore(savedicons=None, restore=False):
import struct, commctrl, win32gui, win32con, win32api
dthwnd = win32gui.FindWindow(None, 'Program Manager')
ukhwnd = win32gui.GetWindow(dthwnd, win32con.GW_CHILD)
slvhwnd = win32gui.GetWindow(ukhwnd, win32con.GW_CHILD)
pid = ctypes.create_string_buffer(4)
p_pid = ctypes.addressof(pid)
ctypes.windll.user32.GetWindowThreadProcessId(slvhwnd, p_pid)
hProcHnd = ctypes.windll.kernel32.OpenProcess(win32con.PROCESS_ALL_ACCESS, False, struct.unpack("i",pid)[0])
pBuffertxt = ctypes.windll.kernel32.VirtualAllocEx(hProcHnd, 0, 4096, win32con.MEM_RESERVE|win32con.MEM_COMMIT, win32con.PAGE_READWRITE)
copied = ctypes.create_string_buffer(4)
p_copied = ctypes.addressof(copied)
lvitem = LVITEMW()
lvitem.mask = ctypes.c_uint32(commctrl.LVIF_TEXT)
lvitem.pszText = ctypes.c_uint64(pBuffertxt)
lvitem.cchTextMax = ctypes.c_int32(4096)
lvitem.iSubItem = ctypes.c_int32(0)
pLVI = ctypes.windll.kernel32.VirtualAllocEx(hProcHnd, 0, 4096, win32con.MEM_RESERVE| win32con.MEM_COMMIT, win32con.PAGE_READWRITE)
win32api.SetLastError(0)
ctypes.windll.kernel32.WriteProcessMemory(hProcHnd, pLVI, ctypes.addressof(lvitem), ctypes.sizeof(lvitem), p_copied)
num_items = win32gui.SendMessage(slvhwnd, commctrl.LVM_GETITEMCOUNT)
if restore is False:
p = POINT()
pBufferpnt = ctypes.windll.kernel32.VirtualAllocEx(hProcHnd, 0, ctypes.sizeof(p), win32con.MEM_RESERVE|win32con.MEM_COMMIT, win32con.PAGE_READWRITE)
icons = {}
for i in xrange(num_items):
# Get icon text
win32gui.SendMessage(slvhwnd, commctrl.LVM_GETITEMTEXT, i, pLVI)
target_bufftxt = ctypes.create_string_buffer(4096)
ctypes.windll.kernel32.ReadProcessMemory(hProcHnd, pBuffertxt, ctypes.addressof(target_bufftxt), 4096, p_copied)
key = target_bufftxt.value
# Get icon position
win32api.SendMessage(slvhwnd, commctrl.LVM_GETITEMPOSITION, i, pBufferpnt)
p = POINT()
ctypes.windll.kernel32.ReadProcessMemory(hProcHnd, pBufferpnt, ctypes.addressof(p), ctypes.sizeof(p), p_copied)
icons[key] = (i,p)
ctypes.windll.kernel32.VirtualFreeEx(hProcHnd, pLVI, 0, win32con.MEM_RELEASE)
ctypes.windll.kernel32.VirtualFreeEx(hProcHnd, pBuffertxt, 0, win32con.MEM_RELEASE)
ctypes.windll.kernel32.VirtualFreeEx(hProcHnd, pBufferpnt, 0, win32con.MEM_RELEASE)
win32api.CloseHandle(hProcHnd)
return icons
else: # RESTORE ICON POSITIONS PROBLEM IS HERE SOMEWHERE!!!
win32gui.SendMessage(slvhwnd, win32con.WM_SETREDRAW, 0, 0)
for i in xrange(num_items):
# Get icon text
win32gui.SendMessage(slvhwnd, commctrl.LVM_GETITEMTEXT, i, pLVI)
target_bufftxt = ctypes.create_string_buffer(4096)
ctypes.windll.kernel32.ReadProcessMemory(hProcHnd, pBuffertxt, ctypes.addressof(target_bufftxt), 4096, p_copied)
key = target_bufftxt.value
if key in savedicons.keys():
# Set icon position
p = savedicons[key][1] # p is ctypes POINT
p_lng = point_to_long(p) # explicitly convert to HIWORD/LOWORD and c_long
# Reserve space for input variable in foreign process and get pointer to it the that memory space
pBufferpnt = ctypes.windll.kernel32.VirtualAllocEx(hProcHnd, 0, ctypes.sizeof(p_lng), win32con.MEM_RESERVE|win32con.MEM_COMMIT, win32con.PAGE_READWRITE)
# Write the desired coordinates in to the space just created
ret = ctypes.windll.kernel32.WriteProcessMemory(hProcHnd, pBufferpnt, ctypes.addressof(p_lng), ctypes.sizeof(p_lng), p_copied)
if ret == 0:
raise WindowsError
# Send the message to change the position for that item's index and the pointer to the new position
ret = win32gui.SendMessage(slvhwnd, commctrl.LVM_SETITEMPOSITION, i, pBufferpnt)
if ret == 0:
raise WindowsError
# Release the reserved memory for the variable (I recognize that I probably don't need to aLloc/free this within the loop)
ctypes.windll.kernel32.VirtualFreeEx(hProcHnd, pBufferpnt, 0, win32con.MEM_RELEASE)
win32gui.SendMessage(slvhwnd, win32con.WM_SETREDRAW, 1, 0)
ctypes.windll.kernel32.VirtualFreeEx(hProcHnd, pLVI, 0, win32con.MEM_RELEASE)
ctypes.windll.kernel32.VirtualFreeEx(hProcHnd, pBuffertxt, 0, win32con.MEM_RELEASE)
win32api.CloseHandle(hProcHnd)
return None
def point_to_long(p):
ret = (p.y * 0x10000) + (p.x & 0xFFFF)
return ctypes.c_long(ret)
if __name__ == '__main__':
mysavedicons = icon_save_restore(restore=False)
icon_save_restore(mysavedicons, restore=True)
I think there may be a problem with either 1) something to do with 32 bit and 64 bit memory address space, but the other components where I write the LVITEM structure or read the icon text work ok or 2) there is some issue in the way I am converting the coordinate information or calling SendMessage for GETITEMPOSITION. Any insight or help would be greatly appreciated.
Turns out that there is version that uses 32-bit addresses (LVM_SETITEMPOSITION32) that I wish MSDN would have cross-referenced in their documentation: https://msdn.microsoft.com/en-us/library/windows/desktop/bb761194(v=vs.85).aspx
This accepts the POINT structure directly so there is no need to try to convert to HIWORD/LOWORD. Before posting, I did indeed try using a 32 bit shift and longlong (64 bit version of long) and that didn't work either with LVM_SETITEMPOSITION. In any case, with the change, everything works as expected.
Related
I need to access all memory of a running process in my local Windows 7-64bit. I am new to winapi.
Here is my problem; Whenever I try to Open a process and reads its memory, I get Access is Denied error.
I searched and found something. It is said that If I run the main process as Administrator and use PROCESS_ALL_ACCESS on OpenProcess, I would have enough right to do it as it is said. OK, I did it. but nothing is changed. On reading memory, I still get Access is Denied.
So, I kept searching and found another thing which is enabling SeDebugPrivilege. I have also done that but nothing is changed. I still get the error.
I've read the quest and his answer here;
Windows Vista/Win7 Privilege Problem: SeDebugPrivilege & OpenProcess .
But as I said, I am really new to winapi. I could not solve my problem yet. Is there anything which which I need to configure in my local operating system?
Here is my Python code with pywin32;
from _ctypes import byref, sizeof, Structure
from ctypes import windll, WinError, c_buffer, c_void_p, create_string_buffer
from ctypes.wintypes import *
import win32security
import win32api
import gc
import ntsecuritycon
from struct import Struct
from win32con import PROCESS_ALL_ACCESS
from struct import calcsize
MEMORY_STATES = {0x1000: "MEM_COMMIT", 0x10000: "MEM_FREE", 0x2000: "MEM_RESERVE"}
MEMORY_PROTECTIONS = {0x10: "PAGE_EXECUTE", 0x20: "PAGE_EXECUTE_READ", 0x40: "PAGEEXECUTE_READWRITE",
0x80: "PAGE_EXECUTE_WRITECOPY", 0x01: "PAGE_NOACCESS", 0x04: "PAGE_READWRITE",
0x08: "PAGE_WRITECOPY"}
MEMORY_TYPES = {0x1000000: "MEM_IMAGE", 0x40000: "MEM_MAPPED", 0x20000: "MEM_PRIVATE"}
class MEMORY_BASIC_INFORMATION(Structure):
_fields_ = [
("BaseAddress", c_void_p),
("AllocationBase", c_void_p),
("AllocationProtect", DWORD),
("RegionSize", UINT),
("State", DWORD),
("Protect", DWORD),
("Type", DWORD)
]
class SYSTEM_INFO(Structure):
_fields_ = [("wProcessorArchitecture", WORD),
("wReserved", WORD),
("dwPageSize", DWORD),
("lpMinimumApplicationAddress", DWORD),
("lpMaximumApplicationAddress", DWORD),
("dwActiveProcessorMask", DWORD),
("dwNumberOfProcessors", DWORD),
("dwProcessorType", DWORD),
("dwAllocationGranularity", DWORD),
("wProcessorLevel", WORD),
("wProcessorRevision", WORD)]
class PyMEMORY_BASIC_INFORMATION:
def __init__(self, MBI):
self.MBI = MBI
self.set_attributes()
def set_attributes(self):
self.BaseAddress = self.MBI.BaseAddress
self.AllocationBase = self.MBI.AllocationBase
self.AllocationProtect = MEMORY_PROTECTIONS.get(self.MBI.AllocationProtect, self.MBI.AllocationProtect)
self.RegionSize = self.MBI.RegionSize
self.State = MEMORY_STATES.get(self.MBI.State, self.MBI.State)
# self.Protect = self.MBI.Protect # uncomment this and comment next line if you want to do a bitwise check on Protect.
self.Protect = MEMORY_PROTECTIONS.get(self.MBI.Protect, self.MBI.Protect)
self.Type = MEMORY_TYPES.get(self.MBI.Type, self.MBI.Type)
ASSUME_ALIGNMENT = True
class TARGET:
"""Given a ctype (initialized or not) this coordinates all the information needed to read, write and compare."""
def __init__(self, ctype):
self.alignment = 1
self.ctype = ctype
# size of target data
self.size = sizeof(ctype)
self.type = ctype._type_
# get the format type needed for struct.unpack/pack.
while hasattr(self.type, "_type_"):
self.type = self.type._type_
# string_buffers and char arrays have _type_ 'c'
# but that makes it slightly slower to unpack
# so swap is for 's'.
if self.type == "c":
self.type = "s"
# calculate byte alignment. this speeds up scanning substantially
# because we can read and compare every alignment bytes
# instead of every single byte.
# although if we are scanning for a string the alignment is defaulted to 1 \
# (im not sure if this is correct).
elif ASSUME_ALIGNMENT:
# calc alignment
divider = 1
for i in xrange(4):
divider *= 2
if not self.size % divider:
self.alignment = divider
# size of target ctype.
self.type_size = calcsize(self.type)
# length of target / array length.
self.length = self.size / self.type_size
self.value = getattr(ctype, "raw", ctype.value)
# the format string used for struct.pack/unpack.
self.format = str(self.length) + self.type
# efficient packer / unpacker for our own format.
self.packer = Struct(self.format)
def get_packed(self):
"""Gets the byte representation of the ctype value for use with WriteProcessMemory."""
return self.packer.pack(self.value)
def __str__(self):
return str(self.ctype)[:10] + "..." + " <" + str(self.value)[:10] + "..." + ">"
class Memory(object):
def __init__(self, process_handle, target):
self._process_handle = process_handle
self._target = target
self.found = []
self.__scann_process()
def __scann_process(self):
"""scans a processes pages for the target value."""
si = SYSTEM_INFO()
psi = byref(si)
windll.kernel32.GetSystemInfo(psi)
base_address = si.lpMinimumApplicationAddress
max_address = si.lpMaximumApplicationAddress
page_address = base_address
while page_address < max_address:
page_address = self.__scan_page(page_address)
if len(self.found) >= 60000000:
print("[Warning] Scan ended early because too many addresses were found to hold the target data.")
break
gc.collect()
return self.found
def __scan_page(self, page_address):
"""Scans the entire page for TARGET instance and returns the next page address and found addresses."""
information = self.VirtualQueryEx(page_address)
base_address = information.BaseAddress
region_size = information.RegionSize
next_region = base_address + region_size
size = self._target.size
target_value = self._target.value
step = self._target.alignment
unpacker = self._target.packer.unpack
if information.Type != "MEM_PRIVATE" or \
region_size < size or \
information.State != "MEM_COMMIT" or \
information.Protect not in ["PAGE_EXECUTE_READ", "PAGEEXECUTE_READWRITE", "PAGE_READWRITE"]:
return next_region
page_bytes = self.ReadMemory(base_address, region_size)
for i in xrange(0, (region_size - size), step):
partial = page_bytes[i:i + size]
if unpacker(partial)[0] == target_value:
self.found.append(base_address + i)
del page_bytes # free the buffer
return next_region
def ReadMemory(self, address, size):
cbuffer = c_buffer(size)
success = windll.kernel32.ReadProcessMemory(
self._process_handle,
address,
cbuffer,
size,
0)
assert success, "ReadMemory Failed with success == %s and address == %s and size == %s.\n%s" % (
success, address, size, WinError(win32api.GetLastError()))
return cbuffer.raw
def VirtualQueryEx(self, address):
MBI = MEMORY_BASIC_INFORMATION()
MBI_pointer = byref(MBI)
size = sizeof(MBI)
success = windll.kernel32.VirtualQueryEx(
self._process_handle,
address,
MBI_pointer,
size)
assert success, "VirtualQueryEx Failed with success == %s.\n%s" % (
success, WinError(win32api.GetLastError())[1])
assert success == size, "VirtualQueryEx Failed because not all data was written."
return PyMEMORY_BASIC_INFORMATION(MBI)
def AdjustPrivilege(priv):
flags = win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY
p = win32api.GetCurrentProcess()
htoken = win32security.OpenProcessToken(p, flags)
id = win32security.LookupPrivilegeValue(None, priv)
newPrivileges = [(id, win32security.SE_PRIVILEGE_ENABLED)]
win32security.AdjustTokenPrivileges(htoken, 0, newPrivileges)
win32api.CloseHandle(htoken)
def OpenProcess(pid=win32api.GetCurrentProcessId()):
# ntsecuritycon.SE_DEBUG_NAME = "SeDebugPrivilege"
AdjustPrivilege(ntsecuritycon.SE_DEBUG_NAME)
phandle = windll.kernel32.OpenProcess( \
PROCESS_ALL_ACCESS,
0,
pid)
assert phandle, "Failed to open process!\n%s" % WinError(win32api.GetLastError())[1]
return phandle
PID = 22852
process_handle = OpenProcess(PID)
Memory(process_handle, TARGET(create_string_buffer("1456")))
Here is the error I always get;
AssertionError: ReadMemory Failed with success == 0 and address == 131072 and size == 4096.
[Error 5] Access is denied.
I do not know what information else about my code and my personal Windows 7 operating system, I should provide to you. If you need to know more, please ask it from me, I will provide it to solve that problem.
I guess, this is about a lack of configuration in my operating system , not about pywin32. I'll be waiting for your solutions.
I have an image and rotate it with both MS Paint and GDI. I want to show that the rotated image from both methods are the same.
Here is the code I have to rotate image with GDI
#include <GDIPlus.au3>
_GDIPlus_Startup()
$hImage1 = _GDIPlus_ImageLoadFromFile(#ScriptDir & "\Picture.gif")
$hGraphic1 = _GDIPlus_ImageGetGraphicsContext($hImage1)
$hImage2 = _GDIPlus_BitmapCreateFromGraphics(_GDIPlus_ImageGetWidth($hImage1), _GDIPlus_ImageGetHeight($hImage1), $hGraphic1)
$hGraphic2 = _GDIPlus_ImageGetGraphicsContext($hImage2)
$matrix = _GDIPlus_MatrixCreate()
_GDIPlus_MatrixRotate($matrix,90)
_GDIPlus_GraphicsSetTransform($hGraphic2, $matrix)
_GDIPlus_GraphicsDrawImage($hGraphic2, $hImage1, 0, -590)
_GDIPlus_ImageSaveToFile($hImage2, #ScriptDir & "\out.gif")
_GDIPlus_MatrixDispose($matrix)
_GDIPlus_GraphicsDispose($hGraphic1)
_GDIPlus_GraphicsDispose($hGraphic2)
_GDIPlus_ImageDispose($hImage1)
_GDIPlus_ImageDispose($hImage2)
_GDIPlus_ShutDown ()
Then I used this code to compare 2 images:
$bm1 = _GDIPlus_ImageLoadFromFile(#ScriptDir & "\Picture1.gif")
$bm2 = _GDIPlus_ImageLoadFromFile(#ScriptDir & "\out.gif")
if ComparePicture($bm1, $bm2) == True Then
MsgBox(0, "Test result", "Same image!")
Else
MsgBox(0, "Test result", "Different image!")
EndIf
_GDIPlus_ImageDispose($bm1)
_GDIPlus_ImageDispose($bm2)
_GDIPlus_Shutdown()
Func ComparePicture($bm1, $bm2)
$Bm1W = _GDIPlus_ImageGetWidth($bm1)
$Bm1H = _GDIPlus_ImageGetHeight($bm1)
$BitmapData1 = _GDIPlus_BitmapLockBits($bm1, 0, 0, $Bm1W, $Bm1H, $GDIP_ILMREAD, $GDIP_PXF08INDEXED )
$Stride = DllStructGetData($BitmapData1, "Stride")
$Scan0 = DllStructGetData($BitmapData1, "Scan0")
$ptr1 = $Scan0
$size1 = ($Bm1H - 1) * $Stride + ($Bm1W - 1) * 4
$Bm2W = _GDIPlus_ImageGetWidth($bm2)
$Bm2H = _GDIPlus_ImageGetHeight($bm2)
$BitmapData2 = _GDIPlus_BitmapLockBits($bm2, 0, 0, $Bm2W, $Bm2H, $GDIP_ILMREAD, $GDIP_PXF08INDEXED)
$Stride = DllStructGetData($BitmapData2, "Stride")
$Scan0 = DllStructGetData($BitmapData2, "Scan0")
$ptr2 = $Scan0
$size2 = ($Bm2H - 1) * $Stride + ($Bm2W - 1) * 4
$smallest = $size1
If $size2 < $smallest Then $smallest = $size2
$call = DllCall("msvcrt.dll", "int:cdecl", "memcmp", "ptr", $ptr1, "ptr", $ptr2, "int", $smallest)
_GDIPlus_BitmapUnlockBits($bm1, $BitmapData1)
_GDIPlus_BitmapUnlockBits($bm2, $BitmapData2)
Return ($call[0]=0)
EndFunc
I tried changing the file type, color depth, etc. but I could not get the code to show that they are the same. When I do not rotate the picture i.e
_GDIPlus_MatrixRotate($matrix,0)
then it recognize the same image. When I rotate right 90, it doesn't. Does anyone knows what might be going on?
Thanks
For reference, this question has also been asked on the AutoIt forums here.
I think $GDIP_PXF08INDEXED is modifying the images differently. Try it without setting it and it should work.
Furthermore, you can use this code to flip the image:
$hImage1 = _GDIPlus_ImageLoadFromFile(#ScriptDir & "\Picture1.gif")
_GDIPlus_ImageRotateFlip($hImage1, 1) ;90°
_GDIPlus_ImageSaveToFile($hImage1, #ScriptDir & "\out.gif")
_GDIPlus_ImageDispose($hImage1)
Br,
UEZ
I've been tasked to maintain a VB6 app; I've got some OpenGL experience but all using SDL and this one seems to manually handle all it's contexts.
The program appears to crash on a call to ChoosePixelFormat. I'm passing in a non-null handle. It works on most systems, so I'm guessing its a graphics driver issue, but it appears to be up to date.
Does anyone know why this function would crash instead of, say, returning an error code? I understand the PIXELFORMATDESCRIPTOR may have values that don't exist on a given system:
.nSize = Len(glPFD)
.nVersion = 1
.dwFlags = PFD_DRAW_TO_BITMAP Or PFD_SUPPORT_OPENGL Or PFD_SUPPORT_GDI
.iPixelType = PFD_TYPE_RGBA
.cColorBits = 32
.cDepthBits = 16
.iLayerType = PFD_MAIN_PLANE
But would that make it crash?
Edit: On the system on which this fails, it works with a previous version in which this code was identical. I read a forum somewhere that said loading/unloading opengl32.dll can cause gdi32.dll to break things. This might be the case.
Edit: I've attached the full code below (it's kind of ugly, but it helped me narrow down where the crash was). No functions return 0 before ChoosePixelFormat crashes.
hDCdib = CreateCompatibleDC(0) 'DC compatible with screen
Call MsgBox("Create Compatible DC " + CStr(hDCdib))
'Setup a Device Independent Bitmap Section
With bmInfo.bmiHeader
.biSize = Len(bmInfo)
If lngWidth = 0 Then
.biHeight = objDest.ScaleY(objDest.Height, vbTwips, vbPixels)
.biWidth = objDest.ScaleX(objDest.Width, vbTwips, vbPixels)
Else
.biHeight = objDest.ScaleY(lngHeight, vbTwips, vbPixels)
.biWidth = objDest.ScaleX(lngWidth, vbTwips, vbPixels)
End If
.biPlanes = 1
.biBitCount = 32
.biCompression = BI_RGB 'No Compression
.biSizeImage = 0
.biClrImportant = 0
.biClrUsed = 0
.biXPelsPerMeter = 0
.biYPelsPerMeter = 0
End With
Call MsgBox("after header")
hDIBSection = CreateDIBSection(hDCdib, bmInfo, DIB_RGB_COLORS, hBitMapData, _
&O0, &O0)
Call MsgBox("CreateDIBSection" + CStr(hDIBSection))
'Select the DIB into a device context
hBitMapOld = SelectObject(hDCdib, hDIBSection)
Call MsgBox("SelectObject " + CStr(hBitMapOld))
'Create an OpenGL drawing area in that DIB
With glPFD
.nSize = Len(glPFD)
.nVersion = 1
.dwFlags = PFD_DRAW_TO_BITMAP Or PFD_SUPPORT_OPENGL Or PFD_SUPPORT_GDI
.iPixelType = PFD_TYPE_RGBA
.cColorBits = 32
.cDepthBits = 16
.iLayerType = PFD_MAIN_PLANE
End With
Call MsgBox("glPFD ")
'Ignacio issue is in the next line about ChoosePixelFormat
lngFormat = ChoosePixelFormat(hDCdib, glPFD)
Call MsgBox("ChoosePixelFormat " + CStr(lngFormat))
Htemp = SetPixelFormat(hDCdib, lngFormat, glPFD)
Call MsgBox("SetPixelFormat " + CStr(Htemp))
hGLrc = wglCreateContext(hDCdib)
Call MsgBox("wglCreateContext " + CStr(hGLrc))
wglMakeCurrent hDCdib, hGLrc
Call MsgBox("wglMakeCurrent ")
An OpenGL context on a DIB isn't accelerated, so you'll get a software renderer (at least that was the case some years back and I doubt it's changed, although I could be wrong). If you want to do hardware accelerated rendering, create a window and use that to generate your GL context. If you want, hide the window.
In short, I want to write a Gnome-Shell-style window switcher. So I need to fetch snapshots of all the windows. My current program looks like this:
char filename[101];
sprintf(filename, "%d.png", (int)win_list[i]);
GdkWindow *win_gdk = gdk_x11_window_foreign_new_for_display
(gdk_display_get_default(), win_list[i]);
gint _w, _h;
gdk_drawable_get_size(GDK_DRAWABLE(win_gdk), &_w, &_h);
XEvent _xevent;
_xevent.xexpose =
(XExposeEvent)
{
.type = Expose,
.send_event = True,
.display = xsu_vars.dpy,
.window = win_list[i],
.x = 0, .y = 0, .width = _w, .height = _h,
.count = 0
};
XSendEvent(xsu_vars.dpy, win_list[i], False, 0, &_xevent);
GdkPixbuf *_pb = gdk_pixbuf_get_from_drawable(
NULL, GDK_DRAWABLE(win_gdk), NULL, 0, 0, 0, 0, _w, _h);
if(_pb != NULL) {
cairo_surface_t *_surf_cairo = cairo_image_surface_create(
CAIRO_FORMAT_RGB24, _w, _h);
cairo_t *_cr = cairo_create(_surf_cairo);
gdk_cairo_set_source_pixbuf(_cr, _pb, 0, 0);
cairo_paint(_cr);
cairo_surface_write_to_png(_surf_cairo, filename);
printf("%s saved successfully!\n", filename);
} else {
printf("failed...\n");
}
The program works well well, but it will not generate correct images for those windows which are on a different desktop of minimized -- they would look like this:
Note that I send a expose event to all windows before generating pixbufs of them.
UPDATE:
It seems that xlib doesn't support that. So the only way may be creating cache manually.
This is possible with Composite extension - see "Preventing the backing pixmap from being freed when the window is hidden/destroyed" section in tutorial.
Yes, your update is correct. When a window is unmapped (or covered up), X just discards its contents; they don't exist anywhere in order to be snapshotted.
I believe libwnck contains code to do this and other parts of writing a switcher, btw. It's basically a library for writing things like window switchers.
I am trying to call the standard Win32 API functions to get file version info, using the win32-api library.
The 3 version.dll functions are GetFileVersionInfoSize, GetFileVersionInfo, and VerQueryValue. Then I call RtlMoveMemory in kernel32.dll to get a copy of the VS_FIXEDFILEINFO struct (see Microsoft documentation: http://msdn.microsoft.com/en-us/library/ms646997%28VS.85%29.aspx).
I drew from an example I saw using VB: http://support.microsoft.com/kb/139491.
My problem is that the data that finally gets returned doesn't seem to match the expected struct, in fact it doesn't even return a consistent value. I suspect the data is getting mangled at some point, probably in VerQueryValue or RtlMoveMemory.
Here is the code:
GetFileVersionInfoSize = Win32::API.new('GetFileVersionInfoSize','PP','I','version.dll')
GetFileVersionInfo = Win32::API.new('GetFileVersionInfo','PIIP','I', 'version.dll')
VerQueryValue = Win32::API.new('VerQueryValue','PPPP','I', 'version.dll')
RtlMoveMemory = Win32::API.new('RtlMoveMemory', 'PPI', 'V', 'kernel32.dll')
buf = [0].pack('L')
version_size = GetFileVersionInfoSize.call(myfile + "\0", buf)
raise Exception.new if version_size == 0 #TODO
version_info = 0.chr * version_size
version_ok = GetFileVersionInfo.call(file, 0, version_size, version_info)
raise Exception.new if version_ok == 0 #TODO
addr = [0].pack('L')
size = [0].pack('L')
query_ok = VerQueryValue.call(version_info, "\\\0", addr, size)
raise Exception.new if query_ok == 0 #TODO
# note that at this point, size == 4 -- is that right?
fixed_info = Array.new(13,0).pack('L*')
RtlMoveMemory.call(fixed_info, addr, fixed_info.length)
# fixed_info.unpack('L*') #=> seemingly random data, usually only the first two dwords' worth and the rest 0.
This is the full code I got to work, in case others are looking for such a function.
Returns an array with four parts of product/file version number (i.e., what is called "File Version" in a dll file properties window):
def file_version ref, options = {}
options = {:path => LIBDIR, :extension => 'dll'}.merge(options)
begin
file = File.join(ROOT, options[:path],"#{ref}.#{options[:extension]}").gsub(/\//,"\\")
buf = [0].pack('L')
version_size = GetFileVersionInfoSize.call(file + "\0", buf)
raise Exception.new if version_size == 0 #TODO
version_info = 0.chr * version_size
version_ok = GetFileVersionInfo.call(file, 0, version_size, version_info)
raise Exception.new if version_ok == 0 #TODO
addr = [0].pack('L')
size = [0].pack('L')
query_ok = VerQueryValue.call(version_info, "\\\0", addr, size)
raise Exception.new if query_ok == 0 #TODO
fixed_info = Array.new(18,0).pack('LSSSSSSSSSSLLLLLLL')
RtlMoveMemory.call(fixed_info, addr.unpack('L')[0], fixed_info.length)
fixed_info.unpack('LSSSSSSSSSSLLLLLLL')[5..8].reverse
rescue
[]
end
end
The answer in https://stackoverflow.com/a/2224681/3730446 isn't strictly correct: the VS_FIXEDFILEINFO struct contains separate FileVersion and ProductVersion. That code returns a version number consisting of the two more-significant components of the ProductVersion and the two less-significant components of the FileVersion. Most times I've seen, that wouldn't matter because both Product- and FileVersion have the same value, but you never know what you might encounter in the wild.
We can see this by comparing the VS_FIXEDFILEINFO struct from http://msdn.microsoft.com/en-us/library/windows/desktop/ms646997(v=vs.85).aspx and the format string we're using to pack and unpack the buffer:
typedef struct tagVS_FIXEDFILEINFO {
DWORD dwSignature; // 0: L
DWORD dwStrucVersion; // 1: S
// 2: S
DWORD dwFileVersionMS; // 3: S
// 4: S
DWORD dwFileVersionLS; // 5: S
// 6: S
DWORD dwProductVersionMS; // 7: S
// 8: S
DWORD dwProductVersionLS; // 9: S
// 10: S
DWORD dwFileFlagsMask; // 11: L
DWORD dwFileFlags; // 12: L
DWORD dwFileOS; // 13: L
DWORD dwFileType; // 14: L
DWORD dwFileSubtype; // 15: L
DWORD dwFileDateMS; // 16: L
DWORD dwFileDateLS; // 17: L
} VS_FIXEDFILEINFO;
Subscripts 5 to 8, then, consists of dwFileVersionLS and dwProductVersionMS. Getting FileVersion and ProductVersion correctly would look like this:
info = fixed_info.unpack('LSSSSSSSSSSLLLLLLL')
file_version = [ info[4], info[3], info[6], info[5] ]
product_version = [ info[8], info[7], info[10], info[9] ]