Newsgroup: it.comp.lang.visual-basic
Titolo: [?]Ridimensionare il Desktop

Ciao a tutti
vorrei tanto fare una form in grado di comportarsi come la barra di
Avvio di Window o la barra delle applicazioni di Office (di cui sto
cercando di fare un clone). Non so neanche come incominciare ad
affrontare il problema: cosa succede quando sposto la barra di avvio ad
es di lato? si spostano tutte le icone del desktop e, massimizzata
qualsiasi applicazione, essa non invade la barra nella nuova posizione.
Scusate ma non so definire meglio il problema.
Grazie in anticipo
Jean

cercando di fare un clone). 
Quello che vuoi realizzare si chiama esattamente "Application Desktop
Toolbar" o, piu' brevemente, Appbar. Se guardi sul CD di MSDN ci trovi
per filo e per segno come si fa una Appbar in Visual C++ e non e'
neppure tanto complicato: si tratta di subclassare una comune finestra
in modo da fargli intercettare alcuni messaggi particolari e poi di
registrla con l'API SHAppBarMessage. Il problema e' fare la stessa
cosa in VB. Io dovrei avere qualcosa ma non perfettamente funzionante.
Se pazienti qualche giorno te lo cerco.
Ciao
shrike

Ok, trovato. Mi scuso per la lunghezza del post ma credo che un
attachment fosse peggio e una mail privata avrebbe escluso un sacco di
gente a cui la cosa potrebbe interessare (almeno spero). Comunque per
ricreare il progetto fai cosi': apri un nuovo progetto con un form
(Form1) ed un modulo (Module1). Imposta nel form le seguenti
proptietà: BorderStyle = "3 - Fixed Dialog", Caption = "", ControlBox
= "False", Icon = "(none)". Nel form inserisci il pulsante Command1
(servira' per fermare l'esecuzione) con proprietà Left e Top entrambe
uguali a 0. Esegui il copia e incolla delle due sezioni allegate qui
sotto ed infine imposta la proprietà Startup Object del progetto su
"Sub Main". Esegui il progetto e avrai la tua bella appbar (che pero'
ha ancora diversi problemi) appollaiata sul lato superiore dello
schermo. Io ho cercato di tradurre il piu' fedelmente possibile il
codice di esempio da C++ a VB ma c'e' qualcosa che non mi torna. Il
problema principale del progetto e' che la nuova WindowProc non riceve
mai il messaggio APPBAR_CALLBACK. Se qualcuno riesce e svelare
l'arcano mi fa un grosso favore. Occhio a non fermare il progetto
senza scaricare il form: l'area occupata dalla appbar non viene
liberata e si ottiene un bel GPF.
Ciao
shrike
|----------------------------------------|
|  INIZIO CODICE DA INSERIRE NEL MODULO  |
|----------------------------------------|
Option Explicit
Public Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type
Public Type AppbarData
    cbSize As Long
    hWnd As Long
    uCallbackMessage As Long
    uEdge As Long
    rc As RECT
    lParam As Long
End Type
Public Const ABE_TOP = 1
Public Const ABE_LEFT = 0
Public Const ABE_RIGHT = 2
Public Const ABE_BOTTOM = 3
Public Const ABM_NEW = &H0
Public Const ABM_REMOVE = &H1
Public Const ABM_QUERYPOS = &H2
Public Const ABM_SETPOS = &H3
Public Const ABM_GETSTATE = &H4
Public Const ABM_ACTIVATE = &H6
Public Const ABM_WINDOWPOSCHANGED = &H9
Public Const ABN_STATECHANGE = &H0
Public Const ABN_POSCHANGED = &H1
Public Const ABN_FULLSCREENAPP = &H2
Public Const ABS_ALWAYSONTOP = &H2
Public Const WM_ACTIVATE = &H6
Public Const WM_WINDOWPOSCHANGED = &H47
Public Const WM_APP = &H8000
Public Const WM_USER = &H400
Public Const SM_CXSCREEN = 0
Public Const SM_CYSCREEN = 1
Public Const HWND_TOPMOST = -1
Public Const HWND_BOTTOM = 1
Public Const SWP_NOSIZE = &H1
Public Const SWP_NOMOVE = &H2
Public Const SWP_NOACTIVATE = &H10
Public Const GWL_WNDPROC = (-4)
Public Declare Function SHAppBarMessage Lib "shell32.dll" (ByVal
dwMessage As Long, ByRef pData As AppbarData) As Long
Public Declare Function GetWindowLong Lib "user32" Alias
"GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Public Declare Function SetWindowLong Lib "user32" Alias
"SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal
dwNewLong As Long) As Long
Public Declare Function CallWindowProc Lib "user32" Alias
"CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long,
ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Function MoveWindow Lib "user32" (ByVal hWnd As Long,
ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight
As Long, ByVal bRepaint As Long) As Long
Public Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As
Long) As Long
Public Declare Function SetWindowPos Lib "user32" (ByVal hWnd As Long,
ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal
cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Public Declare Function GetWindowRect Lib "user32" (ByVal hWnd As
Long, lpRect As RECT) As Long
'questo e' il mio "application defined message"
Public Const APPBAR_CALLBACK = WM_APP + &H1
'Variabili globali
Public glngOriginalWindowProc As Long
Public glngSide As Long
Public gfAppRegistered As Boolean
'Entry point
Public Sub Main()
    Dim pData As AppbarData
 
    'inizializza le variabili globali
    glngOriginalWindowProc = 0
    glngSide = ABE_TOP
    gfAppRegistered = False
 
    'carica il form per la appbar
    Load Form1
 
    'subclassing
    glngOriginalWindowProc = SetWindowLong(Form1.hWnd, _
                                           GWL_WNDPROC, _
                                           AddressOf WindowProc)
    'registra la appbar
    Call RegisterAccessBar(Form1.hWnd, True)
 
    'posiziona correttamente la appbar
    pData.hWnd = Form1.hWnd
    pData.cbSize = Len(pData)
    Call AppBarPosChanged(pData)
 
    'mostra il form
    Form1.Show
End Sub
Public Function WindowProc(ByVal lngHWnd As Long, _
                           ByVal lngMsg As Long, _
                           ByVal lngWParm As Long, _
                           ByVal lngLParm As Long) As Long
 
    Dim pData As AppbarData
    Select Case lngMsg
    Case WM_ACTIVATE
 
        'inizializza la struttura per i dati
        pData.hWnd = lngHWnd
        pData.cbSize = Len(pData)
 
        'rimanda il messagio ABM_ACTIVATE
        Call SHAppBarMessage(ABM_ACTIVATE, pData)
        'WindowProc = CallWindowProc(glngOriginalWindowProc, lngHWnd,
lngMsg, _
        '    lngWParm, lngLParm)
 
    Case WM_WINDOWPOSCHANGED
 
        'inizializza la struttura per i dati
        pData.hWnd = lngHWnd
        pData.cbSize = Len(pData)
 
        'rimanda il messagio ABM_ACTIVATE
        Call SHAppBarMessage(ABM_WINDOWPOSCHANGED, pData)
        'WindowProc = CallWindowProc(glngOriginalWindowProc, lngHWnd,
lngMsg, _
        '    lngWParm, lngLParm)
 
    Case APPBAR_CALLBACK
        Call AppBarCallback(lngHWnd, lngWParm, lngLParm)
 
    'per gli altri messaggi richiama la WindowProc originale
    Case Else
        WindowProc = CallWindowProc(glngOriginalWindowProc, lngHWnd,
lngMsg, _
            lngWParm, lngLParm)
    End Select
End Function
'RegisterAccessBar  - Registra o deregistra una appbar
'
'Parametri:
'   lngAppbarHWnd   - handle della finestra appbar
'   fRegister       - flag di registrazione/deregistrazione
'
'Variabili globali:
'   glngSide        - lato dello schermo (per default ABE_TOP)
'   gfAppRegistered - flag che indica se l'appbar è registrata o meno
'
'Ritorno:
'   TRUE se ha succcesso, altrimenti FALSE
'
Public Function RegisterAccessBar(ByVal lngAppbarHWnd As Long, _
                                  ByVal fRegister As Boolean) As
Boolean
    Dim pData As AppbarData
 
    'inizializzazione del valore ritornato
    RegisterAccessBar = False
 
    'discrimina in base al fleg di registrazione
    If fRegister Then
 
        'prepara la struttura da passare alla SHAppBarMessage con
ABM_NEW
        pData.hWnd = lngAppbarHWnd
        pData.uCallbackMessage = APPBAR_CALLBACK
        pData.cbSize = Len(pData)
 
        'esegue la SHAppBarMessage e, se questa ha successo, imposta
di conseguenza
        'le variabili globali ed il valore ritornato dalla funzione
        If SHAppBarMessage(ABM_NEW, pData) <> 0 Then
            glngSide = ABE_TOP
            gfAppRegistered = True
            RegisterAccessBar = True
        End If
 
    Else
 
        'prepara la struttura da passare alla SHAppBarMessage con
ABM_REMOVE
        pData.hWnd = lngAppbarHWnd
        pData.cbSize = Len(pData)
 
        'esegue la SHAppBarMessage, resetta il flag globale di
registrazione e
        'ritorna in ogni caso true
        Call SHAppBarMessage(ABM_REMOVE, pData)
        gfAppRegistered = False
        RegisterAccessBar = True
    End If
End Function
'AppBarQuerySetPos  - imposta dimensioni e posizione di una appbar
'
'lngEdge    - lato dello schermo al quale la appbar è ancorata
'pRect      - rettangolo in cui la appbar è attualmente confinata
'pData      - struttura APPBARDATA con i menbri hWnd e cbSize
valorizzati
'
Public Sub AppBarQuerySetPos(ByVal lngEdge As Long, _
                             ByRef pRect As RECT, _
                             ByRef pData As AppbarData)
    Dim lngHeight As Long
    Dim lngWidth As Long
 
    'copia la struttura pRect in pData.rc e valorizza pData.uEdge
    pData.rc.Left = pRect.Left
    pData.rc.Right = pRect.Right
    pData.rc.Top = pRect.Top
    pData.rc.Bottom = pRect.Bottom
    pData.uEdge = lngEdge
 
    'Copy the screen coordinates of the appbar's bounding
    'rectangle into the APPBARDATA structure.
    If lngEdge = ABE_LEFT Or lngEdge = ABE_RIGHT Then
        lngWidth = pData.rc.Right - pData.rc.Left
        pData.rc.Top = 0
        pData.rc.Bottom = GetSystemMetrics(SM_CYSCREEN)
    Else
        lngHeight = pData.rc.Bottom - pData.rc.Top
        pData.rc.Left = 0
        pData.rc.Right = GetSystemMetrics(SM_CXSCREEN)
    End If
 
    'Richiede al sistema delle dimensioni ed una posizione approvate
    pData.cbSize = Len(pData)
    Call SHAppBarMessage(ABM_QUERYPOS, pData)
 
    'Corregge il rettangolo in base al lato cui la appbar è ancorata
    Select Case lngEdge
    Case ABE_LEFT:
        pData.rc.Right = pData.rc.Left + lngWidth
    Case ABE_RIGHT:
        pData.rc.Left = pData.rc.Right - lngWidth
    Case ABE_TOP:
        pData.rc.Bottom = pData.rc.Top + lngHeight
    Case ABE_BOTTOM:
        pData.rc.Top = pData.rc.Bottom - lngHeight
    End Select
 
    'Passa il rettangolo finale al sistema
    pData.cbSize = Len(pData)
    Call SHAppBarMessage(ABM_SETPOS, pData)
 
    'Sposta e ridimensiona la appbar conformemente al rettangolo
    'indicato dal sistema
    Call MoveWindow(pData.hWnd, pData.rc.Left, pData.rc.Top, _
        pData.rc.Right - pData.rc.Left, pData.rc.Bottom -
pData.rc.Top, _
        True)
End Sub
'AppBarCallback     - processa i messaggi di notifica inviati dal
sistema
'
'lngHWndAccessBar   - handle della appbar
'lngNotifyMsg       - identificatore del messaggio
'lngLParam          - parametro del messaggio
'
Public Sub AppBarCallback(ByVal lngHWndAccessBar As Long, _
                          ByVal lngNotifyMsg As Long, _
                          ByVal lngLParam As Long)
    Dim pData As AppbarData
    Dim lngState As Long
 
    Beep
 
    Select Case lngNotifyMsg
 
    Case ABN_STATECHANGE
 
        'Controlla se lo stato "always-on-top" della taskbar è
cambiato
        pData.hWnd = lngHWndAccessBar
        pData.cbSize = Len(pData)
        lngState = SHAppBarMessage(ABM_GETSTATE, pData)
 
        'cambia di conseguenza anche lo stato di questa appbar
        Call SetWindowPos(lngHWndAccessBar, IIf((ABS_ALWAYSONTOP And
lngState) <> 0, _
            HWND_TOPMOST, HWND_BOTTOM), 0, 0, 0, 0, SWP_NOMOVE Or
SWP_NOSIZE Or _
            SWP_NOACTIVATE)
 
 
    Case ABN_FULLSCREENAPP
 
        'Una applicazione a schermo intero è stata avviato o l'ultima
applicazione a
        'scermo intero e stata chiusa. Corregge di conseguenza
l'ordine Z della appbar
        If lngLParam <> 0 Then
            Call SetWindowPos(lngHWndAccessBar, IIf((ABS_ALWAYSONTOP
And lngState) <> 0, _
                HWND_TOPMOST, HWND_BOTTOM), 0, 0, 0, 0, SWP_NOMOVE Or
SWP_NOSIZE Or _
                SWP_NOACTIVATE)
        Else
            pData.hWnd = lngHWndAccessBar
            pData.cbSize = Len(pData)
            lngState = SHAppBarMessage(ABM_GETSTATE, pData)
            If (lngState And ABS_ALWAYSONTOP) <> 0 Then
                Call SetWindowPos(lngHWndAccessBar, HWND_TOPMOST, 0,
0, 0, 0, _
                    SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOACTIVATE)
            End If
        End If
 
    Case ABN_POSCHANGED
 
        'La taskbar o un'altra appbar ha modificato le sue dimensioni
o la sua posizione
        pData.hWnd = lngHWndAccessBar
        pData.cbSize = Len(pData)
        Call AppBarPosChanged(pData)
 
    End Select
End Sub
'AppBarPosChanged   - corregge le dimensioni e la posizione della
appbar
'
'pData              - struttura APPBARDATA che contiene le
informazioni usate
'                     per correggere dimensioni e posizione
'
Public Sub AppBarPosChanged(ByRef pData As AppbarData)
    Dim pRect As RECT
    Dim pRectWindow As RECT
    Dim lngHeight As Long
    Dim lngWidth As Long
 
    'inizializza la struttura pRect con i dati dello schermo
    pRect.Top = 0
    pRect.Left = 0
    pRect.Right = GetSystemMetrics(SM_CXSCREEN)
    pRect.Bottom = GetSystemMetrics(SM_CYSCREEN)
 
    'inizializza la struttura pRectWindow con i dati della finestra
Appbar e
    'ne calcola altezza e larghezza
    Call GetWindowRect(pData.hWnd, pRectWindow)
    lngHeight = pRectWindow.Bottom - pRectWindow.Top
    lngWidth = pRectWindow.Right - pRectWindow.Left
 
    'A seconda del lato di appoggio ricalcola le dimensioni
    'della Appbar
    Select Case glngSide
    Case ABE_TOP:
        pRect.Bottom = pRect.Top + lngHeight
    Case ABE_BOTTOM:
        pRect.Top = pRect.Bottom - lngHeight
    Case ABE_LEFT:
        pRect.Right = pRect.Left + lngWidth
    Case ABE_RIGHT:
        pRect.Left = pRect.Right - lngWidth
 
    End Select
 
    'richiede la verifica della nuova posizione
    Call AppBarQuerySetPos(glngSide, pRect, pData)
End Sub
|----------------------------------------|
|   FINE CODICE DA INSERIRE NEL MODULO   |
|----------------------------------------|
|----------------------------------------|
|   INIZIO CODICE DA INSERIRE NEL FORM   |
|----------------------------------------|
Option Explicit
Private Sub Command1_Click()
    Unload Me
End Sub
Private Sub Form_Unload(Cancel As Integer)
    Call RegisterAccessBar(Me.hWnd, False)
End Sub
|----------------------------------------|
|    FINE CODICE DA INSERIRE NEL FROM    |
|----------------------------------------|

Grazie per il codice della appbar. Non sarei mai riuscito a risolvere il
problema da solo.
Penso mi ci vorra' un bel po' a interpretare ogni singola parte del codice
per poterlo correggere dalle varie anomalie.
Comunque grazie-grazie-grazie
Jean

Se hai qualche problema o qualcosa che non ti è chiaro chiedi pure.
Dopo tutto interessa anche a me.
Ciao 
shrike

shrike
CIAO Kickaha

Fammi sapere se ti risulta
Yesss. 
La cosa l'avevo notata anche io ma ho notato che funziona anche se
metti Form1.BorderStyle = 3 - Fixed Dialog a patto di togliere la
caption ed il contol box (in prtatica il form non deve avere la
classica barra di trascinamento e non deve apparire nella taskbar). La
ragione esatta di questo comportamento non la conosco ma ipotizzo una
diversa gestione della non client area a livello di window procedure
originale (purtroppo il VB non ce la fa vedere). 
Comunque di questo non mi preoccuperei piu' di tanto. La cosa che
invece mi preoccupa e' che il subclassing funziona ma non rileva mai
il messaggio di callback. All'avvio la appbar sembra funzionare
correttamente ma se sposti la taskbar ti accorgi che la finestra viene
trattata come una comune window e non come una appbar. Questo
compostamento e' dovuto al fatto che alla WindowProc arrivano i
normali messaggi WM_ ma non APPBAR_CALLBACK. Se si riuscisse ad
intercettare anche questo messaggio credo proprio che avremmo risolto
tutti i problemi.
Ciao
shrike

tutti i problemi.
Detto...fatto. ;-)
Riguardandomi il codice da te scritto ho notato che il tuo APPBAR_CALLBACK
viene costruito a partire da un WM_APP da te creato, il cui valore è &H8000.
Il problema è che Windows riserva i messaggi tra &H8000 ed &HBFFF per usi
futuri del sistema operativo e, anche se uno di questi valori dovrebbe
essere buono (perchè, diciamo così, "vuoto"), Windows (98 nel mio caso)
neanche se ne accorge dei messaggi suddetti.
D'altra parte non capisco perché tu non abbia usato semplicemente un valore
tra &H400 (WM_USER) ed &H7FFF ponendo, ad esempio:
APPBAR_CALLBACK = WM_USER + &H1
Mi è sfuggito qualcosa????
Ho anche provato ha generare sistematicamente messaggi tra &H8000 ed &HBFFF,
cercando di trapparli (collo SPY di M$) ma niente. Credo che il problema sia
proprio qua.
Comunque a me con APPBAR_CALLBACK messo come sopra (e cioé WM_USER + &H1) il
tutto mi funziona a meraviglia.
Fammi sapere.
CIAO
--
Kickaha, Master of MY Universe
E-MAIL: 
IO.Ki...@mclink.it
--------------------------
Moltissima gente confonde le opinioni con i pensieri
         "Herbert V. Prochnow"
Una conclusione è il punto dove sei stanco di pensare
         "Arthur Wright"

Ora che finalmente funziona tutto, ho avuto modo di trovare un altro bug
(piano piano li scopro e li posto; non è affatto facile come si pensa!!!!).
Se fai passare la appbar dal lato sinistro/destro a quello in alto/basso, la
appbar occupa tutto lo schermo disponibile (tranne, ovviamente la taskbar).
Questo avviene perché ad AppBarQuerySetPos viene passato come altezza per la
appbar l'altezza che la stessa ha quando è ancorata a destra/sinistra...a
tutto ciò ho ovviato con un sistema un po' rozzo, ma ingegnoso, che consiste
nel far "ruotare" di 90° la appbar stessa (ovvero di far sì che l'altezza
della appbar, passando da destra/sinistra in alto/basso, sia pari alla
larghezza che la appbar aveva quando era ancorata a destra/sinistra).
Ovviamente vale anche il viceversa (da alto/basso a destra/sinistra).
Fammi sapere se trovi un metodo più..ehm...diciamo..elegante


sbaglio????
Entrambe le cose sono fattibili. Per quanto riguarda la proprietà
"alwais on top" credo si possa procedere come per le normali finestra
con la API SetWindowPos. Invece per la proprieta' "auto hide" si deve
utilizzare ABM_SETAUTOHIDEBAR al posto di ABM_NEW al momento della
registrazione. Questo dovrebbe assicurare una corretta gestione del
desktop. Per il fatto di nascondere e far riapparire la finestra penso
che poi bisogna monitorare la posizione del puntatore perche mi sembra
che non lo faccia da solo. Occhio che se provi a registrare una appbar
autohide per un lato dove esiste gia' un'altra appbar anche essa
autohide, la SHAppBarMessage fallisce (te ne accorgi perche ritorna
0). 
Ti ringrazio nuovamente per aver corretto quel maledettisimo bug. La
collaborazione sui ng fa miracoli. 
Ciao
shrike