Visual2000 · Архив статей А.Колесова & О.Павловой

Советы тем, кто программирует на VB & VBA

Андрей Колесов, Ольга Павлова

© 2000, Андрей Колесов, Ольга Павлова
Авторский вариант. Статья была опубликована c незначительной литературной правкой в журнале "КомпьютерПресс" 11/2000, компакт-диск.


Совет 322. Получение даты последней коррекции файла

Для работы иногда полезно получить данные о дате создания или последней коррекции файла. Для этого можно использовать функцию FileDateTime(ИмяФайла), например:

Dim MyDateTime As Date
MyDateTime = FileDateTime("D:\Calc.bas")

С помощью этой же функции можно определить время создания каталога, но следует иметь в виду, что имя каталога в данном случае не должно заканчиваться на обратную косую черту:

MyDateTime = FileDateTime("D:\TMP\")     ' имя каталога задано неверно
MyDateTime = FileDateTime("D:\TMP")      '    имя каталога задано правильно

Но получить дату создания корневого каталога (то есть диска) с использование FileDateTime нельзя.

В начало статьи

Совет 323. Преобразование кода цвета из DOS в Windows

Как известно, в стандартном варианте DOS использовалось 16 цветов, а сейчас в Windows их 16 777 216 (16M). Точнее говоря, такая палитра определяется не операционной системой, а техническими характеристиками мониторов.

Несмотря на такое мощное расширение состава палитры, для решения многих задач бывает удобнее воспользоваться ограниченным количеством цветов. К тому же порой необходимо точно воспроизвести в VB-цвета, которые использовались в DOS (например, при работе с QuickBasic).

В любом случае, возникает задача преобразования кодов цветов из DOS в Windows, которая, казалось бы, легко решается с помощью встроенной функции QBColor:

WindowsColor = QBColor(DosColor)

Однако небольшое исследование показывает, что такой вариант представляет собой весьма приблизительное решение. С помощью следующей конструкции:

For i = 0 To 16
  Debug.Print i; QBColor(i)
Next

можно получить таблицу соответствия кодов DOS и Windows.

Здесь хорошо видны принципы кодирования цветов в DOS и Windows. В обоих случаях цвет задается комбинацией красного, зеленого и синего (Red, Green, Blue). Но в первом варианте используется лишь один разряд (то есть две градации цвета — "есть" или "нет" данной составляющей), а во втором — целый байт (256 градаций). В DOS увеличение цветовой гаммы в два раза достигается наличием четвертого разряда, который задает нормальную или повышенную яркость (но не для каждого компонента, а для всей комбинации).

Казалось бы, на этом можно поставить точку, но из таблицы видно некоторое несоответствие в логике преобразования кода. Почти для всех цветов замена кода выполняется по такому правилу: для нормальной яркости двоичный разряд со значением 1 заменяется на &h80, для повышенной яркости — на &hFF. Но имеется также исключение из этого правила для цветов 7 и 8. Следует обратить внимание и на разные названия, например одинаковых цветов, которые используются в английском и русском вариантах Help. Оказывается, это не неточность перевода, а отражение реального несоответствия цветов в DOS и в Windows после преобразования кодов с помощью QBColor.

Визуальное сравнение изображений палитры из 16 цветов, полученных в среде QB и VB, показало заметное расхождение в красках, поэтому мы выполнили следующий эксперимент:импортировали графический файл с палитрой QB в VB и прочитали RGB-код каждого цвета. По результатам замеров мы сделали свою функцию преобразования кодов цветов из DOS в Windows:

Public Function QBColorMy(DosColor%) As Long
  ' Преобразование    кода из QB (16 цветов) в VB (RGB - 16М)
  ' Значения RGB-кодов получены на основе исследования
  ' 16-цветной палитры, полученной в QB/DOS
  Select Case DosColor
    Case 0: QBColorMy = &H0        '    Black
    Case 1: QBColorMy = &HAA0000   ' Blue
    Case 2: QBColorMy = &HAA00&    '    Green
    Case 3: QBColorMy = &HAAAA00   ' Cyan
    Case 4: QBColorMy = &HAA       ' Red
    Case 5: QBColorMy = &HAA00AA   ' Magenta
    Case 6: QBColorMy = &H55AA&    '    Brown (! нарушение    алгоритма)
    Case 7: QBColorMy = &HAAAAAAA  '    White
    Case 8: QBColorMy = &H555555   ' Gray
    Case 9: QBColorMy = &HFF5555   ' Light Blue
    Case 10: QBColorMy = &H55FF55  '    Light Green
    Case 11: QBColorMy = &HFFFF55  '    Light Cyan
    Case 12: QBColorMy = &H5555FF  '    Light Red
    Case 13: QBColorMy = &HFF55FF  '    Light Magenta
    Case 14: QBColorMy = &H55FFFF  '    Yellow
    Case 15: QBColorMy = &HFFFFFF  '    Bright White
  End Select
End Function

Здесь следует обратить внимание на следующие моменты.

Для нормальной яркости двоичный разряд со значением 1 меняется на &hAA, а для повышенной яркости — на &hFF. Но в последнем случае нулевой двоичный разряд меняется на шестнадцатиричное 55 (в двоичном варианте это выглядит как 01010101).

Как ни странно, код 6 опять отличается от ожидаемой величины (было бы логично увидеть &hAAAA). При этом нужно отметить, что этот код действительно Brown (коричневый) — именно так он называется в QB. В документации VB он именуется как Yellow, что явно не соответствует действительности.

В начало статьи

Совет 324. Как отличить обычные элементы управления от компонентов массива

Для того чтобы найти программным образом на форме элемента управления определенного типа, например TextBox, можно воспользоваться следующим кодом:

Dim ctl As Control
For Each ctl In Controls
  If TypeName(ctl) = "TextBox" Then
    MsgBox ctl.Name & " - это элемент управления   TextBox."
  End If
Next ctl

Но данный вариант не может отличить обычные элементы управления от массивов. Для решения этой проблемы следует воспользоваться такой конструкцией: TypeName(Controls(ctl.Name))

Для обычных элеменов управления она будет выдавать "TextBox", а для компонентов массива — "Object". Программа, которая будет выдавать более точную информацию, может выглядеть следующим образом:

Dim ctl As Control
  For Each ctl In Controls
  ' MsgBox TypeName(ctl)
  If TypeName(ctl) = "TextBox" Then
    If TypeName(Controls(ctl.Name)) = "TextBox" Then
      MsgBox ctl.Name & " - обычный    TextBox"
    Else
      MsgBox "Элемент    массива"    & ctl.Name & _
         "/ Индекс    = " & ctl.Index
    End If
  End If
Next ctl

В начало статьи

Совет 325. Преобразование числа в двоичное представление и наоборот

В VB (и QB) имеются функции для представления целого числа в символьном виде в восьмеричной (Oct) и шестнадцатеричной (Hex) системах счисления:

Print Hex(59)  ' будет напечатано 3B
Print Oct(59)  ' будет напечатано 73

Соответственно можно также использовать специальный вид констант для задания чисел в восьмеричном или шестнадцатеричном виде:

iValue = &H3B   ' = 59 (приставка &h)
iValue = &O73   ' = 59 (приставка &o — второй символ буква "o")

Еще раз хотим обратить внимание на нюанс, связанный с преобразованием типов Integer в Long и наоборот. Проблема заключается в том, что мы имеем дело со знаковыми числами, в которых старший, знаковый разряд должен обрабатываться особым образом. Покажем эту ситуацию на примере шестнадцатеричной константы.

Правило формирования константы &H таково: если в ней указано поле приставки менее пяти символов, то формируется переменная Integer, в противном случае — Long. Если необходимо сразу получить тип Long, то константа должна иметь в конце суффикс &.

Соответственно, вы можете получить такой неожиданный результат:

Const MyConst As Long = &hFFFF
Print Hex$(MyConst)      ' будет напечатано FFFFFFFF

Это объясняется тем, что &hFFFF соответствует значению -1 (Integer). При переводе этого числа в тип Long получается код &hFFFFFFFF. Если же требуется сформировать число типа Long, в котором два старшие байта были нулевыми, то следует указать суффикс &:

Const MyConst As Long = &hFFFF&
Print Hex$(MyConst)      ' будет    напечатано FFFF

Если число задано в виде символьной записи, то его можно преобразовать следующим образом:

MyStr$ = "3B"
MyValue& = Val ("&h" & MyStr$)

Однако довольно часто бывает полезно представлять числа в двоичной системе счисления. Хотя шестнадцатеричный и восьмеричный вид чисел хорошо показывать двоичную структуру числа, все же такое преобразование требует внимания и не гарантирует от ошибок. Даже опытному программисту может понадобиться некоторое время, чтобы записать число, в котором 7-й, 8-й и 10-й разряды (считая справа! — 1011000000) были бы равны единице, а остальные — нулю.

Для решения этой задачи можно использовать функции iBin% и sBin$, приведенные на листинге 325:

Print Hex(iBin%("1011000000")  ' будет напечатано 2C0
Print sBin$(34)                ' будет напечатано   100010

Здесь нужно обратить внимание на то, что значительная часть кода связана с особой обработкой знакового разряда. Мы реализовали эти функции для целой типа Integer, учитывая два обстоятельства.

Именно такой диапазон чисел чаще всего используется для представления в двоичный формат (16 разрядов — это довольно много для визуального восприятия).

Для Long необходимо написать такие функции, чтобы они же по умолчанию работали и для Integer. Все это — из-за автоматического преобразования типов данных при обращении к процедурам и проблем такого преобразования, описанных выше. В этой связи приведем такой пример: строка "1000000000000000" (15 нулей справа) в терминах Integer равна -32768, а Long — +32768. Таким образом, для двух типов данных необходимы разные процедуры. (В VB 7 обещано появление Overloading — возможности использования одного и того же идентификатора для обозначения разных процедур. Выбор нужной процедуры зависит от типа аргумента.)

В начало статьи

Совет 326. Функции с двухсторонним обращением

Довольно часто встречаются случаи парных процедур, которые выполняют связанные как бы прямые и обратные операции. Для удобства работы с ними полезно обозначить один из них идентификатором, при этом конкретная операция должна определяться формой записи:

y = MyFunc(x)
MyFunc(y) = x

Примером такого решения является популярная функция Mid:

Sym$ = Mid$(MyStr$, i, k)   ' выбирает    из строки фрагмент
Mid$(MyStr$, i, k) = Sym$   ' вставляет    фрагмент строку

Обратите внимание, что в первом случае конструкция называется функцией, а во втором — оператором (Statement). Если бы мы писали второй вариант сами, то данная процедура имела бы тип Sub, а ее запись выглядела бы так:

Call MyMid(MyStr$, i, k, Sym$)

Однако если использовать процедуры типа Property, то можно создать пару таких связанных функций одним именем. Например, мы хотим сделать две процедуры, одна из которых должна выбирать младший байт из длинной переменной, а другая — вставить его обратно:

Public Property Get LowByte(LongVar As Long) As Byte
  ' прочитать младший    байт переменной LongVar
  LowByte = LongVar And &HFF
End Property
   
Public Property Let LowByte(LongVar As Long, ByVal NewByte As Byte)
  ' Записать в    младший байт переменной LongVar новое значение NewByte
  LongVar = (LongVar And &HFFFFFF00)    Or NewByte
End Property

Обращение к ним будет выглядеть следующим образом:

Dim LongVar$, ByteVar As Byte
LongVar = &h12345678
ByteVar = LowByte(LongVar)
Print Hex(ByteVar)   ' напечатано    78

ByteVar = &hF1
LowByte(LongVar) = ByteVar
Print Hex(LongVar)   ' напечатано    123456F1

На самом деле такие связанные функции представляют собой созданные выше iBin% и sBin$ (см. Совет 325). Их можно легко преобразовать в пару процедур Property:

Public Property Get MyBin(Source%) As String
  ' Число Source%    в символьную запись двоичного кода
  MyBin = sBin$(Source%)
End Property
   
Public Property Let MyBin(Source%, ByVal NewStr As String)
  ' Символьную    запись двоичного кода NewStr$ в число Source%
  Source% = iBin%(NewStr)
End Property

В этом случае обращение к ним будет выглядеть следующим образом:

MyBin(intBin) = "1011000"
Print Hex(intBin)   ' напечатано 58
StrBin$ = MyBin(intBin)
MsgBox (StrBin)      ' напечатано 101100

В начало статьи

Совет 327. Преобразование величины размера памяти

В составе Internet Explorer имеется библиотека SHLWAPI.DLL (Shell Windowing API), в которую включена функция, преобразующая число байтов в символьную строку типа "1,41 KB" или "1,32 MB". Обращение к этой функции выглядит следующим образом:

Private Declare Function StrFormatByteSize _
  Lib "shlwapi" Alias "StrFormatByteSizeA"    _
  (ByVal dw As Long, ByVal pszBuf As String, _
  ByVal cchbuf As Long) As Long
   
Public Function FormatKB(ByVal Amount As Long) As String
  Dim Buffer As String, i%
  Buffer = Space$(255) ' резервируем буфер
  Call StrFormatByteSize(Amount, Buffer, Len(Buffer))
  i = InStr(Buffer, vbNullChar)
  If i > 1 Then FormatKB = Left$(Buffer, i - 1)
End Function

В начало статьи

Совет 328. Переключение вида заголовков элемента управления ListView

С помощью приведенного ниже кода можно переключать представление заголовков элемента управления ListView из плоского вида в объемное и наоборот:

Private Declare Function GetWindowLong _
   Lib "user32" Alias "GetWindowLongA" _
     (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong _
  Lib "user32" Alias "SetWindowLongA" _
  (ByVal hwnd As Long, ByVal nIndex As Long, _
  ByVal dwNewLong As Long) As Long
Private Declare Function SendMessage _
  Lib "user32" Alias "SendMessageA" _
  (ByVal hwnd As Long, ByVal wMsg As Long, _
  ByVal wParam As Long, lParam As Any) As Long

Private Const GWL_STYLE = -16
Private Const LVM_FIRST = &H1000
Private Const LVM_GETHEADER = LVM_FIRST + 31
Private Const HDS_BUTTONS = &H2

Private Sub Command1_Click()
  Call ToggleHeader(ListView1.hwnd)
End Sub
   
Public Sub ToggleHeader(lsvhWnd As Long)
  Dim hHeader As Long, lStyle As Long
  hHeader = SendMessage(lsvhWnd, LVM_GETHEADER, 0, ByVal 0&)
  lStyle = GetWindowLong(hHeader, GWL_STYLE)
  SetWindowLong hHeader, GWL_STYLE, lStyle Xor HDS_BUTTONS
End Sub

В начало статьи

Совет 329. Генерация динамических HTML-страниц

Полтора года назад мы опубликовали (КомпьютерПресс N 5/99) две статьи, посвященные созданию Web- и DHTML-приложений. Нам удалось создать вполне работоспособные примеры, однако технология разработки показалась нам весьма запутанной. Возможно, создавать динамические HTML-страницы проще с помощью знакомой ASP-технологии? С помощью VB это делается достаточно просто. Ниже приведены содержание ASP-страницы, которое можно сделать с помощью самого простого текстового редактора, и код процедуры, которая используется для генерации HTML-кода.

Тестовая Active Server Page:

<html>
<head></head>
<body>
<h2>Далее будет приведена некоторая информация пользователя </h2>
<%
Dim objRSServer
Set objRSServer = Server.CreateObject("TestASP.Htmlcrt")
objRSServer.WriteSomeThing Response, "Привет    всем!"
%>
</body>
</html>

Программный код (проект TestASP.DLL, модуль класса Htmlcrt):

Public Sub WriteSomeThing _
   (ASPResponse As ASPTypeLibrary.Response, SomeText$)
   With ASPResponse
     .Write "<font size=4 color='#cc3366'>"
     .Write SomeText$
     .Write "</font>"
   End With
End Sub

В данном случае все выглядит достаточно понятным. Сначала мы создали ActiveX DLL с именем TestASP, в модуле класса Htmlcrt которой написали процедуру WhiteSomeThing (она превратилась, таким образом, в метод объекта TestASP.Htmlcrl). При этом для нашей DLL мы сделали ссылку на библиотеку Microsoft Active Server Page Object Library, чтобы получить доступ к объекту Response. Далее мы написали ASP-страницу с тривиальным скриптом — сначала создается объект TestASP.Htmlcrt, а затем следует обращение к его методу WhiteSomeThing с передачей объекта Response и некоторого набора параметров. В данном случае передается текстовая строка, которая выводится в HTML-страницу.

Напомним, что обращение к ASP-странице должно выполняться через Internet Infomation Server (NT) или Personal Web Server (Windows 9x). В последнем случае ASP-файл должен быть помещен в одну из папок сервера, обращение к которому из браузера выполняется по адресу http://localhost/MyDir/Test.ASP. Понятно, что никакого практического применения подпрограмма WhiteSomeThing не имеет, поэтому предлагаем вашему вниманию процедуру ShowRecordSetASP (листинг 329), которая выводит на HTML-страницу содержимое заданного набора данных. Теперь, сделав в ASP обращение к методу ShowRecordSetASP:

objRSServer. ShowRecordSet(Response,   _
  "Provider=Microsoft.Jet.OLEDB.3.51;" & _
  "Persist Security Info=False;" & _
  "Data Source=C:\vb-db\nwind.mdb",  _
  "Select FirstName +' ' + LastName as Имя_Фамилия," & _
  "Title as Должность, " & _
  "BirthDate as ДатаРождения, HomePhone as ДомашнийТелефон" & _
  " from Employees Order by LastName, FirstName", _
  "Список сотрудников"

с помощью браузера можно будет посмотреть полезную информацию, хранимую на Web-сервере (рис. 329).

Рис. 329

В начало статьи

Совет 330. Как отлаживать ASP-скрипты

Отладить процедуры для создания ASP-страниц непросто: необходимо писать код, потом создавать DLL, затем обращаться из браузера к ASP. Увидев ошибки, найти их причину, исправить в VB программу, создать DLL... С этой проблемой мы столкнулись при разработке довольно простой процедуры ShowRecordSetASP (см. совет 329), решив ее следующим образом. Мы сделали стандартный VB-проект, в котором создали процедуру ShowRecordSet такого вида:

Public Sub ShowRecordSet(strConnectString$, strSQL$, strHeading$)
...
  Nf=FreeFile
  Open "d:\file.htm" For Output As #Nf
  Print #Nf, "<html><head></head><body>"
  ' формирование таблицы
  Print #Nf, "<table cellpadding=3 border=0><tr>"
 ...  ' мы опустили код, который приведен в ShowRecordSet

  Print #Nf, "</body></html>"
  Close #Nf
End Sub

Так выглядит процедура, формирующая нужный фрагмент кода в виде готового HTM-файла, который сразу можно просматривать браузером. Затем после отладки мы убрали строчки:

Open "d:\file.htm" For Output As #Nf
Print #Nf, "<html><head></head><body>"
...
Print #Nf, "</body></html>"
Close #Nf

и в режиме Replace заменили "Print #1," на ".Write", а потом давили логические скобки:

With ASPResponse
   ...
End With

Существует еще один вариант разработки кода для ASP-страниц. Делаем такую процедуру, которая формирует автономный HTM-файл:

Public Sub MyShowRecordSet(strConnectString$, strSQL$, strHeading$)

  Nf=FreeFile
  Open "с:\file.htm" For Output As #Nf
  Print #Nf, "<html><head></head><body>"
  Call MyShowRecordSetMain(strConnectString$, strSQL$, strHeading$)
  Print #Nf, "</body></html>"
  Close #Nf

Подпрограмма MyShowRecordSetMain записывает нужный код в HTM-файл. Для ASP пишем еще пару подпрограмм:

Public Sub MyShowRecordSetASP _
  (ASPResponse As ASPTypeLibrary.Response, _
  strConnectString$, strSQL$, strHeading$)

  Nf=FreeFile
   FileName$ = "с:\TempFile.htm"
  ' формируем промежуточный текстовый файл
  Open FileName$ For Output As #Nf
  Call MyShowRecordSetMain(strConnectString$, strSQL$, strHeading$)
  Close #Nf
  ' переписываем его в ASP
  Call CopyFromFileToASP(FileName$, ASPResponse)
  Kill FileName$    ' удалить временный файл
End Sub

Public Sub CopyFromFileToASP(FileName$, _
      ASPResponse As ASPTypeLibrary.Response)
  ' переписываем содержимое текстового файла в ASP
  Dim a$
  nf= FreeFile
  Open FileName$ For Input As #Nf
  Do while not eof(Nf) ' переписываем
    Line Input #Nf, a$
    ASPResponse.Write a$
  Loop
  Сlose #Nf
End Sub

В начало статьи

Совет 331. Как улучшить читаемость кода

Качество оформления программного кода — один из важных факторов, влияющих на эффективность разработки приложений. Прежде, когда программист имел дело с бумажными распечатками кода, считалось, что одна процедура не должна превышать 60-90 строк кода (1-1,5 страницы). Такой объем текста человек может охватить одним взглядом и, таким образом, легко воспринять логику работы данного фрагмента. С внедрением персональных компьютеров необходимость в бумажных листингах отпала, наступило время интерактивной работы с кодом с помощью экрана монитора.

В текстовом режиме экрана (когда мы работали с QuickBasic) мы придерживались правила, что объем процедуры не должен превышать 40-50 строк, то есть занимать не более двух экранных страниц, которые можно легко перелистывать «туда-сюда». Теперь, при работе в графическом режиме программист может гибко управлять размером шрифта и окна с кодом. Но чаще всего приведенные выше пределы числа строк можно рекомендовать и в этом случае.

С размером текста процедур непосредственно связан вопрос навигации между отдельными компонентами VB-проекта. Следует открыто признать, что навигация в VB оставляет желать лучшего. К сожалению, сегодня многие VB-программисты даже не представляют себе, что этот процесс можно сделать более удобным. Довольно удачным примером этого может служить окно выбора нужной процедуры, которое было реализовано в VB 3.0 (рис. 331).

Рис. 331

Оно гораздо удобнее существующего сегодня Object Browser хотя бы потому, что для модуля формы показываются все реально сформированные процедуры.

Из сказанного выше понятно, что повышение удобства читаемости программного кода cвязано, в частности, с уменьшением числа строк текста. В этой связи хотелось бы напомнить о возможностях синтаксиса VB, о которых, как выясняется, многие программисты даже не подозревают.

  1. Возможность написания нескольких операторов в одной строке. Для этого используется разделитель операторов в виде двоеточия. Согласитесь, что такой код:

    A = 1: C$ = "Строковая переменная": D = #09/30/2000#
    

    или:

    Select Case MyVar
       Case 0: OtherVar = 1
       Case 1: OtherVar = 45
    End Select
    

    который читается достаточно хорошо, но при этом занимает меньше строк.

  2. Оператор If ... Then ... Else может писать в одну строку, причем в последнем случае не нужно использовать закрывающую скобку End If. Вот несколько примеров использования этой возможности:

    Вместо

    If k = 0 Then 
       A =1
    End If
    

    Вместо:

    If k = 0 Then
      A = 1
      B = 2
    Else 
      A = 2
      B = 1
    End If
    

В начало статьи

Совет 332. Проверка на недействительные символы

С этой задачей приходится сталкиваться довольно часто. Самый простой пример — ввод строки, которая должна содержать число. Ранее мы уже приводили простые процедуры собственной разработки, но сейчас хотим показать возможности библиотеки SHLWAPI.DLL, которая входит в состав Internet Explorer:

Declare Function StrSpn Lib "SHLWAPI" Alias _
   "StrSpnW" (ByVal psz As Long, ByVal pszSet As Long) As Long

Public Function IsDecimal (ByVal sString As String) As Boolean
  Const DECIMAL_NUM As String = "0123456789"
  Dim iPos

  iPos = StrSpn (StrPtr(sString), StrPtr(DECIMAL_NUM)
  ' если возвращается значение, не равное длине исходной строки,
  ' то значит найдены символы, не являющиеся цифрами
  IsDecimal = (iPos = Len(sString))
End Function

В начало статьи

Совет 333. Проверка дубликатности элементов списка

При добавлении нового элемента списка полезно проверить, не содержит ли он уже такие строки. Это можно сделать с помощью следующего кода:

Declare Function SendMessageByString _
   Lib "User32" Alias "SendMessageA _
   (ByVal hWnd As Long,   _
   ByVal wMsg As Long, _
   ByVal wParam As Long, _
   ByVal lParam As String) As Long
   
   ...
   NewListItem$ = "Новый элемент списка"
   If SendMessageByString _
       (List1.hWnd, &H1A2,    -1, NewListItem$) = - 1 then
     List1. AddItem          ' добавить новый элемент
   Else
     MsgBox NewListItem$ & "- такой    элемент уже есть в списке"
   End If

В начало статьи

Совет 334. Как организовать выбор каталога

Vы уже приводили пример решения этой задачи с помощью обращения к DLL-функциям (см. Совет 205). Наш читатель Сергей Мичковский предложил более компактный вариант решения этой задачи:

Sub Main()
  Dim oShell As New Shell32.Shell
  Dim oFolder As Shell32.FolderSet
  oFolder = oShell.BrowseForFolder _
    (UserControl.hWnd, "Выберите компьютер и нажмите кнопку OK", _
    &H1000, &H12)
  MsgBox oFolder.Title
End Sub

В начало статьи