На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела Visual Basic: Общие вопросы
Здесь обсуждаются вопросы по языку Visual Basic 1-6 (а так же по схожим языкам, как, например, PowerBASIC).
Вопросы по Visual Basic .NET (это который входит в состав Visual Studio 2002/2003/2005/2008+, для тех, кто не в курсе) обсуждаются в разделе .NET.

Обратите внимание:
1. Прежде чем начать новую тему или отправить сообщение, убедитесь, что Вы не нарушаете правил форума!
2. Обязательно воспользуйтесь поиском. Возможно, Ваш вопрос уже обсуждали. Полезные ссылки приведены ниже.
3. Темы с просьбой выполнить какую-либо работу за автора в этом разделе не обсуждаются. Студенты, вам сюда: ПОМОЩЬ СТУДЕНТАМ!
4. Используйте теги [ code=vba ] ...текст программы... [ /code ] для выделения текста программы подсветкой.
5. Помните, здесь телепатов нет. Формулируйте свой вопрос максимально грамотно и чётко: Как правильно задавать вопросы
6. Запрещено отвечать в темы месячной (и более) давности, без веских на то причин.

Полезные ссылки:
user posted image FAQ Сайта user posted image FAQ Раздела user posted image Кладовка user posted image Наши Исходники user posted image API-Guide user posted image Поиск по Разделу user posted image MSDN Library Online user posted image Google

Ваше мнение о модераторах: user posted image SCINER, user posted image B.V.
Модераторы: SCINER, B.V.
  
> Как устроить такой фокус: строка как байтовый массив
    Возможно ли, и как, если возможно, проделать такой фокус: чтобы некая переменная-строка и некая переменная - массив байтов ссылались на один и тот же участок памяти? Чтобы изменение байтового массива было бы одновременно и изменением строки?

    На сколько я понимаю, такое должно получиться, если та и другая переменная будут пользоваться одним и тем же указателем... Я правильно понимаю?
      Неужели на VBS не ответили?
        B.V.
        Там направили. Правда, как раз туда, откуда у меня сама мысль появилась.

        А чё, если в одном месте спросил, в другом уже нельзя спрашивать?
          Фокус:

          ExpandedWrap disabled
            Option Explicit
             
            Private Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (ByRef pArr() As Any) As Long
            Private Declare Sub GetMem4 Lib "msvbvm60" (ByVal pSrc As Long, ByVal pDst As Long)
             
            Private Sub Form_Load()
                Dim bArr(0 To 23) As Byte
                Dim strFoo As String
                Dim pSA As Long
                strFoo = "some text"
                
                GetMem4 ArrPtr(bArr), VarPtr(pSA)
                
                Dim pStr As Long
                pStr = StrPtr(strFoo)
                
                GetMem4 VarPtr(pStr), (pSA + 12)
                
                bArr(2) = Asc("n")
                
                MsgBox strFoo
            End Sub


          Аплодисменты...
            Бинго! Здорово!!!

            А как это работает, не расскажешь? Секрет фонкуса, так сказать?

            Что такое 12? какой-то сдвиг в три лонга?

            Переписал в процедуру, взгляни, всё правильно:
            ExpandedWrap disabled
              Public Sub stringIsBytes(s As String, b() As Byte)
               
                  ReDim b(LenB(s) - 1) As Byte
                  
                  Dim pSA As Long
                  GetMem4 ArrPtr(b), VarPtr(pSA)
                  
                  Dim pStr As Long
                  pStr = StrPtr(s)
                  
                  GetMem4 VarPtr(pStr), (pSA + 12)
                  
              End Sub


            Надо ли как-то по-особенному обращаться с полученным массивом (как-нибудь не просто так удалять или ещё чего)?
              Цитата Артур @
              А как это работает, не расскажешь?


              Все уже рассказано в тех статьях, к которым тебя направили. Если коротко -- из указателя на указатель на SAFEARRAY я получаю указатель на SAFEARRAY, затем просто копирую в SAFEARRAY::pvData указатель на строку.

              Цитата Артур @
              Что такое 12?


              Это смещение, по которому находится pvData (указатель на данные).

              Единственное, что не помешало бы проверить -- чему равен pvData сразу после ReDim. Если он указывает на данные, то их, наверно, надо как-то освобождать перед подменой указателя (SafeArrayDestroyData?).
                B.V.
                Спасибо! Даже уже почти понял. А строки устроены не так, как байтовые массивы? в смысле, у них нет SAFEARRAY?

                Насчёт редимов методом тыка выяснил следующее: если обнулить или переписать строку, связь прерывается (как мне объяснили, под строку выделяется новый адрес и новый участок памяти). Но данные в b() не пропадают. Интересно, почему - ведь если обычную строку обнулить, данные в ней вроде как должны очиститься? Если редимить b(), то связь сохраняется, но строка не увеличивается (это вроде бы понятно), но и не уменьшается - получается, данные опять же не очищаются? (есть подозрение, что они просто помечаются как неиспользуемые и через какое-то время в них может оказаться что-то совсем левое?)

                SafeArrayDestroyData попробовал прикрутить так:
                ExpandedWrap disabled
                  Public Sub stringIsBytes(s As String, b() As Byte)
                      
                      ReDim b(LenB(s) - 1) As Byte
                      
                      Dim psa As Long
                      GetMem4 ArrPtr(b), VarPtr(psa)
                      SafeArrayDestroyData ByVal psa
                      Dim pStr As Long
                      pStr = StrPtr(s)
                      
                      GetMem4 VarPtr(pStr), (psa + 12)
                      
                  End Sub

                Правильно?

                Добавлено
                Всё-таки, мне кажется, что если уж очищать данные массива, то перед редимом, а не после. Может, вот так:

                ExpandedWrap disabled
                  Public Sub stringIsBytes(s As String, b() As Byte)
                      
                      Dim psa As Long
                      GetMem4 ArrPtr(b), VarPtr(psa)
                   
                      If psa Then
                        SafeArrayDestroyData ByVal psa
                        ReDim b(LenB(s) - 1) As Byte
                      Else
                        ReDim b(LenB(s) - 1) As Byte
                        GetMem4 ArrPtr(b), VarPtr(psa)
                      End If
                    
                      Dim pStr As Long
                      pStr = StrPtr(s)
                      
                      GetMem4 VarPtr(pStr), (psa + 12)
                      
                  End Sub
                  Цитата Артур @
                  А строки устроены не так, как байтовые массивы? в смысле, у них нет SAFEARRAY?


                  [Общее] Устройство некоторых типов данны

                  Цитата Артур @
                  Но данные в b() не пропадают. Интересно, почему


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

                  Цитата Артур @
                  есть подозрение, что они просто помечаются как неиспользуемые и через какое-то время в них может оказаться что-то совсем левое


                  Весьма вероятно :)

                  Цитата Артур @
                  SafeArrayDestroyData попробовал прикрутить так:


                  Я SafeArrayDestroyData предложил просто как вариант. Я не знаю, стоит (и можно) ли её тут использовать или нет.
                    Нет народ, то что вы намутили с SafeArrayDestroyData - глупость.
                    Нужно вместо redim использовать Erase (для очистки от старых данных если они были) а затем ручками выставлять все поля структуры.
                      ANDLL
                      То есть, можно вот так:
                      ExpandedWrap disabled
                        Public Sub stringIsBytes(s As String, b() As Byte)
                          
                              Erase b
                              ReDim b(LenB(s) - 1) As Byte
                         
                            Dim psa As Long
                            GetMem4 ArrPtr(b), VarPtr(psa)
                            
                            Dim pStr As Long
                            pStr = StrPtr(s)
                            GetMem4 VarPtr(pStr), (psa + 12)
                            
                        End Sub
                        Нет, именно так и нельзя.
                        Я же говорю, инициализировать поля структуры лучше без Redim, ручками.
                          ANDLL
                          Ну так раскрой мысль. И почему нельзя - чем черевато?
                            Ну видимо тем что после каждой такой итерации память куском в LenB(s) - 1 окажется неприкаянной.
                              ANDLL
                              Но ведь это та же самая память, которая в строке - она не освободится вместе со строкой?
                                Нет, я про ту память которую выделил Redim
                                  Кажется, я понял. После редима мы выделяем под массив LenB(s) - 1 байт. После подмены указателя наш массив ссылается уже на другие данные, а те, что мы самы выделили, остаются где-то в пустоте. Правильно понял?

                                  Значит, LenB(s) нужно записать в SAFEARRAY.rgsabound.cElements? так что ли?

                                  Добавлено
                                  Написал так:
                                  ExpandedWrap disabled
                                    Public Sub stringIsBytes(s As String, b() As Byte)
                                        
                                          Erase b
                                    '      ReDim b(LenB(s) - 1) As Byte
                                          ReDim b(0) As Byte
                                     
                                        Dim pSa As Long
                                        GetMem4 ArrPtr(b), VarPtr(pSa)
                                        
                                        
                                        Dim pStr As Long
                                        pStr = StrPtr(s)
                                     
                                        GetMem4 VarPtr(pStr), (pSa + 12)
                                        PutMem4 pSa + 16, LenB(s)
                                        
                                    End Sub

                                  Вроде, работает. Не редимить совсем не получилось - у пустого массива pSa=0
                                    Цитата Артур @
                                    а те, что мы самы выделили, остаются где-то в пустоте. Правильно понял?


                                    Правильно

                                    Добавлено
                                    Цитата Артур @
                                    Значит, LenB(s) нужно записать в SAFEARRAY.rgsabound.cElements? так что ли?


                                    Вот тут не уверен. Не уверен, что SafeArrayDestroyData затирает это поле.

                                    Цитата Артур @
                                    Вроде, работает. Не редимить совсем не получилось - у пустого массива pSa=0


                                    Надо объявить структуру SAFEARRAY, выделить под неё память, присвоить указатель, ручками проинициализировать... Слушай, а оно тебе надо?
                                      B.V.
                                      Мне кажется, что после redim b(0) все поля, кроме длинны массива, уже нормально проинициализованы. Длину я могу вписать PutMem4 pSa + 16, LenB(s) А один неприкаяный байт прога как-нибудь переживёт.

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

                                      ExpandedWrap disabled
                                        Public Sub stringIsBytes(s As String, b() As Byte)
                                         
                                            Dim psa As Long
                                            GetMem4 ArrPtr(b), VarPtr(psa) 'в pSa получить адрес SAFEARRAY
                                            
                                            If psa = 0 Then 'массив ещё не проредимлен
                                              ReDim b(0) As Byte
                                              GetMem4 ArrPtr(b), VarPtr(psa)
                                            End If
                                            
                                            Dim pStr As Long
                                            pStr = StrPtr(s) ' адрес данных строки
                                         
                                            GetMem4 VarPtr(pStr), (psa + 12) 'подмена данных массива данными строки
                                            
                                            PutMem4 psa + 16, LenB(s) 'записать длинну массива в SAFEARRAY.rgsabound.cElements
                                            
                                        End Sub

                                      А чтобы бейсик сам не уничтожал массивы, делать так:

                                      ExpandedWrap disabled
                                        Public Sub DestroySAFEARRAY(b() As Byte)
                                          Dim pSa As Long, pAr As Long
                                          pAr = ArrPtr(b)
                                          GetMem4 pAr, VarPtr(pSa)
                                          If pSa Then
                                            SafeArrayDestroyDescriptor ByVal pSa
                                            PutMem4 pAr, 0
                                          End If
                                        End Sub


                                      А до того, как байт станет неприкаеным, мы ведь можем узнать, где он лежит - может его можно освободить как-нибудь вручную?
                                        Цитата Артур @
                                        один неприкаяный байт прога как-нибудь переживёт


                                        Это уже моветон. Утечек допускать нельзя.

                                        Еще раз по поводу всей этой затеи с массивом и строкой... если SafeArrayDestroyData освобождает только данные, не изменяя остальные поля структуры, то делай себе ReDim на здоровье (разумеется, перед вызовом SafeArrayDestroyData). Если же SafeArrayDestroyData все-таки изменяет остальные поля -- то
                                        Цитата B.V. @
                                        Надо объявить структуру SAFEARRAY, выделить под неё память, присвоить указатель, ручками проинициализировать
                                          B.V.
                                          Вроде бы, как раз изменяет - после неё массив пуст. Ну а если самому заполнить структуру - как её потом пристегнуть к массиву-то?

                                          А может всё же можно как-нибудь вручную освободить этот единственный байт - ведь его адрес известен? Как вариант борьбы с моветоном: может, запоминать адрес данных массива после redim b(0) и до подмены данных? А затем вместо уничтожения возврощать b исходное состояние. Тогда ведь он сам уничтожится без всякой утечки?
                                            Цитата Артур @
                                            Ну а если самому заполнить структуру - как её потом пристегнуть к массиву-то?


                                            Гм. Так...
                                            ExpandedWrap disabled
                                              GetMem4 pMySA, ArrPtr(bArr)


                                            Цитата Артур @
                                            А может всё же можно как-нибудь вручную освободить этот единственный байт - ведь его адрес известен?


                                            Можно, конечно. Только надо знать, чем (CoTaskMemFree/LocalFree/HeapFree...).
                                              Цитата B.V. @
                                              Можно, конечно. Только надо знать, чем (CoTaskMemFree/LocalFree/HeapFree...).

                                              А от чего это зависит?

                                              Цитата B.V. @
                                              GetMem4 pMySA, ArrPtr(bArr)
                                              А эта пристёгнутая структура должна же будет где-то жить? Под неё же тоже нужно выделять память? а потом и удалять? и всё это во что-то запоминать?

                                              Цитата Артур @
                                              может, запоминать адрес данных массива после redim b(0) и до подмены данных? А затем вместо уничтожения возврощать b исходное состояние
                                              Может, лучше этот способ? Или с ним что-нибудь не так?
                                                Цитата Артур @
                                                А от чего это зависит?


                                                От того, чем выделялась память.

                                                Цитата Артур @
                                                А эта пристёгнутая структура должна же будет где-то жить? Под неё же тоже нужно выделять память? а потом и удалять? и всё это во что-то запоминать?


                                                Ну разумеется.

                                                Цитата Артур @
                                                Может, лучше этот способ?


                                                Можешь сделать так.
                                                  В общем, родил такую вот штуку:

                                                  ExpandedWrap disabled
                                                    Option Explicit
                                                     
                                                    Private Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (ByRef pArr() As Any) As Long
                                                    Private Declare Sub GetMem4 Lib "msvbvm60" (ByVal pSrc As Long, ByVal pDst As Long)
                                                    Public Declare Function PutMem4 Lib "msvbvm60" (ByVal pDst As Long, ByVal NewValue As Long) As Long
                                                     
                                                    '_____________________________________________________________________________________________________
                                                     
                                                    Public Function stringIsBytes(s As String, b() As Byte) As Long 'функция будет возвращать адрес данных массива до подмены
                                                                                                                    '(или ноль, если массив уже привязан к строке)
                                                     
                                                        Dim pSa As Long
                                                        GetMem4 ArrPtr(b), VarPtr(pSa) 'в pSa получить адрес на SAFEARRAY массива
                                                        
                                                        If pSa = 0 Then 'массив ещё не проредимлен
                                                          ReDim b(0) As Byte
                                                          b(0) = 98 'чтобы перед уничтожением проверить, а вернулись ли данные на место. Потом закомментирую
                                                          GetMem4 ArrPtr(b), VarPtr(pSa)
                                                          
                                                          Dim pOld As Long
                                                          GetMem4 (pSa + 12), VarPtr(pOld) 'запоминаем данные по этому адресу (потому что мы их сейчас перепишем)
                                                          stringIsBytes = pOld 'функция возвращает адрес старых данных
                                                        End If
                                                        
                                                        Dim pStr As Long
                                                        pStr = StrPtr(s) ' адрес данных строки
                                                     
                                                        GetMem4 VarPtr(pStr), (pSa + 12) 'подмена данных массива данными строки
                                                        
                                                        PutMem4 pSa + 16, LenB(s) 'записать длинну массива в SAFEARRAY.rgsabound.cElements
                                                        
                                                    End Function
                                                     
                                                    '_____________________________________________________________________________________________________
                                                     
                                                    Public Sub DestroySAFEARRAY(b() As Byte, pOld As Long)
                                                          Dim pSa As Long
                                                          GetMem4 ArrPtr(b), VarPtr(pSa) 'получаем адрес SAFEARRAY
                                                          If pSa Then
                                                            GetMem4 VarPtr(pOld), (pSa + 12)  ' записываем старые данные на место
                                                            PutMem4 pSa + 16, 1 ' записываем старую длинну (у нас был там всего один меченый байт)
                                                            Debug.Print b(0) 'проверяем, вернулись ли старые данные на место (должно быть 98)
                                                            Erase b
                                                          End If
                                                    End Sub


                                                  Специально пометил неприкаянный байт перед подменой, чтобы проверить, вернётся ли он на место перед уничтожением. Вернулся. Вроде бы, вопрос решён?
                                                    Ну, если ты это называешь решением... то да.
                                                      B.V.
                                                      А по какому поводу сарказм? как было бы лучше?
                                                        Цитата B.V. @
                                                        Надо объявить структуру SAFEARRAY, выделить под неё память, присвоить указатель, ручками проинициализировать

                                                        И ничего не придётся запоминать, возвращать...
                                                          Тогда по поводу чего было
                                                          Цитата
                                                          Ну разумеется.


                                                          Мой вариант может и корявый, но мне понятный. А вариант с объявлением SAFEARRAY я представляю довольно смутно, и он мне не то чтобы сильно понятен. Если разберусь, то сделаю... но пока не разобрался.

                                                          допустим, я в функции объявляю sf as SAFEARRAY, заполняю поля и пристёгиваю её к массиву. Но после выхода из функции sf ведь умрёт и память, выделенная под неё, освободится?
                                                            Цитата Артур @
                                                            допустим, я в функции объявляю sf as SAFEARRAY, заполняю поля и пристёгиваю её к массиву


                                                            Эм. А ты правда читал те статьи, ну к которым тебя направили и с которых у тебя мысль появилась? Или ты дальше заголовка их не читал?
                                                            http://www.vbstreets.ru/VB/Articles/65977.aspx, обрати внимание на функцию CreateSAFEARRAY.
                                                              B.V.
                                                              Ты переоцениваешь мою способность понимать такие статьи. Читал, и уже не один раз (и даже, наверное, не десять). И с каждым разом всё больше понятно... Сейчас уже понимаю процентов 5.

                                                              В основном, понятным становится то, что получается использовать. Как использовать CreateSAFEARRAY, мне не понятно. Ну тупой я. Кстати, массив всё рано нужно будет уничтожать самому - ну и какая тогда разница: так создавать и уничтожать или как у меня? Только в том, что с каждой строкой я храню один лонг? Ну ещё в том, что мой вариант не так изящен. Зато я сам до него додумался.


                                                              Цитата
                                                              Надо объявить структуру SAFEARRAY
                                                              В таком случае, к чему был этот совет? у Сергея структура не объявлена (просто описана), а заполняет он её теми же гетмем и путмем. Да ещё наводящим на меня ужас SafeArrayAllocDescriptor

                                                              Кстати, сейчас ещё раз перечитал эту CreateSAFEARRAY - она, оказывается, уже почти понятна :) Через годик уже смогу использовать.

                                                              Добавлено
                                                              Ну вот - и года не прошло...

                                                              ExpandedWrap disabled
                                                                Option Explicit
                                                                 
                                                                Private Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (ByRef pArr() As Any) As Long
                                                                Private Declare Sub GetMem4 Lib "msvbvm60" (ByVal pSrc As Long, ByVal pDst As Long)
                                                                Public Declare Function PutMem4 Lib "msvbvm60" (ByVal pDst As Long, ByVal NewValue As Long) As Long
                                                                Private Declare Sub SafeArrayAllocDescriptor Lib "oleaut32.dll" (ByVal cDims As Long, ppsaOut As Any)
                                                                Private Declare Sub SafeArrayDestroyDescriptor Lib "oleaut32.dll" (psa As Any)
                                                                 
                                                                 
                                                                Public Function stringIsBytes(s As String, b() As Byte) As Long
                                                                 
                                                                    Dim psa As Long, pAr As Long
                                                                    pAr = ArrPtr(b)'адрес массива
                                                                    GetMem4 pAr, VarPtr(psa) 'в pSa получить адрес на SAFEARRAY массива
                                                                    
                                                                    If psa = 0 Then 'массив ещё не проредимлен
                                                                       SafeArrayAllocDescriptor 1, ByVal pAr 'выделяем новый дескриптор массива и память под него
                                                                       GetMem4 pAr, VarPtr(psa) 'получаем дескриптор SAFEARRAY
                                                                       PutMem4 psa + 4, 1 'записываем размер элемента массива (массив байтовый, так что 1)
                                                                    End If
                                                                    
                                                                    Dim pStr As Long
                                                                    pStr = StrPtr(s) ' адрес данных строки
                                                                 
                                                                    GetMem4 VarPtr(pStr), (psa + 12) 'подмена данных массива данными строки
                                                                    
                                                                    PutMem4 psa + 16, LenB(s) 'записать длинну массива в SAFEARRAY.rgsabound.cElements
                                                                    
                                                                End Function
                                                                 
                                                                Public Sub DestroySAFEARRAY(b() As Byte)
                                                                  Dim psa As Long, pAr As Long
                                                                  pAr = ArrPtr(b)
                                                                  GetMem4 pAr, VarPtr(psa)
                                                                  If psa Then
                                                                    SafeArrayDestroyDescriptor ByVal psa
                                                                    PutMem4 pAr, 0
                                                                  End If
                                                                End Sub
                                                              Сообщение отредактировано: Артур -
                                                                Цитата Артур @
                                                                В таком случае, к чему был этот совет?


                                                                Ой, сам не знаю :) В общем, первые два пункта -- SafeArrayAllocDescriptor.
                                                                  В смысле SafeArrayAllocDescriptor это и объявление структцры и выделение памяти? Ну да, я уже догадался :)

                                                                  А то, как я переписал - правильно? Вроде, работает.
                                                                    Цитата Артур @
                                                                    А то, как я переписал - правильно?


                                                                    Вроде как да. Только ты оставил непроинициализированными cDims и lLbound.
                                                                      B.V.
                                                                      Ну так lLbound ведь ноль - какой смысл инициировать?

                                                                      А cDims, вроде бы, заполняется самой SafeArrayAllocDescriptor - у меня размерность одна, так что я сразу 1 и записал
                                                                        Цитата Артур @
                                                                        какой смысл инициировать?


                                                                        Что бы было совсем красиво. Это не обязательно.
                                                                          Я так полагаю, что теперь-то точно - вопрос решён? Жму кнопку :)

                                                                          Добавлено
                                                                          Кстати, в комментариях к статье предложен как раз подход без функций SafeArrayЛяляля - там предлагается рядом с объявлением массива объявлять и его SAFEARRAY, а размерности (которые могут быть разными) как-то хитро хранить в строках (я недопонял).

                                                                          Так мой способ с сохранением адреса старых данных, мне кажется, тоже не так уж и плох. Тем более, что заморачиваться по поводу полей стуктуры вообще не пришлось бы, а один лонг, лежащий рядом с массивом - это даже меньше, чем одна SAFEARRAY, лежащая там же.
                                                                          0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                                                          0 пользователей:


                                                                          Рейтинг@Mail.ru
                                                                          [ Script execution time: 0,1120 ]   [ 16 queries used ]   [ Generated: 13.02.26, 02:33 GMT ]