RMVA切换到后台时停止BGM思路

微博图床挂了……
效果
借助 @晴兰 和 @yangff 大佬的 WndProc 总算实现了切换后台事件的监听,之前从第三方 Player 入手一直不成功,目测是加载 RGSSXXX.dll 后 WndProc 被接管了。
WIN32API
这样一来就可以处理 RM 默认切到后台后渲染暂停但 BGM 继续播放的情况,避免演出和音乐不同步。

原版 Audio 没有暂停,续播功能只支持 ogg 格式,需要使用第三方音乐引擎。

目前找到的有

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
$apicache = {}
class String
def apicall(*args)
$apicache[self] = Win32API.new(*self.split("|")) unless $apicache.include? self
$apicache[self].call(*args)
end
end
class Symbol
def method_missing(*args, &block)
self.to_s.send(*args, &block)
end
end
class Win32API
WM_MOVE = 0x3
WM_MOVING = 0x216
WM_ACTIVATE = 0x6
WM_WINDOWPOSCHANGED = 0x47
WM_WINDOWPOSCHANGING = 0x46
end
module Roost
module MainWindow
def self.getwndproc
@wndproc
end
def self.setwndproc(obj)
@wndproc=obj
end
def self.addwndproc(obj)
@@wndprocs.push(obj)
end
@@wndprocs=[]
@wndproc = lambda {|hwnd, msg, wp, lp|
@@handle=false
for i in @@wndprocs
i.call(hwnd, msg, wp, lp)
end
return Roost::MainWindow.use_rm_proc(hwnd, msg, wp, lp)
}
def self.use_rm_proc(hwnd,msg,wp,lp)
if (not @@handle)
@@handle=true
return :"user32|CallWindowProc|iiiii|i".apicall(@@oHandle,hwnd,msg,wp,lp)
end
end
def self.disable_rm_proc
@@handle=true
end
def self.enable
Roost::MainWindow.instance_eval do
def self.enable
end
end
Roost::MainWindow.instance_eval do
@msg = "\0"*24
@hwnd = 0
while @hwnd==0
:"user32|GetMessage|piii|v".apicall(@msg, 0, 0, 0)
@kmsg = @msg
:"user32|TranslateMessage|p|v".apicall(@kmsg)
:"user32|DispatchMessage|p|v".apicall(@kmsg)
@hwnd = @msg.unpack("i*")[0]
end
@hdc = :"user32|GetDC|i|i".apicall(@hwnd)
def self.hwnd
return @hwnd
end
def self.hdc
return @hdc
end

def self.findProc(l, n)
lib = :"kernel32|LoadLibrary|p|i".apicall(l);
ret = :"kernel32|GetProcAddress|ip|l".apicall(lib, n);
:"kernel32|FreeLibrary|l|v".apicall(lib)
return ret
end
def self.loadfindProc(l, n)
lib = :"kernel32|LoadLibrary|p|i".apicall(l);
ret = :"kernel32|GetProcAddress|ip|l".apicall(lib, n);
return ret
end
def self.enableWndProc
Roost::MainWindow.instance_eval do
def self.enableWndProc
end
end
@malloc = :"msvcrt|malloc|i|i"
@memcpy = :"msvcrt|memcpy|ipi|v"


sprintf = self.findProc("msvcrt", "sprintf")
rgsseval = self.findProc("RGSS300", "RGSSGetInt")
old = :"user32|GetWindowLong|ll|l".apicall(@hwnd, -4)
buf = @malloc.apicall(1024)
fmt = @malloc.apicall(2048)
sprintfvar = @malloc.apicall(8)
rgssevalvar= @malloc.apicall(8)
oldvar = @malloc.apicall(8)
fmtvar = @malloc.apicall(8)
bufvar = @malloc.apicall(8)
defvar = @malloc.apicall(8)

:"msvcrt|strcpy|pp|p".apicall(fmt, "Roost::MainWindow.getwndproc.call(%d,%d,%d,%d)")

@memcpy.apicall(sprintfvar, [sprintf].pack("i"), 4)
@memcpy.apicall(rgssevalvar,[rgsseval].pack("i"), 4)
@memcpy.apicall(oldvar, [old].pack("i"), 4)
@memcpy.apicall(fmtvar, [fmt].pack("i"), 4)
@memcpy.apicall(bufvar, [buf].pack("i"), 4)
@memcpy.apicall(defvar, [self.findProc("user32", "DefWindowProcA")].pack("i"), 4)

@code = [0x55,0x89,0xe5,0xff,0x75,0x14,
0xff,0x75,0x10,0xff,0x75,0x0c,
0xff,0x75,0x08,0xff,0x35].pack('C*')
@code << [fmtvar].pack('l') << [0xff, 0x35].pack('C*')
@code << [bufvar].pack('l') << [0xff, 0x15].pack('C*')
@code << [sprintfvar].pack("l")
@code << [0xff, 0x15].pack('C*')
@code << [rgssevalvar].pack("l")
=begin
@code << [0x83,0xc4,0x18, 0xff, 0x75, 0x14,
0xff,0x75, 0x10, 0xff, 0x75, 0x0c,
0xff,0x75, 0x08, 0xff, 0x15].pack('C*')
@code << [oldvar].pack("l")
=end
@code << [0x83,0xc4,0x18].pack('C*')
@code << [0xc9,0xc2,0x10,0x00].pack('C*')
#0xD1, 0xE8
@shellcode = @malloc.apicall(2048)
@memcpy.apicall(@shellcode, @code, @code.size)
@@oHandle= :"user32|SetWindowLong|iii|i".apicall(@hwnd, -4, @shellcode)
#Returning Test : should be 5
#Roost::MainWindow.setwndproc (lambda{|hwnd, msg, wp, lp| 5})
#p :"user32|CallWindowProc|iiiii|i".apicall(@shellcode, 0, 0, 0, 0)
end
end

end

end
end

Roost::MainWindow.enable
Roost::MainWindow.enableWndProc

Roost::MainWindow.addwndproc (lambda {|hwnd, msg, wp, lp|
if msg == Win32API::WM_MOVING
p "WM_MOVING"
unless RPG::BGM.last.name.empty?
Audio.bgm_stop # 停止bgm
end
end
if msg == Win32API::WM_MOVE
p "WM_MOVE"
unless RPG::BGM.last.name.empty?
RPG::BGM.last.replay # 继续bgm
end
end
if msg == Win32API::WM_WINDOWPOSCHANGED
p "WM_WINDOWPOSCHANGED"
unless RPG::BGM.last.name.empty?
Audio.bgm_stop # 停止bgm
end
end
if msg == Win32API::WM_WINDOWPOSCHANGING
p "WM_WINDOWPOSCHANGING"
unless RPG::BGM.last.name.empty?
RPG::BGM.last.replay # 继续bgm
end
end
if msg==Win32API::WM_ACTIVATE
if wp == 0
p "WM_ACTIVATE", wp
unless RPG::BGM.last.name.empty?
Audio.bgm_stop # 停止bgm
end
else
p "WM_ACTIVATE", wp
unless RPG::BGM.last.name.empty?
RPG::BGM.last.replay # 继续bgm
end

end
end
})

事件码值参考 MSDN 或者 这里