Поиск

Полнотекстовый поиск:
Где искать:
везде
только в названии
только в тексте
Выводить:
описание
слова в тексте
только заголовок

Рекомендуем ознакомиться

'Конкурс'
Конкурс входит в «Перечень олимпиад и иных конкурсных мероприятий, по итогам которых присуждаются премии для поддержки талантливой молодежи» Министер...полностью>>
'Документ'
Цели и принципы стандартизации в Российской Федерации установлены Федеральным законом от 27 декабря 2002 г. № 184-ФЗ «О техническом регулировании», а ...полностью>>
'Программа'
Содержание курса: предмет и задачи экологии, факториальная экология, популяционная экология, экология сообществ, системная экология, прикладная эколог...полностью>>
'Лекции'
анализ и аудит» Специализация: «Бухгалтерский учет, анализ и аудит в банках» № Наименование Осенняя сессия Виды текущей аттестации студентов п/п дисц...полностью>>

Visual Basic Основы работы с базами данных

Главная > Документ
Сохрани ссылку в одной из сетей:

И последнее

Я писал всё это в расчёте на понимающего читателя, который не будет пытаться передать пустышке Variant ByVal, или Double тем же способом. Параметры размером 4 байта все. Для работы с API этого более чем достаточно, а для передачи больших данных используются указатели (передать Variant ByRef – всегда пожалуйста). Генератор кода пустышек будет стараться исправлять ваши возможные ошибки.

Автор: Сергей Гергерт

 

Комментарии

Статья замечательная, однако, есть ряд неучтённых нюансов, которые делают предложенный метод не очень удобным.

Начнём с мелочей.

В качестве инструкции ветвления автор предлагает использовать инструкцию ветвления  - безусловный переход по относительному адресу:

jmp  rel32

Однако, очевидно, что данный метод весьма неудобен по ряду причин. Если вдруг захочется изменить адрес, придётся заново обращаться к SetFunction, а это не очень удобно. Да и зачем это делать зазря , если можно и вовсе обойтись без повторного обращения к SetFunction? Достаточно лишь использовать безусловный переход по адресу, содержащемуся в таком-то месте адресного пространства. Под <таким-то местом адресного пространства> я подразумеваю наибанальнейшую (глобальную) переменную. Ладно, от разговора на языке жестов перейдём к инструкции:

Public vCallAddress as Long, Ia() as Byte

ReDim Ia(1 to 6)

'jmp dword ptr ds:[imm32=VarPtr(vCallAddress)]

Ia(1)=&HFF: Ia(2)=&H35: PutMem4 VarPtr(Ia(3)), VarPtr(vCallAddress)

Массив объявлен условно, лишь для того, чтобы показать, как это выглядит в машинных кодах.

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

xor  eax,eax

ret  4

которые тоже занимают пять байт. А наша инструкция занимает шесть байт. Но это очень просто поправить, достаточно переписать PlaceholderFor1ParamFunction в виде

Public Function PlaceholderFor1ParamFunction(ByVal p As Long) As Long

  #If ReleaseBuild Then

    PlaceholderFor1ParamFunction = 1&

  #Else

    Dim a As Long

    On Error Resume Next

    a = mCol(CStr(ReturnMe(AddressOf
modFuncCall.PlaceholderFor1ParamFunction)))

    On Error GoTo 0

    If a Then PlaceholderFor1ParamFunction = CallFunction(a, 1, p)

  #End If

End Function

тогда при линковке туда будут записаны инструкции

mov  eax,imm32

ret  4

занимающие восемь байт, теперь перезаписываемый фрагмент будет выглядеть

ReDim Ia(1 to 8)

'jmp dword ptr ds:[imm32=VarPtr(vCallAddress)]

Ia(1)=&HFF: Ia(2)=&H35: PutMem4 VarPtr(Ia(3)), VarPtr(vCallAddress)

'nop

'nop

Ia(7)=&H90: Ia(8)=&H90

Теперь, чтобы сделать переадресацию, не нужно больше обращаться к SetFunction, а достаточно всего лишь:

vCallAddress=vNewValue

и можно опять обращаться к PlaceholderFor1ParamFunction.

 

Это были мелкие замечания, так сказать, <по процедурному вопросу>. Теперь хотелось бы поговорить об упущенных нюансах, имеющих, тем не менее, глобальный характер.

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

Какие будут мои аргУменты? Да очень простые! Давайте сначала вспомним, как вообще процессор <понимает> из какого места адресного пространства необходимо исполнять инструкцию. Для указания адреса исполняемой инструкции используется регистр eip. Причём, нет инструкций явных чтения/записи из этого регистра. А в качестве неявно изменяющих содержимое регистра eip используются jmp, jxx и пары call-ret, enter-leave. VB'эшные компилятор и интерпретатор обычно используют jmp, jxx и пару call-ret. Последняя, как раз, нас и интересует. Выполнение инструкции

call operand

сводится.

push eip

jmp  operand

или ещё подробнее к

mov  dword ptr ds:[esp-4],eip

sub  esp,4

mov  eip,operand

После того, как старое значение eip сохраняется в стеке (в регистре esp находится указатель на начало стека), а значение operand'а загружается в eip, процессор <сам по себе> переходит к выполнению инструкции, находящейся по этому адресу.

Итак <на вершине стека> у нас находится указатель на то место, куда будет необходимо вернуться после выполнения вызываемой функции. Возврат осуществляется инструкцией

ret  optional imm16

optional imm16 используется для очистки стека, если это необходимо.
Развёрнутая интерпретация этой инструкции выглядит

add  esp,optional imm16+4

mov  eip,dword ptr ds:[esp-optional imm16-4]

Причём, если прочее содержимое стека не меняется, то место по адресу [esp-optional imm16-4] после этого уже не содержит адрес возврата. Теперь после маленького ликбеза <вернёмся к нашим баранам>.

В среде VB из любой процедуры, независимо от того IDE это или откомпилированный файл, возврат ВСЕГДА осуществляется с помощью инструкции

ret  imm16

Таким образом, если внутри нашей процедуры изменить значение <на вершине
стека>, то вместо возврата произойдёт передача управления на тот адрес,
который мы запишем по адресу, содержащемуся в esp. Но как узнать значение регистра esp, как узнать адрес начала стека? Заметьте, сам автор пишет:
<перед передачей управления в функцию : стек приобретает вид:>

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

ret  imm16

А где у нас находится начало стека при передаче управления, скажем, в такую
функцию:

Private Function Test(byval vP1 as Long)as Long

:

End Function

Ну, разумеется, в откомпилированном виде начало стека будет находиться по
адресу:

vESP = VarPtr(vP1) - 4

А в IDE, спросите вы, где будет начало стека при вызове функции в IDE? Ну, здесь всё очень просто. По ряду причин, которые я не хотел бы здесь обсуждать, вызов процедуры <из под> IDE осуществляется почти так же, как осуществляется в откомпилированном виде вызов функции из чужого ActiveX (кстати, думаю, что самые сметливые уже догадались, почему так происходит в IDE). Что это значит? Да вот что: начало стека и первый параметр разделяет ещё один параметр (указатель на адреса ObjTable). То есть начало стека находится следующим образом:

vESP = VarPtr(vP1) - 8

Теперь достаточно сохранить этот адрес в <какой-нибудь> (специально для этого предназначенной) переменной

Public pRet as Long

CopyMem VarPtr(pRet), vESP, 4

после этого по адресу vESP можно записать новый (необходимый нам) адрес.

И далее, процессор <сам по себе> переходит на адрес, скажем, на следующий переключатель в маш. кодах:

Public RDa() as Byte, pRD as Long

Private Sub InitRD()

 ReDim RDa(1 to 16)

 'восстанавливаем значение регистра esp

 'sub esp,imm8=4

 RDa(1)=&H83: RDa(2)=&HEC: RDa(3)=4

 'заново прописываем адрес возврата, предварительно сохранённый нами в pRet

 'push      dword ptr ds:[imm32=VarPtr(pRet)]

 RDa(4)=&HFF: RDa(5)=&H34: RDa(6)=&H35: PutMem4 VarPtr(RDa(7)), VarPtr(pRet)

 'ну, теперь, наконец, можно перейти по интересующему нас адресу

 'предварительно записанному нами в vCallAddress

 'jmp dword ptr ds:[imm32=VarPtr(vCallAddress)]

 RDa(11)=&HFF: RDa(12)=&H35: PutMem4 VarPtr(RDa(13)), VarPtr(vCallAddress)

 pRD=VarPtr(RDa(1))

End Sub

А тело функции Test необходимо оформить так, чтобы у нас внутри неё вычислялось значение pRet и вместо адреса возврата записывался указатель на наш переключатель. Тело функции должно выглядеть следующим образом:

Private Function Test(byval vP1 as Long)as Long

 #If ReleaseBuild Then

  'вычисляем адрес начала стека

  vESP = VarPtr(vP1) - 4

 #Else

  'вычисляем адрес начала стека

  vESP = VarPtr(vP1) - 8

 #End If

 'сохраняем адрес возврата в переменной pRet

 CopyMem VarPtr(pRet), vESP, 4

 'прописываем в начало стека новый адрес pRD

 PutMem4 vESP, pRD

End Function

И всё! И не нужны никакие CallWindowProc! Всё работает и под IDE, и в откомпилированном виде.

 

Теперь поговорим немного <о Сусанине>. Если по каким-либо причинам переадресация, предложенная мною в откомпилированном виде кажется нежелательной (например, требуется большая скорость и не хочется терять даже десятка полтора тактов процессора на приведенный выше вариант, которые при частом обращении (например, в цикле!) могут сыграть значительную роль).
Тогда можно пойти как тем поправленным вариантом предложенным выше мною, так и авторским. И всё же. Если уж многократное обращение происходит в цикле (а <потерянное> время бывает критичным только в этом случае), тогда можно извратиться несколько больше, чем предложил автор.

Дело в том, что для обращения к какой-либо <собственной> функции используется стандартная инструкция:

call rel32

Если взять авторский вариант, то у нас получится последовательность вызовов

call rel32=Offset PlaceholderFor1ParamFunction

jmp  rel32=Offset <Вызываемая функция>

или в поправленном мною выше варианте

call rel32=Offset PlaceholderFor1ParamFunction

jmp  dword ptr ds:[imm32=VarPtr(vCallAddress)] функция>

Ну, почему бы нам вообще не избавиться от <лишнего> jmp, коли уж так сильно поджимает нас время? И избавимся! Да ещё и будем ходить по условному адресу.
Для этого немного напишем функцию переключателя

#If ReleaseBuild Then

Private Ra() as Byte

Public Function ReDirect(Optional ByVal vP0 As Long, Optional ByVal vP1 As
Long) As Long

 vESP=VarPtr(vP0)-4

 CopyMem VarPtr(pRet), vESP, 4

 ReDim Ra(1 to 7)

 'call      dword ptr ds:[imm32=VarPtr(vCallAddress]

 Ra(1)=&H3E: Ra(2)=&HFF: Ra(3)=&H25: PutMem VarPtr(Ra(4)),
VarPtr(vCallAddress)

 WriteProcessMemory GetCurProcess, pRet-7, VarPtr(Ra(1)), 7, 0&

 PutMem4 vESP, pRD

End Function

#End If

а обращаться к этой функции будем

vCallAddress=vNewAddress

vReturnValue=ReDirect(,vParamValue)

<концовка> подобного обращения (из любого места VB-программы!) в маш. кодах выглядит

push imm8=0

call rel32

что занимает семь байт. Но инструкция

call dwrod ptr ds:[imm32]

На самом деле занимает шесть байт. Что делать? Не вставлять же nop'ы в конце? Ведь это опять получится дополнительная, хотя и пустая (один такт) операция процессора. Есть простое решение указанная выше инструкция по умолчанию адресуется относительно регистра ds, достаточно приписать перед этой инструкцией <уточняющий> префикс &H3E, который будет делать тоже самое, но в явном виде. Что в итоге? В итоге имеем те самые семь байт.

Что же на самом деле делает функция ReDirect? При обращении к этой функции, она вычисляет адрес возврата уже описанным выше способом. Затем меняет предшествующие адресу возврата семь байт. Таким образом, если обращение из этого места происходит либо в цикле, либо просто часто, то в следующий раз обращение по нужному адресу (vCallAddress) будет происходить прямо оттуда!
Кстати говоря, несмотря на то, что функция ReDirect, вроде бы имеет два параметра, при повторных обращениях задержки из-за пустого параметра всё равно не происходит, так как

push imm8=0

мы затираем при первом обращении.

Есть ещё один не менее изящный, на мой взгляд, вариант.

#If ReleaseBuild Then

Private REa() as Byte

Public Function REx(ByVal vP0 As Long, ByVal vP1 As Long) As Long

 vESP=VarPtr(vP0)-4

 CopyMem VarPtr(pRet), vESP, 4

 ReDim REa(1 to 5)

 'pop       eax

 REa(1)=&H67: REa(2)=&H38

 'call      eax

 REa(3)=&H67: REa(4)=&HFF: REa(5)=&HD0

 WriteProcessMemory GetCurProcess, pRet-5, VarPtr(REa(1)), 5, 0&

 PutMem4 vESP, pRD

End Function

#End If

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

vReturnValue=REx(vCallAddress,vParamValue)

То есть теперь инструкция

call rel32

заменяется инструкциями

pop  eax

call eax

Как мы помним инструкция

call rel32

занимает пять байт, в то время, как наша замена - всего три. Как и выше, <недостающие байты> мы <добираем> за счёт <уточняющих> префиксов. В данном случае это префикс &H67. Его <безболезненно> можно ставить перед любой из наших инструкций. В итоге получаем искомые пять байт. Заметьте, что последний вариант при передаче управления по условному адресу выполняется максимально быстро. Если же, мы знаем, что обращение будет происходить на один и тот же адрес, то максимально быстро будет работать вот такой вариант

#If ReleaseBuild Then

Public vNewAddress as Long

 

Private Function GetPA(ByVal vProcAddress as Long)as Long

 GetPA=vProcAddress

End Function

 

Public Function ReD(ByVal vP1 As Long) As Long

 vESP=VarPtr(vP0)-4

 CopyMem VarPtr(pRet), vESP, 4

 'до обращения в vNewAddress должен содержаться <абсолютный> адрес

 'после вычисления там будет содержаться относительный

 vNewAddress=pRet-GetPA(AddressOf ReD)+vNewAddress

 WriteProcessMemory GetCurProcess, pRet-4, VarPtr(vNewAddress), 4, 0&

 PutMem4 vESP, pRD

End Function

#End If

Теперь обращение к этой функции обычное.

vNewAddress=<адрес вызываемой процедуры>

:

vReturnValue=ReD(vParamValue)

А что происходит в функции ReD? Просто в инструкции

call rel32

перезаписывается само значение rel32.

 

Вот, собственно говоря, и все замечания. В прочих случаях, не требующих столь изощрённых методов оптимизации предлагаю пользоваться переадресацией альтернативной (если честно, то не очень-то поворачиваются пальцы на клавиатуре их сравнивать) CallWindowProc.

Техника программирования сложных окон в Visual Basic

Введение

Mногие из Вас наверняка видели в Windows программах окна нестандартной формы (круглые, треугольные и т.д.) и задавали себе вопрос: как мне сделать такое окно? Если прочитать документацию по Visual Basic, то можно сделать вывод, что стандартные средства языка не предоставляют такой возможности. А что же делать, если очень хочется? Тогда следует вспомнить, что в распоряжении программиста на VB есть еще и Windows API, который должен нам в этом помочь.

Теоретические основы

Для начала давайте разберемся, как это можно сделать теоретически. Из документации Windows видно, что каждое окно в системе описывается множеством параметров, из которых нас с Вами интересует <видимая область окна>. Видимая область окна в системе, создаваемое Visual Basic <по умолчанию> имеет вид прямоугольника, но, в принципе, ничто не мешает изменить форму этой области. Данная область окна описывается с помощью специального объекта, который называется Region. Регион можно представить в виде поверхности, ограниченной координатами, описываемыми угловые точки этой области. Проще говоря, можно описать область любой формы, затем создать из неё, с помощью специальных функций, регион и <прикрепить> его к нужому нам окну.

Существует несколько функций Windows API для создания регионов, основными из которых являются следующие:

  • CombineRgn - Комбинирует два региона между собой

  • CreateEllipticRgn - Создает регион в виде эллипса или окружности

  • CreatePolygonRgn - Создает регион в виде многоугольника

  • CreateRectRgn - Создает прямоугольный регион

  • CreateRoundRectRgn - Создает регион со скругленными краями из прямоугольной области

  • SetWindowRgn - Прикрепляет регион к указанному окну

Я не буду приводить подробное описание этих функций, так как его можно найти в описании Win32 API. Кроме этих функций существуют ещё несколько функций для работы с регионами, но нам они не потребуются.

Создание простых нестандартных окон

Теперь, когда нам известны основные функции, для создания регионов, мы можем применить полученные знания на практике. Загрузите проект pTestRgn и внимательно изучите его код. В этом проете, для изменения формы окна на овальную, используется всего три строки кода и три функции Win32 API. Вначале с помощью CreateEllipticRgn создается регион, затем он прикрепляется к окну и, наконец, завершающая фаза удаление, ставшего ненужным, созданного нами региона. Если же Вы не удалите ненужный Вам больше объект, то Windows, создав регион для Вас будет хранить его в своих <недрах> и ждать дальнейших указаний по его использованию. В общем, нехорошо <захламлять> выделенную память, и настигнет Вас кара небесная, и затянется небо тучами синими, и будет страшный суд над всеми неверующими: Короче код выглядит так:

Private Sub cmbCreateOval_Click()

Dim lRgn As Long

lRgn = CreateEllipticRgn(0, 0, Me.ScaleWidth / Screen.TwipsPerPixelX, _

Me.ScaleHeight / Screen.TwipsPerPixelY)

SetWindowRgn Me.hwnd, lRgn, True

DeleteObject lRgn

End Sub

Так же всё просто, скажете Вы? Да, на первый взгляд всё очень просто, но это только кажется. Тот пример, который Вы только что видели, почти не имеет практического применения в настоящих приложениях Windows. Кому же нужно просто овальное окно, которое к тому же жестко задается на этапе программирования? А вот окно, которое свободно могло бы менять свою форму вполне может потребоваться. Примеры? Пожалуйста, WinAmp, Помощник в Microsoft Office и другие программы. Как же там всё это реализовано? Давайте разберемся с таким применением регионов.

Создание сложных нестандартных окон

Допустим, что у нас есть рисунок в BMP формате, из которого нужно сделать форму, а белый цвет (например) на нём означает <пустоту>. Как же сделать форму? Очень просто, нужно взять все <не белые> пиксели на рисунке, создать из их координат регион и прикрепить его к нужному нам окну. Анализировать пиксели можно GetPixel, эта функция по координатам возвращает его цвет. Давайте теперь напишем такой алгоритм для анализа BMP матрицы. Я думаю, что такой алгоритм Вам известен, и мы не будем его подробно разбирать, отмечу только, что анализ производится построчно и Pixel-и добавляются в регион не по одному, а группами построчно. Такой подход сильно экономит ресурсы процессора, выигрыш в производительности достигает 100%.

Public Function lGetRegion(pic As PictureBox, lBackColor As Long) As Long

Dim lRgn As Long

Dim lSkinRgn As Long

Dim lStart As Long

Dim lX As Long

Dim lY As Long

Dim lHeight As Long

Dim lWidth As Long

'создаем пустой регион, с которого начнем работу

lSkinRgn = CreateRectRgn(0, 0, 0, 0)

With pic

'подсчитаем размеры рисунка в Pixel

lHeight = .Height / Screen.TwipsPerPixelY

lWidth = .Width / Screen.TwipsPerPixelX

For lX = 0 To lHeight - 1

lY = 0

Do While lY < lWidth

'ищем нужный Pixel

Do While lY < lWidth And GetPixel(.hDC, lY, lX) = lBackColor

lY = lY + 1

Loop

If lY < lWidth Then

lStart = lY

Do While lY < lWidth And GetPixel(.hDC, lY, lX) <> lBackColor

lY = lY + 1

Loop

If lY > lWidth Then lY = lWidth

'нужный Pixel найден, добавим его в регион

lRgn = CreateRectRgn(lStart, lX, lY, lX + 1)

CombineRgn lSkinRgn, lSkinRgn, lRgn, RGN_OR

DeleteObject lRgn

End If

Loop

Next

End With

lGetRegion = lSkinRgn

End Function

Итак, для проверки на практике этого алгоритма загрузите пример pTestRgnSkin и внимательно изучите его код. В этом проекте нужный нам рисунок, для удобства, <зашит> в файле ресурсов, кроме того проект запускается процедурой Main, в которой и происходят все преобразования. Вначале загружается форма, затем в PictureBox из ресурсов загружается нужный нам рисунок, далее вызывается функция, которая создает регион и, наконец, завершающий этап - прикрепление региона к нужному нам окну. Для удобства здесь же вызывается функция, помещающая окно <поверх всех>, чтобы оно <не потерялось> у Вас на рабочем столе Windows. Кроме того, для нормальной работы программы необходимо, чтобы для PictureBox свойство AutoRedraw было установленно в True, иначе ничего не получится.

Sub Main()

Dim lRgn As Long

Load frmTestRgnSkin

frmTestRgnSkin.pic.Picture = LoadResPicture(101, vbResBitmap)

lRgn = lGetRegion(frmTestRgnSkin.pic, vbWhite)

SetWindowRgn frmTestRgnSkin.hWnd, lRgn, True

DeleteObject lRgn

frmTestRgnSkin.Show

SetFormPosition frmTestRgnSkin.hWnd, True

End Sub

Теперь можно запускать проект... О, знакомое лицо, скажите Вы, это же <Скрепыш> из Microsoft Office. Да, похож, но не совсем, <Скрепыш> двигается, а этот нет. Что же нужно сделать, чтобы это окно динамически изменяло свою форму по рисунку, отображаемому в данный момент времени в PictureBox?

Динамическое изменение формы окна

Существуют программы в которых необходимо динамически во время работы изменять форму окна (например анимированный персонаж из Microsoft Office). Все это не очень сложно реализовать, нужно в событие PictureBox.Change добавить следующий код:

lRgn = lGetRegion(frmTestRgnSkin.pic, vbWhite)

SetWindowRgn frmTestRgnSkin.hWnd, lRgn, True

DeleteObject lRgn

SetFormPosition frmTestRgnSkin.hWnd, True

В принципе всё готово, осталось только добавить код для изменения картинки на форме, и <Скрепыш> оживёт. В нашем примере изменять рисунок будем в Timer циклически, т.е. анимация будет непрерывна, так проще. Итак, добавим на форму Timer и поместим <в него> небольшой код, отвечающий за изменения рисунка в PictureBox. Рисунков в файле ресурсов десять штук, поэтому I должно изменяться от 101 до 110. Код изменения выглядит так:

Static i As Long

If i < 101 Then i = 101

If i > 110 Then i = 101

frmAnimateForm.pic.Picture = LoadResPicture(i, vbResBitmap)

i = i + 1

Готово, можно запускать проект, и если Вы счастливый обладатель Pentium III или Athlon, то Вам улыбнется удача, так как <Скрепыш> будет двигаться. Но если Ваш процессор Pentium II и ниже, то компьютер не сможет выполнять необходимые расчеты за нужное нам время, так как для плавной анимации необходимо (для нашего случая) показывать порядка 15 кадров в секунду, а точнее каждые 80 милисекунд по кадру и ещё оставлять время для других задач компьютера. Как мы видим наши алгоритмы явно не тянут для таких задач и предназначены для <работ> не требующих таких быстрых изменений формы окна, так как, например на Celeron 333 один кадр формируется около 100 милисекунд. Что же делать?

Оптимизация алгоритма для быстрой анимации

Анализ работы алгоритма показывает, что наибольшие затраты времени приходятся на функцию GetPixel. Это происходит потому, что анализ картинки идет непосредственно на экране. Единственный путь увеличения быстродействия алгоритма, это перенос анализа в память компьютера и использование при этом Win 32 API. Такие алгоритмы существуют, но это тема отдельного разговора, скажу только, что для оптимизации работы алгоритм пишется отдельно для каждой глубины цвета и при применении такой схемы быстродействие увеличивается почти в четыре раза и позволяет делать практически любую анимацию.

Visual Basic и Системный Реестр Windows

Системный Реестр (Registry) Windows - это база данных для хранения системной и пользовательской информации в Windows. Системный Реестр состоит из двух файлов - SYSTEM.DAT, в нем содержится информация о конкретном компьторе, и USER.DAT, содержащий пользовательские установки и данные.
Для работы с Системным Реестром обычно применяют специальный редактор Системного Реестра - REGEDIT.EXE, или специальные функции Win32 API, или некоторые встроенные функции и операторы Visual Basic.
Остановимся на последнем и рассмотрим работу с данными в Системном Реестре с помощью Visual Basic. Для этого Вам придется сделать следующее:

  • Запустите редактор Системного Реестра, он находится в каталоге Windows - C:\Windows\Regedit.exe. При запуске редактора в окне появятся шесть ключей-папок (эти ключи как раз и представляют содержимое файлов SYSTEM.DAT и USER.DAT). Выберите ключ HKEY_CURRENT_USER (этот ключ входит в состав файла USER.DAT) и щелкните на плюсике "+". В раскрывшемся списке выберите подключ - Software - и раскройте его. Найдите во вновь открывшемся списке папку - VB and VBA Program Setting - это и есть раздел Системного Реестра куда с помошью встроенных операторов и функций Visual Basic Вы можете записывать и считывать необходимую Вам информацию.

  • Запустите Visual Basic и откройте новый проект. Поместите на форму четыре командные кнопки и в свойстве Caption" , каждой из них введите соответственно: SaveSetting, GetSetting, GetAllSettings, DeleteSetting. Запомните проект под именем, скажем, MYREG.


А теперь приступим к работе.

Запись значений с помощью оператора SaveSetting

Дважды щелкните на командной кнопке SaveSetting и в раскрывшемся окне введите следующий код:

Private Sub Command1_Click()
SaveSetting App.Title, "PortSettings", "Connect using", "COM1"
End Sub

Запустите программу на выполнение и нажмите на кнопку SaveSetting, потом переключитесь на окно редактора Системного Реестра. Откройте папку VB and VBA Program Setting и там Вы увидите вновь созданную папку MYREG, а в ней подраздел с именем PortSettings. Открыв его, в левом окне редактора, Вы прочтете запись которую только что создали - Connect using   "COM1".

***Если Вы правильно выполнили все что написано, но ничего не увидели, не волнуйтесь. Нажмите клавишу F5 (Refresh) и перед вами появится искомая запись.

Теперь расмотрим по-подробнее, что Вы написали в окне кода. Синтаксис оператора SaveSetting следующий:
SaveSetting VBKeyName, Section, Key, Setting, где:

  • SaveSetting - имя самого оператора;

  • VBKeyName   - строковое значение, которое является именем внутреннего подраздела VB and VBA Program Setting. В нашем случае онo будет носить имя нашей программы - MYREG, т.к. мы использовали свойство объекта App.Title;

  • Section     - строковое значение, которое является именем внутреннего подраздела VBKeyName. В нашем случае оно носит имя PortSettings;

  • Key    - строковое значение, которое представляет имя параметра в созданном подразделе PortSettings.В подразделе может быть много параметров.

  • Setting    - строковое значение, которое Вы хотите присвоить данному параметру. В нашем случае параметру Connect using приваиваем значение COM1;

Чтение значений с помощью функции GetSetting()

***При выполнении нижеследующих действий объявите в разделе Declaration формы переменные CrtMsg As String, MySet As Variant

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

Private Sub Command2_Click()
MySet = GetSetting(App.Title, "PortSettings", "Connect using", CrtMsg)
Debug.Print MySet
End Sub

Запустите программу на выполнение и намите на кнопку GetSetting. В окне Immediate Вы увидете введенное ранее (оператором SaveSetting) значение - COM1.
Теперь расмотрим подробнее, что Вы написали в окне кода. Синтаксис оператора GetSetting следующий:
MySet = GetSetting ( VBKeyName, Section, Key [, Default ] )
 

  • MySet   - строка для хранения возвращаемого функцией GetSetting значения;

  • GetSetting - имя самой функции;

  • VBKeyName   - строковое значение, которое является именем внутреннего подраздела VB and VBA Program Setting. Мы используем свойство объекта App.Title, т.к. хотим прочесть информацию из созданного нашей программой раздела ;

  • Section     - строковое значение, которое является именем внутреннего подраздела VBKeyName. В нашем случае он носит имя PortSettings;

  • Key    - строковое значение, которое представляет имя параметра в созданном подразделе PortSettings.В подразделе может быть много параметров и по-этому, мы указываем на желаемое - Connect using;
    Default     - необязательный аргумент, представляющий строковое значение, которое будет возвращено функцией в случае ошибки (если такого параметра нет). Функция возвращает строковое значение, присвоенное аргументу Default. К примеру CrtMsg = "Такого параметра не существует.".

Чтение значений с помощью функции GetAllSettings()

***Предварительно добавте следующий код к имеющемуся коду командной кнопки
SaveSetting:
SaveSetting App.Title, "PortSettings", "Data bits", "8"
SaveSetting App.Title, "PortSettings", "Parity", "None"
SaveSetting App.Title, "PortSettings", "Stop bits", "1"
SaveSetting App.Title, "PortSettings", "Flow control", "None"
Произведите операцию записи данных в Системный Реестр.

Для получения из реестра массива, содержащего все значения параметров из определенного подраздела (например, PortSettings) применяется функция GetAllSettings(). Для этого дважды щелкните на командной кнопке GetAllSettings и в раскрывшемся окне введите следующий код:

Private Sub Command3_Click()
Dim intSettings As Integer
MySet = GetAllSettings(App.Title, "PortSettings")
  
For intSettings = LBound(MySet, 1) To UBound(MySet, 1)
     
Debug.Print MySet(intSettings, 0), MySet(intSettings, 1)
  
Next intSettings
End Sub

Запустите программу на выполнение и нажмите на кнопку GetAllSettings. В окне Immediate Вы увидете введенные ранее (оператором SaveSetting) значения:

Connect using      COM1
Bits per second    2400
Data bits          8
Parity             None
Stop bits          1
Flow control       None

Теперь расмотрим подробнее, что Вы написали в окне кода. Синтаксис оператора GetAllSetting следующий:

MySet = GetAllSettings ( VBKeyName, Section)

  • MySet   - возвращаемый функцией массив значений, он должен быть типа Variant

  • GetSetting - имя самой функции;

  • VBKeyName   - строковое значение, которое является именем внутреннего подраздела VB and VBA Program Setting. Мы используем свойство объекта App.Title, т.к. хотим прочесть информацию из созданного нашей программой раздела ;

  • Section     - строковое значение, которое является именем внутреннего подраздела VBKeyName. В нашем случае он носит имя PortSettings;

Остальные операторы помогают обработать массив значений и получить информацию в удобном виде.

Удаление раздела параметров с помощью оператора DeleteSetting

Вы уже создали целый массив параметров! А что делать если он больше ненужен? Для удаления параметров применяется оператор DeleteSetting. Дважды щелкните на командной кнопке DeleteSetting и в раскрывшемся окне введите следующий код:

Private Sub Command4_Click()
DeleteSetting App.Title,"PortSettings", "Flow control "
End Sub

Запустите программу на выполнение и намите на кнопку DeleteSetting, потом переключитесь на окно редактора Системного Реестра и Вы увидете, что параметр Flow control отсутствует.

***Если Вы правильно выполнили все что написано, но ничего не увидели, не волнуйтесь. Нажмите на клавишу F5 - Refresh.

Теперь расмотрим подробнее, что Вы написали в окне кода. Синтаксис оператора DeleteSetting следующий:

DeleteSetting VBKeyName, Section, Key , где:

  • DeleteSetting - имя самого оператора;

  • VBKeyName   - строковое значение, которое является именем внутреннего подраздела VB and VBA Program Setting. Мы используем свойство объекта App.Title, т.к. хотим удалить информацию из созданного нашей программой  раздела ;

  • Section     - строковое значение, которое является именем внутреннего подраздела VBKeyName. В нашем случае он носит имя PortSettings;

  • Key    - строковое значение, которое представляет имя параметра в подразделе PortSettings.В подразделе может быть много параметров и по-этому, мы указываем на желаемое - Flow control;


Если Вы захотите удалить сразу весь подраздел, а не некоторые параметры, то для этого используйте следующий синтаксис оператора DeleteSetting:

 DeleteSetting VBKeyName, Section ;

Поместив код DeleteSetting App.Title,"PortSettings" в Private Sub Command4_Click(), Вы удалите весь подраздел PortSettings.

А если Вы захотите убрать вообще все Ваши установки из Сиастемного Реестра, то воспользуйтесь следующим кодом:

Private Sub Command4_Click()
DeleteSetting App.Title
End Sub

***Внимание!!! Пользуйтесь оператором DeleteSetting очень осторожно! 



Скачать документ

Похожие документы:

  1. Работа с базами данных

    Документ
    База данных — это организованная структура, предназначенная для хранения информации. Внимательный читатель, знающий из первой главы этого пособия о том, что данные и информация — понятия взаимосвязанные, но не тождественные, должен
  2. Задачи: Изучить среду Visual Basic; Подобрать задачи исследовательского характера для визуализации экспериментального процесса; Разработать программы и исследовать информационные процессы

    Исследовательская работа
    Visual Basic; (VB) - это среда разработки программ, которая позво­ляет быстро и легко создавать приложения (прикладные программы) для Windows. В нее включено все, что необходимо для создания, модифика­ции, тестирования, корректирования
  3. «Обработка баз данных при помощи Microsoft Access»

    Документ
    Когда необходимо хранить и использовать большие объемы данных, необходим инструмент для простой и быстрой обработки информации. В семействе Microsoft Office таким инструментом является MS Access.
  4. Базы данных (2)

    Документ
    В основе решения многих задач лежит обработка информации. Для облегчения обработки информации создаются информационные системы (ИС). Автоматизированными называют ИС, в которых применяют технические средства, в частности ЭВМ.
  5. Visual basic for applications (vba)

    Документ
    Visual Basic является мощным программным средством, с помощью которого можно реализовать широкий спектр прак­тических задач. Основное достоинство этого языка програм­мирования состоит в том, что он оптимально сочетает в себе простоту

Другие похожие документы..