Skip to content

Commit 930ab41

Browse files
committed
fix: Resolve Windows PyInstaller path issues and hardcoded paths
- Fix ctypes.wintypes.HRESULT compatibility for Windows executable - Replace hardcoded paths with proper AppData directory handling - Add fallback mechanisms for missing wintypes in PyInstaller bundles - Ensure proper path resolution for both development and packaged environments - Use centralized path management in file_conversion.py This resolves two critical issues: 1. Windows executable failing to start due to missing HRESULT type 2. Hardcoded paths causing file access problems in packaged builds
1 parent 43ffa35 commit 930ab41

3 files changed

Lines changed: 83 additions & 13 deletions

File tree

backend/config.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import sys
23
import yaml
34
from pathlib import Path
45
from typing import Dict, Any, List
@@ -25,10 +26,56 @@ class Config:
2526

2627

2728
def __init__(self):
28-
self.config_file = Path(__file__).parent.parent / "config.yaml"
29+
self.config_file = self._get_config_path()
2930
self.config = self._load_default_config()
3031
self.load()
3132

33+
@staticmethod
34+
def _get_config_path() -> Path:
35+
"""Get the configuration file path"""
36+
if getattr(sys, 'frozen', False):
37+
# Running as PyInstaller bundle - use AppData location
38+
import os
39+
if os.name == 'nt': # Windows
40+
import ctypes
41+
42+
# Define HRESULT manually for PyInstaller compatibility
43+
try:
44+
from ctypes import wintypes
45+
HRESULT = wintypes.HRESULT
46+
except AttributeError:
47+
# PyInstaller sometimes doesn't include all wintypes
48+
HRESULT = ctypes.c_long
49+
50+
CSIDL_LOCAL_APPDATA = 0x001c
51+
SHGetFolderPath = ctypes.windll.shell32.SHGetFolderPathW
52+
53+
try:
54+
from ctypes import wintypes
55+
SHGetFolderPath.argtypes = [wintypes.HWND, ctypes.c_int, wintypes.HANDLE, wintypes.DWORD, wintypes.LPCWSTR]
56+
SHGetFolderPath.restype = HRESULT
57+
path_buf = ctypes.create_unicode_buffer(wintypes.MAX_PATH)
58+
result = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, path_buf)
59+
if result == 0:
60+
return Path(path_buf.value) / "Echo" / "config.yaml"
61+
else:
62+
return Path.home() / "AppData" / "Local" / "Echo" / "config.yaml"
63+
except AttributeError:
64+
# Fallback if wintypes are not available
65+
SHGetFolderPath.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_uint, ctypes.c_wchar_p]
66+
SHGetFolderPath.restype = HRESULT
67+
path_buf = ctypes.create_unicode_buffer(260) # MAX_PATH
68+
result = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, path_buf)
69+
if result == 0:
70+
return Path(path_buf.value) / "Echo" / "config.yaml"
71+
else:
72+
return Path.home() / "AppData" / "Local" / "Echo" / "config.yaml"
73+
else:
74+
return Path.home() / ".echo" / "config.yaml"
75+
else:
76+
# Running in development environment
77+
return Path(__file__).parent.parent / "config.yaml"
78+
3279
def _load_default_config(self) -> Dict[str, Any]:
3380
"""Load default configuration"""
3481
return {

backend/file_conversion.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,9 @@ async def convert_files(self, request: FileConversionRequest) -> FileConversionR
241241
output_filename = f"{base_filename}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.yaml"
242242

243243
# 5. Save YAML file
244-
exams_dir = Path(__file__).parent.parent / "exams"
245-
exams_dir.mkdir(exist_ok=True)
244+
from .paths import get_paths
245+
paths = get_paths()
246+
exams_dir = paths.exams_dir
246247
output_path = exams_dir / output_filename
247248

248249
try:

backend/paths.py

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,42 @@ def _get_base_path(self) -> Path:
3636
# Running as PyInstaller bundle
3737
if os.name == 'nt': # Windows
3838
import ctypes
39-
from ctypes import wintypes
39+
40+
# Define HRESULT manually for PyInstaller compatibility
41+
try:
42+
from ctypes import wintypes
43+
HRESULT = wintypes.HRESULT
44+
except AttributeError:
45+
# PyInstaller sometimes doesn't include all wintypes
46+
HRESULT = ctypes.c_long
4047

4148
CSIDL_LOCAL_APPDATA = 0x001c
4249
SHGetFolderPath = ctypes.windll.shell32.SHGetFolderPathW
43-
SHGetFolderPath.argtypes = [wintypes.HWND, ctypes.c_int, wintypes.HANDLE, wintypes.DWORD, wintypes.LPCWSTR]
44-
SHGetFolderPath.restype = wintypes.HRESULT
45-
46-
path_buf = ctypes.create_unicode_buffer(wintypes.MAX_PATH)
47-
result = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, path_buf)
4850

49-
if result == 0:
50-
return Path(path_buf.value) / "Echo"
51-
else:
52-
return Path.home() / "AppData" / "Local" / "Echo"
51+
try:
52+
from ctypes import wintypes
53+
SHGetFolderPath.argtypes = [wintypes.HWND, ctypes.c_int, wintypes.HANDLE, wintypes.DWORD, wintypes.LPCWSTR]
54+
SHGetFolderPath.restype = HRESULT
55+
56+
path_buf = ctypes.create_unicode_buffer(wintypes.MAX_PATH)
57+
result = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, path_buf)
58+
59+
if result == 0:
60+
return Path(path_buf.value) / "Echo"
61+
else:
62+
return Path.home() / "AppData" / "Local" / "Echo"
63+
except AttributeError:
64+
# Fallback if wintypes are not available
65+
SHGetFolderPath.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_uint, ctypes.c_wchar_p]
66+
SHGetFolderPath.restype = HRESULT
67+
68+
path_buf = ctypes.create_unicode_buffer(260) # MAX_PATH
69+
result = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, path_buf)
70+
71+
if result == 0:
72+
return Path(path_buf.value) / "Echo"
73+
else:
74+
return Path.home() / "AppData" / "Local" / "Echo"
5375
else:
5476
return Path.home() / ".echo"
5577
else:

0 commit comments

Comments
 (0)