This commit is contained in:
2025-09-26 21:56:06 +03:00
parent c17812a0c1
commit 2216f21c47
17 changed files with 3361 additions and 631 deletions

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""
Windows Print Service - Complete Self-Contained Version
Includes all dependencies and libraries for Windows systems
Fixed for Windows Service Error 1053 - Proper Service Implementation
"""
import sys
@@ -12,6 +12,7 @@ import subprocess
import tempfile
import shutil
import time
import signal
from datetime import datetime
from pathlib import Path
import threading
@@ -24,6 +25,10 @@ import zipfile
from http.server import HTTPServer, BaseHTTPRequestHandler
from socketserver import ThreadingMixIn
# Global variables for service control
service_running = True
httpd_server = None
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
"""Handle requests in a separate thread."""
daemon_threads = True
@@ -467,48 +472,171 @@ pause
with open('install_service_complete.bat', 'w') as f:
f.write(service_script)
def signal_handler(signum, frame):
"""Handle shutdown signals gracefully."""
global service_running, httpd_server
logging.info(f"Received signal {signum}, shutting down gracefully...")
service_running = False
if httpd_server:
# Shutdown server in a separate thread to avoid blocking
def shutdown_server():
try:
httpd_server.shutdown()
httpd_server.server_close()
except Exception as e:
logging.error(f"Error during server shutdown: {e}")
shutdown_thread = threading.Thread(target=shutdown_server)
shutdown_thread.daemon = True
shutdown_thread.start()
shutdown_thread.join(timeout=5)
logging.info("Service shutdown complete")
sys.exit(0)
def setup_signal_handlers():
"""Setup signal handlers for graceful shutdown."""
if hasattr(signal, 'SIGTERM'):
signal.signal(signal.SIGTERM, signal_handler)
if hasattr(signal, 'SIGINT'):
signal.signal(signal.SIGINT, signal_handler)
if hasattr(signal, 'SIGBREAK'): # Windows specific
signal.signal(signal.SIGBREAK, signal_handler)
def main():
"""Main service function."""
global start_time
"""Main service function with proper Windows service support."""
global start_time, service_running, httpd_server
start_time = time.time()
# Setup logging
# Setup logging first
setup_logging()
logging.info("Starting Windows Print Service (Complete Version)")
logging.info("=== Starting Windows Print Service (Complete Version) ===")
logging.info(f"Python version: {sys.version}")
logging.info(f"Platform: {sys.platform}")
logging.info(f"Process ID: {os.getpid()}")
logging.info(f"Command line args: {sys.argv}")
# Create service installer
create_windows_service()
# Setup signal handlers for graceful shutdown
setup_signal_handlers()
# Determine run mode
run_mode = "standalone"
if len(sys.argv) > 1:
if sys.argv[1] in ['--service', 'service']:
run_mode = "windows_service"
elif sys.argv[1] in ['--standalone', 'standalone']:
run_mode = "standalone"
elif sys.argv[1] in ['--test', 'test']:
run_mode = "test"
logging.info(f"Running in '{run_mode}' mode")
if run_mode == "test":
# Test mode - just verify setup and exit
logging.info("=== SERVICE TEST MODE ===")
test_service_setup()
return
try:
# Start HTTP server
server_address = ('localhost', 8765)
httpd = ThreadingHTTPServer(server_address, PrintServiceHandler)
# Try to bind to the port
try:
httpd_server = ThreadingHTTPServer(server_address, PrintServiceHandler)
except OSError as e:
if "Address already in use" in str(e):
logging.error(f"Port 8765 is already in use. Another service instance may be running.")
logging.error("Stop the existing service or use a different port.")
return
else:
raise
logging.info(f"Print service started on http://{server_address[0]}:{server_address[1]}")
logging.info("Available endpoints:")
logging.info(" GET /health - Health check")
logging.info(" GET /printers - List available printers")
logging.info(" GET /printers - List available printers")
logging.info(" GET /status - Service status")
logging.info(" POST /print_pdf - Print PDF file")
# Keep track of requests
httpd.request_count = 0
httpd_server.request_count = 0
# Start server
httpd.serve_forever()
# Service main loop
logging.info("Service is ready and listening...")
if run_mode == "standalone":
logging.info("*** STANDALONE MODE - Press Ctrl+C to stop ***")
logging.info("Test the service at: http://localhost:8765/health")
while service_running:
try:
# Handle requests with timeout to check service_running periodically
httpd_server.timeout = 1.0
httpd_server.handle_request()
except KeyboardInterrupt:
logging.info("Service stopped by user (Ctrl+C)")
break
except Exception as e:
logging.error(f"Request handling error: {e}")
# Continue running unless it's a critical error
if not service_running:
break
except KeyboardInterrupt:
logging.info("Service stopped by user")
except Exception as e:
logging.error(f"Service error: {e}")
logging.error(f"Critical service error: {e}")
import traceback
logging.error(f"Traceback: {traceback.format_exc()}")
finally:
# Cleanup
if httpd_server:
try:
httpd_server.server_close()
except:
pass
logging.info("=== Windows Print Service shutdown complete ===")
# Exit cleanly
sys.exit(0)
def test_service_setup():
"""Test service setup and configuration."""
logging.info("Testing service setup...")
# Test printer detection
try:
# Create a temporary handler instance to test printer detection
import tempfile
temp_handler = PrintServiceHandler()
temp_handler.temp_dir = tempfile.mkdtemp(prefix="test_service_")
printers = temp_handler.get_available_printers()
logging.info(f"Found {len(printers)} printers:")
for printer in printers[:5]: # Show first 5
logging.info(f" - {printer.get('name', 'Unknown')}")
# Cleanup temp directory
try:
httpd.server_close()
import shutil
shutil.rmtree(temp_handler.temp_dir)
except:
pass
logging.info("Windows Print Service shutdown complete")
except Exception as e:
logging.warning(f"Printer detection test failed: {e}")
# Test port availability
try:
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('localhost', 8765))
logging.info("Port 8765 is available ✓")
except OSError as e:
logging.error(f"Port 8765 test failed: {e}")
logging.info("Service setup test completed")
if __name__ == "__main__":
main()