1- import os
2- import platform
3- import subprocess
1+ import os , platform , subprocess , tempfile
42import tkinter as tk
53import ctypes
64from tkinter import simpledialog , messagebox
@@ -12,20 +10,44 @@ def detect_sd_card():
1210 """Detect connected SD Cards."""
1311 devices = []
1412 if platform .system () == "Windows" :
15- # Windows specific logic
1613 bitmask = ctypes .windll .kernel32 .GetLogicalDrives ()
1714 for letter in range (26 ): # Check from A to Z
1815 if bitmask & (1 << letter ):
1916 drive_letter = f"{ chr (65 + letter )} :\\ "
2017 drive_type = ctypes .windll .kernel32 .GetDriveTypeW (drive_letter )
2118 if drive_type == 2 : # DRIVE_REMOVABLE == 2
22- devices .append (drive_letter )
19+ try :
20+ volume_name_buffer = ctypes .create_unicode_buffer (1024 )
21+ volume_serial = ctypes .c_ulong (0 )
22+ max_component_length = ctypes .c_ulong (0 )
23+ file_system_flags = ctypes .c_ulong (0 )
24+ file_system_name_buffer = ctypes .create_unicode_buffer (1024 )
25+
26+ if ctypes .windll .kernel32 .GetVolumeInformationW (
27+ drive_letter ,
28+ volume_name_buffer ,
29+ ctypes .sizeof (volume_name_buffer ),
30+ ctypes .byref (volume_serial ),
31+ ctypes .byref (max_component_length ),
32+ ctypes .byref (file_system_flags ),
33+ file_system_name_buffer ,
34+ ctypes .sizeof (file_system_name_buffer )
35+ ):
36+ label = volume_name_buffer .value
37+ # If there is a label, show both letter and label
38+ if label :
39+ devices .append (f"{ drive_letter [0 ]} :\\ ({ label } )" )
40+ else :
41+ devices .append (drive_letter )
42+ else :
43+ devices .append (drive_letter )
44+ except Exception as e :
45+ print (f"Error getting volume name: { e } " )
46+ devices .append (drive_letter )
2347 elif platform .system () == "Darwin" : # macOS
24- # macOS specific logic
2548 volumes = [os .path .join ("/Volumes" , d ) for d in os .listdir ("/Volumes" ) if os .path .ismount (os .path .join ("/Volumes" , d ))]
2649 devices .extend (volumes )
2750 elif platform .system () == "Linux" :
28- # Linux specific logic
2951 user = os .getenv ("USER" , "default_user" )
3052 media_path = f"/media/{ user } "
3153 if os .path .exists (media_path ):
@@ -36,72 +58,40 @@ def get_disk_identifier(volume_path):
3658 """Returns disk identifier for given volume path."""
3759 system = platform .system ()
3860
39- if system == "Darwin" :
40- result = subprocess .run (['diskutil' , 'info' , volume_path ], capture_output = True , text = True )
41- for line in result .stdout .splitlines ():
42- if "Device Identifier" in line :
43- return line .split (":" )[1 ].strip ()
44- elif system == "Windows" :
61+ if system == "Windows" :
4562 try :
46- # PowerShell command to get device ID
47- command = f"powershell -Command (Get-WmiObject Win32_LogicalDisk | Where-Object {{$_.DeviceID -eq '{ volume_path } '}}).PNPDeviceID"
48- result = subprocess .run (command , capture_output = True , text = True , shell = True )
49- output = result .stdout .strip ()
50-
51- if output :
52- return output
53- else :
54- print (f"Error: No PNPDeviceID found for { volume_path } " )
63+ drive_letter = volume_path .split (" " )[0 ]
64+ command = f"""
65+ $driveLetter = '{ drive_letter [0 ]} '
66+ $removableDrives = Get-WmiObject -Query "SELECT * FROM Win32_DiskDrive WHERE InterfaceType='USB'"
67+ foreach ($drive in $removableDrives) {{
68+ $partitions = Get-WmiObject -Query "ASSOCIATORS OF {{Win32_DiskDrive.DeviceID='$($drive.DeviceID)'}} WHERE AssocClass=Win32_DiskDriveToDiskPartition"
69+ foreach ($partition in $partitions) {{
70+ $logicalDisks = Get-WmiObject -Query "ASSOCIATORS OF {{Win32_DiskPartition.DeviceID='$($partition.DeviceID)'}} WHERE AssocClass=Win32_LogicalDiskToPartition"
71+ foreach ($logicalDisk in $logicalDisks) {{
72+ if ($logicalDisk.DeviceID -eq '{ drive_letter [0 ]} ') {{
73+ return $drive.PNPDeviceID
74+ }}
75+ }}
76+ }}
77+ }}
78+ """
79+ result = subprocess .run (["powershell" , "-Command" , command ], capture_output = True , text = True , shell = True )
80+
81+ if result .returncode != 0 :
82+ print (f"Error in PowerShell command: { result .stderr } " )
5583 return None
84+
85+ return result .stdout .strip ()
5686 except Exception as e :
5787 print (f"Error during retrieving device identifier: { e } " )
5888 return None
5989 return None
6090
6191
62- def get_volume_by_letter (letter ):
63- try :
64- # Esegui il comando DiskPart per elencare i volumi
65- diskpart_script = "list volume"
66-
67- process = subprocess .Popen (
68- ["diskpart" ],
69- stdin = subprocess .PIPE ,
70- stdout = subprocess .PIPE ,
71- stderr = subprocess .PIPE ,
72- text = True
73- )
74-
75- # Invia il comando al processo
76- stdout , stderr = process .communicate (input = diskpart_script )
77-
78- if stderr :
79- print (f"Errore: { stderr } " )
80- return None
81-
82- # Filtriamo l'output per ignorare intestazioni e altre righe non pertinenti
83- volume_lines = []
84- for line in stdout .splitlines ():
85- line = line .strip ()
86- if line and line .startswith ("Volume" ): # Solo righe con informazioni sui volumi
87- volume_lines .append (line )
88-
89- # Cerca la lettera specificata nei volumi
90- for line in volume_lines :
91- if letter in line :
92- print (f"Volume trovato per { letter } : { line } " )
93- return line
94-
95- print (f"Volume con lettera { letter } non trovato." )
96- return None
97-
98- except Exception as e :
99- print (f"Errore: { str (e )} " )
100- return None
101-
10292def refresh_sd_devices (sd_select , sd_dropdown , identifier ):
10393 sd_devices = detect_sd_card () or ["Click to refresh" ]
104- if sd_devices [0 ] == "Click to refresh" :
94+ if sd_devices [0 ] == "Click to refresh" or sd_devices [ 0 ] == "None" :
10595 selected_sd = "Click to refresh"
10696 else :
10797 selected_sd = get_volume_name (identifier )
@@ -115,53 +105,55 @@ def refresh_sd_devices(sd_select, sd_dropdown, identifier):
115105
116106def eject_sd (sd_device , sd_select , sd_dropdown , terminal ):
117107 system_os = platform .system ()
118- identifier = get_disk_identifier (sd_device )
119-
120- if not identifier :
121- print (f"Error: Can't find { sd_device } disk identifier." )
122- terminal .message (f"Error: Can't find { sd_device } disk identifier." )
123- return False
108+
109+ if system_os == "Windows" and "(" in sd_device :
110+ drive_letter = sd_device .split (" " )[0 ]
111+ else :
112+ drive_letter = sd_device
124113
125114 if sd_device != "Click to refresh" :
126115 try :
127- if system_os == "Linux" :
116+ if system_os == "Windows" :
117+ # Modified PowerShell command to properly exit after ejection
118+ script = f"""
119+ (New-Object -comObject Shell.Application).Namespace(17).ParseName(\" { drive_letter [0 ]} :\" ).InvokeVerb(\" Eject\" )
120+ Start-Sleep -Seconds 1
121+ exit
122+ """
123+
124+ # Use CREATE_NO_WINDOW to avoid GUI freezing
125+ result = subprocess .run (
126+ ["powershell" , "-Command" , script ],
127+ capture_output = True ,
128+ text = True ,
129+ creationflags = subprocess .CREATE_NO_WINDOW
130+ )
131+
132+ if result .returncode == 0 :
133+ terminal .message (f"{ drive_letter } successfully ejected." )
134+ else :
135+ terminal .message (f"Error ejecting { drive_letter } : { result .stderr } " )
136+
137+ elif system_os == "Linux" :
128138 os .system (f"umount { sd_device } " )
129139 print (f"{ sd_device } correctly ejected." )
130140 terminal .message (f"{ sd_device } correctly ejected." )
131141 elif system_os == "Darwin" :
132142 os .system (f"diskutil unmount { sd_device } " )
133143 print (f"{ sd_device } correctly ejected." )
134144 terminal .message (f"{ sd_device } correctly ejected." )
135- elif system_os == "Windows" :
136- # Prima smontiamo il volume
137- os .system (f"mountvol { sd_device } /p" )
138- print (f"{ sd_device } correctly ejected." )
139- terminal .message (f"{ sd_device } correctly unmounted." )
140-
141- # Ora rimuoviamo il dispositivo usando pnputil
142- try :
143- # Comando pnputil per rimuovere il dispositivo
144- pnputil_command = f"pnputil /remove-device \" { identifier } \" "
145- subprocess .run (pnputil_command , check = True , shell = True )
146- print (f"{ sd_device } correctly ejected." )
147- terminal .message (f"{ sd_device } correctly ejected." )
148- except subprocess .CalledProcessError as e :
149- print (f"Error trying to eject { sd_device } : { e } " )
150- terminal .message (f"Error trying to eject { sd_device } : { e } " )
151-
152145 else :
153146 print (f"{ system_os } OS not supported for disk ejection." )
154147 return False
148+
149+ refresh_sd_devices (sd_select , sd_dropdown , get_disk_identifier (sd_device ))
155150 except Exception as e :
156151 print (f"Error trying to eject { sd_device } : { e } " )
157- return False
152+ terminal . message ( f"Error trying to eject { sd_device } : { e } " )
158153 else :
159154 print ("No SD found to eject." )
160155 terminal .message ("No SD found to eject." )
161156
162- refresh_sd_devices (sd_select , sd_dropdown , identifier )
163-
164-
165157def get_volume_name (disk_identifier ):
166158 """
167159 RETURN NAME OF VOLUME ASSOCIED WITH DISK.
@@ -170,7 +162,7 @@ def get_volume_name(disk_identifier):
170162 disk_identifier (str): identifier disk (es. '/dev/disk4', '/dev/sdb', 'D:')
171163
172164 Returns:
173- str: name of volume, or None is not found.
165+ str: name of volume, or None if not found.
174166 """
175167 system = platform .system ()
176168
@@ -202,6 +194,7 @@ def get_volume_name(disk_identifier):
202194
203195 elif system == "Windows" :
204196 # Usa PowerShell per ottenere informazioni sul disco
197+ print (f"disk_identifier[0]: { disk_identifier } " )
205198 result = subprocess .run (
206199 [
207200 "powershell" , "-Command" ,
@@ -211,6 +204,7 @@ def get_volume_name(disk_identifier):
211204 stderr = subprocess .PIPE ,
212205 text = True
213206 )
207+
214208 return result .stdout .strip ()
215209
216210 else :
@@ -243,43 +237,58 @@ def format_sd_card(sd_path, display, callback, sd_selector):
243237 os_type = platform .system ()
244238 # print(sd_path)
245239 if sd_selector [0 ].get () == "Click to refresh" :
246- display .message ("Formatting Click to refresh to FAT32… did you plug-in an sd?" )
240+ display .message ("Formatting ??? to FAT32… did you plug-in an sd?" )
247241 return False
248242 try :
249243 if os_type == "Windows" :
250- # Ask user volume name
244+ # Chiedi all'utente il nome del volume
251245 def on_volume_name_enter (volume_name ):
252246 if not volume_name or len (volume_name .strip ()) == 0 :
253247 messagebox .showerror ("Error" , "Volume name cannot be empty!" )
254248 return
255- volume_name = volume_name .upper () # convert uppercase to avoid errors
249+ volume_name = volume_name .upper () # Converti il nome in maiuscolo per evitare errori
256250 identifier = get_disk_identifier (volume_name )
257251
258- # create formatting script
252+ if not identifier :
253+ messagebox .showerror ("Error" , "Unable to get disk identifier." )
254+ return
255+
256+ # Crea lo script di formattazione
259257 script = f"""
260258 select volume { sd_path [0 ]}
261259 format fs=fat32 quick label={ volume_name }
262260 exit
263261 """
262+
264263 try :
265- # Scrivi e esegui il comando DiskPart
264+ # Scrivi ed esegui lo script diskpart
266265 with open ("format_script.txt" , "w" ) as script_file :
267266 script_file .write (script )
268- subprocess .run ("diskpart /s format_script.txt" , check = True , shell = True )
267+
268+ # Esegui diskpart
269+ result = subprocess .run ("diskpart /s format_script.txt" , check = True , shell = True , capture_output = True , text = True )
270+
271+ # Debug: stampa l'output di diskpart
272+ print (f"Diskpart Output (stdout): { result .stdout } " )
273+ print (f"Diskpart Output (stderr): { result .stderr } " )
274+
269275 os .remove ("format_script.txt" )
270- # refresh_sd_devices(sd_selector[0], sd_selector[1], identifier)
271276 display .message (f"Formatting completed!\n Your SD card has been formatted with the name '{ volume_name } '!" )
277+
278+ # Chiamata al callback
272279 try :
273280 callback_thread = threading .Thread (target = callback )
274281 callback_thread .start ()
275282 except :
276283 traceback .print_exc ()
277284 return True
285+
278286 except subprocess .CalledProcessError as e :
279287 messagebox .showerror ("Error" , f"Error while formatting: { e } " )
288+ print (f"Error while formatting: { e } " )
280289 return False
281290
282- # Chiedi il nome del volume
291+ # Chiedi all'utente di inserire un nome valido per il volume
283292 display .user_input ("Enter the name for your new volume:" , on_volume_name_enter )
284293 elif os_type == "Darwin" :
285294 # Get disk identifier for macOS
@@ -334,3 +343,43 @@ def on_volume_name_enter(volume_name):
334343 messagebox .showerror ("Error" , f"Error while formatting: { e } " )
335344 print (e .stderr ) # Show detailed error
336345 return False
346+
347+ def run_powershell_script (command ):
348+ """Esegui uno script PowerShell in background senza aprire la finestra PowerShell."""
349+
350+ # Crea un file temporaneo per il comando PowerShell
351+ with tempfile .NamedTemporaryFile (suffix = ".ps1" , delete = False ) as temp_file :
352+ temp_file .write (command .encode ("utf-8" ))
353+ temp_file .close () # Chiudiamo il file temporaneo per poterlo eseguire
354+
355+ # Imposta la politica di esecuzione per consentire l'esecuzione di script non firmati
356+ set_policy_command = "Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force"
357+
358+ try :
359+ # Esegui il comando PowerShell
360+ subprocess .run (
361+ ["powershell" , "-Command" , set_policy_command ],
362+ capture_output = True ,
363+ text = True ,
364+ creationflags = subprocess .CREATE_NO_WINDOW
365+ )
366+
367+ # Esegui lo script PowerShell in background senza finestra
368+ result = subprocess .run (
369+ ["powershell.exe" , "-ExecutionPolicy" , "Unrestricted" , "-File" , temp_file .name ],
370+ capture_output = True ,
371+ text = True ,
372+ creationflags = subprocess .CREATE_NO_WINDOW
373+ )
374+
375+ # Stampa l'output e gli errori (se ci sono)
376+ if result .returncode != 0 :
377+ print (f"PowerShell Error: { result .stderr } " )
378+ raise Exception (f"Error executing PowerShell script: { result .stderr } " )
379+
380+ except Exception as e :
381+ print (f"Error during script execution: { e } " )
382+ finally :
383+ # Rimuovi il file temporaneo dopo l'esecuzione
384+ os .remove (temp_file .name )
385+
0 commit comments