#--------------------------------------------------------------------------
# ● RMXP用 Graphics.snap_to_bitmap
#
# 就算窗口被遮盖或者显示不全也可以正常截图
#
# 附赠: Graphics.snap_to_clipboard
#
# 截图到剪贴板
#
#
# ● 作者:晴兰 [url=home.php?mod=space&uid=67509]@[/url] rpg.blue
#
# 使用到紫苏前辈的获取窗口句柄脚本,其他部分为原创
#
# ● 备注:如果你因为任何原因改变了窗口大小(或者常说的分辨率)
#
# 搜索并且修改"# 如果屏幕宽度改变请折腾下面这两处"附近
#--------------------------------------------------------------------------
#--------------------------------------------------------------------------
# ● 紫苏前辈的获取窗口句柄
# 部分细节已修改
#--------------------------------------------------------------------------
module Kernel
#--------------------------------------------------------------------------
# ● 需要的 Windows API 函数
#--------------------------------------------------------------------------
GetWindowThreadProcessId = Win32API.new("user32", "GetWindowThreadProcessId", "LP", "L")
GetWindow = Win32API.new("user32", "GetWindow", "LL", "L")
GetClassName = Win32API.new("user32", "GetClassName", "LPL", "L")
GetCurrentThreadId = Win32API.new("kernel32", "GetCurrentThreadId", "V", "L")
GetForegroundWindow = Win32API.new("user32", "GetForegroundWindow", "V", "L")
#--------------------------------------------------------------------------
# ● 获取窗口句柄
#--------------------------------------------------------------------------
def hwnd
# 获取调用线程(RM 的主线程)的进程标识
threadID = GetCurrentThreadId.call
# 获取 Z 次序中最靠前的窗口
hWnd = GetWindow.call(GetForegroundWindow.call, 0)
# 枚举所有窗口
while hWnd != 0
# 如果创建该窗口的线程标识匹配本线程标识
if threadID == GetWindowThreadProcessId.call(hWnd, 0)
# 分配一个 11 个字节的缓冲区
className = " " * 12 # ●
# 获取该窗口的类名
GetClassName.call(hWnd, className, 12) # ●
# 如果匹配 RGSS Player 则跳出循环
break if className[0, 11] == "RGSS Player"
end
# 获取下一个窗口
hWnd = GetWindow.call(hWnd, 2)
end
return hWnd
end
end
module SAFX
module_function
class ::Integer; def pack; self; end; def to_param; 'i'; end; def ord; self; end; end
class ::String;def pack; [self].pack('p').unpack('L').first; end; def to_param; 'p'; end; end
def api(dll,func)
lambda{|*args|
Win32API.new(dll,func,args.map{|x|x.to_param}, 'i').call *args
}
end
def callproc(addr, type = :cdecl)
stdcall = type == :stdcall
raise "addr == 0 " if addr.pack == 0
apicall = api('user32', 'CallWindowProcW')
lambda{|*args|apicall.call((code=[0x55,0xe589].pack('CS')+args.map{|x| [0x68, x.pack].pack('CL')}.reverse.join+[0xb8, addr.pack, 0xD0FF, 0xc481, (stdcall ? 0 : args.length*4) , 0x10c2c9].pack('CLSSLL')),0,0,0,0)}
end
LL = api("kernel32", "LoadLibrary")
GPA = api("kernel32", "GetProcAddress")
def funcaddr(dll, func)
x = GPA.call(LL.call(dll), func)
x == 0 ? nil : x
end
def readmem(start, len)
api('kernel32', 'RtlMoveMemory').call buf = "\0"*len, start, len
buf
end
def writemem(start, len, value)
api('kernel32', 'RtlMoveMemory').call start, value, [len, value.length].min
end
end
class << Graphics
# 如果屏幕宽度改变请折腾下面这两处
def width
640
end
def height
480
end
def do_screen_stuff
hwnd = self.hwnd
src = SAFX.api('user32', 'GetDC').call(hwnd)
memdc = SAFX.api('gdi32', 'CreateCompatibleDC').call(src)
hb = SAFX.api('gdi32', 'CreateCompatibleBitmap').call(src, width, height)
ho = SAFX.api('gdi32', 'SelectObject').call(memdc, hb)
bp = SAFX.funcaddr("user32", "GetDC")
ov = "\0"*4
str = [0xb8, memdc, 0xc2, 0x04, 0x00].pack("CLC*") # magic, touch
SAFX.api('Kernel32', 'VirtualProtect').call(bp, str.length, 0x40, ov)
oldstr = SAFX.readmem(bp, str.length)
SAFX.writemem bp, str.length, str
SAFX.api('user32', 'UpdateWindow').call(hwnd)
Graphics.update
SAFX.writemem bp, str.length, oldstr
yield hb
ensure
SAFX.api('user32', 'ReleaseDC').call(hwnd, src)
hb = SAFX.api('gdi32', 'SelectObject').call(memdc, ho)
SAFX.api('gdi32', 'DeleteObject').call(ho)
SAFX.api('gdi32', 'DeleteDC').call(memdc)
end
def snap_to_bitmap
bm = Bitmap.new(width, height)
addr = bm.object_id * 2
addr, = SAFX.readmem(addr + 16, 4).unpack("L")
addr, = SAFX.readmem(addr + 8, 4).unpack("L")
addr, = SAFX.readmem(addr + 16, 4).unpack("L")
do_screen_stuff do |hb|
bits = "\0"*(width * height * 4)
SAFX.api('gdi32', 'GetBitmapBits').call(hb, bits.length, bits)
r = SAFX.api('kernel32', 'RtlMoveMemory')
a = bits.pack
stride = width * 4
h = height
(h-1).downto(0){|i|
r.call addr + i * stride, a + (h - i - 1)*stride, stride
}
bm
end
end
def snap_to_clipboard
return false if SAFX.api('user32', 'OpenClipboard').call(hwnd) == 0
do_screen_stuff do |hb|
SAFX.api('user32', 'SetClipboardData').call(2, hb)
SAFX.api('user32', 'CloseClipboard').call
true
end
end
end