powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / Visual Basic [игнор отключен] [закрыт для гостей] / Примечания для кнопок в API-Toolbar:глюки:лечение:проблема:CopyMemory/VarPtr
5 сообщений из 5, страница 1 из 1
Примечания для кнопок в API-Toolbar:глюки:лечение:проблема:CopyMemory/VarPtr
    #38176816
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вопрос не простой.
Думаю если кто может объяснить/помочь и т.п. то м.б. Antonary или скукотища, поэтому в основном на них и рассчитываю

Была такая моя тема давно:
Как отобразить примечания для кнопок в API-Toolbar

Был там такой код:
смотреть примерно это место топика
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
    Case WM_NOTIFY 'toolbar
      Call CopyMemory(uNMHDR, ByVal lParam, Len(uNMHDR))
...
        Case TTN_GETDISPINFO
          Call CopyMemory(uNMTTDI, ByVal lParam, Len(uNMTTDI))
          Select Case uNMTTDI.hdr.idFrom
            Case 1:
              s = "Создать"
            Case 2:
              s = "Обновить"
...
          ss = StrConv(s, vbFromUnicode)
          Debug.Print uNMTTDI.lpszText 'чтоб не глючило в IDE
          uNMTTDI.lpszText = StrPtr(ss)
          Call CopyMemory(ByVal lParam, uNMTTDI, Len(uNMTTDI))
        Case Else
...


с подсказкой Antonary насчет Debug.Print

Код работал 3 года в этом виде, но обратил внимание на проблему,
если
1) Примечания на русском
2) Наблюдается на некоторых системах Виста (включая чисто русскую) либо Win7 (на английских)
3) Все системы настроены на поддержку "Русский язык для программ не поддерживающих Юникод" -т.е. это условие выполняется

Но примечания отображаются как "?????????????".

Имея на тек. момент некоторые представления о глючности StrConv(s, vbFromUnicode),
описанные например здесь API -ф-ция возвращает указатель на LPWSTR, как переварить в нормальный вид.
и какие-то наработки,
сделал так (на пальцах-метод проверенный):
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
    Case WM_NOTIFY 'toolbar
      Call CopyMemory(uNMHDR, ByVal lParam, Len(uNMHDR))
...
        Case TTN_GETDISPINFO
          Call CopyMemory(uNMTTDI, ByVal lParam, Len(uNMTTDI))
          Select Case uNMTTDI.hdr.idFrom
            Case 1:
              s = "Создать"
            Case 2:
              s = "Обновить"
...
          uNMTTDI.lpszText = VarPtr(StringToMas(s).ByteStr(0)) 'StrPtr(ss)
          Call CopyMemory(ByVal lParam, uNMTTDI, Len(uNMTTDI))
        Case Else
...
, где

Public Type StringToChar
  ByteStr() As Byte
End Type

Function StringToMas(str As String) As StringToChar
    Dim data As StringToChar
    Dim temp As String
    Dim Ln As Long
    Dim i As Integer
    Dim Ptr As Long
    temp = str
    Ln = Len(temp)
    ReDim data.ByteStr(0 To Ln)
    If Ln > 0 Then
        For i = 0 To Ln - 1
            data.ByteStr(i) = Asc(Mid(temp, i + 1, 1))
        Next i
    End If
    data.ByteStr(Ln) = 0
    StringToMas = data
End Function


Т.е.
1) тупо конвертирую строку в массив байтов ByteStr() As Byte, последний из которых '\0'
2) Подсовываю в uNMTTDI.lpszText указатель на байт (0) этого массива VarPtr(ByteStr(0))

Но: получилось что первые 4 символа строки читаются при выводе примечания как случайная абракодабра , а последующие без ошибок.
Т.е. какая-то сволочь стабильно затирает первые 4 байта: 0-1-2-3

Я выкрутился так:

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
        Case TTN_GETDISPINFO
          Call CopyMemory(uNMTTDI, ByVal lParam, Len(uNMTTDI))
          Select Case uNMTTDI.hdr.idFrom
            Case 1:
              s = "Создать"
            Case 2:
              s = "Обновить"
...

          uNMTTDI.lpszText = VarPtr(StringToMasPlus4(s).ByteStr(4)) 'StrPtr(ss)
          Call CopyMemory(ByVal lParam, uNMTTDI, Len(uNMTTDI))
...
Public Function StringToMasPlus4(str As String) As StringToChar
  Dim data As StringToChar
  Dim temp As String
  Dim Ln As Long
  Dim i As Integer
  Dim Ptr As Long
  temp = str
  Ln = Len(temp)
  ReDim data.ByteStr(0 To Ln + 4)
  If Ln > 0 Then
    For i = 0 To Ln - 1
      data.ByteStr(i + 4) = Asc(Mid(temp, i + 1, 1))
    Next i
  End If
  data.ByteStr(Ln + 4) = 0
  StringToMasPlus4 = data
End Function



Т.е.
1) Резервирую на 4 байта больше
2) пишу текст начиная с байт №4
3) Подсовываю в uNMTTDI.lpszText сразу указатель на байт (4) этого массива VarPtr(ByteStr(4))

Этот workaround заработал.
В том числе на "проблемных" Vista и Win7 ради чего сыр-бор.

Но мне все таки непонятно и в этом вопрос:
Какая сволочь пишет фигню в первые 4 байта?
Уж не CopyMemory ли идущая следом?
И как объяснить этой сволочи чтоб не перезаписывала эти 4 байта?

Ибо вдруг сволочь захочет еще и следующие 4 байта испохабить...
...
Рейтинг: 0 / 0
Примечания для кнопок в API-Toolbar:глюки:лечение:проблема:CopyMemory/VarPtr
    #38176960
Фотография Antonariy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Боюсь, я не смогу помочь, в этом вопросе ты разбираешься уже лучше меня :)
...
Рейтинг: 0 / 0
Примечания для кнопок в API-Toolbar:глюки:лечение:проблема:CopyMemory/VarPtr
    #38177663
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Antonariy, в этом вопросе ты разбираешься уже лучше меня :)
Да ладно.

Там возможно нужно что-то типа AllocateMemory, FreeMemory.
Ну это на уровне дилетанства.
Т.е. VarPtr(Byte(0)) -первые 4 байта из памяти кто-то успевает затереть и делает это стабильно - причем рэндомной информацией.
Т.е. первые 4 символа примечания при тыканье мышкой - каждый раз разная аброкодабра.
Или ошибка в где структуре?
...
Рейтинг: 0 / 0
Примечания для кнопок в API-Toolbar:глюки:лечение:проблема:CopyMemory/VarPtr
    #38177800
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Короче создал тестовый пример для понимания (во вложении)

Во избежании таких глюков надо резервировать память и туда писать строку:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
Private Sub CommandGetPtr_Click()
  Dim str As String
  str = Text1.Text
  hMem = GlobalAlloc(&H40, Len(str) + 1)
  MyPtr = GlobalLock(hMem)
  CopyMemory ByVal MyPtr, ByVal VarPtr(StringToMas(str).ByteStr(0)), (Len(str) + 1)
End Sub



И тогда строка будет храниться по адресу памяти MyPtr сколь угодно долго, и можно ее оттуда читать.
Код: vbnet
1.
  MsgBox PtrToString(MyPtr)


А когда она уже не нужна, надо память разблокировать, дабы не транжирить:
Код: vbnet
1.
GlobalFree hMem



Применительно к примечаниям API-тулбар рабочий код будет такой:

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
Private Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Function GlobalLock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function GlobalFree Lib "kernel32" (ByVal hMem As Long) As Long

...
  Dim s As String
  Dim MyPtr As Long
  Static hMem As Long
...
       Case TTN_GETDISPINFO
          Call CopyMemory(uNMTTDI, ByVal lParam, Len(uNMTTDI))
          Select Case uNMTTDI.hdr.idFrom
            Case 1:
              s = "Create"
...
          hMem = GlobalAlloc(&H40, Len(s) + 1)
          MyPtr = GlobalLock(hMem)
          CopyMemory ByVal MyPtr, ByVal VarPtr(StringToMas(s).ByteStr(0)), (Len(s) + 1)
          uNMTTDI.lpszText = MyPtr
          Call CopyMemory(ByVal lParam, uNMTTDI, Len(uNMTTDI))
        Case TTN_SHOW
        'Case TTN_POP
          GlobalFree hMem


Причем GlobalFree hMem в TTN_GETDISPINFO делать нельзя,
это можно (и думаю нужно) сделать
в TTN_SHOW (когда примечание уже выводится на экран)
либо
в TTN_POP (когда примечание убирается с экрана)

По какой-то причине незащищенный ptr на НЕродной lpszText не гарантирован от перезаписывания прочими сволочами.

Поправьте если неправ.

Или может первый вариант со сдвигом на 4 байта использовать? (но первый вариант это шаманство, я не могу объяснить его логически) Мнения есть какие?
...
Рейтинг: 0 / 0
Примечания для кнопок в API-Toolbar:глюки:лечение:проблема:CopyMemory/VarPtr
    #38277808
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Дмитрий77,

Вариант с блокировкой памяти в крайнем посте безусловно правильный.
Вышло так, что в одном из своих проектов я про него забыл.
Причем сдвиг на 4 байта не помог (иногда глючило на длинных -но меньше 80 (!) Tooltip), понадобился сдвиг на 8 байтов откуда следует что сдвиг методом тыка несерьезно.

Но за этим делом обнаружил простое и ясное решение которое не глючит:
NMTTDISPINFO structure

szText
Type: TCHAR
Buffer that receives the tooltip text. An application can copy the text to this buffer instead of specifying a string address or string resource.

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
Private Type NMTTDISPINFO_SHORT
  hdr As NMHDR
  lpszText As Long
  szText(0 To 79) As Byte
  hInst As Long
  uFlags As Long
End Type

А поэтому

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
        Case TTN_GETDISPINFO
          Call CopyMemory(uNMTTDI, ByVal lParam, Len(uNMTTDI))
          Select Case uNMTTDI.hdr.idFrom
...
            Case 16:
              s = "Help"
            Case Else
              s = ""
          End Select
          '---
          Dim Ln As Long
          Dim i As Integer
          Ln = Len(s)
          If Ln > 0 Then
            For i = 0 To Ln - 1
              uNMTTDI.szText(i) = Asc(Mid(s, i + 1, 1))
            Next i
          End If
          uNMTTDI.szText(Ln) = 0
          '---
          Call CopyMemory(ByVal lParam, uNMTTDI, Len(uNMTTDI))


Просто, ясно, без strconv и без издевательств над памятью.
И ничего нигде не глючит, ни на русском, ни на английском, ни на XP ни на Вистах.
...
Рейтинг: 0 / 0
5 сообщений из 5, страница 1 из 1
Форумы / Visual Basic [игнор отключен] [закрыт для гостей] / Примечания для кнопок в API-Toolbar:глюки:лечение:проблема:CopyMemory/VarPtr
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]