Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Получение имени устройства


Автор: Sonechka 6.2.2012, 11:19
Доброго времени суток, уважаемые! Имеется у меня исходник на Delphi, который выводит список всех устройств в TreeView, как в "Диспетчере устройств", дополнительно выводит в  ListBox сетевые устройства и дисковые накопители. Ввиду того, что мной изучался Delphi (давненько по программистским меркам это и было), а он сейчас (да и тогда) был не актуален, появилось у меня желание овладеть C++ (2 недели уже овладеваю). Начал не с самых азов (все языки ведь похожи). Начал с простого, стал переписывать более-менее понятный мне код. Для начала, переписал кнопочки для вывода сетевых и дисковых устройств в соответствующие ListBox'ы, но в обоих обработчиках события OnClick есть функция GetDeviceName (понятно из названия зачем она), а перевести её не могу. Ниже привожу пример её, написанной на Delphi (вроде она простая, понять не трудно будет):

Код

function GetDeviceName(PnPHandle: HDEVINFO; const DevData: TSPDevInfoData): string;
var
  BytesReturned: DWORD;
  RegDataType: DWORD;
  Buffer: array [0..256] of CHAR;
begin
  BytesReturned := 0;
  RegDataType := 0;
  Buffer[0] := #0;
  SetupDiGetDeviceRegistryProperty(PnPHandle, DevData, SPDRP_FRIENDLYNAME,
    RegDataType, PByte(@Buffer[0]), SizeOf(Buffer), BytesReturned);
  Result := Buffer;
  if Result<>'' then exit;
  BytesReturned := 0;
  RegDataType := 0;
  Buffer[0] := #0;
  SetupDiGetDeviceRegistryProperty(PnPHandle, DevData, SPDRP_DEVICEDESC,
    RegDataType, PByte(@Buffer[0]), SizeOf(Buffer), BytesReturned);
  Result:=Buffer;
end;


Собственно о том, что может быть непонятно в коде (сам этого не помню, специально искал):
#0 - признак окончания строки;
@ - адрес переменной;

Дополнительно привожу пример обработчика для вывода дисковых устройств (для сетевых всё то же самое, только одна переменная иначе названа (А то подумаете, что за халявой пришёл smile). А я не за халявой, а чтобы показали и объяснили smile Я учится хочу , но без помощи не получается.

Код

                 /* ------------------ ДЛЯ ДИСКОВЫХ УСТРОЙСТВ ------------------ */
        private: System::Void button7_Click(System::Object^  sender, System::EventArgs^  e) {
                // Импровизированный раздел описания переменных (НАД ЭТИМ НЕ СМЕЙТЕСЬ, ПРОСТО МНЕ ТАК УДОБНО, Я К ДЕЛЬФЯМ ПРИВЫКШИЙ)
                HDEVINFO DrivePnPHandle; // Этот, да и все остальные типы данных описаны соответственно в setupapi.h
                DWORD DeviceNumber;
                PSP_DEVINFO_DATA DevData;
                PSP_DEVICE_INTERFACE_DATA  DeviceInterfaceData;
                BOOL RES; 
                
                // Раздел кода программы
                DrivesListBox->Items->Clear();
                DrivePnPHandle = SetupDiGetClassDevs((LPGUID)&GUID_DEVCLASS_DISKDRIVE, 0, 0, DIGCF_PRESENT /*or DIGCF_PROFILE*/); // GUID_DEVCLASS_DISKDRIVE - чтобы работало #include "devguid.h" написал.
                if  (DrivePnPHandle = INVALID_HANDLE_VALUE) {
                    return;
                };
                DeviceNumber = 0;
                do { // В исходнике использовался дельфийский цикл repeat - until not, как мне кажется do - while - альтернатива в C++
                    DeviceInterfaceData->cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
                    DevData->cbSize = sizeof(SP_DEVINFO_DATA);
                    RES = SetupDiEnumDeviceInfo(DrivePnPHandle, DeviceNumber, DevData);
                    if (RES) {
                        DrivesListBox->Items->Add(GetDeviceName(DrivePnPHandle, DevData)); // Функция в процессе написания...
                        DeviceNumber++;
                    };
                }
                while (RES);
                SetupDiDestroyDeviceInfoList(DrivePnPHandle);
                DrivesListBox->Enabled = true;
         }


Для сетевых код такой же, только вместо DrivePnPHandle - NetPnPHandle, ну и ListBox не DrivesListBox, а NetDevicesListBox.

Заодно проверите этот код для кнопки, но компилятор только ругается на GetDeviceName.

Показывать то, что я навоял в процессе переписывания GetDeviceName стыдно, но вот:

Код

        int GetDeviceName(HDEVINFO PnPHandle, const PSP_DEVINFO_DATA DevData) {
            // Импровизированный раздел описания переменных
            PDWORD BytesReturned = 0;
            PDWORD RegDataType = 0;
            CHAR Buffer[257];

            // Раздел кода программы
            Buffer[0] = #0; // Тут конечно не так должно быть, не нашёл как обзывается признак конца строки
            SetupDiGetDeviceRegistryProperty(PnPHandle, DevData, SPDRP_FRIENDLYNAME, RegDataType, PBYTE(@Buffer[0]), sizeof(Buffer), BytesReturned);
            return Buffer;
            if (Buffer!="") { 
                return;    // Тут тоже не так
            };
            BytesReturned = 0;
            RegDataType = 0;
            Buffer[0] = #0; // Тут тоже не так
            SetupDiGetDeviceRegistryProperty(PnPHandle, DevData, SPDRP_FRIENDLYNAME, RegDataType, PBYTE(@Buffer[0]), sizeof(Buffer), BytesReturned);
            return Buffer;
        }


В общем, хочу чтобы вы мне показали и объяснили (прошу не отсылать к литературе, лучше понимаю на примерах).

Автор: xvr 6.2.2012, 14:20
У вас почти все правильно. Должно быть так -
Код

        AnsiString GetDeviceName(HDEVINFO PnPHandle, const PSP_DEVINFO_DATA DevData) {
            // Импровизированный раздел описания переменных
            DWORD BytesReturned = 0; // P* не нужен
            DWORD RegDataType = 0;
            CHAR Buffer[257];
            // Раздел кода программы
            Buffer[0] = 0; // # не нужен. В С++ символы и целые числа - одно и тоже
            SetupDiGetDeviceRegistryProperty(PnPHandle, DevData, SPDRP_FRIENDLYNAME, &RegDataType, Buffer, sizeof(Buffer), &BytesReturned);           
            if (Buffer[0]!="") return Buffer; // Непустая строка имеет вервым символом не 0
            BytesReturned = 0;
            RegDataType = 0;
            Buffer[0] = 0;
            SetupDiGetDeviceRegistryProperty(PnPHandle, DevData, SPDRP_DEVICEDESC, &RegDataType, Buffer, sizeof(Buffer), &BytesReturned);
            return Buffer;
        }
Но и тут есть много лишнего - переменные BytesReturned и RegDataType можно вообще убрать, а вместо ссылко на них передать в SetupDiGetDeviceRegistryProperty NULL


Автор: feodorv 12.2.2012, 05:12
Девятая строка кода выглядит как-то неправильно ;)
Цитата(xvr @  6.2.2012,  14:20 Найти цитируемый пост)
            if (Buffer[0]!="") return Buffer; // Непустая строка имеет вервым символом не 0


Автор: xvr 12.2.2012, 13:24
Цитата(feodorv @  12.2.2012,  05:12 Найти цитируемый пост)
Девятая строка кода выглядит как-то неправильно ;)

Угу, посыпаю голову пеплом  smile Там доджен быть просто  0
Код

if (Buffer[0]!=0) return Buffer; // Непустая строка имеет вервым символом не 0

Автор: maxfox 15.2.2012, 14:02
Подскажите пожалуйста, почему не получается скомпилировать описанный выше код?
На сам код студия не ругается, но выдаёт кучу ошибок во включаемых файлах setupapi.h, и prsht.h и spapidef.h.
При этом в проект я включил файл: #include <setupapi.h>
Я предполагаю, что необходимо ещё включить какие то заголовочные файлы или библиотеки, но не знаю, какие. Подскажите пожалуйста!

Автор: xvr 15.2.2012, 17:14
Цитата(maxfox @  15.2.2012,  14:02 Найти цитируемый пост)
Подскажите пожалуйста, почему не получается скомпилировать описанный выше код?

Судя по наличию типа AnsiString код предназначался не для студии, а для Bulder'а

Цитата(maxfox @  15.2.2012,  14:02 Найти цитируемый пост)
На сам код студия не ругается, но выдаёт кучу ошибок во включаемых файлах setupapi.h, и prsht.h и spapidef.h

windows.h в начале включен?

Автор: maxfox 16.2.2012, 13:46
xvr, 
Мне кажется, что судя по синтаксису заголовка процедуры:
Код

        private: System::Void button7_Click(System::Object^  sender, System::EventArgs^  e) {

-это студия и проект Windows Forms. Но. могу и ошибаться.

windows.h - не было. когда включил, все предыдущие ошибки ушли, но появилась другая:
В проекте пользуюсь NET библиотекой, чтобы взять путь, от куда запущена программа: System::IO::Directory::GetCurrentDirectory();
однако в библиотеке WinBase.h, есть такая строчка: #define GetCurrentDirectory  GetCurrentDirectoryW
На сколько я понял, эта строчка переопределяет(или что то в этом роде) функцию из NET - библиотеке.
В общем. выдаёт такую ошибку: error C2039: GetCurrentDirectoryW: не является членом "System::IO::Directory"
Я не понимаю сути этой ошибки, т.к. у меня в тексте нигде нет переменной:  GetCurrentDirectoryW

Автор: xvr 16.2.2012, 14:19
Цитата(maxfox @  16.2.2012,  13:46 Найти цитируемый пост)
Мне кажется, что судя по синтаксису заголовка процедуры:

Этот исходник да - на .NET (C++/CLI)

Цитата(maxfox @  16.2.2012,  13:46 Найти цитируемый пост)
windows.h - не было. когда включил, все предыдущие ошибки ушли, но появилась другая:

Это проблема с совместимостью .NET и WinAPI. windows.h расчитана на чистый С, и она явно передралась с .NET заголовками.

В WinAPI #define GetCurrentDirectory  GetCurrentDirectoryW - это стандартный способ настройки приложения под Unicode.
Советую не рожать помесь ужа с ежём, а писать либо на C# либо на чистом С++. С++/CLI - это порождение проблем совместимости, и делать на нем проект с нуля это явный идиотизм и самоубийство  smile 

Автор: GremlinProg 16.2.2012, 15:43
Цитата(maxfox @  16.2.2012,  15:46 Найти цитируемый пост)
В общем. выдаёт такую ошибку: error C2039: GetCurrentDirectoryW: не является членом "System::IO::Directory"

после включения windows.h можно конечно прописать:
Код

#undef GetCurrentDirectory

но не факт, что не появится чего-то другого,

вобщем, это будет куча undef'ов, пока не появятся проблемы с дублированием типов данных и функций ( и это будет финиш ),

p.s.: вот потом посмотришь на эту гору костылей и наверное поймешь, что проще было пользоваться чем-то одним  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)