Выделить память под строку любым удобным способом. Правда все остальные способы типа VirtualAlloc, HeapAlloc будут очень неудобными с учётом того, что тут задействован VB со своей переменной типа As String. Этот тип просто заставляет использовать SysAllocStringByteLen. Хотя можно использовать и VirtualAlloc, но при этом кода будет написано значительно больше.
cresta Память, выделенная VirtualAlloc не будет освобождаться в VB, а вот Heap и Global вполне можно юзать и все "лишние телодвижения" при замене SysAllocStringByteLen например на GlobalAlloc сводятся всего к добавлению пары строчек В твоем примере: Код (Text): ... lea edi,[ebx+eax+5] invoke GlobalAlloc 0, edi sub edi,5 mov [eax],edi lea edi,[eax+4] ... А если эти строчки в процедурку оформить типа vbStrAlloc, то и вовсе будет тоже самое
ага, только ты забыл ещё vb-часть дописать Dim retString As String Dim strAddr As Long 'тут получаем из dll строку retString = bla-bla. 'тут восстанавливаем адрес, полученый от GlobalAlloc strAddr = StrPtr(bla-bla-bla) - 4 'тут освобождаем её: GlobalFree (strAddr) ну и плюс твои "пара строчек". Зачем все это, если vb позволяет обойтись без всего этого? Если хочется мазохизма, то можно к примеру драйвер написать, там выделить nonPaged память, соединить в ней строки, затем скопировать результат в shared section, взвести event, чтобы vb забрал эту секцию, и т.д. и т.п. Способов можно миллион придумать, вот только удобней и проще чем SysAlloc ничего не получится
cresta Да с чего ты это взял ? За чистый VB ручаться не могу (не проверял), а вот к примеру VBA MS Word сам прекрасно освобождает память, выделенную Heap\GlobalAlloc. Можешь сам проверить, добавив в dll функцию контроля выделенного блока Код (Text): --------- dll ------------ .data saveptr dd 0 sfree db 0,0,0,0,"???",0 fmt db "Len = %d",13,10,"Str = %s",0 caption db "CheckStrPtr",0 msg db 256 dup (0) SomeFunc proc ... ... invoke GlobalAlloc 0, edi mov saveptr, eax ;сохраняем указатель для контроля ... SomeFunc endp CheckStrPtr proc uses ebx mov eax,saveptr lea ebx, [sfree+4] test eax,eax je @F invoke GlobalFlags,eax cmp eax,GMEM_INVALID_HANDLE je @F mov ebx,saveptr add ebx,4 @@: invoke wsprinf, addr msg, addr fmt,[ebx-4],ebx ;add esp,4*4 invoke MessageBox,0,addr msg,addr caption,0 ret CheckStrPtr endp --------- VB ------------ sub XXX () Dim S as string S = SomeFunc(...) CheckStrPtr 'блок занят строкой, возвращаемой SomeFunc MsgBox S 'S = "" CheckStrPtr 'блок свободен или перезаписан другими данными end sub sub YYY () 'для надеги можно проверить отдельно CheckStrPtr end sub Ну это как посмотреть Если бы SysAlloc сидела в kernel32, то конечно незачем, а вот ради экономии двух строчек цеплять oleout32 если тот же vb позволяет обойтись без этого - кому-то может показаться излишеством И потом использование Heap\Global позволяет например не только возвращать строку из функции, но и перезаписывать строку, переданную ByRef - простым Realloc (хотя сисами наверное тоже можно
А есть ли возможность передавать в ВБ массив (строк или чисел) приблизительно так? Код (Text): Declare Function Conversion(ByVal inputData as long,ByRef outputData() as long) as long Private D() as long Sub XXX() Dim i as long di=12.3E+12 Conversion di,D For i=0 to Ubound(D) Me.Print ,i,CStr(D(i)) Next End Sub То есть передать в длл-ку неинициализированный массив, а получить на выходе обработанные данные в массиве.
OFFSIDE Ну если ты уже разобрался со структуами BSTR\ABSTR, то теперь можешь смело браться и за SafeArray )
leo Не могу вернуть значение: Код (Text): StrProc proc stroka:dword,stroka1:dword mov eax,stroka mov stroka1,eax ret StrProc Endp Код (Text): Declare Function StrProc(ByVal inputData as String,ByRef outputData as String) as long Private D as String Sub XXX() Dim Di as String di="Бла-Бла" StrProc di,D Me.Print D End Sub Как сделать возврат в приложение?
Нашел ответ ): Код (Text): StrProc proc stroka:dword,stroka1:dword mov esi,[stroka1] mov eax,stroka mov [esi],eax ret StrProc Endp
Сами догадалися? Не зря яж в бегиннерс посты засылаю. Ваша помощь неоценима: Код (Text): StrTest proc Str1:dword,Str2:dword mov esi,[Str2] mov ebx,[esi] invoke szRemove,Str1,Str1,ebx invoke lstrlen,Str1 invoke SysAllocStringByteLen,NULL,eax invoke lstrcpy,eax,Str1 mov [esi],eax ret StrTest endp
Если по смыслу, то лучше назвать их pStr1, ppStr2. Проще invoke SysAllocStringByteLen,Str1,eax Система сама скопирует строку. Читай справку что ли.
OFFSIDE Молодец, часть ответа нашел Но такая прямая перезапись указателя может приводить к утечке памяти. По хорошему нужно делать проверку [esi] - если не равно 0 (NULL) и не равно указателю на первую ByVal строку, то нужно освободить память, иначе после перезаписи указателя старый блок памяти так и останется висеть в куче. А указатель [esi]=0 будет только для неициализированной строки, если же ей что-нибудь присвоить, хоть S="", то под стоку выделяется память и ее нужно освобождать перед изменением указателя Как можно освобождать\выделять память под ByRef строку и изменять ее длину не используя SysXXX (ой, боюсь cresta будет ругаться Если ты еще не понял, как устроена ABSTR, то вот подсказка: ByRef Str2 это указатель на указатель, т.е. mov esi, [Str2] ;esi - указатель на указатель mov ebx,[esi] ;ebx - указатель на ASCIIZ строку, он передается при ByVal lea edi,[ebx-4] ;edi - указатель на дворд длины строки = начало блока памяти для Heap\GlobalFree mov ecx,[edi] == mov ecx,[ebx-4] ;ecx - длина строки без учета замык.нуля Т.е. перед строкой находится дворд, в котором хранится длина строки, и указатель на этот дворд (edi = ebx-4) и является началом выделенного блока памяти. Если строка объявлена как ByVal, то структура остается той же самой, просто в функцию передается сразу значение ebx (но перед ним все равно сидит дворд длины) В твоих примерах #22,23 строка возвращалась с мусором в конце, т.к. ты укорачивал строку, но не уменьшал ее длину - если длина стала меньше, то нужно записать новое значение по смещению (-4) от указателя на строку. Если длина становится больше, то на старое место ее писать опасно - нужно освобождать старый блок по адресу edi=ebx-4, выделять новый блок и записывать в него сначала дворд длины, а затем символы строки с замык.нулем. При этом в [esi] или в eax, если возврат идет через функцию As String, нужно записывать ес-но указатель на первый символ строки, т.е. +4 от начала блока
leo Хитро, весьма кстати. А можно вопрос - почему -4? Длина строки - дворд? И еще - почему если вызвать из вб LoadLibrary и FreeLibrary память не освобождается?
IceStudent Спасибо за указание. По масму даже встроеный хелп весьма хилый, а MSDN весь перечитать - долго и нудно. Но необходимость есть, я это понимаю. И повторюсь - ВАШ опыт весьма ценен. Ваша помощь мне нужна, так как кодить на асме собираюсь вместо поднадоевшего вб, а си я не знаю, и знать не хочу... Может и зря, но учить еще один язык(Си) как-то впадло... Пусть уж будет Ассемблер.
Да. Какая память? LoadLibrary для загрузки библиотек. Не надо весь. Можно просто справку смотреть по конкретной функции. А придётся, т.к. в MSDN примеры на сях, много сорцов на нём и, наконец, заголовочные файлы со структурами и константами.
Надо еще и знать, какой функции... Сажусь за СИ Страдаю косноязычием. Не выгружается созданная мной библиотека из адресного пространства процесса. Смотрю процесс эксплорероом.
Если библиотека была загружена, LoadLibrary увеличивает счётчик ссылок и одним FreeLibrary не обойтись. Или проверь, что возвращает FreeLibrary и GetLastError.
Код (Text): Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000 Const LANG_NEUTRAL = &H0 Private Declare Function StrProc Lib "testdll.dll" (ByVal stroka As String, stroka1 As String) As Long Private Declare Function GetLastError Lib "kernel32" () As Long Private Declare Function LoadLibrary Lib "kernel32.dll" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long Private Declare Function FreeLibrary Lib "kernel32.dll" (ByVal hLibModule As Long) As Long Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" (ByVal dwFlags As Long, lpSource As Any, ByVal dwMessageId As Long, ByVal dwLanguageId As Long, ByVal lpBuffer As String, ByVal nSize As Long, Arguments As Long) As Long Private Sub Cmd_Click() Dim c As Long, e As Long, buffer As String buffer = Space(200) c = LoadLibrary("testdll.dll") Dim k As String k = " " StrProc "ds gg hf g", k Me.Print k e = FreeLibrary(c) MsgBox e FormatMessage FORMAT_MESSAGE_FROM_SYSTEM, ByVal 0&, GetLastError, LANG_NEUTRAL, buffer, 200, ByVal 0& MsgBox buffer End Sub Возвращает 1, GetLastError = 0... Но из процесса ссылка на ддл-ку не уходит... А что нужно еще?
OFFSIDE Я не знаю VB, но вот эта строка, судя по остальным, не просто объявляет функцию, а и импортирует её. Кстати, попутный вопрос. Если библиотека загружена лоадером, её нельзя выгрузить через FreeLibrary(GetModuleHandle("lib.dll")) ? У меня не получилось, FreeLibrary возвращает true, но библиотека не выгружается. -- Разобрался, лоадер кеширует загруженные библиотеки и помечает те, которые были загружены не явно, а для таблицы импорта. Поэтому и не даёт выгрузить последние.
IceStudent Хотелось бы подгрузить длл-ку и освободить ее после выполнения кода(как к примеру, стелс - вирус), чтобы ушастый юзер не мог увидеть ее никоим образом...