Source file: /~heha/hs/usbview.zip/src/usbview.c

/*++
Copyright (c) 1997-1998 Microsoft Corporation
Module Name:	USBVIEW.C
Abstract:	This is the GUI goop for the USBVIEW application.
Environment:	user mode
Revision Hist:	04-25-97 : created
Todo:		Save/restore WinPos/Divider, menu switches
--*/

//*****************************************************************************
// I N C L U D E S
//*****************************************************************************

#include <windows.h>
#include <basetyps.h>
#include <windowsx.h>
#include <shlwapi.h>
#include <devguid.h>
#include <setupapi.h>
#include <initguid.h>
#include <devioctl.h>
#include <usbioctl.h>
#include <dbt.h>

#include "usbview.h"

//*****************************************************************************
// D E F I N E S
//*****************************************************************************

// window control defines
//
#define SIZEBAR             0
#define WINDOWSCALEFACTOR   15

//*****************************************************************************
// G L O B A L S    P R I V A T E    T O    T H I S    F I L E
//*****************************************************************************

HINSTANCE       ghInstance;
HWND            ghMainWnd;
HMENU           ghMainMenu;
HWND            ghTreeWnd;
HWND            ghEditWnd;
HWND            ghStatusWnd;
HCURSOR         ghSplitCursor;

BOOL            gbButtonDown    = FALSE;
HTREEITEM       ghTreeRoot      = NULL;

struct _CONFIG Config;

HDEVNOTIFY      gNotifyDevHandle;
HDEVNOTIFY      gNotifyHubHandle;


//*****************************************************************************
//
// WalkTree()
//
//*****************************************************************************

static VOID WalkTree (
    HTREEITEM        hTreeItem,
    LPFNTREECALLBACK lpfnTreeCallback,
    DWORD            dwRefData
)
{
    if (hTreeItem)
    {
        // Recursively call WalkTree on the node's first child.
        //
        WalkTree(TreeView_GetChild(ghTreeWnd, hTreeItem),
                 lpfnTreeCallback,
                 dwRefData);

        //
        // Call the lpfnCallBack on the node itself.
        //
        (*lpfnTreeCallback)(ghTreeWnd, hTreeItem);

        //
        //
        // Recursively call WalkTree on the node's first sibling.
        //
        WalkTree(TreeView_GetNextSibling(ghTreeWnd, hTreeItem),
                 lpfnTreeCallback,
                 dwRefData);
    }
}

//*****************************************************************************
//
// ExpandItem()
//
//*****************************************************************************

static VOID ExpandItem (
    HWND      hTreeWnd,
    HTREEITEM hTreeItem
)
{
    //
    // Make this node visible.
    //
    TreeView_Expand(hTreeWnd, hTreeItem, TVE_EXPAND);
}


#if DBG

//*****************************************************************************
//
// Oops()
//
//*****************************************************************************

void Oops(LPCTSTR File,ULONG Line) {
 TCHAR s[256];
 wnsprintf(s,elemof(s), T("File: %s, Line %d"), File, Line);
 MessageBox(ghMainWnd, s, T("Assertion failed!"), MB_OK);
}

#endif

//*****************************************************************************
//
// ResizeWindows()
//
// Handles resizing the two child windows of the main window.  If
// bSizeBar is true, then the sizing is happening because the user is
// moving the bar.  If bSizeBar is false, the sizing is happening
// because of the WM_SIZE or something like that.
//
//*****************************************************************************

static void ResizeWindows(BOOL bSizeBar, int BarLocation) {
 RECT	MainClientRect;
 RECT	MainWindowRect;
 RECT	TreeWindowRect;
 RECT	StatusWindowRect;
 int	xdiv;	// the moveable divider
 int	ydiv;	// the fixed border to the status window
 HDWP	dwp;

    // Is the user moving the bar?
    //
 if (!bSizeBar) {
  SendMessage(ghStatusWnd,WM_SIZE,0,0);
  BarLocation = Config.BarLocation;
 }
 GetClientRect(ghMainWnd, &MainClientRect);
 GetWindowRect(ghStatusWnd, &StatusWindowRect);
 ydiv=MainClientRect.bottom-StatusWindowRect.bottom+StatusWindowRect.top;
    // Make sure the bar is in a OK location
 if (bSizeBar) {
  if (BarLocation < GetSystemMetrics(SM_CXSCREEN)/WINDOWSCALEFACTOR) return;
  if ((MainClientRect.right - BarLocation) < GetSystemMetrics(SM_CXSCREEN)/WINDOWSCALEFACTOR) return;
 }
    // Save the bar location
 Config.BarLocation = BarLocation;
 dwp=BeginDeferWindowPos(2);
    // Resize the tree window
 dwp=DeferWindowPos(dwp,ghTreeWnd,0,0,0,BarLocation,ydiv,SWP_NOZORDER);

    // Get the size of the window (in case move window failed
 GetWindowRect(ghTreeWnd, &TreeWindowRect);
 GetWindowRect(ghMainWnd, &MainWindowRect);

 xdiv = TreeWindowRect.right-MainWindowRect.left+SIZEBAR;
    // Move the edit window with respect to the tree window
 dwp=DeferWindowPos(dwp,ghEditWnd,0,xdiv,0,MainClientRect.right-xdiv,ydiv,SWP_NOZORDER);
 EndDeferWindowPos(dwp);
}


//*****************************************************************************
// AboutDlgProc()
//*****************************************************************************
static INT_PTR CALLBACK AboutDlgProc(HWND Wnd,UINT Msg,WPARAM wParam,LPARAM lParam) {
  switch (Msg) {
    case WM_INITDIALOG: return TRUE;
    case WM_COMMAND: switch (LOWORD(wParam)) {
      case IDOK:
      case IDCANCEL: EndDialog(Wnd,0); break;
    }break;
  }
  return FALSE;
}

//*****************************************************************************
// DestroyTree()
//*****************************************************************************
static VOID DestroyTree () {
    // Clear the selection of the TreeView, so that when the tree is
    // destroyed, the control won't try to constantly "shift" the
    // selection to another item.
 TreeView_SelectItem(ghTreeWnd, NULL);
    // Destroy the current contents of the TreeView
 if (ghTreeRoot) {
  WalkTree(ghTreeRoot, CleanupItem, 0);
  TreeView_DeleteAllItems(ghTreeWnd);
  ghTreeRoot = NULL;
 }
}

//*****************************************************************************
// RefreshTree()
//*****************************************************************************
static void RefreshTree() {
 TCHAR s[128];	// string
    // Clear the selection of the TreeView, so that when the tree is
    // destroyed, the control won't try to constantly "shift" the
    // selection to another item.
 TreeView_SelectItem(ghTreeWnd,NULL);
    // Clear the edit control
 SetWindowText(ghEditWnd,T(""));
    // Destroy the current contents of the TreeView
 if (ghTreeRoot) {
  WalkTree(ghTreeRoot, CleanupItem, 0);
  TreeView_DeleteAllItems(ghTreeWnd);
  ghTreeRoot = NULL;
 }
    // Create the root tree node
 LoadString(ghInstance,2,s,elemof(s));	//"My Computer"	- should be loaded from elsewhere!!
 ghTreeRoot = AddLeaf(TVI_ROOT,0,0,"%s",s);
 if (ghTreeRoot) {
  int devicesConnected;
  TCHAR t[128];	// template
	// Enumerate all USB buses and populate the tree
  EnumerateHostControllers(ghTreeRoot, &devicesConnected);
        // Expand all tree nodes
  WalkTree(ghTreeRoot, ExpandItem, 0);
	// Update Status Line with number of devices connected
  LoadString(ghInstance,3,t,elemof(t));	//"Devices Connected: %d   Hubs Connected: %d"
  wnsprintf(s,elemof(s),t,devicesConnected,TotalHubs);
  SetWindowText(ghStatusWnd,s);
 }else OOPS();
}

void LoadConfig() {
 HKEY key;
 if (!RegOpenKeyEx(HKEY_CURRENT_USER,T("Software\\h#s\\usbview"),0,KEY_QUERY_VALUE,&key)) {
  DWORD size=sizeof(Config);
  RegQueryValueEx(key,T("Config"),NULL,NULL,(BYTE*)&Config,&size);
  RegCloseKey(key);
 }
}

void SaveConfig() {
 HKEY key;
 if (!RegCreateKeyEx(HKEY_CURRENT_USER,T("Software\\h#s\\usbview"),0,NULL,0,KEY_SET_VALUE,NULL,&key,NULL)) {
  TCHAR s[128];
  int len=LoadString(ghInstance,1,s,elemof(s))+1;	//"USB tree view"
  RegSetValueEx(key,NULL,0,REG_SZ,(BYTE*)s,len*sizeof(TCHAR));	// write a human-readable description
  RegSetValueEx(key,T("Config"),0,REG_BINARY,(BYTE*)&Config,sizeof(Config));
  RegCloseKey(key);
 }
}

//*****************************************************************************
// Update the GUI for changing flags
//*****************************************************************************
void ConfigChanged(BYTE mask) {
 if (mask&CFLAG_AUTOREFRESH) CheckMenuItem(ghMainMenu,17,Config.flags&CFLAG_AUTOREFRESH?MF_CHECKED:MF_UNCHECKED);
 if (mask&CFLAG_CONFIGDESC) CheckMenuItem(ghMainMenu,18,Config.flags&CFLAG_CONFIGDESC?MF_CHECKED:MF_UNCHECKED);
 if (mask&CFLAG_ENUM_NAME) CheckMenuItem(ghMainMenu,19,Config.flags&CFLAG_ENUM_NAME?MF_CHECKED:MF_UNCHECKED);
 if (mask&CFLAG_ENUM_GUID) CheckMenuItem(ghMainMenu,20,Config.flags&CFLAG_ENUM_GUID?MF_CHECKED:MF_UNCHECKED);
 if (mask&(CFLAG_CONFIGDESC|CFLAG_ENUM_NAME|CFLAG_ENUM_GUID)) RefreshTree();
}

//*****************************************************************************
// USBView_OnInitDialog()
//*****************************************************************************
static BOOL USBView_OnCreate(HWND Wnd, LPCREATESTRUCT cs) {
 HFONT                           hFont;
 HIMAGELIST                      himl;
 DEV_BROADCAST_DEVICEINTERFACE   broadcastInterface;
 SP_CLASSIMAGELIST_DATA cid;
 int iComputer, iUSB;	// image indices

 ghTreeWnd = CreateWindowEx(WS_EX_CLIENTEDGE,WC_TREEVIEW,T(""),
		WS_VISIBLE|WS_CHILD|WS_TABSTOP|TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT,
		0,0,0,0,Wnd,(HMENU)1,ghInstance,NULL);
 ghEditWnd = CreateWindowEx(WS_EX_CLIENTEDGE,WC_EDIT,T(""),
		WS_VISIBLE|WS_CHILD|WS_TABSTOP|WS_HSCROLL|WS_VSCROLL|ES_MULTILINE|ES_READONLY,
		0,0,0,0,Wnd,(HMENU)2,ghInstance,NULL);
 ghStatusWnd=CreateWindowEx(0,STATUSCLASSNAME,T(""),
		WS_VISIBLE|WS_CHILD|SBARS_SIZEGRIP,
		0,0,0,0,Wnd,(HMENU)3,ghInstance,NULL);

 ghMainMenu= GetMenu(Wnd);

    // Register to receive notification when a USB device is plugged in.
 broadcastInterface.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
 broadcastInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
 broadcastInterface.dbcc_classguid=GUID_CLASS_USB_DEVICE;
 gNotifyDevHandle = RegisterDeviceNotification(Wnd,&broadcastInterface,DEVICE_NOTIFY_WINDOW_HANDLE);
    // Now register for Hub notifications.
 broadcastInterface.dbcc_classguid=GUID_CLASS_USBHUB;
 gNotifyHubHandle = RegisterDeviceNotification(Wnd,&broadcastInterface,DEVICE_NOTIFY_WINDOW_HANDLE);
    //end add


    //added
 himl=ImageList_Create(16,16,ILC_MASK,6,1);
 if (!himl) OOPS();
 
 cid.cbSize=sizeof(cid);
 if (!SetupDiGetClassImageList(&cid)) OOPS();
 if (!SetupDiGetClassImageIndex(&cid,(LPGUID)&GUID_DEVCLASS_COMPUTER,&iComputer)) OOPS();
 if (!SetupDiGetClassImageIndex(&cid,(LPGUID)&GUID_DEVCLASS_USB,&iUSB)) OOPS();

 ImageList_AddIcon(himl,ImageList_GetIcon(cid.ImageList,iComputer,0));	// Index 0: Tree root (Monitor)
 ImageList_AddIcon(himl,ImageList_GetIcon(cid.ImageList,iUSB,0));	// Index 1: Host Controller
 ImageList_AddIcon(himl,LoadImage(ghInstance,MAKEINTRESOURCE(1),IMAGE_ICON,16,16,LR_SHARED));	// Index 2: Port without device
 ImageList_AddIcon(himl,LoadImage(ghInstance,MAKEINTRESOURCE(2),IMAGE_ICON,16,16,LR_SHARED));	// Index 3: Port with device
 ImageList_AddIcon(himl,LoadImage(ghInstance,MAKEINTRESOURCE(3),IMAGE_ICON,16,16,LR_SHARED));	// Index 4: Port's device is a HUB
 ImageList_AddIcon(himl,LoadImage(0,IDI_WARNING,IMAGE_ICON,16,16,LR_SHARED));		// Index 5: Error
 
 SetupDiDestroyClassImageList(&cid);

 TreeView_SetImageList(ghTreeWnd,himl,TVSIL_NORMAL);
 TreeView_SetIndent(ghTreeWnd,0);	// less waste of space?
    // end add

 ConfigChanged(0xFF);

 if (!ghMainMenu) OOPS();

 hFont=CreateFont(13,8,0,0,400,0,0,0,0,1,2,1,49,T("Courier"));
 SendMessage(ghEditWnd,WM_SETFONT,(WPARAM)hFont,0);
 RefreshTree();
 return TRUE;
}

//*****************************************************************************
// USBView_OnClose()
//*****************************************************************************
static void USBView_OnClose(HWND Wnd) {
 WINDOWPLACEMENT wp;
 wp.length=sizeof(wp);
 if (GetWindowPlacement(Wnd,&wp)) {
  Config.WinPos.left  =(short)wp.rcNormalPosition.left;
  Config.WinPos.top   =(short)wp.rcNormalPosition.top;
  Config.WinPos.right =(short)wp.rcNormalPosition.right;
  Config.WinPos.bottom=(short)wp.rcNormalPosition.bottom;
  Config.showCmd=wp.showCmd;
 }
 UnregisterDeviceNotification(gNotifyDevHandle);
 UnregisterDeviceNotification(gNotifyHubHandle);
 DestroyTree();
 PostQuitMessage(0);
}

//*****************************************************************************
// USBView_OnCommand()
//*****************************************************************************
static void USBView_OnCommand(HWND Wnd, int id, HWND hwndCtl, UINT codeNotify) {

 switch (id) {
  case 17:
   Config.flags^=CFLAG_AUTOREFRESH;
   ConfigChanged(CFLAG_AUTOREFRESH);
  break;

  case 18:
   Config.flags^=CFLAG_CONFIGDESC;
   ConfigChanged(CFLAG_CONFIGDESC);
  break;

  case 19:
   Config.flags^=CFLAG_ENUM_NAME;	// then ensure one enumeration flag is ON
   if (!(Config.flags&(CFLAG_ENUM_NAME|CFLAG_ENUM_GUID))) Config.flags|=CFLAG_ENUM_GUID;
   ConfigChanged(CFLAG_ENUM_NAME|CFLAG_ENUM_GUID);
  break;

  case 20:
   Config.flags^=CFLAG_ENUM_GUID;	// then ensure one enumeration flag is ON
   if (!(Config.flags&(CFLAG_ENUM_NAME|CFLAG_ENUM_GUID))) Config.flags|=CFLAG_ENUM_NAME;
   ConfigChanged(CFLAG_ENUM_NAME|CFLAG_ENUM_GUID);
  break;

  case 9:
   DialogBox(ghInstance,MAKEINTRESOURCE(9),Wnd,AboutDlgProc);
  break;

  case 7:
   SendMessage(Wnd,WM_CLOSE,0,0);
  break;

  case 8:
   RefreshTree();
  break;
 }
}

//*****************************************************************************
// USBView_OnLButtonDown()
//*****************************************************************************
static void USBView_OnLButtonDown(HWND Wnd, BOOL fDoubleClick, int  x, int  y, UINT keyFlags) {
 gbButtonDown = TRUE;
 SetCapture(Wnd);
}

//*****************************************************************************
// USBView_OnLButtonUp()
//*****************************************************************************
static void USBView_OnLButtonUp(HWND Wnd, int x, int y, UINT keyFlags) {
 gbButtonDown = FALSE;
 ReleaseCapture();
}

//*****************************************************************************
// USBView_OnMouseMove()
//*****************************************************************************
void USBView_OnMouseMove(HWND hWnd, int x, int y, UINT keyFlags) {
 SetCursor(ghSplitCursor);
 if (gbButtonDown) ResizeWindows(TRUE, x);
}

//*****************************************************************************
// USBView_OnSize();
//*****************************************************************************
static void USBView_OnSize(HWND hWnd, UINT state, int cx, int cy) {
 ResizeWindows(FALSE, 0);
}

//*****************************************************************************
// USBView_OnNotify()
//*****************************************************************************
static LRESULT USBView_OnNotify(HWND hWnd, int DlgItem, LPNMHDR lpNMHdr) {
 if (lpNMHdr->code == TVN_SELCHANGED) {
  HTREEITEM hTreeItem = ((NM_TREEVIEW *)lpNMHdr)->itemNew.hItem;
  if (hTreeItem) {
   UpdateEditControl(ghEditWnd,ghTreeWnd,hTreeItem);
  }
 }
 return 0;
}

//*****************************************************************************
// USBView_OnDeviceChange()
//*****************************************************************************
static BOOL USBView_OnDeviceChange(HWND hwnd, UINT uEvent, DWORD dwEventData) {
 if (Config.flags&CFLAG_AUTOREFRESH) switch (uEvent) {
  case DBT_DEVICEARRIVAL:
  case DBT_DEVICEREMOVECOMPLETE: RefreshTree(); break;
 }
 return TRUE;
}

//*****************************************************************************
// MainWndProc()
//*****************************************************************************
static INT_PTR CALLBACK MainWndProc(HWND Wnd,UINT Msg,WPARAM wParam,LPARAM lParam) {
 switch (Msg) {
  HANDLE_MSG(Wnd, WM_CREATE,		USBView_OnCreate);
  HANDLE_MSG(Wnd, WM_CLOSE,		USBView_OnClose);
  HANDLE_MSG(Wnd, WM_COMMAND,		USBView_OnCommand);
  HANDLE_MSG(Wnd, WM_LBUTTONDOWN,	USBView_OnLButtonDown);
  HANDLE_MSG(Wnd, WM_LBUTTONUP,		USBView_OnLButtonUp);
  HANDLE_MSG(Wnd, WM_MOUSEMOVE,		USBView_OnMouseMove);
  HANDLE_MSG(Wnd, WM_SIZE,		USBView_OnSize);
  HANDLE_MSG(Wnd, WM_NOTIFY,		USBView_OnNotify);
  HANDLE_MSG(Wnd, WM_DEVICECHANGE,	USBView_OnDeviceChange);
 }
 return DefWindowProc(Wnd,Msg,wParam,lParam);
}

//*****************************************************************************
// AddLeaf()
//*****************************************************************************
extern HTREEITEM AddLeaf(HTREEITEM hTreeParent, LPARAM lParam, int iImage, LPCSTR lpszText,...) {
 TCHAR           szBuffer[1024];
 va_list         list;
 TV_INSERTSTRUCT tvins;
 HTREEITEM       hti;

    // added for tree view icons
 PUSB_NODE_CONNECTION_INFORMATION_EX ConnectInfo = NULL;

 if (lParam) ConnectInfo = ((PUSBDEVICEINFO)lParam)->ConnectionInfo;

    // end add

 va_start(list, lpszText);
#ifdef UNICODE
 {WCHAR t[256];
  MultiByteToWideChar(CP_ACP,0,lpszText,-1,t,elemof(t));
  wvsprintf(szBuffer,t,list);
 }
#else
 wvsprintf(szBuffer,lpszText,list);
#endif

 ZeroMemory(&tvins,sizeof(tvins));
    // Set the parent item
 tvins.hParent = hTreeParent;
 tvins.hInsertAfter = TVI_LAST;
    // pszText and lParam members are valid
 tvins.item.mask = TVIF_TEXT | TVIF_PARAM;
    // Set the text of the item.
 tvins.item.pszText = szBuffer;
    // Set the user context item
 tvins.item.lParam = lParam;
    // Add the item to the tree-view control.
 hti = TreeView_InsertItem(ghTreeWnd, &tvins);
            // added
 tvins.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
 tvins.item.hItem = hti;
 tvins.item.iImage = tvins.item.iSelectedImage = iImage;
/*
 if (!ConnectInfo) {
  if (!(lstrcmpA("My Computer",lpszText))) {
   tvins.item.iImage = tvins.item.iSelectedImage = giComputer;
  }else{
            //it is the host controller
  }

 }else{
  if (ConnectInfo->DeviceIsHub) { // Device is a Hub!!
   tvins.item.iImage = tvins.item.iSelectedImage = giHub;
  }
  if (!ConnectInfo->CurrentConfigurationValue) {
   tvins.item.iImage = tvins.item.iSelectedImage = giBadDevice;
   if (NoDeviceConnected == ConnectInfo->ConnectionStatus) { // Empty Port
    tvins.item.iImage = tvins.item.iSelectedImage = giNoDevice;
   }
  }
 }
*/
 TreeView_SetItem(ghTreeWnd, &tvins.item);
 return hti;
}

//*****************************************************************************
// CreateMainWindow()
//*****************************************************************************
static BOOL CreateMainWindow() {
 RECT rc;
 WNDCLASSEX wc;
 TCHAR title[64];
 ZeroMemory(&wc,sizeof(wc));
 wc.cbSize=sizeof(wc);
 wc.style=CS_DBLCLKS;
 wc.lpszClassName=T("USBVIEW");
 wc.lpszMenuName=MAKEINTRESOURCE(1);
 wc.lpfnWndProc=MainWndProc;
 wc.hIcon=LoadIcon(ghInstance,MAKEINTRESOURCE(1));
 wc.hbrBackground=(HBRUSH)(COLOR_MENU+1);
 
 wc.hIconSm=LoadImage(ghInstance,MAKEINTRESOURCE(1),IMAGE_ICON,
   GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),LR_SHARED);
 RegisterClassEx(&wc);

 InitCommonControls();
 LoadString(ghInstance,1,title,elemof(title));
 ghMainWnd = CreateWindowEx(0,T("USBVIEW"),title,WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN,
   CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
   0,0,ghInstance,NULL);
// ghMainWnd = CreateDialog(ghInstance,
//   MAKEINTRESOURCE(1),
//   NULL,
//   MainDlgProc);
 if (!ghMainWnd) {
  OOPS();
  return FALSE;
 }
 if (Config.showCmd) {
  WINDOWPLACEMENT wp;
  wp.length=sizeof(wp);
  if (GetWindowPlacement(ghMainWnd,&wp)) {
   wp.rcNormalPosition.left  =Config.WinPos.left;
   wp.rcNormalPosition.top   =Config.WinPos.top;
   wp.rcNormalPosition.right =Config.WinPos.right;
   wp.rcNormalPosition.bottom=Config.WinPos.bottom;
//   wp.showCmd=Config.showCmd;
   SetWindowPlacement(ghMainWnd,&wp);
  }
 }else{
  GetWindowRect(ghMainWnd, &rc);
  Config.BarLocation = (short)((rc.right - rc.left) / 3);
 }
// ResizeWindows(FALSE, 0);
 ShowWindow(ghMainWnd,Config.showCmd?Config.showCmd:SW_SHOWDEFAULT);
 return TRUE;
}

//*****************************************************************************
// WinMain()
//*****************************************************************************
void WINAPI WinMainCRTStartup() {
 MSG	msg;
 HACCEL	hAccel;
 WORD	ver;

 ghInstance = GetModuleHandle(NULL);
 ghSplitCursor = LoadCursor(0,IDC_SIZEWE);
 if (!ghSplitCursor) OOPS();
 hAccel = LoadAccelerators(ghInstance,MAKEINTRESOURCE(1));
 if (!hAccel) OOPS();

 Config.flags=0x0F;
 ver=(WORD)GetVersion();
 ver=ver<<8|ver>>8;	// swap bytes: major = HIGH, minor = LOW
 if (ver>=0x501) Config.flags=0x0B;	// with WinXP, remove EnumName method by default
 LoadConfig();				// ... otherwise, two trees will appear
 UsbIdsLoad(T("usb.ids"));
 if (!CreateTextBuffer()) return;
 if (!CreateMainWindow()) return;

 while (GetMessage(&msg,0,0,0)) {
  if (TranslateAccelerator(ghMainWnd,hAccel,&msg)) continue;
  if (IsDialogMessage(ghMainWnd,&msg)) continue;	// handle TAB key
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }
 DestroyTextBuffer();
 SaveConfig();
 CHECKFORLEAKS();
 ExitProcess((UINT)msg.wParam);
}
Detected encoding: ASCII (7 bit)2