No, in inglese, credo si chiami "Quich Launch bar".
Non ricordo se esiste da WinXP o da Win7
Una volta attivata, ci trascini dentro i collegamenti che ti interessano, oppure basta copiare i collegamenti in questa directory:
C:\Users\<IlTuoUtente>\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch
Per ogni icona che compare nella barra, basta farci sopra un click, e parte l'applicativo.
Nell'allegato, in alto c'è la barra di avvio veloce, sotto quella di stato
Ciao, Mario
Nella versione Linux, le carico da immagine (bmp, ico, jpg, ecc).
Nella versione Windows direttamente da eseguibile/link
Il codice che uso è questo:
function EstraiIcona(const NomeFileExe: String; WrkIcona: TIcon; WrkDimIco:Integer): Boolean;
{$IFDEF MSWINDOWS}
var SysStr: Widestring;
FileInfo: SHFILEINFOw;
HandleImageList:HIMAGELIST;
IdxImg:Integer;
{$ENDIF}
begin
Result:=False;
{$IFDEF MSWINDOWS}
HandleImageList:=ImageList_Create(WrkDimIco, WrkDimIco, ILC_COLOR32, 0, 1);
try
SysStr:=UTF8ToUTF16(NomeFileExe);
IdxImg:=SHGetFileInfoW(PWideChar(SysStr), 0, FileInfo, SizeOf(FileInfo), SHGFI_ICON);
if (IdxImg <> 0) then begin
WrkIcona.Handle:=FileInfo.hIcon;
//Carico l'icona in una struttura di appoggio che fa sì che non venga perso
//il bit relativo alla trasparenza (a differenza di quanto succede con "EstraiIconaDaExe")
IdxImg:=ImageList_AddIcon(HandleImageList, WrkIcona.Handle);
if (IdxImg <> -1) then begin
IdxImg:=ImageList_ReplaceIcon(HandleImageList, IdxImg, WrkIcona.Handle);
if (IdxImg <> -1) then begin
//Ora posso assegnare l'icona letta all'output della funzione, e comunicare
//al chiamante che tutto è ok
WrkIcona.Handle:=ImageList_GetIcon(HandleImageList, 0, ILD_TRANSPARENT + ILD_MASK + ILD_IMAGE);
Result:=True;
end;
end;
end;
finally
ImageList_Destroy(HandleImageList);
end;
{$ENDIF}
end;
Ora sono di fretta, e non riesco a verificare se è compilabile, o se gli serve qualcosa che prendo da altre unit.
provalo, ed eventualmente fammi sapere se e cosa manca
Ciao, Mario
Uses Windows, ShellApi;
var IconIndex: word;
var IconHandle: HIcon;
IconIndex := 0;
IconHandle := ExtractAssociatedIcon(HInstance, nomedelifile, IconIndex);
Vado a memoria questo ti estrare l'Handle dell'icona "abbinata" di un qualsiasi file in Windows. Funziona su qualsiasi file, non solo gli eseguibili.
L'handle poi puoi assegnarlo oppure puoi salvarti l'immagine.
Ciao
@DragoRosso,
ho provato il codice che hai postato ma non riesco a capire una cosa, l'ultimo parametro immagino sia l'indice dell'icona all'interno del file (exe o dll), l'ho settato a 0 ma in compilazione mi restituisce questo errore:
main.pas(196,99) Error: Incompatible type for arg no. 3: Got "Word", expected "LPWORD"
cosa non gli "piace" nella dichiarazione ?
procedure TTrayLauncher.ReadSettings;
var IconIndex: word;
var IconHandle: HIcon;
...
...
..
IconIndex := 0;
IconHandle := ExtractAssociatedIcon(HInstance, appSpec[countTotalApp].appName, IconIndex);
M
In effetti è errata la chiamata ..... (qui il riferimento Microsoft): https://learn.microsoft.com/it-it/windows/win32/api/shellapi/nf-shellapi-extractassociatediconw (https://learn.microsoft.com/it-it/windows/win32/api/shellapi/nf-shellapi-extractassociatediconw)
La chiamata corretta è:
IconHandle := ExtractAssociatedIcon(HInstance, appSpec[countTotalApp].appName, @IconIndex);
IconIndex (c'è la chioccciola in chiamata) e il nomefile vengono variati se il file è un eseguibile o una dll: nel nomefile la funzione scrive la path completa con il nomedelfile, e in IconIndex viene caricato l'indice dell'icona presente nel file eseguibile è che è associata a file stesso (non è detto sia la prima).
Per tutti gli altri file (ad esempio un collegamento a una pagina web) viene ritornato solo l'Handle all'icona abbinata al collegamento (potrebbe essere l'icona del browser standard oppure l'icona identificativa del sito web).
Occhio che la stringa nomedelfile potrebbe essere variata, quindi è meglio usare un buffer per passare il nomedelfile ... il chiamato sicuramente non và a testare l'allocazione della stringa e si rischia di avere LEAK.
Ti posto un esempio concreto ....
Esatto:
WrkImage.Picture.Assign(WrkIcona);
Scusami, mi ero dimenticato di scriverlo !
Ciao, Mario
Sempre proseguendo nello sviluppo della task bar alternativa, mi sono posto questa domanda:
dato che creo runtime un numero indefinito di oggetti TTabSheet e TImage, sarebbe logico che li distruggessi in fase di chiusura... però non è vero che in fase di chiusura della applicazione, tutto ciò che è collegato ad essa viene distrutto ?
Secondo voi, quale approccio sarebbe il più corretto e come?
pensavo ad un loop tipo questo:
for i := Self.ComponentCount-1 downto 1 do
begin
if Self.Components[i] is TImage then TImage(Self.Components[i]).Free;
end;
Tutti i componenti che vengono creati con il riferimento alla Form principale (generalmente pippo := Tpippo.Create(self)) vengono automaticamente liberati (ossia distrutti) alla chiusura della Form stessa (ossia dell'applicazione).
Questo vale per qualunque componente creato con un riferimento ad un altro componente (generalemnte chiamti componenti Padri e Figli, Parents and Childs).
Quale è la buona regola: implementare un distruttore per ogni componente personalizzato o creato a runtime se andiamo a creare / instanziare oggetti (ad esempio array dinamici). In tale metodo andremo a liberare tutti ciò che abbiamo creato a runtime (tale metodo verrà automaticamente chiamato dal componente padre).
dato che creo runtime un numero indefinito di oggetti TTabSheet e TImage, sarebbe logico che li distruggessi in fase di chiusura... però non è vero che in fase di chiusura della applicazione, tutto ciò che è collegato ad essa viene distrutto ?
Esatto, se sono componenti creati con xxxx.Create(self), verranno distrutti automaticamente. Ciò però non è vero con oggetti creati con xxxx.Create(nil) oppure se usi gli array dinamici, e in molte altre situazioni (occorre trattare il concetto di visibilità / vita / "count reference" degli oggetti).
Occhio anche all'uso dei puntatori, soprattutto con array statici: tutto ciò che è statico viene rilasciato dall'applicazione (non si può rilasciare a codice) PERO' IL CONTENUTO DEGLI ARRAY NO !!! se tale contenuto è un oggetto istanziato !!!!!
Esempio:
//TObject può essere un qualsiasi oggetto noto
var Pippo: array [0..9] of TObject;
i: integer;
for i := low(Pippo) to High(Pippo) do
Pippo[i] := TObject.Create(self);
//Questo array verrà liberato alla chiusura del programma (o all'uscita del metodo se dichiarato in un metodo) ma gli oggetti creati NO, anche se usano il "self" come parent. E' necessario liberare a mano ogni contenuto del singolo elemento dell'array alla chiusura (o quando necessita).
for i := low(Pippo) to High(Pippo) do
begin
if assigned(Pippo[i]) then
begin
Pippo[i].Free;
Pippo[i] := nil; <------------- RICORDARSI SEMPRE DI PORRE A NIL SE LIBERATE RISORSE A MANO !!!!!!!!!!
end;
Secondo voi, quale approccio sarebbe il più corretto e come?
pensavo ad un loop tipo questo:
for i := ComponentCount-1 downto 1 do
begin
//CORRETTO ........
if Components[i] is TImage then
begin
(Components[i]) as TImage).Free;
Components[i]) := nil;
end;
end;
Anche questo è corretto (anche se non necessario in questo particolare caso), l'unica accortezza è di aggiungere l'assegnazione a "nil" dopo il FREE, questo perchè nel caso il "PADRE" (self) chiamasse il distruttore del "Figlio" (Image) andrebbe in AV senza il "nil".
Ciao
Grazie DragoRosso,
domanda, perchè ottengo un errore di compilazione con il seguente codice...
for i := Self.ComponentCount-1 downto 1 do
begin
if Self.Components[i] is TImage then
begin
(Self.Components[i] as TImage).Free;
Self.Components[i] := nil;
end;
end;
ecco l'errore:
main.pas(174,27) Error: No member is provided to access property
Era parzialmente fatto con l'esempio che avevo riportato:
La FORM deve avere il bordo di tipo "bsToolWindow", e poi devi aggiungere queste righe nel FORMCREATE:
procedure TFMain.FormCreate(Sender: TObject);
var
iStyle: Integer;
begin
iStyle:= GetWindowLong( Application.Handle, GWL_EXSTYLE );
SetWindowLong( Application.Handle, GWL_EXSTYLE, iStyle or WS_EX_TOOLWINDOW );
end;
Ciao
Ho fatto la prova con il "bsNone" su molti PC con Windows 11 (Home, Pro) e su tutti il programma non appare con "ALT TAB".
Ciao,
ho provato il tuo codice, e funziona bene anche da me.
Probabilmente nel mio progetto c'è qualcosa che va in conflitto, e fa sì che non funzioni.
Non fa niente, sarebbe solo stata una finezza in più !
Ciao, Mario
Trovato !!!
Nel file .lpr, prima della creazione del form, avevo aggiunto questo:
Application.MainFormOnTaskBar:=True;
Serve per nascondere l'icona del programma dalla taskbar di Windows. Disabilitando questa istruzione, adesso il tuo codice funziona !
Grazie ancora, Mario
Programma aggiornato.
Se come settato nel codice postato usi TTabsheet, devi adattare parte del codice per ampliare la larghezza del controllo (si deve ampliare il TPageControl).
https://cloud.dyn-o-saur.com/QuickLaunch.zip (https://cloud.dyn-o-saur.com/QuickLaunch.zip)
P.S.: puoi usare diversi controlli, ma alcuni di quelli che ho provato non gestiscono correttamente il drag and drop, oppure la visualizzazione o gli eventi ....
Nel controllo che userai come DROP devono essere impostati gli eventi (vedi allegato):
//Questi eventi devono essere impostati dall'Object Inspector e non a Run TIME !!!!!!
//Alcuni controlli li espongono come Protected, quindi bisognerebbe derivare una classe per
//poterli gestire a runtime.
// OnDragDrop -> FormDragDrop
// OnDragOver -> FormDragOver
L'unica cosa che mi manca, ma credo che quello sia impossibile, è il fatto di mettere la mia barra sopra a quella di stato di Windows11, e fare in modo che TUTTE le altre finestre abbiano il "top della mia" come limite invalicabile, ma questo credo proprio che sia impossibile da ottenere.
Ciao, Mario
Sono riuscito ad aggirare il problema, grazie al codice qui sotto:
function IsMyFormCovered(const MyForm: TForm): Boolean;
var
MyRect: TRect;
MyRgn, TempRgn: HRGN;
RType: Integer;
hw: HWND;
begin
MyRect := MyForm.BoundsRect; // screen coordinates
MyRgn := CreateRectRgnIndirect(MyRect); // MyForm not overlapped region
hw := GetTopWindow(0); // currently examined topwindow
RType := SIMPLEREGION; // MyRgn type
// From topmost window downto MyForm, build the not overlapped portion of MyForm
while (hw<>0) and (hw <> MyForm.handle) and (RType <> NULLREGION) do
begin
// nothing to do if hidden window
if IsWindowVisible(hw) then
begin
GetWindowRect(hw, MyRect);
TempRgn := CreateRectRgnIndirect(MyRect);// currently examined window region
RType := CombineRgn(MyRgn, MyRgn, TempRgn, RGN_DIFF); // diff intersect
DeleteObject( TempRgn );
end; {if}
if RType <> NULLREGION then // there's a remaining portion
hw := GetNextWindow(hw, GW_HWNDNEXT);
end; {while}
DeleteObject(MyRgn);
Result := RType = NULLREGION;
end;
function IsMyFormVisible(const MyForm : TForm): Boolean;
begin
Result:= MyForm.visible and
isWindowVisible(MyForm.Handle) and
not IsMyFormCovered(MyForm);
end;
In pratica, ho messo un timer che ogni secondo controlla se il miò form è "not IsMyFormVisible", se è così, lo riporto in primo piano !!!
Ora il mio programma dovrebbe essere completo !!!
Ciao, Mario