Windows-App möglich mit win32service, win32pipe und py2exe?

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Ich möchte was eigentlich ganz simples mit Python realisieren. Es geht um eine reine Windows-Lösung:

Ein erstes Python-Programm soll als Dienst im Hintergrund laufen und vorerst nur auf weitere Anweisungen warten.

Ein zweites Python-Programm wird mit einem Parameter aufgerufen, den es an den Dienst übermitteln soll. Mehr nicht, dann 'exit'. Also nur ein Hilfsprogramm, um dem Dienst etwas mitzuteilen – eine ID zum Beispiel.

Der Dienst bekommt diesen Parameter und soll ihn erstmal nur wieder ausgeben (später wird der Parameter ausgewertet und verarbeitet).

Fragen:

1) Ein nativer Windows-Dienst (für Windows NT und XP) lässt sich mit 'win32service' erstellen, indem man das .py Skript mit dem Parameter 'install' aufruft. Ich möchte den Dienst später auf Rechnern starten, auf denen kein Python installiert ist. Idealerweise soll der Dienst einfach eine .exe Datei sein, die sich beim Aufruf von 'dienst.exe install' als Dienst registriert und ggf. auch gleich startet. Der Dienst bleibt dann ständig im System (Starttyp: Atomatisch). Also Python wird dann auf dem Rechner nicht zur Verfügung stehen! Kann ich dafür überhaupt 'win32service' verwenden oder funktioniert das ausschließlich für .py Skripte?

2) Das zweite Programm müsste demnach ebenfalls eine .exe Datei sein, die ich mit 'py2exe' erzeugen möchte. Ich habe schon ein paar Kommandozeilen-Tools mit Python geschrieben und die entstandenen .exe haben bislang immer fehlerfrei funktioniert. Nun soll diese .exe seine Parameter an den Dienst übermitteln. Beide müssen kommunizieren. Mir wurde empfohlen, zwischen Prozessen mittels 'named pipes' zu kommunizieren. Für Python gibt es 'win32pipe'. Ist das zur Datenübermittlung an einen Dienst überhaupt sinnvoll/machbar oder kann man dem Dienst vielleicht auch ohne Hilfsprogramm oder auch viel einfacher (ohne 'named pipes') etwas mitteilen?

Ich habe das Ganze schon auf Basis von Server- (Dienst) und Client-Prinzip (Hilfsprogramm, das dem Dienst etwas mitteilt) versucht, aber erstens versagten die daraus erstellten .exe Dateien (die .py haben funktioniert) und zweitens ist das ein unnötiger großer Umweg. Ich will es direkter.

Ich bin etwas aufgeschmissen bei der Dokumentation zu den win32-Modulen, denn irgendwie gibt es kaum eine … zumindest keine ausführliche, die mir sagt, was möglich ist und v.a. wie (Syntax, Klassen, Methoden). Ich bräuchte einen Anstoß bei der Arbeit mit win32service und evtl. win32pipe.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

win32service kannst du problemlos mit py2exe verpacken, dafür gibt es sicher Dokumentation in deren Wiki.

Statt der Named Pipes würde ich für die IPC XML-RPC nehmen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Johi
User
Beiträge: 22
Registriert: Sonntag 21. November 2004, 20:08

droptix hat geschrieben:Ich habe das Ganze schon auf Basis von Server- (Dienst) und Client-Prinzip (Hilfsprogramm, das dem Dienst etwas mitteilt) versucht [...] Ich will es direkter.
Meinst du Mit Client/Server generell so socket-ähnliches zeug, netzwerkbezogen oder wie?
weil wenn du das nicht willst hat es ja nicht direkt was mit netzwerken zu tun, oder? :)
kann auch sein, dass ich da was generell falsch verstehe.

EDIT: entschuldigung, dass ich in diesem post nichts zur lösung beigetragen, sondern nur unnötig rumgefragt hab. aber um andere ansätze zu liefern muss man ja erstmal wissen was genau du willst/nicht willst.
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Letztendlich soll es schon eine Netzwerk-App werden. So richtig mit socket und so :D

Sowas hab ich auch schon hingekriegt. Nur bis dahin möchte ich erstmal diese Aufgabe lösen: Ein Dienst (später dann der Client) läuft im Leerlauf, bis man ihm etwas mitteilt, was er dem Server übertragen soll. Der Server wertet die Anfrage aus und gibt dem Client eine Antwort zurück.

Das Problem dabei ist nur, dass der Client keine GUI haben soll. Letztendlich möchte ich Verknüpfungen auf dem Windows-Desktop oder im Startmenü erstellen, die dem Client etwas mitteilen müssen. Das geht sicher nur über den Umweg eines Hilfsprogrammes, dass z.B. über 'named pipes' dem Client was mitteilt.
Leonidas hat geschrieben:Statt der Named Pipes würde ich für die IPC XML-RPC nehmen.
Was ist das? Klingt für mich völlig neu? Aber ist egal, wenn's stabil ist und letztlich funktioniert. Kannst davon ausgehen, dass mein Hilfsprogramm einfach eine ID an den Client übermitteln soll (einen Integer-Wert also).

Ich dachte auch daran, dass ich vielleicht gar kein Hilfsprogramm brauche, wenn die Windows-API was Natives schon bereit stellen würde. Habe aber keine Ahnung, ob es da schon was Eingebautes in Windows gibt, das mit Diensten kommunizieren kann... sicher nicht, oder?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

droptix hat geschrieben:
Leonidas hat geschrieben:Statt der Named Pipes würde ich für die IPC XML-RPC nehmen.
Was ist das? Klingt für mich völlig neu? Aber ist egal, wenn's stabil ist und letztlich funktioniert. Kannst davon ausgehen, dass mein Hilfsprogramm einfach eine ID an den Client übermitteln soll (einen Integer-Wert also).

Ich dachte auch daran, dass ich vielleicht gar kein Hilfsprogramm brauche, wenn die Windows-API was Natives schon bereit stellen würde. Habe aber keine Ahnung, ob es da schon was Eingebautes in Windows gibt, das mit Diensten kommunizieren kann... sicher nicht, oder?
XML-RPC ist ein Protokoll für Interprozesskommunikation welches auf XML und HTTP setzt. Python hat sowohl XML-RPC Server als auch Clients schon in der Stdlib.

Wenn du eingebaute Windows Techniken nutzt bringt dir das hauptsächlich zusätzliche Arbeit: du könntest sowas vermutlich schon machen, jeodch gewinnst du dadurch nichts. Denn weder DDE noch DCOM sind besonders toll dokumentiert (genausogut wie halt named pipes auch).
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Mit Dokumentationen zu 'named pipes' hatte ich auch so meine Probleme, v.a. um erstmal zu verstehen, was das eigentlich genau ist. So 100%ig habe ich es immernoch nicht verstanden, aber im Groben schon...

XML und HTML sagt mir da schon eher zu, weil reichlich Vorkenntnisse vorhanden sind. Nur bei der Schnittstelle über Pyhton hakt es etwas. Könntest du mir zufällig noch eine gute Stelle für Dokus oder Tutorials nennen oder ein paar Links zum Anlesen geben?

Puh, wird sicher ein hartes Ding, das zu raffen, aber das krieg ich schon hin. Vielleicht bleibst du ja noch ne kurze Weile bei diesem Thread am Ball und könntest mich in der Not nochmal in die richtige Richtung schubsen... wie gesagt: ein paar Links wären schonmal eine ganz Klasse Hilfe. :roll:
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

droptix hat geschrieben:XML und HTML sagt mir da schon eher zu, weil reichlich Vorkenntnisse vorhanden sind. Nur bei der Schnittstelle über Pyhton hakt es etwas. Könntest du mir zufällig noch eine gute Stelle für Dokus oder Tutorials nennen oder ein paar Links zum Anlesen geben?
Von HTML habe ich nicht gesprochen, ich sprach von HTTP.

Oookaaay: Dann hätte ich mal ein Beispiel eines XML-RPC Servers für dich, den ich für die Anforderungen eines Datenbankservers geschrieben habe. Es wird ein XML-RPC Server gestartet, danach wird eine Instanz der Klasse LayerServer gemacht, und die Funktion execute der LayerServer Instanz wird nun beim XML-RPC Server registriert. Nun kann man mit einem Cleient wie

Code: Alles auswählen

import xmlrpclib
server = xmlrpclib.Server('http://127.0.0.1')
print server.execute('Blabal')
auf den Server zugreifen.

Bei Fragen: Frag einfach!
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Sorry, HTTP ist klar. Hatte mir ja eigentlich grad den Wikipedia Eintrag angesehen und durch XML bin ich dann irgendwie bei HTML hängen geblieben... :lol:

Aah, sehr feines Beispiel! Ich teste es und meld mich dann nochmal! Danke soweit!
Gast

Also das ist echt ne feine Sache. Bin zwischendurch noch auf SOAP gestoßen, aber ich denke XMLRPC reicht erst mal. Hier dein von mir abgewandeltes Beispiel. Ich verwende wie du erstmal Port 8080:

Server

Code: Alles auswählen

# -*- encoding: latin-1 -*-
from SimpleXMLRPCServer import SimpleXMLRPCServer

class StoppableSimpleXMLRPCServer(SimpleXMLRPCServer):
    # Overridings
    allow_reuse_address = True

    def serve_forever(self):
        self.stop = False
        while not self.stop:
            self.handle_request()

def my(cmd):
    global server
    print "cmd: %s" % (cmd)
    if(cmd == "stop"):
        print "Server stopped"
        server.stop = True
    elif(cmd == "hello"):
        print "Hello world!"

def main():
    global server
    print 'Booting:'
    port = 8080
    print ' - Creating a XML-RPC server on port %s' % (port)
    server = StoppableSimpleXMLRPCServer(('', port))
    print ' - Registering functions'
    server.register_function(my)
    print ' - Serving: Booting done.'
    server.serve_forever()

if __name__ == '__main__':
    main()
Ich möchte also den "forever" gestarteten Server auch wieder anhalten können.

Client

Code: Alles auswählen

# -*- encoding: latin-1 -*-
import xmlrpclib
server = xmlrpclib.Server('http://127.0.0.1')
print server.my("stop")
Ich bekomme folgende Meldungen:

Server

Code: Alles auswählen

python xmlrpc_server.py
Booting:
 - Creating a XML-RPC server on port 8080
 - Registering functions
 - Serving: Booting done.
Client

Code: Alles auswählen

python xmlrpc_client.py
Traceback (most recent call last):
  File "xmlrpc_client.py", line 4, in ?
    print server.my("stop")
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1096, in __call__
    return self.__send(self.__name, args)
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1383, in __request
    verbose=self.__verbose
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1129, in request
    self.send_content(h, request_body)
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1243, in send_content
    connection.endheaders()
  File "C:\Programme\Python24\lib\httplib.py", line 794, in endheaders
    self._send_output()
  File "C:\Programme\Python24\lib\httplib.py", line 675, in _send_output
    self.send(msg)
  File "C:\Programme\Python24\lib\httplib.py", line 642, in send
    self.connect()
  File "C:\Programme\Python24\lib\httplib.py", line 626, in connect
    raise socket.error, msg
socket.error: (10061, 'Connection refused')
Also auf Port 8080 finden sich beide nicht. Ich hab dann mal Port 80 genommen. Da kommt wenigstens die Verbindung zustande und der Client kann mit dem Server kommunizieren und ihn stoppen. Allerdings gibt mir der Client jedes Mal folgende Meldung:

Code: Alles auswählen

python xmlrpc_client.py
Traceback (most recent call last):
  File "xmlrpc_client.py", line 4, in ?
    print server.my("stop")
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1096, in __call__
    return self.__send(self.__name, args)
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1383, in __request
    verbose=self.__verbose
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1147, in request
    return self._parse_response(h.getfile(), sock)
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1286, in _parse_response
    return u.close()
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 744, in close
    raise Fault(**self._stack[0])
xmlrpclib.Fault: <Fault 1: 'exceptions.TypeError:cannot marshal None unless allo
w_none is enabled'>
Woran liegt es, dass Port 8080 nicht funktioniert (andere auch nicht)? Und wieso geht's auf Port 80, allerdings mit Fehlermeldung? Wie behebe ich den Fehler?

Firewall hatte ich auch schon abgeschaltet, daran sollte es nicht liegen.

Edit (Leonidas): Code in Python-Tags gesetzt.
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Anonymous hat geschrieben:Also auf Port 8080 finden sich beide nicht. Ich hab dann mal Port 80 genommen. Da kommt wenigstens die Verbindung zustande und der Client kann mit dem Server kommunizieren und ihn stoppen. Allerdings gibt mir der Client jedes Mal folgende Meldung:

Code: Alles auswählen

python xmlrpc_client.py
Traceback (most recent call last):
  File "xmlrpc_client.py", line 4, in ?
    print server.my("stop")
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1096, in __call__
    return self.__send(self.__name, args)
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1383, in __request
    verbose=self.__verbose
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1147, in request
    return self._parse_response(h.getfile(), sock)
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1286, in _parse_response
    return u.close()
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 744, in close
    raise Fault(**self._stack[0])
xmlrpclib.Fault: <Fault 1: 'exceptions.TypeError:cannot marshal None unless allo
w_none is enabled'>
Woran liegt es, dass Port 8080 nicht funktioniert (andere auch nicht)? Und wieso geht's auf Port 80, allerdings mit Fehlermeldung? Wie behebe ich den Fehler?

Firewall hatte ich auch schon abgeschaltet, daran sollte es nicht liegen.
Weil du dem Cilent sagen musst, dass er sich auf Port 8080 verbindet :-)
TUFKAB – the user formerly known as blackbird
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Diese Idee hatte ich natürlich auch schon, aber in dem vorgegebenem Beispiel ist nicht ersichtlich, wie das gehen muss. Ich google mich da mal durch... falls einer vor mir die Antwort hat, kann er die natürlich gerne posten. Danke!

Nebenbei, aber wichtig: Welchen Port sollte man nehmen? Gibt's dafür Standards oder vernünftige Begründungen, weshalb ich genau diesen Port nehmen soll?
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Achso, mann muss beim Client den Port direkt in URI reinschreiben. Hätte ich auch gleich drauf kommen können :lol:

Trotzdem bleibt folgender Fehler, auch wenn die Kommunikation klappt:

Code: Alles auswählen

C:\Programme\Python24\XMLRPC>python xmlrpc_client.py
Traceback (most recent call last):
  File "xmlrpc_client.py", line 4, in ?
    print server.my("stop")
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1096, in __call__
    return self.__send(self.__name, args)
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1383, in __request
    verbose=self.__verbose
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1147, in request
    return self._parse_response(h.getfile(), sock)
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 1286, in _parse_response
    return u.close()
  File "C:\Programme\Python24\lib\xmlrpclib.py", line 744, in close
    raise Fault(**self._stack[0])
xmlrpclib.Fault: <Fault 1: 'exceptions.TypeError:cannot marshal None unless allo
w_none is enabled'>
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ja, weil das ursprüngliche XML-RPC Protokoll den Datentyp None nicht kennt, den deine Funktion my() zurückgibt. Deswegen würde ich dir empfehlen, dass du ans Ende deiner Funktion my noch ein return True anhängst.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Aaaaaaaaaaaaah :D !!!

Nicht unbedingt ein 'return True', aber hauptsache überhaupt ein return irgendwas.

Yeah! Tja, so simpel kann das manchmal sein. Jetzt kann ich denke ich richtig loslegen. Danke!

Nebenbei: Da steht ja
Python hat geschrieben:<Fault 1: 'exceptions.TypeError:cannot marshal None unless allow_none is enabled'>
Könnte man auch allow_none anschalten, so dass eine Funktion nicht unbedingt was zurück geben muss? Manchmal hat man ja eine Funktion, die mit einer globalen Variable arbeitet und keinen return bringen muss.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

droptix hat geschrieben:Nicht unbedingt ein 'return True', aber hauptsache überhaupt ein return irgendwas.
Nicht ganz ein return irgendwas, sondern ein return datentyp_mit_dem_das-xml-rpc_protokoll_umgehen_kann. Das sind laut XML-RPC Spezifikation integer, boolean, string, double, datetime und base64. Du siehst, None ist nicht dabei.
droptix hat geschrieben:Könnte man auch allow_none anschalten, so dass eine Funktion nicht unbedingt was zurück geben muss? Manchmal hat man ja eine Funktion, die mit einer globalen Variable arbeitet und keinen return bringen muss.
Die Entscheidung, ob du allow_none machst oder nicht liegt bei dir, jedoch wenn du das machst, dann bist du nicht mehr Konform mit der Spezifikation von XML-RPC. Somit kannst du auch nicht sicher sein, dass andere Clients mit dir kommunizieren werden, da es ein XML-RPC Hack ist.

Davon abgesehen, sollten Funktionen nicht mit globalen Variablen rumhantieren, das ist schlechter Stil. Man sollte wenn, dann diese Funktionen in eine Klasse packen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Oh danke, gut zu wissen! Dann belassen wir es lieber so, dass allow_none nicht erlaubt ist. Will ja keinen gehackten XML-RPC-Server haben... :twisted:

Ich muss schon sagen, das Ding gefällt mir!
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

droptix hat geschrieben:Ich muss schon sagen, das Ding gefällt mir!
Ja, XML-RPC ist ein nettes Spielzeug, jedoch nicht das einzige. So gibt es noch SOAP (die Python-Libs funktionieren zwar, sind aber meiner Meinung nach nicht ganz so gut wie Fredrik Lundhs Arbeit mit XML-RPC), Pyro (wenn du große Anforderungen am Flexibilität hast) und Ice (wenn du große Anforderungen an Geschwindigkeit hast).
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Der Server soll später als echter Dienst laufen. Egal ob auf UNIX oder Windows. Mein Client wird dann wohl eine reine Windows-Lösung, obwohl sie theoretisch auch unter beiden laufen würde.

Kann ich das Python-Skript für den Server als nativen Windows-Dienst installieren (z.B. mit win32service) oder muss man das mit dem Windows ResKit und srvany.exe machen?

Wie verhält sich win32service -> erzeugt das einen echten Dienst oder startet das lediglich Python als Dienst in Verbindung mit dem zugehörigen Skript? Ich möchte schließlich ein Binary erstellen, dass ohne Python auf dem Server lauffähig ist.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dazu musst du py2exe verwenden, die Programme die du damit generierst, kannst du auch als Services generieren lassen, der Python-Interpreter wird eingebunden und somit ist keine Seperate Python-Installation auf dem Server mehr notwendig. Jedoch solltest du zu diesem Thema in das py2exe Wiki und/oder in die Mailingliste schauen, dort steht alles detailierter.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Die .exe Dateien funktionieren :-) Finde ich schon mal sehr gut! Dass man die auch gleich als Service generieren kann, dass ist mir neu -> schau ich mir auf jeden Fall an!

Ich habe gestern noch ein bissel gespielt: Dein Beispiel war ja basierend auf der Python-Standard-Lib xmlrpclib. Dieser XMLRPC-Server kann dem Client "nur" Funktionen/Methoden zur Verfügung stellen, aber keine Klassen.

Nun hab ich bisher noch nicht viel objektorientiertes Python gemacht, da ich aus der PHP-Welt komme, aber seit PHP5 bin ich schon sehr auf den Geschmack gekommen. PHP5 beherrscht nun auch geschützte Klassen, Methoden und Variablen (public, protected, private) – Python jedoch nicht.

-> Deshalb habe ich mir die Lib xmlrpcserver besorgt und diesen "erweiterten" XMLRPC-Server benutzt, weil er auch Klassen zur Verfügung stellen kann. Na gut nicht ganz: er kann Objekte von Klassen zur Verfügung stellen, nicht aber die Klasse selbst... Macht das mehr Sinn als der SimpleXMLRPCServer, der in Python eingebaut ist, wenn ich zunehmend reine OOP-Python-Skripte bauen möchte?
Antworten