|
|
1.1 root 1: /*
2: * net.c
3: *
4: * Purpose:
5: * net functions
6: *
7: * Owner:
8: * MikeSart
9: */
10: #define UNICODE 1
11:
12: #include <windows.h>
13: #include <windowsx.h>
14: #include <string.h>
15: #include <time.h>
16: #include <lm.h>
17: #include "netwatch.h"
18: #include "rcids.h"
19:
20: typedef struct
21: {
22: DWORD dwType;
23: DWORD dwData;
24: TCHAR rgsz[];
25: } LBDATA;
26:
27: // in datetime.c
28: TCHAR *PutTime(time_t tm_t, TCHAR *szStr);
29: TCHAR *PutDate(time_t tm_t, TCHAR *szStr);
30: TCHAR *PutCounterTime(DWORD dw, TCHAR *szStr);
31:
32: // variables
33: TCHAR *szFmtLB[LBITEM_NUMITEMS];
34:
35: // returns TRUE - error
36: // if it fails initing, you should still call
37: // InitNetWatch(FALSE) to clean up
38: BOOL
39: InitNetWatch(BOOL fInit)
40: {
41: DWORD dwT;
42: TCHAR *szCommandLine;
43: BOOL fError = FALSE;
44:
45: if(fInit)
46: {
47: szAppName = AllocAndLoadString(IDS_APPNAME);
48: if(!szAppName)
49: fError = TRUE;
50:
51: // load formatting info for each listbox item
52: for(dwT = 0; dwT < LBITEM_NUMITEMS; dwT++)
53: {
54: szFmtLB[dwT] = AllocAndLoadString(IDS_FMTLB + dwT);
55: if(!szFmtLB[dwT])
56: fError = TRUE;
57: }
58:
59: if(szCommandLine = GetCommandLine())
60: {
61: // skip first arg (our exe name)
62: while(*szCommandLine && (*szCommandLine != ' '))
63: szCommandLine++;
64:
65: // skip spaces
66: while(*szCommandLine && (*szCommandLine == ' '))
67: szCommandLine++;
68:
69: if(*szCommandLine)
70: {
71: if(lstrlen(szCommandLine) > UNCLEN)
72: szCommandLine[UNCLEN] = '\0';
73: if(!(szServerName = GlobalAllocPtr(GHND,
74: (lstrlen(szCommandLine) + 3) * sizeof(TCHAR))))
75: fError = TRUE;
76: else
77: {
78: if(szCommandLine[0] != '\\')
79: {
80: szServerName[0] = szServerName[1] = '\\';
81: lstrcpy(&szServerName[2], szCommandLine);
82: }
83: else
84: {
85: lstrcpy(szServerName, szCommandLine);
86: }
87: }
88: }
89: }
90: return fError;
91: }
92:
93: GlobalFreeNullPtr(szAppName);
94: GlobalFreeNullPtr(szServerName);
95: szAppName = szServerName = NULL;
96:
97: for(dwT = 0; dwT < LBITEM_NUMITEMS; dwT++)
98: {
99: GlobalFreeNullPtr(szFmtLB[dwT]);
100: szFmtLB[dwT] = NULL;
101: }
102:
103: return fError;
104: }
105:
106: LBDATA *
107: AllocExtraData1(DWORD dwType, DWORD dwData, TCHAR *sz1)
108: {
109: LBDATA *plbitem;
110: DWORD cb;
111:
112: if(!sz1)
113: sz1 = szNil;
114:
115: cb = sizeof(LBDATA) + ((wcslen(sz1) + 1) * sizeof(TCHAR));
116: plbitem = (LBDATA *)GlobalAllocPtr(GHND, cb);
117: if(plbitem)
118: {
119: plbitem->dwType = dwType;
120: plbitem->dwData = dwData;
121:
122: wcscpy(plbitem->rgsz, sz1);
123: }
124:
125: return plbitem;
126: }
127:
128: LBDATA *
129: AllocExtraDataUser(DWORD dwType, DWORD dwData, TCHAR *szNetName,
130: TCHAR *szUserName, TCHAR *szShareName, TCHAR *szShareRemark)
131: {
132: LBDATA *plbitem;
133: DWORD cb;
134: DWORD cch1, cch2, cch3;
135: DWORD cch4 = 0;
136: TCHAR *szT;
137:
138: cch1 = wcslen(szNetName) + 1;
139: cch2 = wcslen(szUserName) + 1;
140: cch3 = wcslen(szShareName) + 1;
141: if(szShareRemark && szShareRemark[0])
142: cch4 = wcslen(szShareRemark) + 4; // for 'sharename (comment)' style
143:
144: cb = sizeof(LBDATA) + ((cch1 + cch2 + cch3 + cch4 + 2) * sizeof(TCHAR));
145: plbitem = (LBDATA *)GlobalAllocPtr(GHND, cb);
146: if(plbitem)
147: {
148: plbitem->dwType = dwType;
149: plbitem->dwData = dwData;
150:
151: // add \\ to front of netname
152: plbitem->rgsz[0] = plbitem->rgsz[1] = '\\';
153: szT = wcscpy(plbitem->rgsz + 2, szNetName);
154: szT = wcscpy(szT + cch1, szUserName);
155: szT = wcscpy(szT + cch2, szShareName);
156: if(cch4)
157: {
158: wcscat(szT, TEXT(" ("));
159: wcscat(szT, szShareRemark);
160: wcscat(szT, TEXT(")"));
161: }
162: }
163:
164: return plbitem;
165: }
166:
167: BOOL
168: AddLBItem(HWND hwndLB, DWORD *pdwIndex, TCHAR *pszItem, DWORD dwData)
169: {
170: DWORD dwT;
171: static TCHAR szCurrentItem[256];
172: register DWORD dwIndex = *pdwIndex;
173:
174: // if the current item matches, just set the data
175: if((ListBox_GetText(hwndLB, dwIndex, szCurrentItem) != LB_ERR) &&
176: (!wcscmp(szCurrentItem, pszItem)))
177: {
178: GlobalFreeNullPtr((LPVOID)ListBox_GetItemData(hwndLB, dwIndex));
179: goto SetData;
180: }
181:
182: // if we found an exact match later, del everything up to that
183: dwT = ListBox_FindStringExact(hwndLB, dwIndex, pszItem);
184: if((dwT != LB_ERR) && (dwT > dwIndex))
185: {
186: DWORD dw;
187:
188: for(dw = dwIndex; dw < dwT; dw++)
189: {
190: GlobalFreeNullPtr((LPVOID)ListBox_GetItemData(hwndLB, dwIndex));
191: ListBox_DeleteString(hwndLB, dwIndex);
192: }
193:
194: GlobalFreeNullPtr((LPVOID)ListBox_GetItemData(hwndLB, dwIndex));
195: goto SetData;
196: }
197:
198: // ok, it's a new string, insert it
199: ListBox_InsertString(hwndLB, dwIndex, pszItem);
200:
201: SetData:
202: ListBox_SetItemData(hwndLB, dwIndex, dwData);
203: (*pdwIndex)++;
204:
205: return TRUE;
206: }
207:
208: NET_API_STATUS
209: FilesEnum(HWND hwndLB, DWORD *pdwIndex, TCHAR *sharename,
210: TCHAR *username, TCHAR *sharepath)
211: {
212: FILE_INFO_3 *fi3 = NULL;
213: NET_API_STATUS nas;
214: DWORD dwentriesread;
215: DWORD dwtotalentries;
216:
217: nas = NetFileEnum(szServerName, sharepath, username, 3,
218: (LPBYTE *)&fi3, MAX_PREFERRED_LENGTH,
219: &dwentriesread, &dwtotalentries, NULL);
220: if(nas)
221: goto err;
222:
223: while(dwentriesread)
224: {
225: UINT nBitmap = 4; // assume read bitmap
226:
227: --dwentriesread;
228:
229: if((fi3[dwentriesread].fi3_permissions & PERM_FILE_WRITE) ||
230: (fi3[dwentriesread].fi3_permissions & PERM_FILE_CREATE))
231: nBitmap = 3;
232:
233: wsprintf(szBuffer, szFmtLB[LBITEM_FILE], nBitmap,
234: fi3[dwentriesread].fi3_pathname);
235:
236: AddLBItem(hwndLB, pdwIndex, szBuffer, (DWORD)AllocExtraData1(
237: LBITEM_FILE, fi3[dwentriesread].fi3_id, username));
238: }
239:
240: err:
241: NetApiBufferFree(fi3);
242: if(nas)
243: return nas | FILE_ENUM_ERROR;
244: return nas;
245: }
246:
247: NET_API_STATUS
248: UsersEnum(HWND hwndLB, DWORD *pdwIndex, TCHAR *sharename, TCHAR *sharepath,
249: TCHAR *shareremark, DWORD dwShareType)
250: {
251: DWORD dwentriesread;
252: DWORD dwtotalentries;
253: CONNECTION_INFO_1 *ci1 = NULL;
254: NET_API_STATUS nas;
255:
256: nas = NetConnectionEnum(szServerName, sharename, 1, (LPBYTE *)&ci1,
257: MAX_PREFERRED_LENGTH, &dwentriesread, &dwtotalentries,
258: NULL);
259: if(nas)
260: goto err;
261:
262: dwNumUsers += dwentriesread;
263: while(dwentriesread)
264: {
265: TCHAR *username;
266: TCHAR *netname;
267:
268: --dwentriesread;
269:
270: username = ci1[dwentriesread].coni1_username;
271: netname = ci1[dwentriesread].coni1_netname;
272: if(!username || !username[0])
273: username = szNil;
274: else
275: CharLower(username);
276: if(!netname || !netname[0])
277: netname = szNil;
278: else
279: CharUpper(netname);
280:
281: wsprintf(szBuffer, szFmtLB[LBITEM_USER], username, netname);
282:
283: AddLBItem(hwndLB, pdwIndex, szBuffer, (DWORD)AllocExtraDataUser(
284: LBITEM_USER, dwShareType, netname, username, sharename,
285: shareremark));
286:
287: if(ci1[dwentriesread].coni1_num_opens &&
288: unMenuFlags[IDM_SHOWFILES & 0xff] == MF_CHECKED && !nas)
289: nas = FilesEnum(hwndLB, pdwIndex, sharename, username,
290: sharepath);
291: }
292:
293: err:
294: NetApiBufferFree(ci1);
295: return nas;
296: }
297:
298: void
299: RefreshDisplay(HWND hwnd)
300: {
301: HWND hwndLB = GetWindow(hwnd, GW_CHILD);
302: SHARE_INFO_2 *shi2 = NULL;
303: DWORD dwIndex = 0;
304: DWORD dwentriesread;
305: DWORD dwtotalentries;
306: DWORD dwT;
307: NET_API_STATUS nas;
308: INT n;
309:
310: PunchTimer(FALSE);
311: SetCursor(LoadCursor(NULL, IDC_WAIT));
312: n = ListBox_GetCurSel(hwndLB);
313: SetWindowRedraw(hwndLB, FALSE);
314: dwNumUsers = 0;
315:
316: nas = NetShareEnum(szServerName, 2, (LPBYTE *)&shi2,
317: MAX_PREFERRED_LENGTH, &dwentriesread, &dwtotalentries, NULL);
318: if(nas)
319: goto err;
320:
321: while(dwentriesread)
322: {
323: TCHAR *sharename;
324: TCHAR *shareremark;
325: int nBitmap = 0; // assume normal directory share
326:
327: --dwentriesread;
328: sharename = shi2[dwentriesread].shi2_netname;
329: shareremark = shi2[dwentriesread].shi2_remark;
330:
331: if(!sharename)
332: continue;
333: if(!shareremark)
334: shareremark = szNil;
335:
336: // skip hidden shares
337: if((unMenuFlags[IDM_SHOWHIDDEN & 0xff] == MF_UNCHECKED) &&
338: (sharename[lstrlen(sharename) - 1] == TEXT('$')))
339: continue;
340:
341: if(shi2[dwentriesread].shi2_type == STYPE_IPC) // use IPC$ icon
342: nBitmap = 8;
343: if(shareremark[0])
344: {
345: wsprintf(szBuffer, szFmtLB[LBITEM_SHARE2],
346: nBitmap, sharename, shareremark);
347: }
348: else
349: {
350: wsprintf(szBuffer, szFmtLB[LBITEM_SHARE], nBitmap, sharename);
351: }
352:
353: if((unMenuFlags[IDM_SHOWINUSE & 0xff] != MF_CHECKED) ||
354: shi2[dwentriesread].shi2_current_uses)
355: {
356: AddLBItem(hwndLB, &dwIndex, szBuffer,
357: (DWORD)AllocExtraData1(LBITEM_SHARE, 0, sharename));
358: }
359:
360: if(shi2[dwentriesread].shi2_current_uses)
361: {
362: if(!nas || (nas & FILE_ENUM_ERROR))
363: nas = UsersEnum(hwndLB, &dwIndex, sharename,
364: shi2[dwentriesread].shi2_path, shareremark,
365: shi2[dwentriesread].shi2_type);
366: }
367: }
368:
369: err:
370: if(nas)
371: {
372: AddErrorStringToLB(nas);
373: dwIndex++;
374: }
375:
376: // delete extra strings dangling at the end
377: while((dwT = ListBox_GetItemData(hwndLB, dwIndex)) != LB_ERR)
378: {
379: GlobalFreeNullPtr((LPVOID)dwT);
380: ListBox_DeleteString(hwndLB, dwIndex);
381: }
382:
383: if((ListBox_GetCurSel(hwndLB) == LB_ERR) &&
384: (ListBox_SetCurSel(hwndLB, n) == LB_ERR))
385: ListBox_SetCurSel(hwndLB, 0);
386:
387: SetWindowRedraw(hwndLB, TRUE);
388: SetCursor(LoadCursor(NULL, IDC_ARROW));
389: UpdateWindowText(hwnd, FALSE);
390: NetApiBufferFree(shi2);
391: PunchTimer(TRUE);
392: }
393:
394: NET_API_STATUS
395: DisplayShareDetails(HWND hwnd, LBDATA *plbdata, WORD wAction)
396: {
397: PROPERTIES props;
398: NET_API_STATUS nas = 0;
399: SHARE_INFO_2 *shi2 = NULL;
400: TCHAR szMaxUses[11];
401: TCHAR szMaxCurrent[11];
402:
403: nas = NetShareGetInfo(szServerName, plbdata->rgsz, 2, (LPBYTE *)&shi2);
404: if(!nas && shi2)
405: {
406: props.rgIDSStart = IDS_SHAREPROPS;
407: if(shi2->shi2_type == STYPE_IPC)
408: props.dwrgBmp = 0xfffffff8; // bitmap #8 for IPC$ netname
409: else
410: props.dwrgBmp = 0xfffffff0; // bitmap #0 for folder netname
411:
412: props.rgsz[0] = shi2->shi2_netname;
413: props.rgsz[1] = shi2->shi2_path;
414: props.rgsz[2] = shi2->shi2_remark;
415: if(shi2->shi2_max_uses == SHI_USES_UNLIMITED)
416: {
417: props.rgsz[3] = szFromIDS2(IDS_NOLIMIT);
418: }
419: else
420: {
421: wsprintf(szMaxUses, szFmtNum, shi2->shi2_max_uses);
422: props.rgsz[3] = szMaxUses;
423: }
424: wsprintf(szMaxCurrent, szFmtNum, shi2->shi2_current_uses);
425: props.rgsz[4] = szMaxCurrent;
426: props.rgsz[5] = NULL;
427:
428: if(wAction == VK_DELETE)
429: {
430: // if no one is connected, nuke it, else confirm with user
431: if(!(shi2->shi2_current_uses) ||
432: (MessageBox(hwnd, szFromIDS1(IDS_AREYOUSURE + LBITEM_SHARE),
433: szAppName, MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2) ==
434: IDYES))
435: {
436: nas = NetShareDel(szServerName, plbdata->rgsz, 0);
437: }
438: }
439: else
440: {
441: DialogBoxParam(ghInst, MAKEINTRESOURCE(DLG_PROPERTIES),
442: hwnd, PropDlgProc, (LPARAM)&props);
443: }
444: }
445:
446: NetApiBufferFree(shi2);
447: return nas;
448: }
449:
450: NET_API_STATUS
451: DisplayFileDetails(HWND hwnd, LBDATA *plbdata, WORD wAction)
452: {
453: PROPERTIES props;
454: NET_API_STATUS nas;
455: FILE_INFO_3 *fi3 = NULL;
456: TCHAR szNumLocks[11];
457: UINT nBitmap = 4; // assume read bitmap
458:
459: nas = NetFileGetInfo(szServerName, plbdata->dwData, 3, (LPBYTE *)&fi3);
460: if(!nas && fi3)
461: {
462: props.rgIDSStart = IDS_FILEPROPS;
463:
464: if((fi3->fi3_permissions & PERM_FILE_WRITE) ||
465: (fi3->fi3_permissions & PERM_FILE_CREATE))
466: nBitmap = 3;
467: props.dwrgBmp = 0xfffff072 | (nBitmap << 8);
468:
469: props.rgsz[0] = fi3->fi3_pathname;
470: props.rgsz[1] = fi3->fi3_username;
471: props.rgsz[2] = szFromIDS1(IDS_FILEPROPS + 10 + nBitmap);
472: wsprintf(szNumLocks, szFmtNum, fi3->fi3_num_locks);
473: props.rgsz[3] = szNumLocks;
474: props.rgsz[4] = NULL;
475: props.rgsz[5] = NULL;
476:
477: if(wAction == VK_DELETE)
478: {
479: wsprintf(szBuffer, szFromIDS1(IDS_AREYOUSURE + LBITEM_FILE),
480: plbdata->rgsz, fi3->fi3_pathname);
481: if(MessageBox(hwnd, szBuffer, szAppName,
482: MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2) == IDYES)
483: nas = NetFileClose(szServerName, plbdata->dwData);
484: }
485: else
486: {
487: DialogBoxParam(ghInst, MAKEINTRESOURCE(DLG_PROPERTIES),
488: hwnd, PropDlgProc, (LPARAM)&props);
489: }
490: }
491: NetApiBufferFree(fi3);
492: return nas;
493: }
494:
495: NET_API_STATUS
496: DisplayUserDetails(HWND hwnd, LBDATA *plbdata, WORD wAction)
497: {
498: PROPERTIES props;
499: NET_API_STATUS nas;
500: time_t tm_t;
501: SESSION_INFO_2 *sesi2 = NULL;
502: TCHAR szNumOpens[11];
503: TCHAR szConnectTime[50];
504: TCHAR szIdleTime[50];
505: TCHAR *szNetName = plbdata->rgsz;
506: TCHAR *szUserName = szNetName + lstrlen(szNetName) + 1;
507: TCHAR *szShareName = szUserName + lstrlen(szUserName) + 1;
508:
509: nas = NetSessionGetInfo(szServerName, szNetName, szUserName,
510: 2, (LPBYTE *)&sesi2);
511:
512: if(!nas && sesi2)
513: {
514: props.rgIDSStart = IDS_USERPROPS;
515: if(plbdata->dwData == STYPE_IPC)
516: props.dwrgBmp = 0xfffff817; // IPC$ share
517: else
518: props.dwrgBmp = 0xfffff017; // Drive share
519:
520: if(sesi2->sesi2_user_flags & SESS_GUEST)
521: {
522: lstrcpy(szBuffer, szUserName);
523: lstrcat(szBuffer, szFromIDS1(IDS_GUEST));
524: props.rgsz[0] = szBuffer;
525: }
526: else
527: props.rgsz[0] = szUserName;
528:
529: props.rgsz[1] = szNetName;
530: props.rgsz[2] = szShareName;
531:
532: time(&tm_t);
533: PutTime(tm_t - sesi2->sesi2_time, szIdleTime);
534: PutDate(tm_t - sesi2->sesi2_time, szConnectTime);
535: lstrcat(szConnectTime, TEXT(" "));
536: lstrcat(szConnectTime, szIdleTime);
537: props.rgsz[3] = szConnectTime;
538: props.rgsz[4] = PutCounterTime(sesi2->sesi2_idle_time, szIdleTime);
539:
540: wsprintf(szNumOpens, szFmtNum, sesi2->sesi2_num_opens);
541: props.rgsz[5] = szNumOpens;
542:
543: if(wAction == VK_DELETE)
544: {
545: wsprintf(szBuffer, szFromIDS1(IDS_AREYOUSURE + LBITEM_USER),
546: szUserName);
547: if(MessageBox(hwnd, szBuffer, szAppName,
548: MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2) == IDYES)
549: {
550: nas = NetSessionDel(szServerName, plbdata->rgsz,
551: plbdata->rgsz + lstrlen(plbdata->rgsz) + 1);
552: }
553: }
554: else
555: {
556: DialogBoxParam(ghInst, MAKEINTRESOURCE(DLG_PROPERTIES),
557: hwnd, PropDlgProc, (LPARAM)&props);
558: }
559: }
560:
561: if(!nas && !sesi2)
562: nas = ERROR_NOT_CONNECTED;
563: NetApiBufferFree(sesi2);
564: return nas;
565: }
566:
567: void
568: HandleMenu(HWND hwnd)
569: {
570: HWND hwndLB = GetWindow(hwnd, GW_CHILD);
571: INT nIndex;
572: DWORD dwData;
573:
574: nIndex = ListBox_GetCurSel(hwndLB);
575: dwData = ListBox_GetItemData(hwndLB, nIndex);
576: if((nIndex == LB_ERR) || (dwData == LB_ERR) || !dwData)
577: goto err;
578:
579: switch(((LBDATA *)dwData)->dwType)
580: {
581: case LBITEM_FILE:
582: case LBITEM_SHARE:
583: case LBITEM_USER:
584: ModifyMenu(ghMenu, IDM_DELETERESOURCE, MF_BYCOMMAND | MF_STRING,
585: IDM_DELETERESOURCE,
586: szFromIDS1(IDM_DELETERESOURCE + ((LBDATA *)dwData)->dwType));
587: break;
588:
589: default:
590: goto err;
591: }
592:
593: return;
594:
595: err:
596: EnableMenuItem(ghMenu, IDM_DELETERESOURCE, MF_BYCOMMAND | MF_GRAYED);
597: }
598:
599: void
600: HandleWM_VKEY(HWND hwnd, WORD wAction)
601: {
602: HWND hwndLB = GetWindow(hwnd, GW_CHILD);
603: INT nIndex;
604: DWORD dwData;
605: NET_API_STATUS nas = 0;
606:
607: PunchTimer(FALSE);
608: nIndex = ListBox_GetCurSel(hwndLB);
609: dwData = ListBox_GetItemData(hwndLB, nIndex);
610: if((nIndex == LB_ERR) || (dwData == LB_ERR) || !dwData)
611: goto err;
612:
613: if((wAction == VK_RETURN) || (wAction == VK_DELETE))
614: {
615: switch(((LBDATA *)dwData)->dwType)
616: {
617: case LBITEM_FILE:
618: nas = DisplayFileDetails(hwnd, (LBDATA *)dwData, wAction);
619: break;
620:
621: case LBITEM_SHARE:
622: nas = DisplayShareDetails(hwnd, (LBDATA *)dwData, wAction);
623: break;
624:
625: case LBITEM_USER:
626: nas = DisplayUserDetails(hwnd, (LBDATA *)dwData, wAction);
627: break;
628: }
629: }
630:
631: err:
632: if(nas)
633: {
634: TCHAR *szErrMessage;
635:
636: if(szErrMessage = GetSystemErrMessage(nas))
637: {
638: MessageBox(hwnd, szErrMessage, szAppName, MB_ICONEXCLAMATION);
639: LocalFree((HLOCAL)szErrMessage);
640: }
641: }
642:
643: PunchTimer(TRUE);
644: if(nas || (wAction == VK_DELETE))
645: PostMessage(hwnd, WM_TIMER, 0, 0L);
646: }
647:
648: void
649: AddErrorStringToLB(NET_API_STATUS nas)
650: {
651: TCHAR *szErrMessage;
652:
653: if(nas & FILE_ENUM_ERROR)
654: wsprintf(szBuffer, szFmtLB[LBITEM_DENIED], szFromIDS1(IDS_ERRENUMFILES));
655: else
656: wsprintf(szBuffer, szFmtLB[LBITEM_DENIED], szFromIDS1(IDS_ERRENUMUSERS));
657:
658: if(szErrMessage = GetSystemErrMessage(nas))
659: {
660: lstrcat(szBuffer, TEXT(": "));
661: lstrcat(szBuffer, szErrMessage);
662: LocalFree((HLOCAL)szErrMessage);
663: }
664:
665: ListBox_InsertString(GetWindow(hwndMain, GW_CHILD), 0, szBuffer);
666: ListBox_SetItemData(GetWindow(hwndMain, GW_CHILD), 0, 0);
667: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.