OVH Cloud OVH Cloud

desktop

13 réponses
Avatar
Bonjour

Avec la fonction EnumDesktops, j'obtiens facilement la liste des desktop
(bureau virtuel).
Je souhaiterai récupérer pour chacun d'eux le HDESK nombre fonctionnant
comme un
Handle vers ces bureaux. Comment puis je faire ?

J'ai essayé
EnumThreadWindows
EnumChildWindows
EnumWindows
sans succès.

Merci

10 réponses

1 2
Avatar
Christian ASTOR
@rennes wrote:

Avec la fonction EnumDesktops, j'obtiens facilement la liste des desktop
(bureau virtuel).
Je souhaiterai récupérer pour chacun d'eux le HDESK nombre fonctionnant
comme un Handle vers ces bureaux. Comment puis je faire ?



C'est juste OpenDesktop() (qd c'est possible...)
Avatar
Merci de ta réponse

Oui, j'ai vu que l'on pouvait faire ainsi, mais ça me semble étrange.
En effet, on peut créer un desktop, les énumérés, les ouvrir et les fermer.
Ces derniers points ne rendent rien de visuel.
Ca signifie que le fait d'ouvrir ou de fermer un desktop ne fait qu'allouer
ou désallouer un pointeur.
Le sens de cette fonction n'est pas tellement en rapport avec le nom dans ce
cas.

Question subsidiaire, comment fait on pour fermer un desktop, dans la mesure
ou le closedesktop ne
fait pas cela ? D'après ce que j'ai compris, on peut associé un desktop à un
thread et dans ce cas, le kill du thread tuerai ce desktop. Est ce bien cela
?


"Christian ASTOR" a écrit dans le message de
news: 41a478c9$0$10777$
@rennes wrote:

> Avec la fonction EnumDesktops, j'obtiens facilement la liste des desktop
> (bureau virtuel).
> Je souhaiterai récupérer pour chacun d'eux le HDESK nombre fonctionnant
> comme un Handle vers ces bureaux. Comment puis je faire ?

C'est juste OpenDesktop() (qd c'est possible...)


Avatar
Thierry
Bonjour,

@rennes a écrit :

Avec la fonction EnumDesktops, j'obtiens facilement la liste des desktop
(bureau virtuel).
Je souhaiterai récupérer pour chacun d'eux le HDESK nombre fonctionnant
comme un
Handle vers ces bureaux. Comment puis je faire ?

J'ai essayé
EnumThreadWindows
EnumChildWindows
EnumWindows
sans succès.



Y'a un sample sympa dans le MDSN permettant de creer et basculer entre
differente desktop. C'est pratique.
Y'aurait une appli pas mal a faire : pouvoir faire différents bureau
permettant de s'y loger sous different compte (utilisateur, admin, ...)

--
« Always look at the bright side of the life... »
Avatar
J'ai pas trouvé sur msdn, sinon j'aurai pas posé la question.
Tu peux donner une url ?

Sinon, l'appli que tu décris, y'en a des dizaines de ce type.
google : "virtual desktop"

J'en fait une de ce type justement, et sur certain truc je bloque.


"Thierry" a écrit dans le message de news:

Bonjour,

@rennes a écrit :

> Avec la fonction EnumDesktops, j'obtiens facilement la liste des desktop
> (bureau virtuel).
> Je souhaiterai récupérer pour chacun d'eux le HDESK nombre fonctionnant
> comme un
> Handle vers ces bureaux. Comment puis je faire ?
>
> J'ai essayé
> EnumThreadWindows
> EnumChildWindows
> EnumWindows
> sans succès.

Y'a un sample sympa dans le MDSN permettant de creer et basculer entre
differente desktop. C'est pratique.
Y'aurait une appli pas mal a faire : pouvoir faire différents bureau
permettant de s'y loger sous different compte (utilisateur, admin, ...)

--
« Always look at the bright side of the life... »


Avatar
Thierry
Bonjour,

@rennes a écrit :

J'ai pas trouvé sur msdn, sinon j'aurai pas posé la question.
Tu peux donner une url ?



Dans ta bal.

Sinon, l'appli que tu décris, y'en a des dizaines de ce type.
google : "virtual desktop"



T'en connais un bien et gratos ?

--
« Always look at the bright side of the life... »
Avatar
AMcD®
Thierry wrote:
Bonjour,

@rennes a écrit :

J'ai pas trouvé sur msdn, sinon j'aurai pas posé la question.
Tu peux donner une url ?



Dans ta bal.



Ça c'est naze. C'est un forum public, donne la réponse publiquement, qu'un
maximum de lecteurs en profite.

Moi, c'est simple, les gars qui disent conatctez-moi par mail uniquement,
répondez-moi à cette adresse, blabla, mon cerveau se met en mode GFY, non
mais ! On aide gratos et sur le temsp libre, rien n'est du. Donc, public !

Na.

--
AMcD®

http://arnold.mcdonald.free.fr/
Avatar
Arnaud Debaene
@rennes wrote:
Merci de ta réponse

Oui, j'ai vu que l'on pouvait faire ainsi, mais ça me semble étrange.
En effet, on peut créer un desktop, les énumérés, les ouvrir et les
fermer. Ces derniers points ne rendent rien de visuel.


Non, ca c'est SwitchDesktop.

Ca signifie que le fait d'ouvrir ou de fermer un desktop ne fait
qu'allouer ou désallouer un pointeur.
Le sens de cette fonction n'est pas tellement en rapport avec le nom
dans ce cas.


Ben oui, c'est cohérent avec le nommage de toute l'API Windows : OpenXXX
ouvre un handle sur un objet de type XXX existant : OpenFile, OpenMutex,
OpenSemaphore, OpenService, OpenWindowStation, etc...

Question subsidiaire, comment fait on pour fermer un desktop, dans la
mesure ou le closedesktop ne
fait pas cela ?


Comme tous les objets Windows, un desktop est détruit quand le dernier
handle qui le référence est fermé. Dans le cas des desktops, j'imagine qu'il
y a un cas particulier, qui est que le desktop n'est pas détruit s'il est
visible et actif, même si aucune application n'a de handle dessus (mais ca
semble assez évident...). Si tu veux "fermer un desktop" dans le sens le
rendre non visible à l'écran, il suffit de faire un SwitchDesktop vers un
autre desktop (il y a *toujours* un desktop actif par WindowStation).

D'après ce que j'ai compris, on peut associé un
desktop à un thread et dans ce cas, le kill du thread tuerai ce
desktop. Est ce bien cela ?


Pas du tout. Associer un thread avec un desktop défini simplement sur quel
desktop seront affichées les fenêtres créées par le thread.

D'après tes remarques, j'ai l'impression que tu n'as pas saisi un point
important : un desktop est non seulement une surface d'affichage de
fenêtres, c'est aussi une limite de sécurité importante dans le modèle de
sécurité de Windows : En effet, le desktop défini d'une part le
presse-papier, et d'autre part il permet à différentes applications
d'interagir entre elles et de se "piloter" les unes les autres à coup de
message de fenêtre et de hooks. C'est pour cette raison que, par exemple,
WinLogon tourne dans son propre desktop, afin qu'aucune application ne
puisse "jouer" avec. Il faut toujours garder cet aspect à l'esprit lorsque
l'on programme avec les desktops et les WindowStation.

Arnaud
MVP - VC
Avatar
Thierry
Bonjour,

AMcD® a écrit :

J'ai pas trouvé sur msdn, sinon j'aurai pas posé la question.
Tu peux donner une url ?



Dans ta bal.



Ça c'est naze. C'est un forum public, donne la réponse publiquement,
qu'un maximum de lecteurs en profite.

Moi, c'est simple, les gars qui disent conatctez-moi par mail
uniquement, répondez-moi à cette adresse, blabla, mon cerveau se met
en mode GFY non mais ! On aide gratos et sur le temsp libre, rien
n'est du. Donc, public !



Soit.

Alors on va commencer par desktop.c, puis je vais chercher une solution
pour poster le .ico.

/**************************************************************************
**
* This is a part of the Microsoft Source Code Samples.
* Copyright (C) 1995 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
*
***************************************************************************
**/

/**************************************************************************
**

PROGRAM: desktop.c

PURPOSE: Allow user to switch between active desktops on the User's
WindowStation

USAGE: desktop [-t #threads]
#threads is the number of desktops and corresponding threads to
create

APIS of Importance:
CreateDesktop()
SwitchDesktop()
GetUserObjectInformation()
GetThreadDesktop()
SetThreadDesktop()
CloseDesktop()
OpenDesktop()

FUNCTIONS:
WinMain
StartNewDesktop
CreateAllDesktops
LoadResources
InitApplication
ThreadInit
SaveScreen
PaintMainWnd
RunApp
GetFontHeight
TitleWindow
CreateControls
WndProc
PreviewWndProc
EditProc


COMMENTS: This application demonstrates the multiple desktop
capabilities of Windows NT 3.51, PPC release.



***************************************************************************
*/

#define _WIN32_WINNT 0x0500
#define WINVER 0x0500

#include <windows.h> // required for all Windows applications

#include "switcher.h" // specific to this program
#include "resource.h"

//
// Array of string resources
//
TCHAR SwitchStrings[LAST_STRING-FIRST_STRING + 1][MAXSTRLEN];
#define PSZ(x) SwitchStrings[x-FIRST_STRING]

//
// Structure used for thread-specific data
//
typedef struct _tdata
{
HDESK hDesk; // desktop assigned to new thread
int index; // index into deskArray
HWND hWndStatic; // "Run:" static control
HWND hWndEdit; // edit control for user input
HWND hWndBtn; // button for user input
HWND hWndNew; // button for new desktop
} ThreadData;

char g_szIniPath[MAX_PATH] = {0};
int gMaxIndex; // Highest index for array of desk
handles
HWND gBaseWindow; // Window handle of default desktop
window
HDESK gDeskArray[MAX_THREADS]; // Global array of desktop handles
HWND hWndArray[MAX_THREADS]; // Global array of window handles
HDC gHDCArray[MAX_THREADS]; // global array of memory device
contexts
// these DCs store snapshots of the
desktops
int gWidth, gHeight; // dimensions of desktop rectangles

//
// Keep track of how big the controls need to be to match the
// active system fixed font. These will help determine the
// minimum size of the switcher window.
//
int gStaticWidth; // Edit control label
int gEditWidth; // Edit control
int gBtnWidth; // Button to run app in edit control
int gNewWidth; // Button to create new desktop

HINSTANCE ghInst = NULL; // Global hInstance
#define DEFAULT_DESKTOP gDeskArray[0] // For easy reading
LONG APIENTRY EditProc (HWND, UINT, WPARAM, LPARAM);
TCHAR szAppName[] = TEXT("Switcher!");
TCHAR szClassName[] = TEXT("SwitcherWindow");
TCHAR szPreviewClass[]= TEXT("PreviewWindow");

DWORD AdjustPrivileges();

/**************************************************************************
**
FUNCTION: StartNewDesktop

PURPOSE: Create or open a handle to a desktop and put a switcher
thread
on it.

ARGUMENTS:
int nCount - Which desktop number this is

Assumes gDeskArray[nCount] == NULL

***************************************************************************
*/

void StartNewDesktop (int nCount)
{
ThreadData *ptd;
TCHAR szDesk[50];
char szTemp[20];
DWORD tID;
ptd = (ThreadData*)GlobalAlloc(GMEM_FIXED,sizeof(ThreadData));
if (ptd)
{
ptd->index = nCount;
//
// Give the desktop a name.
//
wsprintf(szTemp, "DeskName%d", nCount);

if (GetPrivateProfileString("DesktopName", szTemp, "", szDesk, 50,
g_szIniPath) == 0)
wsprintf (szDesk, PSZ(IDS_DESKTOPNAME), nCount+1);
//
// First, try to open an existing desktop
//
if ( !(ptd->hDesk = OpenDesktop (szDesk, 0, FALSE, GENERIC_ALL)))
{
//
// Failing an open, Create it
//
if (!(ptd->hDesk= CreateDesktop (szDesk, NULL,
NULL,0,MAXIMUM_ALLOWED,
NULL)))
{

MessageBox (NULL, PSZ(IDS_CREATEERROR),
PSZ(IDS_ERRCAPTION), MB_OK);
//
//Mark this array slot as invalid
//
gDeskArray[nCount] = NULL;

}

}
if (ptd->hDesk)
{
//
// Save the handle to the global array. Start the new thread
//
gDeskArray[ptd->index] = ptd->hDesk;
CloseHandle(CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)ThreadInit,
(LPVOID)ptd, 0, &tID));
}
}
else
{
//
// Out of memory
//
MessageBox (NULL, PSZ(IDS_CREATEERROR),
PSZ(IDS_MEMERRCAPTION), MB_OK);
//
//Mark this array slot as invalid
//
gDeskArray[nCount] = NULL;
}

}
/**************************************************************************
**

FUNCTION: CreateAllDesktops (cThreads)

PURPOSE: Creates desktops and assigns a switcher thread to each.
Updates the global desktop array.

ARGUMENTS:
int cThreads - Number of threads/desktops to open or create

***************************************************************************
*/

void CreateAllDesktops (int cThreads)
{
ThreadData *ptdDefault;


//
// Make sure we allocate for the default desktop first
//
ptdDefault = (ThreadData *)GlobalAlloc (GMEM_FIXED, sizeof(ThreadData));
if (!ptdDefault) {
return;
}
while (cThreads)
{
StartNewDesktop (cThreads);
cThreads--;
}
//
// Main thread is one of the running threads too
//
ptdDefault->index = 0;
ptdDefault->hDesk = DEFAULT_DESKTOP;
ThreadInit((LPVOID)ptdDefault);

}

/**************************************************************************
**

FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, int)

PURPOSE: Creates the threads and desktops
COMMENTS: Each thread has a separate desktop assigned to it.


***************************************************************************
*/
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HANDLE hiMutex = CreateMutex(NULL, TRUE, "DesktopSwitcherMutex");

// On verifie qu'il n'y ait qu'une seule instance du programme.
if (hiMutex!=NULL && GetLastError()!=ERROR_ALREADY_EXISTS)
{
int cThreads; // number of desktops

ghInst = hInstance;

if (hPrevInstance)
{ // Other instances of app running?
return (FALSE); // Exit
} else {
if (!InitApplication ()) {
return FALSE;
}
}

// Ini g_szIniPath
{
char *pciLastSlash = NULL;
GetModuleFileName(ghInst, g_szIniPath, MAX_PATH);
pciLastSlash = strrchr(g_szIniPath, '');
if (pciLastSlash)
strcpy(++pciLastSlash, "Switcher.ini");
else g_szIniPath[0] = '';
}

// parse command line to determine number of desktops
// Assume 9 threads

cThreads = NB_DESKTOPS;

// Create the threads - if zero was specified, default to 1.
// What does 0 threads mean?
if (cThreads == 0)
cThreads = 1;
else if (cThreads > MAX_THREADS)
cThreads = MAX_THREADS;

//
// Account for the main thread - only create extras
//
cThreads--;
gMaxIndex = cThreads; // Keep track of the highest
array index

AdjustPrivileges();

//
// Assign this here, since threads reference it
//
DEFAULT_DESKTOP = GetThreadDesktop(GetCurrentThreadId());
CreateAllDesktops (cThreads);
}
if (hiMutex)
{
ReleaseMutex(hiMutex);
CloseHandle(hiMutex);
}

return 0;
}

/*************************************************************
FUNCTION: LoadResources

PURPOSE: Load string table entries

ARGUMENTS: None

RETURNS: True if all strings are loaded, False otherwise

*************************************************************/

BOOL LoadResources (void)
{
int i;
for (i=0;i<(LAST_STRING-FIRST_STRING+1);i++)
{
if (!LoadString (ghInst, FIRST_STRING + i, SwitchStrings[i],
MAXSTRLEN))
return FALSE;
}
return TRUE;
}
/*************************************************************

FUNCTION: InitApplication

PURPOSE: Register the window class and init global variables

ARGUMENTS:

RETURNS:
TRUE if window class registration and other initialization succeeds

**************************************************************/

BOOL InitApplication (void) {

WNDCLASS wc;
HWND hTemp;
HWINSTA hWndSta;
RECT rect;

if (!LoadResources ())
return FALSE;
//
// Initialize the gHDCArray to all NULLS
//
ZeroMemory (gHDCArray, sizeof (gHDCArray));
hTemp = GetDesktopWindow(); // Call this so User will assign us a
WindowStation.
//
// Initialize gWidth and gHeight
// Get the size of the screen, make gWidth/gHeight == scrnW/scrnH
//
GetClientRect (hTemp, &rect);
gWidth = rect.right/DIVISOR;
gHeight = rect.bottom/DIVISOR;

//
// Make sure this app has a windowstation
//
hWndSta = GetProcessWindowStation();
if (!hWndSta)
{
MessageBox (NULL, PSZ(IDS_WNDSTAERROR), PSZ(IDS_ERRCAPTION), MB_OK);
return FALSE;
}
//
// Register the main window class
//
wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = ghInst;
wc.hIcon = LoadIcon(ghInst, MAKEINTRESOURCE(IDI_SWITCHER));
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = szClassName;

if (!RegisterClass (&wc))
{
return FALSE;
}

//
// Register the preview window class
//
wc.style = 0;
wc.lpfnWndProc = PreviewWndProc;
wc.lpszClassName = szPreviewClass;

if (!RegisterClass (&wc)) {
return FALSE;
}


return TRUE;
}

/*******************************************************************

FUNCTION: ThreadInit

PURPOSE: Given a desktop handle, create a window on it to allow
switching
among desktops.

ARGUMENTS:
LPVOID tData - Thread-specific data

RETURNS:
nothing
********************************************************************/

void ThreadInit(LPVOID tData)
{
MSG msg;
HWND hWnd;
HDC hTemp;
int width; // window width
USEROBJECTFLAGS uof; // To set Desktop attributes

uof.fInherit = FALSE; // If an app inherits multiple desktop
handles,
// it could run on any one of those
desktops
uof.fReserved = FALSE;
//
// Let other account processes hook this desktop
//
uof.dwFlags = DF_ALLOWOTHERACCOUNTHOOK;
SetUserObjectInformation (((ThreadData*)tData)->hDesk,
UOI_FLAGS,
(LPVOID)&uof,
sizeof(uof));
//
// Make sure the handle is valid
//
if (gDeskArray[((ThreadData*)tData)->index])
{
UINT uiStyle =
WS_MINIMIZEBOX|WS_OVERLAPPED|WS_VISIBLE/*|WS_BORDER|WS_CAPTION*/;

//
// Assign new desktop to this thread
//
SetThreadDesktop (((ThreadData*)tData)->hDesk);
// create the cool switcher window
if ((gMaxIndex+1) * gWidth > MINWINDOWWIDTH)
{
// Thierry
// width = (gMaxIndex+1) * gWidth;
width = gWidth * NB_DESKTOPS;
}
else
{
width = MINWINDOWWIDTH;
}
if (!((ThreadData*)tData)->index) // Menu systeme pour le desktop
principal
uiStyle |= WS_SYSMENU;
hWnd = CreateWindow (szClassName,
szAppName, uiStyle,
GetSystemMetrics(SM_CXSCREEN)-width,
GetSystemMetrics(SM_CYSCREEN)-(30+gHeight + CONTROLHEIGHT)-10,
width, 30+gHeight,// + CONTROLHEIGHT,
NULL, NULL, ghInst, tData);
if (!hWnd) // bag it
{
gDeskArray[((ThreadData*)tData)->index] = NULL;
GlobalFree (tData);
return;
}

//
//update the global window array
//
hWndArray[((ThreadData*)tData)->index] = hWnd;

if (hWnd && ((ThreadData*)tData)->index && !FindWindow(NULL,
"TrayNotifyWnd"))
{
PROCESS_INFORMATION pi; // info returned from
CreateProcess
TCHAR szDesk[100]; // data holder
BOOL bAsCurrentUser = TRUE;
DWORD dwLastError = ERROR_SUCCESS;

//
// Need the lpDesktop member so the new process runs on
this desktop
// The lpDesktop member was reserved in previous versions
of NT
//
GetUserObjectInformation (GetThreadDesktop
(GetCurrentThreadId()),
UOI_NAME,
szDesk,
100*sizeof(TCHAR),
NULL);

if (((ThreadData*)tData)->index == 2)
{
/* HANDLE hToken = NULL;

if (LogonUser("Administrateur", "", "",
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &hToken))
{
if (CreateProcessAsUser(hToken,
NULL,
"explorer.exe", // command line
NULL, // process security attributes
NULL, // thread security attributes
TRUE, // inherit handles

CREATE_DEFAULT_ERROR_MODE|CREATE_SEPARATE_WOW_VDM,
NULL, // environment block
NULL, // current directory
&sui, // STARTUPINFO
&pi) == TRUE)
bAsCurrentUser = FALSE;
else dwLastError = GetLastError();
}
else dwLastError = GetLastError();
*/
STARTUPINFOW suiw;

ZeroMemory ((PVOID)&suiw, sizeof(suiw));
suiw.cb = sizeof(suiw);

WCHAR wszDesktop[256];
MultiByteToWideChar(CP_ACP, 0, szDesk, -1, wszDesktop,
sizeof(wszDesktop)/sizeof(wszDesktop[0]) );

if (CreateProcessWithLogonW(L"Administrateur", L"",
L"slurp", 0,
NULL,
L"explorer.exe", // command line
CREATE_DEFAULT_ERROR_MODE|CREATE_SEPARATE_W
OW_VDM,
NULL, // environment block
NULL, // current directory
&suiw, // STARTUPINFO
&pi
) == FALSE)

dwLastError = GetLastError();
}

if (bAsCurrentUser)
{
STARTUPINFO sui; // Process startup info

ZeroMemory ((PVOID)&sui, sizeof(sui));
sui.cb = sizeof (sui);

sui.lpDesktop = szDesk;

CreateProcess(NULL, // image name
"explorer.exe", // command line
NULL, // process security attributes
NULL, // thread security attributes
TRUE, // inherit handles

CREATE_DEFAULT_ERROR_MODE|CREATE_SEPARATE_WOW_VDM,
NULL, // environment block
NULL, // current directory
&sui, // STARTUPINFO
&pi); // PROCESS_INFORMATION
}
}
}
else
{

GlobalFree (tData);
return;
}

//
// Acquire and dispatch messages until a WM_QUIT message is received.
//
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);// Translates virtual key codes
DispatchMessage(&msg); // Dispatches message to window
}
//
// Switch back to the default desktop and close the user-created one
//
SetThreadDesktop (DEFAULT_DESKTOP);
SwitchDesktop (DEFAULT_DESKTOP);
CloseDesktop (((ThreadData*)tData)->hDesk);
//
// NULL out the global array entry so other threads won't try to switch
to
// this desktop
//
gDeskArray[((ThreadData*)tData)->index] = NULL;
//
// cleanup
//
hTemp = gHDCArray[((ThreadData*)tData)->index];
gHDCArray[((ThreadData*)tData)->index] = NULL;
DeleteObject (hTemp);
GlobalFree (tData);

}


/***********************************************************************

FUNCTION: SaveScreen

PURPOSE: Save a snapshot of the desktop to its corresponding
memory DC. StretchBlt!

ARGUMENTS:
int index - Index of memory DC to save bits to

RETURNS: nothing
************************************************************************/

void SaveScreen (int index) {
HDC hdc;
int xSize, ySize;

xSize = GetSystemMetrics (SM_CXSCREEN);
ySize = GetSystemMetrics (SM_CYSCREEN);

if (hdc = CreateDC (TEXT("DISPLAY"), NULL, NULL, NULL))
{
//
// Copy the desktop to a memory DC
//
StretchBlt (gHDCArray[index], 0, 0, gWidth*2, gHeight*2,
hdc, 0, 0, xSize, ySize, SRCCOPY);
DeleteDC (hdc);
}
}

/**************************************************************************
*****

FUNCTION: PaintMainWnd

PURPOSE: Draw the main window. This window has rectangles with
miniature snapshots
of each desktop. The snapshots are retrieved from the gHDCArray and
StretchBlt'd to
the right size.

ARGUMENTS:
HWND hWnd - Window to draw

RETURNS: nothing

***************************************************************************
****/

void PaintMainWnd (HWND hWnd)
{
PAINTSTRUCT ps;
RECT rect;
HPEN hPen, hOld;
ThreadData *ptd;
TCHAR szName[4]; // short name!
int myThread;
HDC hDC; // always need a dc for drawing
int i; // my favorite loop counter

//
// get the array index of this window
//
ptd = (ThreadData *)GetWindowLong (hWnd, GWL_USERDATA);
myThread = ptd->index;

//
//Draw a rectangle for each desktop
//
hDC = BeginPaint (hWnd, &ps);
if (GetClientRect (hWnd, &rect))
{
int right, left;
//
// leave space for edit control and button
//
rect.bottom -= CONTROLHEIGHT;
hPen = CreatePen (PS_SOLID | PS_INSIDEFRAME, 2, RGB(255,16,16));
hOld = (HPEN) SelectObject (hDC, hPen);

//
// draw each desktop rectangle
//
for (i=0;i<=gMaxIndex;i++)
{
right = gWidth * i + gWidth;
left = right - gWidth;
//
// If no snapshot is available, be boring
//
if (!gHDCArray[i])
{
Rectangle (hDC, left, rect.top, right, gHeight);
wsprintf (szName, TEXT("%d"), i+1);
TextOut (hDC, left+(gWidth/2),
gHeight/2, szName,
lstrlen (szName));
}
else
{
//
// BitBlt the snapshot into the rectangle
//
StretchBlt (hDC, left, rect.top, gWidth, gHeight,
gHDCArray[i], 0, 0, gWidth*2, gHeight*2, SRCCOPY);
//
// draw lines around the rectangle
//
MoveToEx (hDC, left, rect.top, NULL);
LineTo (hDC, left, gHeight);
MoveToEx (hDC, right, rect.top, NULL);
LineTo (hDC, right, gHeight);
//
// underline the active one
//
if (myThread == i)
{
MoveToEx (hDC, left, gHeight, NULL);
LineTo (hDC, right, gHeight);
}
}
}
//
// cleanup
//
SelectObject (hDC, hOld);
DeleteObject (hPen);
}

EndPaint (hWnd, &ps);

}

/**************************************************************************
**

FUNCTION: RunApp (HWND)

PURPOSE: Create a process, using contents of HWND's edit control
as the command line.

ARGUMENTS:
HWND hWnd - Handle of active switcher window

RETURNS: nothing

COMMENTS: Make sure proper desktop is passed in STARTUPINFO

**************************************************************************
**/

void RunApp (HWND hWnd)
{
TCHAR szDesk[100]; // data holder
TCHAR szExec[100]; // Command line
STARTUPINFO sui; // Process startup info
PROCESS_INFORMATION pi; // info returned from CreateProcess
ThreadData *ptd;

ptd = (ThreadData*)GetWindowLong (hWnd, GWL_USERDATA);
//
// Most sui members will be 0
//
ZeroMemory ((PVOID)&sui, sizeof(sui));
//
//Get the command line to execute
//
GetDlgItemText (hWnd, IDC_RUNME, szExec, 100*sizeof(TCHAR));
//
//Get the current desktop name
//
GetUserObjectInformation (GetThreadDesktop (GetCurrentThreadId()),
UOI_NAME,
szDesk,
100*sizeof(TCHAR),
NULL);
sui.cb = sizeof (sui);
//
// Need the lpDesktop member so the new process runs on this desktop
// The lpDesktop member was reserved in previous versions of NT
//
sui.lpDesktop = szDesk;
CreateProcess (NULL, // image name
szExec, // command line
NULL, // process security attributes
NULL, // thread security attributes
TRUE, // inherit handles
CREATE_DEFAULT_ERROR_MODE|CREATE_SEPARATE_WOW_VDM,
NULL, // environment block
NULL, // current directory
&sui, // STARTUPINFO
&pi); // PROCESS_INFORMATION

}
/**************************************************************************
**
FUNCTION: GetFontHeight

PURPOSE: Set up widths for controls based on size of the system
font.

ARGUMENTS:
HWND hWnd - Window whose DC to use.

RETURNS:
Return the height of the system fixed font to use for
the controls.

***************************************************************************
*/

LONG GetFontHeight (HWND hWnd)
{
HDC hdc;
TEXTMETRIC tm;
SIZE size;
#define MARGIN 7 // extra space on the button around the text

hdc = GetDC (hWnd);
if (!GetTextMetrics (hdc, &tm))
{
//
// Use defaults
//
gStaticWidth = STATICWIDTH;
gBtnWidth = BTNWIDTH;
gEditWidth = EDITWIDTH;
gNewWidth = BTNWIDTH + 25;
return CONTROLHEIGHT;
}

//
// GetTextExtentPoint32 fills in size with the width and height of
// a string.
//
GetTextExtentPoint32 (hdc, PSZ(IDS_RUNLABEL),
lstrlen(PSZ(IDS_RUNLABEL)), &size);
gStaticWidth = size.cx + MARGIN;
gEditWidth = EDITWIDTH;
GetTextExtentPoint32 (hdc, PSZ(IDS_BTNLABEL),
lstrlen(PSZ(IDS_BTNLABEL)), &size);
gBtnWidth = size.cx + MARGIN;
GetTextExtentPoint32 (hdc, PSZ(IDS_NEWLABEL),
lstrlen(PSZ(IDS_NEWLABEL)), &size);
gNewWidth = size.cx + MARGIN;
ReleaseDC (hWnd, hdc);
return tm.tmHeight + 2;

}

/**************************************************************************
**
FUNCTION: TitleWindow

PURPOSE: Give a switcher window an appropriate title, using its
desktop name.

ARGUMENTS:
HWND hWnd - Window to title

RETURNS: nothing

***************************************************************************
*/

void TitleWindow (HWND hWnd)
{
TCHAR *szTitle, *szName;
ULONG nBytes = 0;

//
// How long is the desktop name?
//
GetUserObjectInformation (GetThreadDesktop(GetCurrentThreadId()),
UOI_NAME,
(LPVOID)&nBytes, // not used since cbInfo is 0
0,
&nBytes);
szName = (LPTSTR)GlobalAlloc (GPTR, nBytes);
if (!szName)
{
return;
}
//
// Now get the desktop name
//
GetUserObjectInformation (GetThreadDesktop(GetCurrentThreadId()),
UOI_NAME,
(LPVOID)szName,
nBytes,
&nBytes);
//
// Now make the window title
//
szTitle = (LPTSTR)GlobalAlloc (
GPTR,
(lstrlen(szAppName)+lstrlen(TEXT(" - "))) * sizeof(TCHAR) +
nBytes);

if (!szTitle)
{
GlobalFree (szName);
return;
}
wsprintf (szTitle, TEXT("%s - %s"), szAppName, szName);
SetWindowText (hWnd, szTitle);
//
// Cleanup
//
GlobalFree (szName);
GlobalFree (szTitle);
}

/**************************************************************************
**

FUNCTION: CreateControls (ThreadData *, HWND)

PURPOSE: Creates UI controls on a switcher window

ARGUMENTS:
ThreadData *ptd - Thread specific data to use/init
HWND hWnd - Parent window

RETURNS:
nothing


***************************************************************************
*/

void CreateControls (ThreadData *ptd, HWND hWnd)
{
// LONG oldproc;

ptd->hWndStatic = NULL;
ptd->hWndEdit = NULL;
ptd->hWndBtn = NULL;
ptd->hWndNew = NULL;
/*
//
// Create the edit control label
//
ptd->hWndStatic = CreateWindow (TEXT("static"), PSZ(IDS_RUNLABELHOT),
WS_CHILD | WS_VISIBLE,
0,0,0,0, hWnd,
(HMENU)IDC_STATIC,
ghInst, NULL);
//
// Create the edit control
//
ptd->hWndEdit = CreateWindow (TEXT("Edit"), TEXT(""),
WS_BORDER | WS_CHILD | WS_VISIBLE |
ES_AUTOHSCROLL,
0,0,0,0, hWnd, (HMENU)IDC_RUNME,
ghInst, NULL);

//
// set the edit control proc and save the default one
//
oldproc = GetWindowLong (ptd->hWndEdit, GWL_WNDPROC);
SetWindowLong (ptd->hWndEdit, GWL_WNDPROC, (LONG)EditProc);
SetWindowLong (ptd->hWndEdit, GWL_USERDATA, oldproc);

//
// Create the execution button
//
ptd->hWndBtn = CreateWindow (TEXT("Button"), PSZ(IDS_BTNLABEL),
WS_CHILD | WS_VISIBLE |
BS_DEFPUSHBUTTON,
0,0,0,0, hWnd,
(HMENU)IDC_RUNMEBTN,
ghInst, NULL);
//
// Create a button for creating new desktops
//
ptd->hWndNew = CreateWindow (TEXT("button"), PSZ(IDS_NEWLABELHOT),
WS_CHILD | WS_VISIBLE,
0,0,0,0, hWnd,
(HMENU)IDC_NEWDSKBTN,
ghInst, NULL);
*/
}
/**************************************************************************
**

FUNCTION: WndProc(UINT, WPARAM, LPARAM)

PURPOSE: Processes messages to the Switcher window

MESSAGES:
WM_RBUTTONDOWN - Switch to desktop whose rectangle is under the
mouse
WM_CLOSE - Send WM_CLOSE to all windows in hWndArray
WM_LBUTTONDOWN - Create a preview window to display a larger view
of
a desktop until WM_LBUTTONUP
WM_COMMAND - Respond to button pushes or edit control entry
WM_SYSCHAR - ALT+R sets focus to the edit control
ALT+N creates a new desktop
WM_CHAR - Carriage return executes command line from
the edit control
WM_HOTKEY
WM_KEYDOWN - Respond to function keys by switching to
the appropriate desktop. Example, F2 means
switch
to Desktop2. Ctrl-F# allows switching without
giving focus to the switcher window
WM_LBUTTONUP - Close the active preview window
WM_CREATE - Initialize controls and global array entries
WM_SIZE - Size the child controls correctly


COMMENTS:

***************************************************************************
*/

LONG APIENTRY WndProc(
HWND hWnd,
UINT message, // type of message
WPARAM wParam, // additional information
LPARAM lParam) // additional information
{

int newThread; // Thread index to switch to
int i;
ThreadData *ptd;
static HWND hShowing = NULL; // which preview window is being
shown
static LONG fntHeight = CONTROLHEIGHT; // height for the edit control
switch (message)
{
case WM_CREATE:
{
HDC hDC;
HBITMAP hBmp;

// Create edit control, button, and label at the bottom of the
window
// This will allow the user to input a program to run

SetWindowLong (hWnd, GWL_USERDATA,
(LONG)((CREATESTRUCT *)lParam)->lpCreateParams);
ptd = (ThreadData *)GetWindowLong (hWnd, GWL_USERDATA);
CreateControls (ptd, hWnd);
fntHeight = GetFontHeight (hWnd);
//
// initialize the DC array entry
//
hDC = CreateDC (TEXT("DISPLAY"), NULL, NULL, NULL);
gHDCArray[ptd->index] = CreateCompatibleDC (hDC);
//
// Halftone is the best stretching algorithm
//
SetStretchBltMode (gHDCArray[ptd->index], HALFTONE);
SetBrushOrgEx (gHDCArray[ptd->index], 0, 0, NULL);
//
// Use a bitmap the same size as the desktop preview rectangles
//
hBmp = CreateCompatibleBitmap (hDC, gWidth*2, gHeight*2);
SelectObject (gHDCArray[ptd->index], hBmp);
DeleteDC (hDC);
SaveScreen (ptd->index);
TitleWindow (hWnd);
//
// Register hot keys
//
for (i=0;i<10;i++)
RegisterHotKey (hWnd, VK_F1+i, MOD_CONTROL | MOD_ALT, VK_F1+i);

SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE |
SWP_NOSIZE);

return 0;
}

case WM_SIZE:
{
//
// Put the child controls at the right places
//
#define PADDING 5

RECT rect;
ThreadData *ptd;
if (GetClientRect (hWnd, &rect))
{
ptd = (ThreadData *)GetWindowLong (hWnd, GWL_USERDATA);
MoveWindow (ptd->hWndStatic, 0, rect.bottom - CONTROLHEIGHT,
gStaticWidth, fntHeight + PADDING, TRUE);

MoveWindow (ptd->hWndEdit, gStaticWidth + 5,
rect.bottom - fntHeight - PADDING,
gEditWidth, fntHeight+PADDING, TRUE);

MoveWindow (ptd->hWndBtn, gStaticWidth + gEditWidth + 10,
rect.bottom - fntHeight - PADDING,
gBtnWidth, fntHeight+PADDING, TRUE);

MoveWindow (ptd->hWndNew, gStaticWidth+gEditWidth+gBtnWidth+15,
rect.bottom - fntHeight- PADDING,
gNewWidth, fntHeight+PADDING, TRUE);


}
return 0;
}
case WM_PAINT:
PaintMainWnd (hWnd);
return 0;

case WM_LBUTTONDOWN:
{
//
// Find the rectangle in which the button was pressed
//
POINTS pts;
ThreadData *ptd;
ptd = (ThreadData *)GetWindowLong(hWnd, GWL_USERDATA);
pts = MAKEPOINTS (lParam);
// Thierry
//if (pts.y > gHeight)
if (pts.y > gHeight || pts.x > gWidth*(int)NB_DESKTOPS)
{
return 1;
}
newThread = pts.x/gWidth;

//
// Get a snapshot of the current desktop
//
SaveScreen (ptd->index);

//
// Switch to the selected desktop
//
if (!gDeskArray[newThread])
{
StartNewDesktop (newThread);
}
if (!SwitchDesktop (gDeskArray[newThread]))
MessageBox (hWnd,
PSZ(IDS_BADDESKTOP),
PSZ(IDS_ERRCAPTION), MB_OK);

return 0;
}

case WM_RBUTTONDOWN:
//
// show the preview window
//
{
POINTS pts;
POINT ptl;
int *index;

pts = MAKEPOINTS (lParam);
if (pts.y > gHeight)
{
return 1;
}
newThread = pts.x/gWidth;
index = (int *) GlobalAlloc (GMEM_FIXED, sizeof(int));
if (!index)
{
return 1;
}
*index = newThread;
//
// Want to show the preview window where the button was clicked.
// Map the given points to screen coords.
// ClientToScreen is expecting a POINT structure, not a POINTS
//
ptl.x = (LONG)pts.x;
ptl.y = (LONG)pts.y;
ClientToScreen (hWnd, &ptl);
hShowing = CreateWindow (szPreviewClass, TEXT(""),
WS_POPUP | WS_VISIBLE | WS_BORDER,
ptl.x+3,
ptl.y+3,
gWidth*2,
gHeight*2,
hWnd,
(HMENU)0, ghInst, (LPVOID)index);
return 0;
}

case WM_CHAR:
if (wParam == VK_RETURN)
{
PostMessage (hWnd, WM_COMMAND, (WPARAM)IDC_RUNMEBTN, 0);
}
return 0;

case WM_SYSCHAR:
{
ThreadData *ptd;
ptd = (ThreadData *)GetWindowLong(hWnd, GWL_USERDATA);
switch (wParam)
{
// alt+r == focus on the edit control
case TEXT('r'):
case TEXT('R'):
if (GetKeyState (VK_MENU))
{
SetFocus (ptd->hWndEdit);
}
return 0;
// alt+n = create a new desktop
case TEXT('n'):
case TEXT('N'):
if (GetKeyState (VK_MENU))
{
PostMessage (hWnd, WM_COMMAND, (WPARAM)IDC_NEWDSKBTN,
0);
}
}
return 0;
}
case WM_HOTKEY:
case WM_KEYDOWN:
//
// F1-F9 switches to corresponding desktop
//

if ((wParam >= VK_F1 && wParam <= VK_F10)
&& (wParam - VK_F1 <= (UINT)gMaxIndex))
{
LONG x, y;
x = (wParam - VK_F1) * gWidth + 2;
y = gHeight - 4;
PostMessage (hWnd, WM_LBUTTONDOWN, 0, MAKELPARAM (x, y));
}
return 0;

case WM_SETFOCUS:
case WM_NCLBUTTONUP:
case WM_LBUTTONUP:

//
// destroy the preview window
//
if (hShowing)
{
DestroyWindow (hShowing);
hShowing = NULL;
}
return 0;



case WM_CLOSE:
//
// to be safe, check for a preview window
//
if (hShowing)
{
DestroyWindow (hShowing);
hShowing = NULL;
}
//
// go to the default desktop so the DestroyWindow calls all
succeed
//
SwitchDesktop (DEFAULT_DESKTOP);
//
// kill the window on this desktop
// all the windows will be destroyed if this is the default
desktop
//
for (i=gMaxIndex;i>=0;i--)
{
DestroyWindow (hWndArray[i]);
}
//
// Unregister the hot keys
//
for (i=0;i<10;i++)
{
UnregisterHotKey (hWnd,VK_F1+i);
}
return 0;

case WM_DESTROY: // message: window being destroyed

PostQuitMessage(0);
return 0;


case WM_COMMAND:
{

switch (LOWORD(wParam))
{
case IDC_RUNMEBTN:
{
RunApp (hWnd);
return 0;
}
/*
case IDC_NEWDSKBTN:
//
// Create a new desktop and resize the windows to show it.
//
{
RECT rect;
int i;
if (gMaxIndex + 1 < MAX_THREADS)
{
gMaxIndex++;
StartNewDesktop (gMaxIndex);
GetWindowRect (hWnd,&rect);
for (i=0;i<gMaxIndex;i++)
{
MoveWindow (hWndArray[i],
rect.left, rect.top,
rect.right + gWidth, rect.bottom-rect.top,
TRUE);

}
}
return 0;
}
*/
default:
return DefWindowProc (hWnd, message, wParam, lParam);
}
}

default: // Passes it on if unprocessed
return (DefWindowProc (hWnd, message, wParam, lParam));
}

}

/***********************************************************

FUNCTION: PreviewWndProc

PURPOSE: Displays an enlarged view of the last snapshot of a desktop

************************************************************/

LONG APIENTRY PreviewWndProc (HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{

int *index;
switch (msg)
{
case WM_CREATE:
//
// save the index
//
SetWindowLong (hWnd, GWL_USERDATA,
(LONG)((CREATESTRUCT *)lParam)->lpCreateParams);

return 0;

case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;
index = (int *)GetWindowLong (hWnd, GWL_USERDATA);
hdc = BeginPaint (hWnd, &ps);
//
// slap in the desktop picture
//
BitBlt (hdc, 0, 0, gWidth*2, gHeight*2,
gHDCArray[*index], 0, 0, SRCCOPY);
EndPaint (hWnd, &ps);
return 0;
}
case WM_LBUTTONUP:
{
//
// In case the button is released in my client area
//
HWND hp;
hp = GetWindow (hWnd, GW_OWNER);
PostMessage (hp, msg, wParam, lParam);
return 0;
}
case WM_CLOSE:
{
//
// cleanup the index pointer
//
index = (int *)GetWindowLong (hWnd, GWL_USERDATA);
GlobalFree (index);

return DefWindowProc (hWnd, msg, wParam, lParam);
}
default:
return DefWindowProc (hWnd, msg, wParam, lParam);

}
}

/********************************************

FUNCTION: EditProc

PURPOSE: subclass the edit control to handle carriage returns

********************************************/

LONG APIENTRY EditProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (WM_CHAR == msg && (TCHAR)wParam == 0xD)
{
PostMessage (GetParent (hWnd), WM_COMMAND, (WPARAM)IDC_RUNMEBTN, 0);
return 0;
}
//
// call the default edit control procedure
//
return CallWindowProc ( (WNDPROC)GetWindowLong (hWnd, GWL_USERDATA),
hWnd, msg, wParam, lParam);
}

DWORD AdjustPrivileges()
{
DWORD dwRetour = ERROR_SUCCESS;
HANDLE process = GetCurrentProcess();

// get a token to adjust privileges
HANDLE token = NULL;
if(!OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES, &token))
dwRetour = GetLastError();
else
{
// set new privileges
static TCHAR *P[] = {
SE_CREATE_TOKEN_NAME,
SE_ASSIGNPRIMARYTOKEN_NAME,
SE_TCB_NAME,
SE_LOAD_DRIVER_NAME,
SE_DEBUG_NAME,
SE_TAKE_OWNERSHIP_NAME,
"SeInteractiveLogonName"

};
int j=0;

TOKEN_PRIVILEGES * Priv;
TCHAR buf[1024] = {0};

Priv = (PTOKEN_PRIVILEGES) buf;
Priv->PrivilegeCount = 0;

for(j = 0; j < sizeof P/sizeof P[0]; ++j)
{
if(LookupPrivilegeValue(NULL, P[j], &Priv-
Privileges[0].Luid))


{
Priv->Privileges[j].Attributes =
SE_PRIVILEGE_ENABLED;
Priv->PrivilegeCount++;
}
else dwRetour = GetLastError();
}
// modify token privileges
if(!AdjustTokenPrivileges(token, FALSE, Priv, 0, NULL, NULL))
dwRetour = GetLastError();
}

return dwRetour;
}




--
« Always look at the bright side of the life... »
Avatar
AMcD®
Thierry wrote:

Soit.

Alors on va commencer par desktop.c, puis je vais chercher une
solution pour poster le .ico.



Fais simple, donne juste les codes hexa du .ico.

--
AMcD®

http://arnold.mcdonald.free.fr/
Avatar
Thierry
Bonjour,

AMcD® a écrit :

Soit.

Alors on va commencer par desktop.c, puis je vais chercher une
solution pour poster le .ico.



Fais simple, donne juste les codes hexa du .ico.



00 00 01 00 02 00 20 20 10 00 00 00 00 00 E8 02
00 00 26 00 00 00 10 10 10 00 00 00 00 00 28 01
00 00 0E 03 00 00 28 00 00 00 20 00 00 00 40 00
00 00 01 00 04 00 00 00 00 00 80 02 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00
00 00 80 00 80 00 80 80 00 00 C0 C0 C0 00 80 80
80 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00
00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 0B 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 0B B0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 BB
00 00 00 00 00 00 B0 00 00 00 00 00 00 00 00 0B
B0 00 00 00 00 00 BB 00 00 00 00 00 00 00 00 00
BB 00 00 00 00 0B BB B0 00 00 00 00 00 00 00 00
0B B0 00 00 00 0B B0 BB 00 00 00 00 00 00 00 00
00 BB 00 00 00 0B B0 0B B0 00 00 00 00 00 00 00
00 0B B0 00 00 0B B0 00 BB 00 00 00 00 00 00 00
00 00 BB 00 00 BB 00 00 0B B0 00 00 00 00 00 00
00 00 0B B0 00 BB 00 00 00 BB 00 00 BB 00 00 00
00 00 00 BB 00 BB 00 00 00 0B B0 00 BB 00 00 00
00 00 00 0B B0 BB 00 00 00 00 BB 00 BB 00 00 00
00 00 00 00 BB BB 00 00 00 00 0B B0 BB 00 00 00
00 00 00 00 0B BB 00 00 00 00 00 BB BB 00 00 00
00 00 00 00 00 BB 00 00 00 0B BB BB BB 00 00 00
00 00 00 00 00 00 00 00 00 0B BB BB BB 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF BF FF FF FF 9F FF
FF FF CF FF 7F FF E7 FF 3F FF F3 FE 1F FF F9 FE
4F FF FC FE 67 FF FE 7E 73 FF FF 3C F9 FF FF 9C
FC F3 FF CC FE 73 FF E4 FF 33 FF F0 FF 93 FF F8
FF C3 FF FC FE 03 FF FF FE 03 FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF 28 00
00 00 10 00 00 00 20 00 00 00 01 00 04 00 00 00
00 00 C0 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80
00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80
00 00 C0 C0 C0 00 80 80 80 00 00 00 FF 00 00 FF
00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF
00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 0B 00 00 00 00 00 00 00 0B 00
00 00 B0 00 00 00 00 B0 00 00 B0 00 00 00 00 B0
00 00 BB 00 00 00 00 0B 00 00 BB 00 00 00 00 00
B0 00 B0 B0 00 00 00 00 B0 0B 00 0B 00 00 00 00
0B 0B 00 0B 00 B0 00 00 0B 0B 00 00 B0 B0 00 00
00 BB 00 00 B0 B0 00 00 00 0B 00 00 0B B0 00 00
00 0B 00 00 BB B0 00 00 00 00 00 00 BB B0 00 00
00 00 00 00 00 00 FF FF 00 00 FF FF 00 00 BF FF
00 00 BF 7F 00 00 DF 7F 00 00 DF 3F 00 00 EF 3F
00 00 F7 5F 00 00 F6 EF 00 00 FA ED 00 00 FA F5
00 00 FC F5 00 00 FE F9 00 00 FE F1 00 00 FF F1
00 00 FF FF 00 00

--
« Always look at the bright side of the life... »
1 2