12fc3a9d5da3011e3d732bc27d1cc6a1ea2b51b7
[reactos.git] / rosapps / downloader / main.c
1 /* PROJECT: ReactOS Downloader
2 * LICENSE: GPL - See COPYING in the top level directory
3 * FILE: base/applications/downloader/xml.c
4 * PURPOSE: Main program
5 * PROGRAMMERS: Maarten Bosma, Lester Kortenhoeven
6 */
7
8 #include <windows.h>
9 #include <commctrl.h>
10 #include <richedit.h>
11 #include <stdio.h>
12 #include <shlwapi.h>
13 #include "resources.h"
14 #include "structures.h"
15
16 #define XML_PATH "C:\\ReactOS\\system32\\downloader.xml"
17
18 HWND hwnd, hCategories, hApps, hDownloadButton, hUninstallButton, hUpdateButton, hHelpButton;
19 HBITMAP hLogo, hUnderline;
20 WCHAR* DescriptionHeadline = L"";
21 WCHAR* DescriptionText = L"";
22 WCHAR ApplicationText[700];
23
24 struct Category Root;
25 struct Application* SelectedApplication;
26
27 INT_PTR CALLBACK DownloadProc (HWND, UINT, WPARAM, LPARAM);
28 BOOL ProcessXML (const char* filename, struct Category* Root);
29 VOID FreeTree (struct Category* Node);
30 WCHAR Strings [STRING_COUNT][MAX_STRING_LENGHT];
31
32
33 BOOL getUninstaller(WCHAR* RegName, WCHAR* Uninstaller) {
34
35 const DWORD ArraySize = 200;
36
37 HKEY hKey1;
38 HKEY hKey2;
39 DWORD Type = 0;
40 DWORD Size = ArraySize;
41 WCHAR Value[ArraySize];
42 WCHAR KeyName[ArraySize];
43 LONG i = 0;
44
45 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",0,KEY_READ,&hKey1) == ERROR_SUCCESS) {
46 while (RegEnumKeyExW(hKey1,i,KeyName,&Size,NULL,NULL,NULL,NULL) == ERROR_SUCCESS) {
47 ++i;
48 RegOpenKeyExW(hKey1,KeyName,0,KEY_READ,&hKey2);
49 Size = ArraySize;
50 if (RegQueryValueExW(hKey2,L"DisplayName",0,&Type,(LPBYTE)Value,&Size) == ERROR_SUCCESS) {
51 Size = ArraySize;
52 if (StrCmpW(Value,RegName) == 0) {
53 if (RegQueryValueExW(hKey2,L"UninstallString",0,&Type,(LPBYTE)Uninstaller,&Size) == ERROR_SUCCESS) {
54 RegCloseKey(hKey2);
55 RegCloseKey(hKey1);
56 return TRUE;
57 } else {
58 RegCloseKey(hKey2);
59 RegCloseKey(hKey1);
60 return FALSE;
61 }
62 }
63 }
64 RegCloseKey(hKey2);
65 Size = ArraySize;
66 }
67 RegCloseKey(hKey1);
68 }
69 return FALSE;
70 }
71
72 void ShowMessage (WCHAR* title, WCHAR* message)
73 {
74 DescriptionHeadline = title;
75 DescriptionText = message;
76 InvalidateRect(hwnd,NULL,TRUE);
77 UpdateWindow(hwnd);
78 }
79
80 void AddItems (HWND hwnd, struct Category* Category, struct Category* Parent)
81 {
82 TV_INSERTSTRUCTW Insert;
83
84 Insert.item.lParam = (UINT)Category;
85 Insert.item.mask = TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE;;
86 Insert.item.pszText = Category->Name;
87 Insert.item.cchTextMax = lstrlenW(Category->Name);
88 Insert.item.iImage = Category->Icon;
89 Insert.item.iSelectedImage = Category->Icon;
90 Insert.hInsertAfter = TVI_LAST;
91 Insert.hParent = Category->Parent ? Category->Parent->TreeviewItem : TVI_ROOT;
92
93 Category->TreeviewItem = (HTREEITEM)SendMessage(hwnd, TVM_INSERTITEM, 0, (LPARAM)&Insert);
94
95 if(Category->Next)
96 AddItems (hwnd,Category->Next,Parent);
97
98 if(Category->Children)
99 AddItems (hwnd,Category->Children,Category);
100 }
101
102 void CategoryChoosen (HWND hwnd, struct Category* Category)
103 {
104 struct Application* CurrentApplication;
105 TV_INSERTSTRUCTW Insert;
106 SelectedApplication = NULL;
107
108 if(Category->Children && !Category->Apps)
109 ShowMessage(Category->Name, Strings[IDS_CHOOSE_SUB]);
110 else if(!Category->Children && Category->Apps)
111 ShowMessage(Category->Name, Strings[IDS_CHOOSE_APP]);
112 else if(Category->Children && Category->Apps)
113 ShowMessage(Category->Name, Strings[IDS_CHOOSE_BOTH]);
114 else
115 ShowMessage(Category->Name, Strings[IDS_NO_APPS]);
116
117 (void)TreeView_DeleteItem(hwnd, TVI_ROOT);
118 (void)TreeView_DeleteItem(hwnd, TVI_ROOT); // Delete twice to bypass bug in windows
119
120 Insert.item.mask = TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE;
121 Insert.hInsertAfter = TVI_LAST;
122 Insert.hParent = TVI_ROOT;
123
124 CurrentApplication = Category->Apps;
125
126 WCHAR Uninstaller[200];
127 while(CurrentApplication)
128 {
129 Insert.item.lParam = (UINT)CurrentApplication;
130 Insert.item.pszText = CurrentApplication->Name;
131 Insert.item.cchTextMax = lstrlenW(CurrentApplication->Name);
132 Insert.item.iImage = 10;
133 if(StrCmpW(CurrentApplication->RegName,L"")) {
134 if(getUninstaller(CurrentApplication->RegName, Uninstaller))
135 Insert.item.iImage = 9;
136 }
137 SendMessage(hwnd, TVM_INSERTITEM, 0, (LPARAM)&Insert);
138 CurrentApplication = CurrentApplication->Next;
139 }
140 }
141
142 BOOL SetupControls (HWND hwnd)
143 {
144 TV_INSERTSTRUCTW Insert = {0};
145 HIMAGELIST hImageList;
146 HINSTANCE hInstance = GetModuleHandle(NULL);
147
148 // Parse the XML file
149 if (ProcessXML (XML_PATH, &Root) == FALSE)
150 return FALSE;
151
152 // Set up the controls
153 hCategories = CreateWindowExW(0, WC_TREEVIEWW, L"Categories", WS_CHILD|WS_VISIBLE|WS_BORDER|TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|TVS_SHOWSELALWAYS,
154 0, 0, 0, 0, hwnd, NULL, hInstance, NULL);
155
156 hApps = CreateWindowExW(0, WC_TREEVIEWW, L"Applications", WS_CHILD|WS_VISIBLE|WS_BORDER|TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|TVS_SHOWSELALWAYS,
157 0, 0, 0, 0, hwnd, NULL, hInstance, NULL);
158
159 hLogo = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_LOGO));
160 hUnderline = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_UNDERLINE));
161
162 hHelpButton = CreateWindowW (L"Button", L"", WS_CHILD|WS_VISIBLE|BS_BITMAP, 550, 10, 40, 40, hwnd, 0, hInstance, NULL);
163 hUpdateButton = CreateWindowW (L"Button", L"", WS_CHILD|WS_VISIBLE|BS_BITMAP, 500, 10, 40, 40, hwnd, 0, hInstance, NULL);
164 hDownloadButton = CreateWindowW (L"Button", L"", WS_CHILD|WS_VISIBLE|BS_BITMAP, 330, 505, 140, 33, hwnd, 0, hInstance, NULL);
165 hUninstallButton = CreateWindowW (L"Button", L"", WS_CHILD|WS_VISIBLE|BS_BITMAP, 260, 505, 140, 33, hwnd, 0, hInstance, NULL);
166
167 SendMessageW(hHelpButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)(HANDLE)LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_HELP)));
168 SendMessageW(hUpdateButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP,(LPARAM)(HANDLE)LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_UPDATE)));
169 SendMessageW(hDownloadButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP,(LPARAM)(HANDLE)LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_DOWNLOAD)));
170 SendMessageW(hUninstallButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP,(LPARAM)(HANDLE)LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_UNINSTALL)));
171 ShowWindow(hUninstallButton, SW_HIDE);
172
173 // Set deflaut entry for hApps
174 Insert.item.mask = TVIF_TEXT|TVIF_IMAGE;
175 Insert.item.pszText = Strings[IDS_CHOOSE_CATEGORY];
176 Insert.item.cchTextMax = lstrlenW(Strings[IDS_CHOOSE_CATEGORY]);
177 Insert.item.iImage = 0;
178 SendMessage(hApps, TVM_INSERTITEM, 0, (LPARAM)&Insert);
179
180 // Create Tree Icons
181 hImageList = ImageList_Create(16, 16, ILC_COLORDDB, 1, 1);
182 SendMessageW(hCategories, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)(HIMAGELIST)hImageList);
183 SendMessageW(hApps, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)(HIMAGELIST)hImageList);
184
185 ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_0)), NULL);
186 ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_1)), NULL);
187 ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_2)), NULL);
188 ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_3)), NULL);
189 ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_4)), NULL);
190 ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_5)), NULL);
191 ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_6)), NULL);
192 ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_7)), NULL);
193 ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_8)), NULL);
194 ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_9)), NULL);
195 ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_10)), NULL);
196
197 // Fill the TreeViews
198 AddItems (hCategories, Root.Children, NULL);
199
200 return TRUE;
201 }
202
203 static void ResizeControl (HWND hwnd, int x1, int y1, int x2, int y2)
204 {
205 // Make resizing a little easier
206 MoveWindow(hwnd, x1, y1, x2-x1, y2-y1, TRUE);
207 }
208
209 static void DrawBitmap (HDC hdc, int x, int y, HBITMAP hBmp)
210 {
211 BITMAP bm;
212 HDC hdcMem = CreateCompatibleDC(hdc);
213
214 SelectObject(hdcMem, hBmp);
215 GetObject(hBmp, sizeof(bm), &bm);
216 TransparentBlt(hdc, x, y, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, 0xFFFFFF);
217
218 DeleteDC(hdcMem);
219 }
220
221 static void DrawDescription (HDC hdc, RECT DescriptionRect)
222 {
223 int i;
224 HFONT Font;
225 RECT Rect = {DescriptionRect.left+5, DescriptionRect.top+3, DescriptionRect.right-2, DescriptionRect.top+22};
226
227 // Backgroud
228 Rectangle(hdc, DescriptionRect.left, DescriptionRect.top, DescriptionRect.right, DescriptionRect.bottom);
229
230 // Underline
231 for (i=DescriptionRect.left+1;i<DescriptionRect.right-1;i++)
232 DrawBitmap(hdc, i, DescriptionRect.top+22, hUnderline); // less code then stretching ;)
233
234 // Headline
235 Font = CreateFont(-16 , 0, 0, 0, FW_EXTRABOLD, FALSE, FALSE, FALSE, ANSI_CHARSET,
236 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, L"Arial");
237 SelectObject(hdc, Font);
238 DrawTextW(hdc, DescriptionHeadline, lstrlenW(DescriptionHeadline), &Rect, DT_SINGLELINE|DT_NOPREFIX);
239 DeleteObject(Font);
240
241 // Description
242 Font = CreateFont(-13 , 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
243 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, L"Arial");
244 SelectObject(hdc, Font);
245 Rect.top += 40;
246 Rect.bottom = DescriptionRect.bottom-2;
247 DrawTextW(hdc, DescriptionText, lstrlenW(DescriptionText), &Rect, DT_WORDBREAK|DT_NOPREFIX); // ToDo: Call TabbedTextOut to draw a nice table
248 DeleteObject(Font);
249
250 }
251
252 void showUninstaller() {
253 int Split_Vertical = 200;
254 RECT Rect;
255
256 GetClientRect(hwnd,&Rect);
257 ShowWindow(hUninstallButton,SW_SHOW);
258 MoveWindow(hDownloadButton,(Split_Vertical+Rect.right-Rect.left)/2,Rect.bottom-Rect.top-45,140,35,TRUE);;
259 }
260
261 void hideUninstaller() {
262 int Split_Vertical = 200;
263 RECT Rect;
264
265 GetClientRect(hwnd,&Rect);
266 ShowWindow(hUninstallButton,SW_HIDE);
267 MoveWindow(hDownloadButton,(Split_Vertical+Rect.right-Rect.left)/2-70,Rect.bottom-Rect.top-45,140,35,TRUE);
268 }
269
270 void startUninstaller(WCHAR* Uninstaller) {
271 STARTUPINFOW si;
272 PROCESS_INFORMATION pi;
273
274 memset(&si, 0, sizeof(si));
275 si.cb = sizeof(si);
276 CreateProcessW(NULL,Uninstaller,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);
277 CloseHandle(pi.hThread);
278 // WaitForSingleObject(pi.hProcess, INFINITE); // If you want to wait for the Unistaller
279 CloseHandle(pi.hProcess);
280 hideUninstaller();
281 }
282
283 LRESULT CALLBACK WndProc (HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
284 {
285 static RECT DescriptionRect;
286
287 switch (Message)
288 {
289 case WM_CREATE:
290 {
291 if(!SetupControls(hwnd))
292 return -1;
293 ShowMessage(Strings[IDS_WELCOME_TITLE], Strings[IDS_WELCOME]);
294 }
295 break;
296
297 case WM_PAINT:
298 {
299 PAINTSTRUCT ps;
300 HDC hdc = BeginPaint(hwnd, &ps);
301 HDC BackbufferHdc = CreateCompatibleDC(hdc);
302 HBITMAP BackbufferBmp = CreateCompatibleBitmap(hdc, ps.rcPaint.right, ps.rcPaint.bottom);
303 SelectObject(BackbufferHdc, BackbufferBmp);
304
305 FillRect(BackbufferHdc, &ps.rcPaint, CreateSolidBrush(RGB(235,235,235)));
306 DrawBitmap(BackbufferHdc, 10, 12, hLogo);
307 DrawDescription(BackbufferHdc, DescriptionRect);
308
309 BitBlt(hdc, 0, 0, ps.rcPaint.right, ps.rcPaint.bottom, BackbufferHdc, 0, 0, SRCCOPY);
310 DeleteObject(BackbufferBmp);
311 DeleteDC(BackbufferHdc);
312 EndPaint(hwnd, &ps);
313 }
314 break;
315
316 case WM_COMMAND:
317 {
318 if(HIWORD(wParam) == BN_CLICKED)
319 {
320 if (lParam == (LPARAM)hDownloadButton)
321 {
322 if(SelectedApplication)
323 DialogBoxW(GetModuleHandle(NULL), MAKEINTRESOURCEW(IDD_DOWNLOAD), 0, DownloadProc);
324 else
325 ShowMessage(Strings[IDS_NO_APP_TITLE], Strings[IDS_NO_APP]);
326 }
327 else if (lParam == (LPARAM)hUninstallButton)
328 {
329 if(SelectedApplication)
330 {
331 WCHAR Uninstaller[200];
332 if(StrCmpW(SelectedApplication->RegName, L"")) {
333 if(getUninstaller(SelectedApplication->RegName, Uninstaller))
334 startUninstaller(Uninstaller);
335 }
336 }
337 }
338 else if (lParam == (LPARAM)hUpdateButton)
339 {
340 ShowMessage(Strings[IDS_UPDATE_TITLE], Strings[IDS_UPDATE]);
341 }
342 else if (lParam == (LPARAM)hHelpButton)
343 {
344 ShowMessage(Strings[IDS_HELP_TITLE], Strings[IDS_HELP]);
345 }
346 }
347 }
348 break;
349
350 case WM_NOTIFY:
351 {
352 LPNMHDR data = (LPNMHDR)lParam;
353 if(data->code == TVN_SELCHANGED)
354 {
355 BOOL bShowUninstaller = FALSE;
356 if(data->hwndFrom == hCategories)
357 {
358 struct Category* Category = (struct Category*) ((LPNMTREEVIEW)lParam)->itemNew.lParam;
359 CategoryChoosen (hApps, Category);
360 }
361 else if(data->hwndFrom == hApps)
362 {
363 SelectedApplication = (struct Application*) ((LPNMTREEVIEW)lParam)->itemNew.lParam;
364 if(SelectedApplication)
365 {
366 ApplicationText[0]=L'\0';
367 if(StrCmpW(SelectedApplication->Version, L"")) {
368 StrCatW(ApplicationText, Strings[IDS_VERSION]);
369 StrCatW(ApplicationText, SelectedApplication->Version);
370 StrCatW(ApplicationText, L"\n");
371 }
372 if(StrCmpW(SelectedApplication->Licence, L"")) {
373 StrCatW(ApplicationText, Strings[IDS_LICENCE]);
374 StrCatW(ApplicationText, SelectedApplication->Licence);
375 StrCatW(ApplicationText, L"\n");
376 }
377 if(StrCmpW(SelectedApplication->Maintainer, L"")) {
378 StrCatW(ApplicationText, Strings[IDS_MAINTAINER]);
379 StrCatW(ApplicationText, SelectedApplication->Maintainer);
380 StrCatW(ApplicationText, L"\n");
381 }
382 if(StrCmpW(SelectedApplication->Licence, L"") || StrCmpW(SelectedApplication->Version, L"") || StrCmpW(SelectedApplication->Maintainer, L""))
383 StrCatW(ApplicationText, L"\n");
384 StrCatW(ApplicationText, SelectedApplication->Description);
385 ShowMessage(SelectedApplication->Name, ApplicationText);
386 WCHAR Uninstaller[200];
387 if(StrCmpW(SelectedApplication->RegName, L"")) {
388 if(getUninstaller(SelectedApplication->RegName, Uninstaller)) {
389 bShowUninstaller = TRUE;
390 }
391 }
392 }
393 }
394 if (bShowUninstaller)
395 showUninstaller();
396 else
397 hideUninstaller();
398 }
399 }
400 break;
401
402 case WM_SIZING:
403 {
404 LPRECT pRect = (LPRECT)lParam;
405 if (pRect->right-pRect->left < 520)
406 pRect->right = pRect->left + 520;
407
408 if (pRect->bottom-pRect->top < 300)
409 pRect->bottom = pRect->top + 300;
410 }
411 break;
412
413 case WM_SIZE:
414 {
415 int Split_Hozizontal = (HIWORD(lParam)-(45+60))/2 + 60;
416 int Split_Vertical = 200;
417
418 ResizeControl(hCategories, 10, 60, Split_Vertical, HIWORD(lParam)-10);
419 ResizeControl(hApps, Split_Vertical+5, 60, LOWORD(lParam)-10, Split_Hozizontal);
420 RECT Rect = {Split_Vertical+5, Split_Hozizontal+5, LOWORD(lParam)-10, HIWORD(lParam)-50};
421 DescriptionRect = Rect;
422
423 MoveWindow(hHelpButton, LOWORD(lParam)-50, 10, 40, 40, TRUE);
424 MoveWindow(hUpdateButton, LOWORD(lParam)-100, 10, 40, 40, TRUE);
425 if(IsWindowVisible(hUninstallButton))
426 MoveWindow(hDownloadButton, (Split_Vertical+LOWORD(lParam))/2, HIWORD(lParam)-45, 140, 35, TRUE);
427 else
428 MoveWindow(hDownloadButton, (Split_Vertical+LOWORD(lParam))/2-70, HIWORD(lParam)-45, 140, 35, TRUE);
429 MoveWindow(hUninstallButton, (Split_Vertical+LOWORD(lParam))/2-140, HIWORD(lParam)-45, 140, 35, TRUE);
430 }
431 break;
432
433 case WM_DESTROY:
434 {
435 DeleteObject(hLogo);
436 if(Root.Children)
437 FreeTree(Root.Children);
438 PostQuitMessage(0);
439 return 0;
440 }
441 break;
442 }
443
444 return DefWindowProc (hwnd, Message, wParam, lParam);
445 }
446
447 INT WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInst,
448 LPSTR lpCmdLine, INT nCmdShow)
449 {
450 int i;
451 WNDCLASSEXW WndClass = {0};
452 MSG msg;
453
454 InitCommonControls();
455
456 // Load strings
457 for(i=0; i<STRING_COUNT; i++)
458 LoadStringW(hInstance, i, Strings[i], MAX_STRING_LENGHT); // if you know a better method please tell me.
459
460 // Create the window
461 WndClass.cbSize = sizeof(WNDCLASSEX);
462 WndClass.lpszClassName = L"Downloader";
463 WndClass.lpfnWndProc = WndProc;
464 WndClass.hInstance = hInstance;
465 WndClass.style = CS_HREDRAW | CS_VREDRAW;
466 WndClass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN));
467 WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
468
469 RegisterClassExW(&WndClass);
470
471 hwnd = CreateWindowW(L"Downloader",
472 Strings[IDS_WINDOW_TITLE],
473 WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN,
474 CW_USEDEFAULT,
475 CW_USEDEFAULT,
476 600, 550,
477 NULL, NULL,
478 hInstance,
479 NULL);
480
481 // Show it
482 ShowWindow(hwnd, SW_SHOW);
483 UpdateWindow(hwnd);
484
485 // Message Loop
486 while(GetMessage(&msg,NULL,0,0))
487 {
488 TranslateMessage(&msg);
489 DispatchMessage(&msg);
490 }
491
492 return 0;
493 }