Динамическое построение структур в памяти

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
Jack Ferre
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 132
Зарегистрирован: 17.02.2014 (Пн) 14:31
Откуда: Казахстан, Костанай

Re: Динамическое построение структур в памяти

Сообщение Jack Ferre » 07.08.2014 (Чт) 17:48

ALX_2002 писал(а):Пришлось конвертить данные под однобайтовые через StrConv, а то функция поиска не находит ключи с Label-ами созданными другой программой.

Я так понимаю, ALX_2002, что вы не видели что внутри этих "Label-ов созданных другой программой". Вангую что там utf8 и вы неправильно их читаете (например из массива байт в String) и VB сам конвертирует строку StrConv(, vbUnicode). А после вы обратно конвертируете StrConv(, vbFromUnicode)

ALX_2002 писал(а):Как сослаться на них для одной структуры это я понял, а когда структур несколько не понимаю как процесс работает.

Объявление функции C_FindObjectsInit в студию!

UPD:
Хакер писал(а):
Jack Ferre писал(а):Для решения этой проблемы замените строку PutString CStr(value) на PutString ВашаФункцияКодированияВUTF8(CStr(value))

За такие вещи надо отрывать руки, в образовавшиеся торцы вкручивать кол.


Подробнее опишите ваше негодование
Последний раз редактировалось Jack Ferre 07.08.2014 (Чт) 18:10, всего редактировалось 2 раз(а).

ALX_2002
Мега гуру
Мега гуру
 
Сообщения: 2054
Зарегистрирован: 25.11.2002 (Пн) 20:03

Re: Динамическое построение структур в памяти

Сообщение ALX_2002 » 07.08.2014 (Чт) 17:50

Хакер, :oops: ушёл читать матчасть по ссылке. Буду исправляться. :)

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Динамическое построение структур в памяти

Сообщение Хакер » 07.08.2014 (Чт) 17:53

И вообще, нужно сперва разобраться.

Предполагается ли один и тот же предзаполненный объект с цепочкой аргументов использовать для нескольких подряд идущих вызовов к API библиотеки (одинаковых или разных).

Если не предполагается, то нет смысла и в классе.

В противном случае, нужно озаботиться о контроле времени жизни отрендеренной структуры. Есть два варианта.
  • Экземпляр-билдер хранит внутри себя внутреннее представление данных. В нужный момент времени пользователь экземпляра вызывает у класса метод Render, который принимает by-ref структуру, имеющую два поля: attrs() as CK_ATTRIBUTE и buffer() as Byte. В момент вызова метода Render билдер конвертирует своё внутреннее представление данных во внешнее представление и заполняет им свободную структуру, которую ему передали. Далее ответственность за отрендеренные данные лежат уже на вызвавшей стороне. Уничтожением и контролем времени жизни занимается она. В экземпляре-билдере можно после вызова метода Render менятоь данные, но отрендеренная структура от этого уже не пострадает (не изменится). Т.е. после вызова метода Render судьба внутренних данных и судьба отрендеренных данных — расходятся в разные стороны и не зависят друг от друга.
  • Другой подход, если рендерить часто не нужно и отрендеренные данные нужно использовать на протяжении долгого времени. И если не желательно иметь копию одних и тех же данных в памяти. Второй подход предполагает следующую идеологию:

    Класс должен иметь две группы методов:
    • Методы для управления цепочкой атрибутов (Add, Delete, Replace и т.п.)
    • Пара методов LockData и UnlockData.

    У экземпляра должен быть внутренний флаг состояния, который может принимать два значения: unlocked/locked.

    По умолчанию экземпляр рождается в состоянии unlocked. В этом состоянии работают и могу быть использованы методы управления данными (add/delete/replace). В этом состоянии неизвестен адрес на начало цепочки структур.

    Затем, когда данные подготовлены, вызывающая сторона вызывает метод LockData. Метод LockData переводит экземпляр в состояние locked. И возвращает указатель на данные.

    В состоянии locked методы управления данными (add/delete/replace) не работают, при попытке их вызвать должна генерироваться ошибка. В этом состоянии экземпляр гарантирует, что данные остаются на своём месте в памяти, что они никуда не сдвинутся, не удалятся и не изменятся изнутри. Указатель считается актуальным до тех пор, пока не будет вызван метод UnlockData.

    Вызов UnlockData переводит экземпляр обратно в состояние unlocked, в котором разблокируются методы управления данными, и одновременно делает указатель неактуальным (вызывающая сторона больше не имеет права его использовать после вызова Unlock).
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Динамическое построение структур в памяти

Сообщение Хакер » 07.08.2014 (Чт) 18:13

Jack Ferre писал(а):Подробнее опишите ваше негодование :)


Какой тип возврата будет иметь твоя ВашаФункцияКодированияВUTF8?
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Jack Ferre
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 132
Зарегистрирован: 17.02.2014 (Пн) 14:31
Откуда: Казахстан, Костанай

Re: Динамическое построение структур в памяти

Сообщение Jack Ferre » 07.08.2014 (Чт) 18:20

String. Но не типа Привет => Привет, а байтовый массив помещенный в строку.

[offtopic]Хакер Сообщения:
15k.png
15k.png (23.64 Кб) Просмотров: 7282

http://bbs.vbstreets.ru/viewtopic.php?f=9&t=45651[/offtopic]

The trick
Постоялец
Постоялец
 
Сообщения: 781
Зарегистрирован: 26.06.2010 (Сб) 23:08

Re: Динамическое построение структур в памяти

Сообщение The trick » 07.08.2014 (Чт) 19:02

Jack Ferre писал(а):При попытки добавления массива байт после любого другого типа данных программа падает.

Приведи пример.
UTF-8 строку можно хранить как хочешь, хоть в String, хоть в массиве байтов, хоть в массиве Double или Currency, разницы нет если только ты не начнешь работать непосредственно родными функциями VB6 с этой строкой, но т.к. VB6 не работает с UTF-8 поэтому это можно смело игнорировать. Конечно самый оптимальный - байтовый массив, получил размер UTF-8 сроки в байтах, переопределил массив на нужное кол-во элементов и передал его функции, т.к. количество байт может быть не кратно двум байтам. Родными функциями VB6 насколько я знаю нельзя выделить строковой буфер в байтах, но можно UCS-2 буфер обрезать через MidB, LeftB и т.п., но практичней все-равно байтовый массив.
UA6527P

Jack Ferre
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 132
Зарегистрирован: 17.02.2014 (Пн) 14:31
Откуда: Казахстан, Костанай

Re: Динамическое построение структур в памяти

Сообщение Jack Ferre » 07.08.2014 (Чт) 19:11

Код: Выделить всё
    Dim objCKData As CTrickCKData
   
    Set objCKData = New CTrickCKData
   
    objCKData.Add 1234&
   
    Dim baValue() As Byte
    ReDim baValue(0)
    baValue(0) = 200
   
    objCKData.Add baValue

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Динамическое построение структур в памяти

Сообщение Хакер » 07.08.2014 (Чт) 19:18

Jack Ferre писал(а):String.

Вот.

А VB-шный тип String — это сокращение и недосказанность. Точнее было иметь тип с название UnicodeString. Ещё точнее было бы иметь тип с названием Ucs2String. Ещё точнее было иметь тип с названием BSTR_String.

Так вот, что такое тип String в VB: это тип выражения, значение которого является указателем на начало строки, перед которым префиксирована длина строки, а сама строка хранится в кодировке UCS-2 с двумя символоми на байт.

Хранить в переменной типа String указатель на абы что — это гадость. За это и надо отрывать руки. Иными словами, UTF8-кодированная строка не не может (не имеет права) храниться в VB-шной String-переменной, прикидываясь UCS-2 строкой.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Динамическое построение структур в памяти

Сообщение Хакер » 07.08.2014 (Чт) 19:21

В более общей позиции: идеологически неверной, грязной и противной является практика использования String-переменных как буферов подо что угодно. В роли буферов подо что угодно должны использоваться байтовые массивы.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

The trick
Постоялец
Постоялец
 
Сообщения: 781
Зарегистрирован: 26.06.2010 (Сб) 23:08

Re: Динамическое построение структур в памяти

Сообщение The trick » 07.08.2014 (Чт) 19:31

Jack Ferre,
Код: Выделить всё
Dim objCKData As CTrickCKData
Dim baValue() As Byte

Set objCKData = New CTrickCKData

objCKData.Add 555&

ReDim baValue(0)
baValue(0) = 200

objCKData.Add baValue


В твоем случае передается массив по ссылке (VT_BYREF), это нужно тоже проверять, но я для примера не делал.
UA6527P

Jack Ferre
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 132
Зарегистрирован: 17.02.2014 (Пн) 14:31
Откуда: Казахстан, Костанай

Re: Динамическое построение структур в памяти

Сообщение Jack Ferre » 07.08.2014 (Чт) 19:48

Хакер писал(а):идеологически неверной

Для чего в VB имеется конвертация String <> ByteArray присвоением?
Хакер писал(а):грязной

Если программист не осознаёт, что он делает.
Хакер писал(а):подо что угодно

Я не призываю всё что угодно пихать в переменные типа String.

Кривоус Анатолий писал(а):т.к. количество байт может быть не кратно двум байтам.

Длина строки (LenB) тоже

В следующем я не уверен, просто рассуждение...
При передачи utf8 строки параметром как StrPtr в конце строки имеется Null-Terminator. А что в случае с массивом? Нолик дописывать?

The trick
Постоялец
Постоялец
 
Сообщения: 781
Зарегистрирован: 26.06.2010 (Сб) 23:08

Re: Динамическое построение структур в памяти

Сообщение The trick » 07.08.2014 (Чт) 20:02

Jack Ferre писал(а):Длина строки (LenB) тоже

Я это знаю, и что? Как ты выделишь например Space или Strings буфер в 3 байта, не используя лишнее преобразование через MidB и т.п.?
Jack Ferre писал(а):В следующем я не уверен, просто рассуждение...
При передачи utf8 строки параметром как StrPtr в конце строки имеется Null-Terminator. А что в случае с массивом? Нолик дописывать?

При передаче в cchWideChar = -1, обрабатывается и нуль-терминал. И вообще при Redim'е массива он заполняется нулями.
UA6527P

Jack Ferre
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 132
Зарегистрирован: 17.02.2014 (Пн) 14:31
Откуда: Казахстан, Костанай

Re: Динамическое построение структур в памяти

Сообщение Jack Ferre » 07.08.2014 (Чт) 20:23

ALX_2002, вы видимо увлеклись изучением мат части :) Сам загуглил объявление:
CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount);
В VB видимо как-то так:
Declare Function C_FindObjectsInit(byval hSession as long, byval pTemplate as long, byval ulCount as long) as Long

hSession - хендл сессии.
pTemplate - указатель на массив структур CK_ATTRIBUTE (получаемый с помощью .lpStruct или .Address)
ulCount - количество элементов (получаемое с помощью .Count)

Зачем вам непосредственный доступ к массиву CK_ATTRIBUTE?

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Динамическое построение структур в памяти

Сообщение Хакер » 07.08.2014 (Чт) 21:48

Jack Ferre писал(а):Для чего в VB имеется конвертация String <> ByteArray присвоением?

Очевидно, что для того, чтобы из строки получать её байтовое представление, а из байтового представления UCS-2 строки получать саму строку.
Как это относится к недопустимости хранить в что попало в String-переменных?

Jack Ferre писал(а):Если программист не осознаёт, что он делает.

Хоть как. Нет вообще никого смысла хранить что попало в строках. Даже если программист осознаёт. Писать хорошо код — это писать код для других программистов, а не для компилятора. Другие программисты должны с первого раза понимать суть происходящего. Код должен быть самодокументирующим. В противном случае код называется write-only-кодом (это такое ругательство в программировании). Когда переменная, предназначенная для хранения цепочки UCS-2 символов, хранит какие вопреки своему предназначению что-то другое — это классный пример плохого кода.

В общем, это всё к вопросу о типизации.

Испольлзовать узко-специализированные типы, писать типобезопасный код. Идеален вариант, при котором объявляет тип UTF8_STRING:
Код: Выделить всё
Type UTF8_STRING
    bytes() as Byte
End Type
.

Функции, которые возвращают UTF8-строки должны иметь возврат этого типа, функции, которые ожидают это — должны иметь аргументы этого типа.

Типобезопасный код. Красота. Но никак не String.

То есть, технически хранить UTF8-байты в переменной можно. Но это сродни тому, чтобы хранить хлеб в унитазе. Технически можно, но идеологически — отвратительно.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

ALX_2002
Мега гуру
Мега гуру
 
Сообщения: 2054
Зарегистрирован: 25.11.2002 (Пн) 20:03

Re: Динамическое построение структур в памяти

Сообщение ALX_2002 » 07.08.2014 (Чт) 23:25

Jack Ferre писал(а):ALX_2002, вы видимо увлеклись изучением мат части :) Сам загуглил объявление:
CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount);
В VB видимо как-то так:
Declare Function C_FindObjectsInit(byval hSession as long, byval pTemplate as long, byval ulCount as long) as Long
hSession - хендл сессии.
pTemplate - указатель на массив структур CK_ATTRIBUTE (получаемый с помощью .lpStruct или .Address)
ulCount - количество элементов (получаемое с помощью .Count)


Дело в том, что эта функция cdecl, а VB нормально не даёт работать с ними в момент отладки. Поэтому я не объявляю её в проекте, а использую через переходник. Поэтому и задумался, как ответить на ваш вопрос :roll:

Jack Ferre писал(а):Зачем вам непосредственный доступ к массиву CK_ATTRIBUTE?

Так мне в pTemplate как раз и нужно передать указатель на массив этих CK_ATTRIBUTE

The trick
Постоялец
Постоялец
 
Сообщения: 781
Зарегистрирован: 26.06.2010 (Сб) 23:08

Re: Динамическое построение структур в памяти

Сообщение The trick » 08.08.2014 (Пт) 2:32

Функция часто вызывается?
Почему бы на этапе разработки не вызывать ее через DispCallFunc, никаких проблем с CDecl.
UA6527P

ALX_2002
Мега гуру
Мега гуру
 
Сообщения: 2054
Зарегистрирован: 25.11.2002 (Пн) 20:03

Re: Динамическое построение структур в памяти

Сообщение ALX_2002 » 08.08.2014 (Пт) 9:54

Кривоус Анатолий, не знал про неё. Посмотрел примеры, везде обращение идёт к функциям в самом коде.

http://mikejuniperhill.blogspot.ru/2013/11/windows-api-dispcallfunc-as-function.html

http://www.vbforums.com/showthread.php?549294-RESOLVED-DispCallFunc-on-VB-Classes

http://akihitoyamashiro.com/en/VBA/FunctionPointer.htm

Не подскажете - где посмотреть пример, как обращаться к функциям в другой библиотеке ? Я так понимаю там нужно сделать LoadLibrary , а потом этой функции передать хэндл ? Или ошибаюсь ?

* 11:05

Вроде бы нашёл. http://akihitoyamashiro.com/VBA/FunctionPointer.htm

The trick
Постоялец
Постоялец
 
Сообщения: 781
Зарегистрирован: 26.06.2010 (Сб) 23:08

Re: Динамическое построение структур в памяти

Сообщение The trick » 08.08.2014 (Пт) 10:09

ALX_2002 писал(а):Не подскажете - где посмотреть пример, как обращаться к функциям в другой библиотеке ? Я так понимаю там нужно сделать LoadLibrary , а потом этой функции передать хэндл ? Или ошибаюсь ?

http://www.cyberforum.ru/post6402869.html
UA6527P

ALX_2002
Мега гуру
Мега гуру
 
Сообщения: 2054
Зарегистрирован: 25.11.2002 (Пн) 20:03

Re: Динамическое построение структур в памяти

Сообщение ALX_2002 » 08.08.2014 (Пт) 10:46

Кривоус Анатолий, очень заинтересовался. Благодарю. :) Вроде бы даже понял принцип. Уже даже попробовал с одной из функций из библиотеки. Задумался о написании Class обёртки, чтобы вызывать разные функции из библиотеки. Возник вопрос - Можно ли программно определить какой тип вызова требует функция stdcall / cdecl, чтобы автоматически подставить в вызов DispCallFunc ?

The trick
Постоялец
Постоялец
 
Сообщения: 781
Зарегистрирован: 26.06.2010 (Сб) 23:08

Re: Динамическое построение структур в памяти

Сообщение The trick » 08.08.2014 (Пт) 11:06

ALX_2002 писал(а):Можно ли программно определить какой тип обращения требует библиотека stdcall / cdecl, чтобы автоматически подставить в вызов DispCallFunc ?

В общем случае нельзя, т.к. STDCALL без аргументов эквивалентен любому CDECL в плане стека. Можно написать дизассемблер и анализировать код, но оно того не стоит. Вообще тип соглашения должен быть известен на момент вызова из документации, иначе вызов некорректен. Можно сохранять стек перед вызовом, а после вызова восстанавливать - тогда можно вызывать CDECL и STDCALL одним кодом.
Последний раз редактировалось The trick 08.08.2014 (Пт) 11:24, всего редактировалось 1 раз.
UA6527P

ALX_2002
Мега гуру
Мега гуру
 
Сообщения: 2054
Зарегистрирован: 25.11.2002 (Пн) 20:03

Re: Динамическое построение структур в памяти

Сообщение ALX_2002 » 08.08.2014 (Пт) 11:18

Кривоус Анатолий, понял. Согласен. Я пока пользовался модулем cCallFunc для обращения к библиотеке. Там разработчик недоступным для моего понимания методом определял тип обращения. Но наверное и правда лучше указывать это самостоятельно. Функционал DispCallFunc мне очень понравился. Буду разбираться с ним.

P.S Тем более Хакер cCallFunc не одобрил. Значит надо переползать на другие варианты. :D

ALX_2002
Мега гуру
Мега гуру
 
Сообщения: 2054
Зарегистрирован: 25.11.2002 (Пн) 20:03

Re: Динамическое построение структур в памяти

Сообщение ALX_2002 » 08.08.2014 (Пт) 14:15

Кривоус Анатолий, видимо я сосредоточение вселенской глупости. :idea: .

Стал собирать Class по аналогии с cCallFunc и упёрся в косяк с параметром prgpvarg. Думал передам в процедуру ParamArray() а функции "скормлю" указатель на первый элемент этого массива и она сама найден где данные.

Почему так подумал ? - По аналогии залез в cCallFunc. Там параметры для функции передаются через ParamArray - Variant(). Выдёргивания адрессов через StrPtr / VarPtr и смещений я там не нашёл. Может это всё делается в ассемблерных вставках ? :|

Посмотрел документацию по функции DispCallFunc на MSDN - ссылка.

Код: Выделить всё
HRESULT DispCallFunc(
  void *pvInstance,
  ULONG_PTR oVft,
  CALLCONV cc,
  VARTYPE vtReturn,
  UINT cActuals,
  VARTYPE *prgvt,
  VARIANTARG **prgpvarg,
  VARIANT *pvargResult
);

Parameters

pvInstance    An instance of the interface described by this type description.
oVft       For FUNC_VIRTUAL functions, specifies the offset in the VTBL.
cc       The calling convention. One of the CALLCONV values, such as CC_STDCALL.
vtReturn      The variant type of the function return value. Use VT_EMPTY to represent void.
cActuals      The number of function parameters.
prgvt       An array of variant types of the function parameters.
prgpvarg      The function parameters.
pvargResult   The function result.


Сделал в класс модуле процедуру CallFunc.

Код: Выделить всё
....

'Метод вызова функций в DLL
Sub CallFunc(ProcName As String, CallConvention As tagCALLCONV, ReturnValueType As VbVarType, ReturnValuePointer As Long, ParamArray Parameters() As Variant)
    Dim lProcAddress As Long
    ReDim aParametersTypes(UBound(Parameters)) As Integer
    Dim i
    'Получаем адрес функции в библиотеке
    lProcAddress = GetProcAddress(lLibraryHandle, ProcName)
   
    If lProcAddress = 0 Then Err.Raise vbObjectError + 1, ERR_SRC, "Function not found"
   
    'Заполняем массив типов переменных
    For i = 0 To UBound(Parameters)
        aParametersTypes(i) = VarType(Parameters(i))
    Next
   
    Dim rv As Long
   
    rv = DispCallFunc(0, _
                      lProcAddress, _
                      CallConvention, _
                      ReturnValueType, _
                      UBound(Parameters) + 1, _
                      VarPtr(aParametersTypes(0)), _
                      VarPtr(Parameters(0)), _
                      ReturnValuePointer)
End Sub

....


Я так понимаю передавать VarPtr(Parameters(0)) архинеправильно ? Т.е нужно городить код по сбору массива с указателями на данные внутри Variant-ов ? :(

Проект добавил в аттаче.

*Добавил позже*

Нашёл пример с ParamArray ссылка

Хм.

Код: Выделить всё
        ReDim lngPtArgs(lngArgs - 1), intVtArgs(lngArgs - 1)
        For n = 0 To lngArgs - 1
            intVtArgs(n) = VarType(Args(n))
            lngPtArgs(n) = VarPtr(Args(n))
        Next


Т.е от от каждого элемента Args() - ParamArray берут VarPtr. :( Без всякого анализа. Оно так должно работать ?

*Добавлено ещё позже*

Попробовал сделать по аналогии с примерами. Сделал вызов MessageBox. Вызов работает, но потом крашит IDE. Чувствую себя первобытным человеком с микроскопом в руках. Последнюю версию своего "чуда" приаттачил.
Вложения
debug_last.zip
(2.33 Кб) Скачиваний: 126

Jack Ferre
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 132
Зарегистрирован: 17.02.2014 (Пн) 14:31
Откуда: Казахстан, Костанай

Re: Динамическое построение структур в памяти

Сообщение Jack Ferre » 08.08.2014 (Пт) 17:25

Попробовал завернуть DispCallFunc в класс. Вот что получилось:

CLib.cls
Код: Выделить всё
Option Explicit

Private Declare Function LoadLibrary Lib "kernel32.dll" Alias "LoadLibraryA" (ByVal lpFileName As String) As Long
Private Declare Function FreeLibrary Lib "kernel32.dll" (ByVal hModule As Long) As Long
Private Declare Function GetProcAddress Lib "kernel32.dll" (ByVal hModule As Long, ByVal lpProcName As String) As Long

Dim m_handle As Long

Public Function LoadLib(sPath As String) As Long
    If m_handle = 0 Then
        m_handle = LoadLibrary(sPath)
        LoadLib = m_handle
    End If
End Function

Public Property Get Loaded() As Boolean
    Loaded = m_handle
End Property

Public Sub FreeLib()
    If m_handle Then
        FreeLibrary m_handle
        m_handle = 0
    End If
End Sub

Public Function GetProcAddr(sProc As String) As Long
    If m_handle Then
        GetProcAddr = GetProcAddress(m_handle, sProc)
    End If
End Function

Private Sub Class_Terminate()
    FreeLib
End Sub


CDispCall.cls
Код: Выделить всё
Option Explicit

Private Declare Function DispCallFunc Lib "OleAut32.dll" ( _
    ByVal pvInstance As Long, _
    ByVal oVft As Long, _
    ByVal cc As Long, _
    ByVal vtReturn As Integer, _
    ByVal cActuals As Long, _
    ByVal prgvt As Long, _
    ByVal prgpvarg As Long, _
    ByVal pvargResult As Long _
    ) As Long

Public Enum CALLCONV
    CC_CDECL = 1
    CC_STDCALL = 4
End Enum

Dim m_count As Long
Dim m_index As Long

Dim m_args() As Variant
Dim m_types() As Integer
Dim m_pointers() As Long



Public Property Let Count(ByVal c As Long)
    m_count = c
    m_index = 0
   
    ReDim m_args(m_count) As Variant
    ReDim m_types(m_count) As Integer
    ReDim m_pointers(m_count) As Long
End Property

Public Sub Push(arg)
    If m_index < m_count Then
        m_args(m_index) = arg
        m_types(m_index) = varType(m_args(m_index))
        m_pointers(m_index) = VarPtr(m_args(m_index))
       
        m_index = m_index + 1
    End If
End Sub

Public Function CallFunc(ByVal lpFunc As Long, ByVal cc As Long, ByVal retType As Integer) As Variant
    If m_index = m_count Then
        Call DispCallFunc( _
            0, _
            lpFunc, _
            cc, _
            retType, _
            m_count, _
            VarPtr(m_types(0)), _
            VarPtr(m_pointers(0)), _
            VarPtr(CallFunc))
    End If
End Function


Пример использования:
Код: Выделить всё
Option Explicit

Private libUser32 As New CLib
Private libNTDll As New CLib

Private Sub Form_Load()
    libUser32.LoadLib "user32"
    libNTDll.LoadLib "ntdll"
   
    MsgBox Array("Ошибка", , , , , , "Да", "Нет")(MessageBoxA(hwnd, "Hello!", "CDispCall sample", vbYesNo))
    MsgBox swprintf("NT dll %d %s %u :)", 777, "sun", 1001001)
End Sub

Private Function MessageBoxA(ByVal hwnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As Long) As Long
    Static lpFunc As Long
   
    If Not libUser32.Loaded Then Exit Function
    If lpFunc = 0 Then lpFunc = libUser32.GetProcAddr("MessageBoxA")
   
    If lpFunc Then
        Dim dc As New CDispCall
       
        dc.Count = 4
       
        dc.Push CVar(hwnd)
        dc.Push CVar(StrConv(lpText, vbFromUnicode))
        dc.Push CVar(StrConv(lpCaption, vbFromUnicode))
        dc.Push CVar(wType)
       
        MessageBoxA = CLng(dc.CallFunc(lpFunc, CC_STDCALL, vbLong))
    End If
End Function

Private Function swprintf(ByVal lpFmt As String, ParamArray params() As Variant) As String
    Static lpFunc As Long
   
    If Not libNTDll.Loaded Then Exit Function
    If lpFunc = 0 Then lpFunc = libNTDll.GetProcAddr("swprintf")
   
    If lpFunc Then
        Dim dc As New CDispCall
       
        dc.Count = 2 + UBound(params) - LBound(params) + 1
       
        Dim s As String
        s = Space(1024)
        dc.Push CVar(StrPtr(s))
       
        dc.Push CVar(StrPtr(lpFmt))
       
        Dim i As Long
        For i = LBound(params) To UBound(params)
            dc.Push CVar(params(i))
        Next i
       
        Dim ret As Integer
        ret = dc.CallFunc(lpFunc, CC_CDECL, vbInteger)
        swprintf = Left$(s, ret)
    End If
End Function


Хакер, Кривоус Анатолий,
Проверьте пожалуйста. Может стоит это добавить в "кирпичный завод".

Jack Ferre
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 132
Зарегистрирован: 17.02.2014 (Пн) 14:31
Откуда: Казахстан, Костанай

Re: Динамическое построение структур в памяти

Сообщение Jack Ferre » 08.08.2014 (Пт) 22:05

Хакер писал(а):Какой тип возврата будет иметь твоя ВашаФункцияКодированияВUTF8?

Jack Ferre писал(а):String.

Что в моем способе решения, что в способе Кривоуса Анатолия, мы на входе получаем Variant и определяем его тип вызовом VarType() и в зависимости от результата заполняем поле type структуры CK_ATTRIBUTE. И если на входе будет массив байт, то структура заполнится неправильно.

Так зачем же
Хакер писал(а):За такие вещи надо отрывать руки, в образовавшиеся торцы вкручивать кол.

?

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Динамическое построение структур в памяти

Сообщение Хакер » 08.08.2014 (Пт) 22:22

Обязательно нужно отрывать. Так так и не понял.

Кроме того, неправильность подхода лежит в двух осях. То, что я уже описал, допустим, была горизонтальная. Есть ещё и вертикальная.

Так вот, дело в том, что каждая вещь должна делать на уровне и в зоне ответственности своего компонента. Мы обсуждаем класс, который предоставляет всей остальной VB-программе услугу по формированию структур в виде и формате, необходимом для потребления их библиотекой PKCS11. Пока всё хорошо. С одной стороны — вся остальная VB-программа, которая ни хочет знать ни грамма подробностей о том, как устроена PKCS11 и в каком формате ей нужны данные. С другой стороны библиотека PKCS11, которая просто ждёт правильно сформированную структуру.

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

Это выглядит как хорошее мышление, хороший способ подходить к проектированию.

На практике ты же делаешь ужасную вещь.
Твой класс как абстрактный чёрный ящик, имеющий «входы» и «выходы», предлагающий всей остальной VB-программе услугу по конвертации данных из одного вида в другой, принимает на вход данные в виде, в котором с ними удобно работать всей остальной VB-программе, а на выходе выдаёт данные, в котором они нужны библиотеке PKCS11.

Поэтому абсолютно недопустимо, чтобы класс требовал от внешнего пользователя, чтобы сам внешний пользователь выполнял какие-то преобразования со строками. Это категорически неправильное и неприемлемое разграничение ответственности.

Класс предоставляет всей остальной VB-программе некий сервис. ПОЭТОМУ ОН ОБЯЗАН ПОЛУЧАТЬ ОТ НЕЁ СТРОКУ В ТОМ ВИДЕ, В КАКОМ СО СТРОКАМИ РАБОТАЕТ ВСЯ ОСТАЛЬНАЯ ПРОГРАММА.
Формат для PKCS11 требует UTF-8, и раз класс абстрагирует всю остальную программу от низкоуровневой специфики PKCS11 — это абсолютно внутреннее дело и внутренняя ответственность класса — выполнять преобразованию BSTR→UTF-8.

_______

Приведу ещё несколько примеров.

Можно взять компьютерный монитор. У монитора есть стандартная вилка, с помощью которой он включается в стандартную розетку. Стандартность вилки и розетки подразумевает, что есть некие требования, наложенные на параметры питания (напряжение, частота тока). Так, например, СНГ-шная/европейская стандартная вилка предполагает, что будучи вставленной в розетку, она «обнаружит там» переменное 220 Vrms, переменку 50 Hz.

Так вот, ЖК-монитору нигде не нужно ни 220 V , ни 50 Hz. Ему внутри нужна постоянка, 12V.

Для этого монитору нужно преобразование. Нужен преобразователь.

Подавляющее большинство мониторов имеет преобразователь внутри себя, и поэтому у них снаружи обычная стандартная вилка, которая вставляется в розетку. Монитор снаружи берёт 220V AC, а внутри себя преобразовывает это в то, что именно ему нужно, не утруждая пользователя монитора знать об этом или задумываться насчёт этого.

Редкое меньшинство мониторов имеет внешний блок питания, выполняющий преобразование. Тогда эти мониторы ждут на свой вход питания уже преобразованное напряжение, но при этом у них обязательно другая вилка/гнездо для низковольтного питания. У внешнего БП, у него да: с одной стороны обычная вилка, вставляемая в розетку, а с другой стороны — специализированная низковольтная вилка.

Есть совершенно дебильный вариант, которым не идёт никто, и который нельзя увидеть: это выпускать монитор, который на вход ждёт низковольтное 12-вольтовое питание и при этом имеет обычную вилку, которую можно вставить в розетку на стене.

Если следовать аналогии, то именно это ты и предлагаешь: прибор с вилкой для 220V, но который «ждёт» питание вовсе не от 220V.

Впридачу ты ещё предлагаешь внешний преобразователь, который делает преобразование 220V→12V, но у твоего преобразователя и вход и выход имеют одинаковый разъём: на входе обычная вилка (вставляемая в обычную розетку) и на выходе обычная розетка (куда можно вставить любую обычную вилку).

Ты предполагаешь, что обычную вилку преобразователя вставят в обычную розетку, а обычную вилку монитора — в преобразователь.
Да, физически, ничего не мешает этому работать.

Но при этом ничего не мешает и не ограничивает пользователя от возможности вставить вилку монитора непосредственно в розетку. Ничего не мешает вставить прибор, расчитанный на 220V — в низковольтный выход преобразователя.

Ничего не мешает вставить вход одного преобразователя в выход другого преобразователя и сделать цепочку из них.

Да, физически ничего не мешает правильной задуманной конфигурации работать.

Но этот дизайн, этот способ делать вещи — АБСОЛЮТНО НЕПРИЕМЛЕМЫЙ.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Jack Ferre
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 132
Зарегистрирован: 17.02.2014 (Пн) 14:31
Откуда: Казахстан, Костанай

Re: Динамическое построение структур в памяти

Сообщение Jack Ferre » 08.08.2014 (Пт) 23:56

Хакер, я совершенно не знаком со спецификой работы с PKCS11, и исхожу только лишь из задачи поставленной автором топика. У него уже имеются (каким-либо способом) полученные utf-8 строки хранящиеся в переменных String. Вот их то мой класс и принимает.

По поводу функции ВашаФункцияКодированияВUTF8 as String - её можно заменить на ВашаФункцияКодированияВUTF8 As Byte(). Но вы видимо не читали весь код в топике и не заметили что я функцией PutByteArray сохраняю данные в переменную (О ужас!) типа String. Связано это с тем, что я не знал (да и сейчас не очень интересно) как извлекать ByteArray из Variant-а и заменил извлечение просто конвертацией CStr. А так как он (массив) уже в строке нет смысла конвертировать его обратно. Кривоус Анатолий взялся таки за извлечение массива, но его код еще надо дописать.
Кривоус Анатолий писал(а):В твоем случае передается массив по ссылке (VT_BYREF), это нужно тоже проверять, но я для примера не делал.


Более того при написании класса CCKData я еще не знал, что строки будут в utf-8. После того как узнал, предложил просто добавить "заплатку" в виде PutString ВашаФункцияКодированияВUTF8(CStr(value)).
А потом еще обнаружил следующее:
ALX_2002 писал(а):Пришлось конвертить данные под однобайтовые через StrConv

и сделал вывод, что в строке уже лежит utf-8 в виде "байт-дырка-байт-дырка" (типа Привет => Привет) и никакая заплатка в этом случае не нужна.

Фух. Я этот пост наверное уже час пишу ... запутался ... и нафиг оно мне надо.

Итог.
В случае оставления стороны проекта ALX_2002 "как есть":
case vbString - не трогать. Она уже строка. В ней уже лежит массив байт utf-8. Можно добавить StcConv(,vbFromUnicode), чтобы не вызывать его каждый раз на стороне проекта.
case vbArray+vbByte - довести до ума извлечение из Variant-а (если кто-нибудь возьмется), Также можно было бы оставить Cstr (в решение Кривоуса Анатолия с непрерывным массивом копировать данные из полученной строки в массив с помощью CopyMemory), но за это отрывают руки, вкручивают колья и всё такое :mrgreen: .
В случае переделывания стороны проекта ALX_2002 "как надо":
Передавать в класс utf-8 строки в виде массива байт. Переписать класс - не определять, а явно указывать тип данных.

Не понимаю почему в мой адрес столько обвинений с вашей стороны.

На десерт:
MSDN писал(а):BSTR is an OLE automation type for transferring length-prefixed strings, either Unicode or ANSI, as well as length-prefixed binary data.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Динамическое построение структур в памяти

Сообщение Хакер » 09.08.2014 (Сб) 0:24

Jack Ferre писал(а):Хакер, я совершенно не знаком со спецификой работы с PKCS11, и исхожу только лишь из задачи поставленной автором топика. У него уже имеются (каким-либо способом) полученные utf-8 строки хранящиеся в переменных String. Вот их то мой класс и принимает.

Это не имеет значение. Проблема находится по эту сторону класса, а не по ту.

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

Jack Ferre писал(а):По поводу функции ВашаФункцияКодированияВUTF8 as String - её можно заменить на ВашаФункцияКодированияВUTF8 As Byte().

Её не нужно заменять. Её нужно засунуть внутрь класса и сделать вообще невидимой снаружи. Это внутренняя работа класса. Это то, что должно быть инкапсулировано в нём и скрыто от стороннего наблюдателя. Если в PKCS11 что-то поменяется, и строки нужно будет слать в другом формате, то все изменения в коде будут ограничены правкой внутри класса. Код за пределами класса вообще не узнает, что что-то там поменялось. Он как до этого передавал строки в своём родном формате, так и будет в нём передавать.

Jack Ferre писал(а):Но вы видимо не читали весь код в топике и не заметили что я функцией PutByteArray сохраняю данные в переменную (О ужас!) типа String. Связано это с тем, что я не знал (да и сейчас не очень интересно) как извлекать ByteArray из Variant-а и заменил извлечение просто конвертацией CStr.

Интерфейс всегда важнее реализации. Реализация имеет право временно быть плохой, а вот интерфейс не имеет. Если интерфейс спроектирован неидеально — это навсегда. Это проклятие, родовая травма и уродство кода (и проекта вообще). Заложенное в интерфейс уродство будет отражено в тысячи местах кода, где этот интерфейс используется. Поэтому интерфейс не может быть кривым.

Реализация может временно быть плохой. С тех пор, как определён прекрасный интерфейс, другие люди пишут код, который работает с ним. Эти люди не знают и не думают о том, что реализация отвратительна. Даже если они вдруг начинают это замечать, реализация может быть переписана полностью, и это никакого никак не затронет, кроме как тем, что всё станет работать ещё лучше.

Поэтому на проблему с интерфейсом я смотрю более серьёзно, чем на проблемы с реализацией.

Jack Ferre писал(а):Более того при написании класса CCKData я еще не знал, что строки будут в utf-8. После того как узнал, предложил просто добавить "заплатку" в виде PutString ВашаФункцияКодированияВUTF8(CStr(value)).

Ну. До тебя никак не может дойти, в чём твоя фатальная ошибка.
Если бы ты предложил заплатку вставить внутрь класса, это было бы окей. Но ты предложил задействовать её снаружи по отношению к классу.
Это фатальная ошибка.
Если ты не понимаешь, почему это столь критически важно, то это ещё более фатальная твоего твоего самопонимания как программиста. Ничто так не важно, как это.

Jack Ferre писал(а):Более того при написании класса CCKData я еще не знал, что строки будут в utf-8.

Плюс ко всему, именно в этом и фишка: грамотный человек правильно проектирует интерфейс, даже не зная этих вещей. Грамотный человек заранее предвидит, что строки могут понадобиться в какой-нибудь хитрой кодировке. Поэтому правильная позиция: мы не знаем, в каком именно формате понадобятся строки на выходе, поэтому мы намеренно провозглашаем, что на входе строки всегда должны быть в неком общепринятом для нашего контекста формате (а конкретно — UCS-2), а уже внутренности класса сами сконвертируют в то, что нужно.

Jack Ferre писал(а):
Пришлось конвертить данные под однобайтовые через StrConv

и сделал вывод, что в строке уже лежит utf-8 в виде "байт-дырка-байт-дырка" (типа Привет => Привет) и никакая заплатка в этом случае не нужна.

Это очень странная манера думать и делать выводы.
Function Умножить_A_на_B(a, b): Умножить_A_на_B = a + b: End Function — эта функция прекрасно выполняет задачу по умножению чисел, ведь для Умножить_A_на_B(2, 2) возвращает 4, как и следовало бы ожидать.

Вот примерно такова твоя аргументация. ALX_2002 просто попробовал что-то сделать на примере строки, состоящий только из кодовых точек с кодом меньше 127. И о чудо, это был тот частный случай, где что сложение, что умножение даёт одинаковый результат. Ой, прошу прощение, не сложение и умножение, а Ucs2ToCp1251 и Ucs2ToUtf8 дали одинаковый результат.

Jack Ferre писал(а):Итог.
В случае оставления стороны проекта ALX_2002 "как есть":
case vbString - не трогать. Она уже строка. В ней уже лежит массив байт utf-8. Можно добавить StcConv(,vbFromUnicode), чтобы не вызывать его каждый раз на стороне проекта.
case vbArray+vbByte - довести до ума извлечение из Variant-а (если кто-нибудь возьмется), Также можно было бы оставить Cstr (в решение Кривоуса Анатолия с непрерывным массивом копировать данные из полученной строки в массив с помощью CopyMemory), но за это отрывают руки, вкручивают колья и всё такое :mrgreen: .
В случае переделывания стороны проекта ALX_2002 "как надо":
Передавать в класс utf-8 строки в виде массива байт. Переписать класс - не определять, а явно указывать тип данных.

Оба хуже (c)

Я уже не говорю про гадости типа vbArray+vbByte вместо vbArray Or vbByte — на фоне прочего они выглядят мелочами.

Передавать в класс строки нужно в обычном привычном для всего остального кода формате. Про UTF8 код, который пользуется классом, знать не должен вообще. Unless UTF8 играет важную в проекте роль независимо от того факта, что библиотека PKCS11 использует UTF8.

Jack Ferre писал(а):На десерт:
[quote=MSDN]BSTR is an OLE automation type for transferring length-prefixed strings, either Unicode or ANSI, as well as length-prefixed binary data.

Тогда я позволю себе напомнить приведённый выше пример насчёт хранения хлеба в унитазе. Некоторые этимологические словари приводят такую этимологию: «универсальный таз». А раз таз универсальный, то значит и хлеб там можно хранить, почему нет?
[/quote]
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Jack Ferre
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 132
Зарегистрирован: 17.02.2014 (Пн) 14:31
Откуда: Казахстан, Костанай

Re: Динамическое построение структур в памяти

Сообщение Jack Ferre » 09.08.2014 (Сб) 0:40

Да что ж такое!

Хакер, я прекрасно понимаю все ваши предыдущие посты касательно строк.
Но почему вы меня обвиняете в чем либо?
Ведь я сделал класс для решения задачи топика "Динамическое построение структур в памяти" в формате необходимом для чтения их функциями API PKCS11 и привел пример, что данные успешно извлекаются. В этом классе в метод PutData в блок Select Case можно добавить хоть все типы поддерживаемые Variant-ом и обрабатывать их к в методе PutData или в функциях Put<ТипДанных> как душе угодно. Я же лишь привел пример с минимальных количеством действий. Этот лишь пример! Его нужно понять и переделать как правильно. Ведь топик (был) не про строки, utf-8, массивы байт и конвертацию.
И всего лишь из-за одной строки
Jack Ferre писал(а):Для решения этой проблемы замените строку PutString CStr(value) на PutString ВашаФункцияКодированияВUTF8(CStr(value))

вы написали пару сотен строк с обвинением меня.
Последний раз редактировалось Jack Ferre 09.08.2014 (Сб) 2:12, всего редактировалось 1 раз.

Jack Ferre
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 132
Зарегистрирован: 17.02.2014 (Пн) 14:31
Откуда: Казахстан, Костанай

Re: Динамическое построение структур в памяти

Сообщение Jack Ferre » 09.08.2014 (Сб) 1:05

Хакер писал(а):Если бы ты предложил заплатку вставить внутрь класса, это было бы окей. Но ты предложил задействовать её снаружи по отношению к классу.
Это фатальная ошибка.

PutString - приватная функция, вызываемая из метода PutData. Интерфейс класса я описывал.
Хакер писал(а):Тогда я позволю себе напомнить приведённый выше пример насчёт хранения хлеба в унитазе

Хлеб нельзя хранить в унитазе по причине гигиены и повышенной влажности. Хлеб испортится (в отличии от массива байт лежащих в BSTR :mrgreen: ).

Хакер писал(а):Я уже не говорю про гадости типа vbArray+vbByte вместо vbArray Or vbByte — на фоне прочего они выглядят мелочами.
.
Пока я признаю только это. Да vbArray - это флаг, но его хватило (2000), чтобы не пересекаться с константами vbVarType. И вселенная не обратилась прахом от моей погрешности.

Хакер писал(а):Это очень странная манера думать и делать выводы.

ALX_2002 писал(а):в документации написано - RFC2279 string.. Поискал что это такое. Пишут, что это UTF-8.

+
ALX_2002 писал(а):ключи с Label-ами созданными другой программой.

+
ALX_2002 писал(а):Пришлось конвертить данные под однобайтовые через StrConv

=
в строке уже лежит utf-8 в виде "байт-дырка-байт-дырка". Мне это кажется логичным :?

Jack Ferre писал(а):И всего лишь из-за одной строки вы написали пару сотен строк с обвинением меня.

А пока я писал предыдущий пост еще сотню.

Jack Ferre
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 132
Зарегистрирован: 17.02.2014 (Пн) 14:31
Откуда: Казахстан, Костанай

Re: Динамическое построение структур в памяти

Сообщение Jack Ferre » 09.08.2014 (Сб) 1:48

Окай! Пусть класс CCKData.cls - 100% говнокод. Ибо объяснения, что это некий черновик, ни к чему не приводят.



Что насчет моей обертки DispCallFunc?

Пред.След.

Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: Majestic-12 [Bot], Yandex-бот и гости: 14

    TopList