Ruby/Win32API

Aus Scientia
Wechseln zu: Navigation, Suche

Die Win32API Klasse bietet ein Rubyinterface für Funktionen einer C-Bibliothek, welches mit der Instanzmethode +call+ die Funktionen ausführen kann. Die Klasse selbst ist in Ruby geschrieben, und benutzt die DL-Bibliothek aus der Ruby Standard-API, welche selbst ein Interface für "dynamic linker" zur Verfügung stellt. Nach Ruby Version 1.9.1, wird die Win32API Klasse als deprecated verzeichnet, und es wird empfohlen direkt mit dem DL-Interface zu arbeiten. Besonders interessant ist diese Klasse für den RPG Maker XP und VX. Sie ist nämlich im RGSS eingebunden, und stellt somit die Möglichkeit zur Verfügung die Spiele mit reinem C-Code zu unterstützen, was annähernd unbegrenzte Möglichkeiten bietet.

Verwendungsbeispiele finden sich unter anderem bei den Codeschnippseln des RGSS.

Überblick: Libraries

Libraries (Befehls-Bibliotheken) sind "shared" (zur Benutzung mit verschiedenen Applikationen freigegebene) OS-Komponenten, die dem Programmierer spezielle Funktionen anbieten. Zum Beispiel kann eine Library Befehle zum einfachen Handhaben und Manipulieren von Bildern enthalten oder dem Speichern unter Dialog aufzurufen. Jedes OS bietet eine Anzahl an "shared" Libraries, um das Programmierer-Leben zu vereinfachen. Mit Ruby ist es möglich, diese externen Libraries einfach und dynamisch zu benutzen! Der Grund für die Nützlichkeit von Libraries liegt darin, dass sie separat von den Applikationen sind, aber zwischen diesen geteilt ("shared") werden. Eine Library-Datei muss nur einmal in den Speicher geladen werden, was Speicherplatz spart, insbesondere bei häufig benutzten Libraries. Sie nützen auch dem Programmierer, da dies bedeutet, dass Sie nicht jedes Mal das Rad wieder neu erfinden brauchen, wenn Sie ein cleveres Feature in Ihrem Programm verwenden wollen. Libraries sind auch nützlich, um Prozeduren - die von mehreren Programmen benötigt werden - aufzunehmen und zu aktualisieren, ohne das eigentliche Hauptprogramm verändern zu müssen. Unter dem Windows OS sind "shared" Libraries gut bekannt unter dem Namen 'DLL's, einer Abkürzung für "Dynamic Link Libraries".

Konstanten

DLL

Ein Hash, der handles zu allen bisher genutzen DLL Funktionen hält.

Die im RGSS eingebundene Win32API Klasse, scheint diese Konstante nicht zu haben.

Klassen Methoden

new

Win32API.new( dll_name, function_name, in, out)
Gibt ein Win32API-Objekt zurück, über das die angegebene Funktion nun aufgerufen werden kann.
Argument Beschreibung
dll_name' Wird ersetzt durch ein String-Objekt, der den Namen der DLL enthält.
function_name Wird ersetzt durch ein String-Objekt, der den Namen der Funktion enthält.
in Kann entweder ein bis zu 16-elementiges Array- oder String-Objekt sein.
out Wird ersetzt durch 1-elementiges String-Objekt, dass wie in eines der vier Buchstaben ist.

Parameter

dll_name

Ein String mit dem Dateinamen der zu öffnenden Shared Library. Normalerweise, wenn Sie keinen Pfad als Teil des Dateinamens mit angeben, wird die Library im Verzeichnis Ihres Programms gesucht, und wenn sie sich nicht dort befindet, wird das Verzeichnis der Systembibliotheken durchsucht. Die Systemvariable %sysdir% besitzt u.a. einen Pfad als Wert, der durchsucht wird.
Die Suche ist nicht case-sensitive. Die Angabe einer Dateiendung wie ".dll" ist nicht nötig.

function_name

Ein String in dem der Name der Funktion steht, die zur Benutzung geladen werden soll.

in

Eine Signatur, welche Datentypen an die Funktion gesendet werden sollen. Es gibt 4 Arten:

Name Buchstabe Steht für welches Ruby-Objekt als Parameter?
Long/Number L, l, N oder n Fixnum- oder Bignum-Objekt
Integer/Ganzzahl I oder i Fixnum- oder Bignum-Objekt
Pointer/Zeiger P oder p String-Objekt
Void V oder v Falls kein Parameter von der Funktion erwartet wird.

Anmerkung: L, N und I werden intern gleich behandelt.
Statt "v" funktioniert auch "" sowie "0" oder nil. Gebräuchlich bleibt aber "v".

out

Das ist der Typ des Rückgabewertes. Also die Art, ob ein Pointer/Zeiger oder Zahl als Antwort erwartet wird. Die Arten sind die gleichen wie bei +in+.


Methoden

call

call( [args]* ) Ruft die API Funktion mit den angegebenen Parametern auf, welche vom Typ der Signatur von der Initialisierung entsprechen müssen.
Die Rückgabe hängt von der aufgerufenen Funktion ab, ihr Typ entspricht aber der Signatur, die beim Initialize als +out+ angegeben wurde.

Call

Alias von Win32API#call.

Anmerkungen zu den Parametern

Oft nehmen die Funktionen der C-Bibliotheken nur Zeiger auf bestimmte Werte, oder Strukturen, bzw. geben diese zurück. Intern verwendet Ruby fast nur Zeiger, aber auf unserer Rubyebene sind sie uns fremd. Allerdings gibt uns Ruby ja kein Werkzeug was nur manchmal funktioniert, weswegen vorgesorgt wurde: In Ruby werden Zeiger auf Werte oder Strukturen durch Strings represäntiert, welche eine Sequenz aus Bytes halten. Arrays haben hierfür die Funktionen +pack+ und Strings die Funktion +unpack+ welche beliebige Werte in den String packen, sodas C-Funktionen mit ihnen Arbeiten können, und wieder auspacken, sodass Ruby damit arbeiten kann.

Ein Beispiel

# API Funktion holen
get_cursor_pos = Win32API.new('user32', 'GetCursorPos', 'p', 'v')
# Die Funktion erwartet einen Zeiger auf 2 LONGs,
# das ist ein Datentyp der Zahlen speichert.
point_struct = [0,0].pack("LL")
# C Funktion aufrufen
get_cursor_pos.call( point_struct )
# Die Funktion schreibt nun in den Speicherbereich 
# auf den der mitgegebene Zeiger zeigt, die zwei
# Longs, bzw. in den angegebenen String.
# Nun holen wir die Zahlen wieder raus;
x, y = point_struct.unpack("LL")
# Ausgeben und fertig
print "Punkt (#{x}|#{y})\n"


Quellcode

Quellcode aus Ruby 1.8.6

# -*- ruby -*-
 
require 'dl'
 
class Win32API
  DLL = {}
 
  def initialize(dllname, func, import, export = "0")
    prototype = (export + import.to_s).tr("VPpNnLlIi", "0SSI").sub(/^(.)0*$/, '\1')
    handle = DLL[dllname] ||= DL::Handle.new(dllname)
    @sym = handle.sym(func, prototype)
  end
 
  def call(*args)
    import = @sym.proto.split("", 2)[1]
    args.each_with_index do |x, i|
      args[i] = nil if x == 0 and import[i] == ?S
      args[i], = [x].pack("I").unpack("i") if import[i] == ?I
    end
    ret, = @sym.call(*args)
    return ret || 0
  end
 
  alias Call call
end

Verwandte Themen

Links

Microsoft hat mit MSDN (Microsoft Developer Network) eine riesige Übersichts- und Funktionsreferenz zur Windows API.