Структура сети
Для того чтобы просмотреть доступные в вашей сети компьютеры, нужно воспользоваться сетевым окружением. Но что, если вам нужно в своей программе сделать просмотр сети? Это очень просто. Сейчас я продемонстрирую программу, с помощью которой можно будет в виде дерева увидеть все компьютеры в сети и их открытые ресурсы.
Создайте новое MFC-приложение в Visual C++ и назовите проект NetNeighbour. В Мастере создания приложений, в разделе Application Type выберите Dialog based, а в разделе Advanced Features — Windows sockets. На жмите кнопку Finish, чтобы среда разработки создала необходимый шаблон приложения.
Прежде чем приступать к программированию, необходимо оформить окно будущей программы. Откройте в редакторе ресурсов диалоговое окно IDD_NETNEIGHBOUR_DIALOG. Растяните по всей свободной поверхности компонент Tree Control ( 4.2).
Чтобы можно было работать с этим компонентом, щелкните по нему правой кнопкой мышки. В появившемся меню выберите пункт Add variable, а в поле Variable name укажите m_NetTree. Эта переменная понадобится для добавления в меню новых пунктов.
4.2. Использование компонента Tree Control
Теперь все готово для рассмотрения исходного кода. Перейдите в файл NetNeighbourDlg.cpp. Здесь найдите функцию OnInitDialog, которая вызывается во время инициализации окна. В этот момент необходимо создать корневой элемент дерева. Это должно происходить следующим образом:
m_hNetworkRoot = InsertTreeItem(TVI_ROOT, NULL, "My Net", DRIVE_RAMDISK +1);
В переменной m_hNetworkRoot сохраняется результат работы функции InsertTreeItem.
Придется несколько раз использовать такой же код для добавления элементов, и чтобы в одном модуле не повторять одни и те же действия, я все оформил отдельной функцией (листинг 4.2).
Листинг 4.2. Добавление нового элемента в дерево сети |
Теперь программа выглядит должным образом и создает корневой элемент, но пока без поиска в сети. Когда программа запущена, и пользователь щелкнет мышкой по элементу дерева, нужно найти все, что есть доступного в сети, относящееся к этому элементу.
Для этого надо написать обработчик события ITEMEXPANDING и в нем производить поиск. Перейдите в редактор ресурсов и выделите компонент Tree Control . В окне Properties щелкните по кнопке Control Events, и вы увидите все события, которые может генерировать выделенный компонент. Щелкните напротив события TVN_ITEMEXPANDING и в выпадающем списке выберите пункт Add, чтобы добавить обработчик события. Код, который должен быть в этом обработчике, приведен в листинге 4.3.
Листинг 4.3. Обработчик события TVN_ITEMEXPANDING |
CWaitCursor CursorWaiting; ASSERT(pNMTreeView); ASSERT(pResult);
if (pNMTreeView-action == 2) { CString sPath = GetItemPath(pNMTreeView-itemNew.hItem);
if(!m_NetTree.GetChildItem(pNMTreeView-itemNew.hItem)) { EnumNetwork(pNMTreeView-itemNew.hItem); if( m_NetTree.GetSelectedItem( ) != pNMTreeView- itemNew.hItem) m_NetTree.SelectItem(pNMTreeView-itemNew.hItem); } }
*pResult = 0; }
Здесь у элемента, который в данный момент пытаются открыть, проверяется наличие дочерних элементов и организуется их поиск. Для этого вызывается функция EnumNetwork, которую можно увидеть в листинге 4.4.
Листинг 4.4. Функция EnumNetwork для просмотра сети |
NETRESOURCE *const pNetResource = (NETRESOURCE *) (m_NetTree.GetItemData(hParent));
DWORD dwResult; HANDLE hEnum; DWORD cbBuffer = 16384; DWORD cEntries = 0xFFFFFFFF; LPNETRESOURCE lpnrDrv; DWORD i; dwResult = WNetOpenEnum(pNetResource ? RESOURCE_GLOBALNET : RESOURCE_CONTEXT, RESOURCETYPE_ANY, 0, pNetResource ? pNetResource: NULL, hEnum );
if (dwResult != NO_ERROR) { return false; }
do { lpnrDrv = (LPNETRESOURCE) GlobalAlloc(GPTR, cbBuffer); dwResult = WNetEnumResource(hEnum, cEntries, lpnrDrv, cbBuffer); if (dwResult == NO_ERROR) { for(i = 0; icEntries; i++) { CString sNameRemote = lpnrDrv[i].lpRemoteName; int nType = 9; if(sNameRemote.IsEmpty()) { sNameRemote = lpnrDrv[i].lpComment; nType = 8; } if (sNameRemote.GetLength() 0 sNameRemote[0] == _T('\\')) sNameRemote = sNameRemote.Mid(1); if (sNameRemote.GetLength() 0 sNameRemote[0] == _T('\\')) sNameRemote = sNameRemote.Mid(1);
if (lpnrDrv[i].dwDisplayType == RESOURCEDISPLAYTYPE_SHARE) { int nPos = sNameRemote.Find( _T('\\')); if(nPos = 0) sNameRemote = sNameRemote.Mid(nPos+1); InsertTreeItem(hParent, NULL, sNameRemote, DRIVE_NO_ROOT_DIR); } else { NETRESOURCE* pResource = new NETRESOURCE; ASSERT(pResource); *pResource = lpnrDrv[i]; pResource-lpLocalName = MakeDynamic(pResource-lpLocalName); pResource-lpRemoteName = MakeDynamic(pResource-lpRemoteName); pResource-lpComment = MakeDynamic(pResource-lpComment); pResource-lpProvider = MakeDynamic(pResource-lpProvider); InsertTreeItem(hParent, pResource, sNameRemote, pResource-dwDisplayType+7); } bGotChildren = true; } } GlobalFree((HGLOBAL)lpnrDrv); if (dwResult != ERROR_NO_MORE_ITEMS) break; } while (dwResult != ERROR_NO_MORE_ITEMS);
WNetCloseEnum(hEnum); return bGotChildren; }
Логика поиска сетевых ресурсов достаточно проста. Для начала нужно открыть поиск функцией WNetOpenEnum, которая выглядит следующим образом:
DWORD WNetOpenEnum( DWORD dwScope, // scope of enumeration DWORD dwType, // resource types to list DWORD dwUsage, // resource usage to list LPNETRESOURCE lpNetResource, // pointer to resource structure LPHANDLE lphEnum // pointer to enumeration handle buffer );
Функция открывает перечисление сетевых устройств в локальной сети. Рассмотрим передаваемые ей параметры:
dwScope — ресурсы, включаемые в перечисление. Возможны комбинации следующих значений:
RESOURCE_GLOBALNET — все ресурсы сети;
RESOURCE_CONNECTED — подключенные ресурсы;
RESOURCE_REMEMBERED — запомненные ресурсы;
dwType — тип ресурсов, включаемых в перечисление. Возможны комбинации следующих значений:
RESOURCETYPE_ANY — все ресурсы сети;
RESOURCETYPE_DISK — сетевые диски;
RESOURCETYPE_PRINT — сетевые принтеры;
dwUsage — использование ресурсов, включаемых в перечисления. Возможны следующие значения:
0 — все ресурсы сети;
RESOURCEUSAGE_CONNECTABLE — подключаемые;
RESOURCEUSAGE_CONTAINER — контейнерные;
lpNetResource — указатель на структуру NETRESOURCE. Если этот параметр равен нулю, то перечисление начинается с самой верхней ступени иерархии сетевых ресурсов. Ноль ставится для того, чтобы получить самый первый ресурс. Потом я передаю в качестве этого параметра указатель на уже найденный ресурс. Тогда перечисление начнется с него и продолжится дальше. Так я повторяю, пока не найдутся все ресурсы;
lphEnum — указатель, который понадобится В функции WnetEnumResource.
Теперь нужно рассмотреть структуру NETRESOURCE:
typedef struct _NETRESOURCE { DWORD dwScope; DWORD dwType; DWORD dwDisplayType; DWORD dwUsage; LPTSTR lpLocalName; LPTSTR lpRemoteName; LPTSTR lpComment; LPTSTR lpProvider; } NETRESOURCE;
Что такое dwScope, dwType и dwUsage, вы уже знаете. А вот остальные рассмотрим подробнее:
dwDisplayType — способ отображения ресурса:
RESOURCEDISPLAYTYPE_DOMAIN — это домен;
RESOURCEDISPLAYTYPE_GENERIC — нет значения;
RESOURCEDISPLAYTYPE_SERVER — сервер;
RESOURCEDISPLAYTYPE_SHARE — разделяемый ресурс;
lpLocalName — локальное имя;
lpRemoteName — удаленное имя;
lpComment — комментарий;
lpProvider — хозяин ресурса. Параметр может быть равен нулю, если хозяин неизвестен.
Теперь можно переходить к следующей функции:
DWORD WNetEnumResource( HANDLE hEnum, // handle to enumeration LPDWORD lpcCount, // pointer to entries to list LPVOID lpBuffer, // pointer to buffer for results LPDWORD lpBufferSize // pointer to buffer size variable );
Параметры функции WnetEnumResource:
hEnum — указатель на возвращенное функцией wNetopenEnum значение;
lpcCount — максимальное количество возвращаемых значений. Не стесняйтесь, ставьте 2000. Если вы зададите 0xFFFFFFFF, то перечислятся все ресурсы. После выполнения функция передаст сюда фактическое число найденных ресурсов;
lpBuffer — указатель на буфер, в который будет помещен результат;
lpBuffersize — размер буфера.
После окончания перечисления вызывается функция WNetCloseEnum, которая закрывает начатое функцией WNetOpenEnum перечисление сетевых ресурсов. В качестве единственного параметра нужно передать указатель на возвращенное функцией WNetOpenEnum значение.
Это все, что касается поиска открытых сетевых ресурсов. Осталось только сделать одно замечание. Функция поиска WNetOpenEnum и соответствующие ей структуры находятся в библиотеке mpr.lib, которая по умолчанию не линкуется к проекту. Чтобы собрать проект без ошибок, необходимо подключить эту библиотеку. Для этого щелкните правой кнопкой мышки по имени проекта в окне Solution Explorer и в появившемся меню выберите пункт Properties. Перед вами откроется окно свойств, в котором надо перейти в раздел Configuration Properties/Linker/Input. Здесь в строке Additional Dependencies напишите имя библиотеки mpr.lib ( 4.3).
4.3. Добавление библиотеки, содержащей функцию WNetOpenEnum
Примечание |
Исходный код примера, описанного в этом разделе, вы можете найти на компакт - диске в каталоге \Demo\Chapter4\NetNeighbour. |