Программирование на языке Ruby - Хэл Фултон
Шрифт:
Интервал:
Закладка:
Пользователи Windows не брошены на произвол судьбы. Существует немало инструментов и библиотек для этой платформы, а разрабатывается еще больше, многие аспекты Ruby, даже механизм потоков, изначально не зависят от платформы. Наибольшие трудности возникают при управлении процессами, выполнении ввода/вывода и других операций низкого уровня.
В прошлом существовало несколько вариантов Ruby для Windows. Интерпретатор мог быть собран компилятором gcc или Visual С, его работа могла зависеть от наличия библиотеки Cygwin DLL и т.д. Но в последние годы появился «моментальный» инсталлятор для Windows (см. раздел 14.6).
Среда изменяется слишком быстро, чтобы можно было ее сейчас документировать, однако в этом разделе мы все же рассмотрим некоторые вопросы написания сценариев и автоматизации на платформе Windows. Описанные приемы и утилиты должны работать в любой ОС. Если возникнут проблемы, сообщество придет на помощь.
14.5.1. Расширение Win32API
Расширение Win32API — исключительно мощный инструмент, если вы собираетесь программировать на относительно низком уровне. Оно позволяет вызывать из Ruby функции Windows API, находящиеся в любой DLL.
Указанная функция становится объектом, а методу new передаются параметры, точно описывающие функцию. Первый параметр — строка, идентифицирующая DLL, в которой находится функция (например, crtdll). Второй параметр — имя самой функции, третий — массив строк, описывающих типы параметров функции (массив импорта), а четвертый — строка, описывающая тип возвращаемого значения (строка экспорта).
Массив импорта может содержать следующие значения (регистр не играет роли):
I целое
L число
N число
P указатель на строку
Строка экспорта также может содержать любое из этих значений, а также значение «V», означающее «void».
После того как объект создан, можно обратиться к его методу call для вызова функции Windows. Синоним — Call.
В примере ниже мы вызываем функцию GetCursorPos, которая возвращает указатель на структуру POINT. Эта структура состоит из двух полей типа long. Чтобы получить их значения, мы можем воспользоваться методом unpack:
require 'Win32API'
result = "0"*8 # Восемь байтов (достаточно для двух long).
getCursorXY = Win32API.new("user32","GetCursorPos",["P"],"V")
getCursorXY.call(result)
x, y = result.unpack("LL") # Два long.
В данном случае функция вернула составные двоичные данные, а иногда такие данные нужно подать на вход функции. Понятно, что для этого нужно воспользоваться методом pack, который упакует данные в строку.
У описанной техники может быть много применений. Еще два примера приведены в разделах 10.1.20 и 14.1.1.
14.5.2. Расширение Win32OLE
Расширение Win32OLE (правильно писать его имя строчными буквами: win32ole) реализует интерфейс к OLE-автоматизации в Windows. Программа на Ruby может выступать в роли клиента любого сервера автоматизации, к числу которых относятся, например, Microsoft Word, Outlook, Internet Explorer, а также многие продукты третьих фирм.
Для того чтобы начать взаимодействие с внешним приложением, мы создаем объект класса WIN32OLE. С его помощью мы получим доступ ко всем свойствам и методам, которые раскрывает данное приложение. В примере ниже объект ассоциируется с редактором Microsoft Word. Атрибуту visible мы присвоим значение true, а в конце вызовем метод quit, чтобы завершить внешнюю программу.
require "win32ole"
word = WIN32OLE.new "Word.Application"
word.visible = true
# ...
word.quit
Свойства сервера автоматизации выглядят как атрибуты объекта. Их можно читать и устанавливать.
Имеется и альтернативная нотация, в которой для доступа к свойствам используется конструкция, напоминающая хэш.
player["FileName"] = "file.wav"
name = player["FileName"]
# Эквивалентно следующим предложениям:
# player.FileName = "file.wav"
# name = player.FileName
У этой нотации есть то преимущество, что она позволяет проще осуществлять динамический доступ к свойствам, как показано в искусственном примере ниже:
puts "Введите имя свойства"
prop = gets
puts "Введите новое значение"
val = gets
old = obj[prop]
obj[prop] = val
puts "#{prop} было #{old}... стало #{obj[prop]}"
Но обратимся к более жизненным примерам. Следующий код получает от пользователя имя файла, передает его Microsoft Word и распечатывает файл:
require "win32ole"
print "Введите имя файла для распечатки: "
docfile = gets
word = WIN32OLE.new "Word.Application"
word.visible = true
word.documents.open docfile
word.options.printBackground = false
# Можно было бы также установить свойство printBackground в true,
# но тогда пришлось бы дожидаться, пока весь файл будет
# скопирован в буфер принтера, и только потом вызывать quit...
word.activeDocument.printout
word.quit
В следующем примере проигрывается WAV-файл. Недостаток заключается в том, что в конце программы мы поставили sleep на случайно выбранное время, а не просто дожидаемся, когда воспроизведение закончится. Предлагаем читателю устранить этот недочет в качестве упражнения.
require "win32ole"
sound = WIN32OLE.new("MCI.MMcontrol")
wav = "с:\windows\media\tada.wav"
sound.fileName = wav
sound.autoEnable = true
sound.command = "Open"
sound.command = "Play"
sleep 7
В листинге 14.2 мы просим Internet Explorer открыть диалог для ввода текста.
Листинг 14.2. Открытие диалога для ввода текста в браузереrequire "win32ole"
def ieInputBox( msg, default )
ie = WIN32OLE.new("InternetExplorer.Application");
ie.visible = false
ie.navigate "about:blank"
sleep 0.01 while (ie.busy)
script = ie.Document.Script;
result = script.prompt(msg,default);
ie.quit
result
end
# Главная программа...
result = ieInputBox( "Введите свое имя",
"Дэйв Боумэн")
if result
puts result
else
puts "Пользователь нажал Cancel"
end
В листинге 14.3 мы открываем IE в небольшом окне и выводим в него HTML-документ.
Листинг 14.3. Для вывода в окно браузера требуется win32olehtml = <<EOF
<html>
<body>
<h3>A теперь что-нибудь</h3>
<h2>совсем</h2>
<h1>другое...</h1>
</body>
</html>
EOF
ie = WIN32OLE.new("InternetExplorer.Application");
ie.left = 150
ie.top = 150
ie.height = 200
ie.width = 300
ie.menubar = 0
ie.toolbar = 0
ie.navigate "about:blank"
ie.visible=TRUE;
ie.document.open
ie.document.write html
ie.document.close
sleep 5
ie.quit
В следующем примере открывается диалоговое окно, где пользователь может выбрать файл из списка:
require "win32ole"
cd = WIN32OLE.new("MSComDlg.CommonDialog")
# Задать фильтр файлов
cd.filter = "All Files(*.*)| *.*" +
"| Ruby Files(*.rb)|*.rb"
cd.filterIndex = 2
cd.maxFileSize = 128 # Установить MaxFileSize.
cd.showOpen()
file = cd.fileName # Получить путь к файлу.
if not file or file==""
puts "Файл не выбран."
else
puts "Пользователь выбрал: #{file}n"
end
И, наконец, определим IP-адрес своего компьютера:
require "win32ole"
ws = WIN32OLE.new "MSWinsock.Winsock"
# Получить свойство LocalIP
ipAddress = ws.localIP
puts "Локальный IP-адрес равен : #{ipAddress}"
Как видите, возможности не ограничены. Развлекайтесь и не забывайте делиться своими программами с другими!
14.5.3. Использование ActiveScriptRuby
Наверняка вам приходилось открывать в браузере Internet Explorer страницы, содержащие код на языке JavaScript или VBScript. (Мы не будем здесь касаться различий между JScript и JavaScript.)
Но сценарий можно написать и на языке ActiveScriptRuby, представляющем собой мост между COM и Ruby. Вот как можно включить код на Ruby в HTML-страницу (листинг 14.4).
Листинг 14.4. Код на Ruby, встроенный в HTML-страницу<html>
<script language="RubyScript">
# Это код на Ruby...
def helloMethod
@window.alert "Работает Ruby!"
end
</script>
<body>