2010年1月21日星期四

Ruby版SendKey

VBScript中的SendKey是个很有用的方法,可以用代码实现一些简单的自动化操作。实际上,通过SendMessage发送WMKEYDOWN和WMKEYUP消息就可以实现同样的功能。而且这个办法可以向任意窗口发送消息,目标窗口并不需要具有焦点。

MSDN可以查到,WM_KEYDOWN和WM_KEYUP消息除了virtual-key code之外,还需要一个scan code。文档只说这个值根据不同的键盘会有所不同,但如何获得这个值却没有提。

好吧,这篇东西值得看的其实也就这一点点了,那就是:MapVirtualKey

以下程序是我用No$gba玩《恶魔城·苍月的十字架》时写来刷25号怪的,该作的魂系统实在太变态,不想点办法我会有一种被游戏玩了的感觉。还别说,有一种当年玩MUD的感觉。

require "Win32API"

module Win32
  module_function

  WM_KEYDOWN = 0x100
  WM_KEYUP = 0x101
  
  VK_SHIFT = 0x10
  VK_NUMPAD4 = 0x64
  VK_A = 0x41
  VK_D = 0x44
  VK_W = 0x57

  def sendMessage(hWnd, nMsg, wParam, lParam)
    f = Win32API.new('user32', 'SendMessage', 'LILL', 'L')
    f.call(hWnd, nMsg, wParam, lParam)
  end
  
  def findWindow(sClass, sTitle)
    f = Win32API.new('user32', 'FindWindow', 'PP', 'L')
    f.call(sClass, sTitle)
  end
  
  def mapVirtualKey(uCode, uMapType)
    f = Win32API.new('user32', 'MapVirtualKey', 'II', 'I')
    f.call(uCode, uMapType)
  end
  
  def sendKey(hWnd, aKeys)
    aKeys.each do |key_code|
      sendMessage(hWnd, WM_KEYDOWN, key_code, 1 + (mapVirtualKey(key_code, 0) << 16))
      sleep(0.1)
    end
    
    aKeys.reverse.each do |key_code|
      sendMessage(hWnd, WM_KEYUP, key_code, 0xc000_0001 + (mapVirtualKey(key_code, 0) << 16))
      sleep(0.1)
    end
  end
end


s = 'No$gba Emulator '
h = Win32.findWindow(nil, s)
while true
  4.times do |n|
    Win32.sendKey(h, [Win32::VK_SHIFT])
    sleep(0.5)
  end
  Win32.sendKey(h, [Win32::VK_A])
  sleep(0.1)
  3.times do |n|
    Win32.sendKey(h, [Win32::VK_SHIFT])
    sleep(0.5)
  end
  Win32.sendKey(h, [Win32::VK_D])
  sleep(0.1)
  Win32.sendKey(h, [Win32::VK_D])
  sleep(0.1)
  5.times do |n|
    Win32.sendKey(h, [Win32::VK_W, Win32::VK_NUMPAD4])
    sleep(0.5)
  end
end

没有评论 :