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

Dienstag 25. Oktober 2005, 23:02

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
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mittwoch 26. Oktober 2005, 19:09

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 Modvoice
Johi
User
Beiträge: 22
Registriert: Sonntag 21. November 2004, 20:08

Mittwoch 26. Oktober 2005, 21:42

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

Freitag 28. Oktober 2005, 11:57

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
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Freitag 28. Oktober 2005, 19:59

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 Modvoice
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Freitag 28. Oktober 2005, 23:21

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
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Samstag 29. Oktober 2005, 16:27

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 Modvoice
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Samstag 29. Oktober 2005, 20:47

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

Freitag 4. November 2005, 02:29

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:

Freitag 4. November 2005, 04:38

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

Freitag 4. November 2005, 16:09

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

Freitag 4. November 2005, 16:29

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
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Sonntag 6. November 2005, 12:59

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 Modvoice
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Sonntag 6. November 2005, 21:08

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
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Montag 7. November 2005, 13:25

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 Modvoice
Antworten