Quantcast
Channel: VBForums - CodeBank - Visual Basic 6 and earlier
Viewing all 1480 articles
Browse latest View live

[VB6] - Editing AVI-files without recompression.

$
0
0
Hello everyone.
This is example of work with AVI-files (cut section and save it to a file). Everything is commented:
Code:

Option Explicit
. . .
ДЕКЛАРАЦИИ
. . .
Dim currentFile As String          ' Текущее имя файла
Dim hAvi        As Long            ' Текущий файл
Dim frameCount  As Long            ' Общее количество кадров в файле
Dim frameStart  As Long            ' Первый кадр
Dim vidStream  As Long            ' Видеопоток
Dim IGetFrame  As Long            ' Объект для рендеринга
Dim vidInfo    As AVI_STREAM_INFO  ' Информация о видеопотоке
 
' // Обновить фрейм
Private Sub Update()
    Dim lpDIB  As Long
    Dim bi      As BITMAPINFOHEADER
    Dim x      As Long
    Dim y      As Long
    Dim dx      As Long
    Dim dy      As Long
    Dim aspect  As Single
   
    If IGetFrame = 0 Then Exit Sub
    ' Получаем фрейм
    lpDIB = AVIStreamGetFrame(IGetFrame, sldFrame.Value)
    ' Получаем информацию о растре
    memcpy bi, ByVal lpDIB, Len(bi)
    ' Центруем
    aspect = bi.biHeight / bi.biWidth
   
    If aspect < 1 Then
        x = 0
        dx = picOut.ScaleWidth
        dy = picOut.ScaleWidth * aspect
        y = (picOut.ScaleHeight - dy) / 2
    Else
        y = 0
        dy = picOut.ScaleHeight
        dx = picOut.ScaleHeight / aspect
        x = (picOut.ScaleWidth - dx) / 2
    End If
    ' Выводим
    StretchDIBits picOut.hdc, x, y, dx, dy, 0, 0, bi.biWidth, bi.biHeight, ByVal lpDIB + bi.biSize, ByVal lpDIB, 0, vbSrcCopy
 
    ' Обновляем время
    Dim tim As Date
   
    tim = TimeSerial(0, 0, (sldFrame.Value - frameStart) / (vidInfo.dwRate / vidInfo.dwScale))
   
    lblTime.Caption = tim
   
End Sub
 
' // Функция загружает AVI файл
Private Sub LoadAVI(fileName As String)
    Dim ret    As Long
    ' Очистка
    Clear
    ' Открываем файл
    ret = AVIFileOpen(hAvi, StrPtr(fileName), OF_READWRITE, ByVal 0&)
    If ret Then GoTo ErrHandler
    ' Открываем поток
    ret = AVIFileGetStream(hAvi, vidStream, streamtypeVIDEO, 0)
    If ret Then GoTo ErrHandler
    ' Получаем информацию о потоке
    AVIStreamInfo vidStream, vidInfo, Len(vidInfo)
    ' Узнаем кадры
    frameStart = AVIStreamStart(vidStream)
    frameCount = AVIStreamLength(vidStream)
    If frameStart = -1 Or frameCount = -1 Then ret = 1: GoTo ErrHandler
    ' Получаем IGetFrame объект
    IGetFrame = AVIStreamGetFrameOpen(vidStream, ByVal AVIGETFRAMEF_BESTDISPLAYFMT)
    If IGetFrame = 0 Then GoTo ErrHandler
   
    currentFile = fileName
   
    sldFrame.Min = frameStart
    sldFrame.Max = frameStart + frameCount - 1
    sldFrame.SelStart = sldFrame.Min
    sldFrame.SelLength = frameCount - 1
   
    picOut.Cls
   
    Update
   
    Exit Sub
   
ErrHandler:
    Clear
    currentFile = vbNullString
   
    MsgBox "Error"
   
End Sub
 
' // Очистка
Private Sub Clear()
    If IGetFrame Then AVIStreamGetFrameClose IGetFrame: IGetFrame = 0
    If vidStream Then AVIStreamRelease vidStream: vidStream = 0
    If hAvi Then AVIFileRelease hAvi: hAvi = 0
End Sub
 
' // Сохранить изменения
Private Sub cmdSave_Click()
    Dim hNewFile    As Long
    Dim hNewStream  As Long
    Dim newFileName As String
    Dim ret        As Long
    Dim info        As AVI_STREAM_INFO
    Dim firstFrame  As Long
    Dim lastFrame  As Long
    Dim curFrame    As Long
    Dim nextKeyFr  As Long
    Dim index      As Long
    Dim sampleCount As Long
    Dim dataSize    As Long
    Dim isKeyFrame  As Boolean
    Dim buffer()    As Byte
 
    If hAvi = 0 Then Exit Sub
    ' Мы не можем просто так скопировать стрим с любого места, т.к. данные в стриме
    ' могут быть зависимы и мы можем копировать стрим только если есть опорный кадр
    ' Ищем ближайший опорный кадр
    firstFrame = AVIStreamFindSample(vidStream, sldFrame.SelStart, FIND_KEY Or FIND_NEXT)
    lastFrame = AVIStreamFindSample(vidStream, sldFrame.SelStart + sldFrame.SelLength, FIND_KEY Or FIND_PREV)
    ' Корректируем
    If firstFrame < 0 Then firstFrame = 0
    If lastFrame < 0 Then lastFrame = 0
    ' Получаем параметры текущего видео стрима
    AVIStreamInfo vidStream, info, Len(info)
    ' Корректируем количество кадров исходя из новой длины
    info.dwLength = lastFrame - firstFrame + 1
    ' Имя результирующего файла
    newFileName = left$(currentFile, Len(currentFile) - 4) & "_Edited.avi"
    ' Создаем новый файл
    ret = AVIFileOpen(hNewFile, StrPtr(newFileName), OF_CREATE Or OF_READWRITE, ByVal 0&)
    If ret Then GoTo ErrHandler
    ' Создаем новый видео стрим
    ret = AVIFileCreateStream(hNewFile, hNewStream, info)
    If ret Then GoTo ErrHandler
    ' Копируем формат
    ret = AVIStreamReadFormat(vidStream, 0, ByVal 0, dataSize)
    If ret Then GoTo ErrHandler
    ReDim buffer(dataSize - 1)
    ret = AVIStreamReadFormat(vidStream, 0, buffer(0), dataSize)
    If ret Then GoTo ErrHandler
    ret = AVIStreamSetFormat(hNewStream, 0, buffer(0), dataSize)
    If ret Then GoTo ErrHandler
    ' Проход по кадрам и их копирование в новый файл
    curFrame = firstFrame
    nextKeyFr = curFrame
   
    prgProgress.Visible = True
   
    Do While index < info.dwLength
        ' Читаем данные
        ret = AVIStreamRead(vidStream, index + firstFrame, AVISTREAMREAD_CONVENIENT, ByVal 0&, 0, dataSize, sampleCount)
        If ret Then GoTo ErrHandler
        ReDim Preserve buffer(dataSize - 1)
        ret = AVIStreamRead(vidStream, index + firstFrame, AVISTREAMREAD_CONVENIENT, buffer(0), dataSize, dataSize, sampleCount)
        If ret Then GoTo ErrHandler
        ' Если это опорный кадр, то
        If curFrame = nextKeyFr Then
            isKeyFrame = True
            ' Ищем следующий опорный кадр
            nextKeyFr = AVIStreamFindSample(vidStream, nextKeyFr + 1, FIND_KEY Or FIND_NEXT)
        End If
 
        If dataSize Then
            ' Если текущий - опорный
            If isKeyFrame Then
                ' Записываем опорный
                ret = AVIStreamWrite(hNewStream, index, sampleCount, buffer(0), dataSize, AVIIF_KEYFRAME, sampleCount, dataSize)
                isKeyFrame = False
            Else
                ' Неопорный
                ret = AVIStreamWrite(hNewStream, index, sampleCount, buffer(0), dataSize, 0, sampleCount, dataSize)
            End If
            If ret Then GoTo ErrHandler
           
        End If
        ' Следующий кадр
        curFrame = curFrame + sampleCount
        index = index + sampleCount
        ' Обновляем прогрессбар
        prgProgress.Value = (index / info.dwLength) * 50
    Loop
    ' Освобождаем стрим
    AVIStreamRelease hNewStream:    hNewStream = 0
   
    Dim audStream  As Long
    Dim firstSample As Long
    Dim lastSample  As Long
    Dim timeStart  As Single
    Dim timeEnd    As Single
    Dim curSample  As Long
    Dim nextKeySmp  As Long
    ' Получаем аудио стрим из файла
    ret = AVIFileGetStream(hAvi, audStream, streamtypeAUDIO, 0)
    If ret Then
        ' Аудио стрима нет
        ret = 0
        GoTo ErrHandler
    End If
    ' Узнаем время кадров
    timeStart = firstFrame / (info.dwRate / info.dwScale)
    timeEnd = lastFrame / (info.dwRate / info.dwScale)
    ' Получаем параметры текущего аудио стрима
    AVIStreamInfo audStream, info, Len(info)
    ' Определяем семплы
    firstSample = AVIStreamFindSample(audStream, (info.dwRate / info.dwScale) * timeStart, FIND_KEY Or FIND_NEXT)
    lastSample = AVIStreamFindSample(audStream, (info.dwRate / info.dwScale) * timeEnd, FIND_KEY Or FIND_PREV)
    ' Создаем новый аудио стрим
    ret = AVIFileCreateStream(hNewFile, hNewStream, info)
    If ret Then GoTo ErrHandler
    info.dwLength = lastSample - firstSample
    ' Копируем формат
    ret = AVIStreamReadFormat(audStream, 0, ByVal 0, dataSize)
    If ret Then GoTo ErrHandler
    ReDim buffer(dataSize - 1)
    ret = AVIStreamReadFormat(audStream, 0, buffer(0), dataSize)
    If ret Then GoTo ErrHandler
    ret = AVIStreamSetFormat(hNewStream, 0, buffer(0), dataSize)
    If ret Then GoTo ErrHandler
    ' Проход по семплам и их копирование в новый файл
    curSample = firstSample
    nextKeySmp = curSample
    index = 0
   
    Do While index < info.dwLength
        ' Читаем данные
        ret = AVIStreamRead(audStream, index + firstSample, AVISTREAMREAD_CONVENIENT, ByVal 0&, 0, dataSize, sampleCount)
        If ret Then GoTo ErrHandler
        ReDim Preserve buffer(dataSize - 1)
        ret = AVIStreamRead(audStream, index + firstSample, AVISTREAMREAD_CONVENIENT, buffer(0), dataSize, dataSize, sampleCount)
        If ret Then GoTo ErrHandler
        ' Если это опорный семпл, то
        If curSample = nextKeySmp Then
            isKeyFrame = True
            ' Ищем следующий опорный кадр
            nextKeySmp = AVIStreamFindSample(audStream, nextKeySmp + sampleCount, FIND_KEY Or FIND_NEXT)
        End If
 
        If dataSize Then
            ' Если текущий - опорный
            If isKeyFrame Then
                ' Записываем опорный
                ret = AVIStreamWrite(hNewStream, index, sampleCount, buffer(0), dataSize, AVIIF_KEYFRAME, sampleCount, dataSize)
                isKeyFrame = False
            Else
                ' Неопорный
                ret = AVIStreamWrite(hNewStream, index, sampleCount, buffer(0), dataSize, 0, sampleCount, dataSize)
            End If
            If ret Then GoTo ErrHandler
 
        End If
        ' Следующий семпл (группа семплов)
        curSample = curSample + sampleCount
        index = index + sampleCount
        ' Обновляем прогрессбар
        prgProgress.Value = (index / info.dwLength) * 50 + 50
    Loop
   
    prgProgress.Visible = False
   
ErrHandler:
    ' Освобождаем ресурсы
    If audStream Then AVIStreamRelease audStream
    If hNewStream Then AVIStreamRelease hNewStream
    If hNewFile Then AVIFileRelease hNewFile
   
    If ret Then MsgBox "Error saving"
   
End Sub
 
' // Установить последний кадр
Private Sub cmdSetEnd_Click()
    If sldFrame.Value < sldFrame.SelStart Then Exit Sub
    sldFrame.SelLength = sldFrame.Value - sldFrame.SelStart
End Sub
 
' // Установить начальный кадр
Private Sub cmdSetStart_Click()
    sldFrame.SelStart = sldFrame.Value
End Sub
 
Private Sub Form_Load()
    AVIFileInit
    SetStretchBltMode picOut.hdc, HALFTONE
End Sub
 
Private Sub Form_Unload(Cancel As Integer)
    Clear
    AVIFileExit
End Sub
 
' // Событие бросания файла на бокс
Private Sub picOut_OLEDragDrop(Data As DataObject, _
                              Effect As Long, _
                              Button As Integer, _
                              Shift As Integer, _
                              x As Single, _
                              y As Single)
   
    If IsAviFile(Data) Then
        LoadAVI Data.Files(1)
    End If
   
End Sub
 
' // Проверяем AVI ли файл?
Private Sub picOut_OLEDragOver(Data As DataObject, _
                              Effect As Long, _
                              Button As Integer, _
                              Shift As Integer, _
                              x As Single, _
                              y As Single, _
                              State As Integer)
   
    If IsAviFile(Data) Then Effect = ccOLEDropEffectMove Else Effect = ccOLEDropEffectNone
   
End Sub
 
' // Является ли AVI файлом
Private Function IsAviFile(Data As DataObject) As Boolean
   
    If Data.Files.Count = 1 Then
        Dim fileName As String
       
        fileName = Data.Files(1)
       
        IsAviFile = LCase(right(fileName, 4)) = ".avi"
       
    End If
       
End Function
 
Private Sub picOut_Paint()
    Update
End Sub
 
Private Sub sldFrame_Change()
    Update
End Sub
 
Private Sub sldFrame_Scroll()
    Update
End Sub

The files must drop on window from the explorer, the Start and End button make the selection.
Attached Files

[VB6] - 3D sound using DirectSound.

$
0
0
Hello everyone.
The example shows an implementation of 3D sound, every object in three-dimensional space "assigned" the sound. To work need a library dx8vb.dll. Shift / Ctrl - deceleration of 10 times, the left button to rotate the camera, right tilt left / right. Spheres - sound sources, each can be turned on / off. Commented only work with sound:
Code:

Option Explicit

Dim dx      As DirectX8                    ' Объект DirectX8
Dim dxs    As DirectSound8                ' Объект DirectSound
Dim dl      As DirectSound3DListener8      ' Слушатель
Dim dp      As DirectSoundPrimaryBuffer8    ' Первичный буфер
Dim ds()    As DirectSoundSecondaryBuffer8  ' Вторичные буфера
Dim db()    As DirectSound3DBuffer8        ' 3D буфера
Dim dev    As Direct3DDevice8              ' Для визуализации ...
Dim d3d    As Direct3D8                    ' ...
Dim d3msh  As D3DXMesh                    ' ...
Dim d3pln  As D3DXMesh                    ' ...

Private Const CountSources = 3      ' Количество источников звука

' // Отключение/включение звука
Private Sub chkSound_Click(Index As Integer)
    ' Если стоит галочка, то
    If chkSound(Index).Value = vbChecked Then
        ' Проигрываем звук с зацикливанием по кругу
        ds(Index).Play DSBPLAY_LOOPING
    Else
        ' Иначе останавливаем
        ds(Index).Stop
    End If
   
End Sub

' // Процедура обрабтки нажатий клавиш
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
    Dim curPos  As D3DVECTOR    ' Текущая позиция слушателя
    Dim curOrt  As D3DVECTOR    ' Текущий вектор ориентации
    Dim curTop  As D3DVECTOR    ' Текущий вектор "макушки" слушателя
    Dim curLft  As D3DVECTOR    ' Вектор влево относительно ориентации слушателя
   
    ' Получаем позицию слушателя
    dl.GetPosition curPos
    ' Получаем ориентацию и направление вверх
    dl.GetOrientation curOrt, curTop
    ' С помощью векторного произведения находим препендикуляр к этим двум векторам, т.е. вектор влево
    D3DXVec3Cross curLft, curOrt, curTop
   
    ' Если нажата Shift/Ctrl
    If Shift Then
        ' Уменьшаем размер в 10 раз
        D3DXVec3Scale curOrt, curOrt, 0.1  ' вектора ориентации
        D3DXVec3Scale curLft, curLft, 0.1  ' вектора влево
       
    End If
   
    ' Получение кода нажатой клавиши
    Select Case KeyCode
    Case vbKeyW, vbKeyUp
        ' Вперед. Прибавляем к текущим координатам вектор ориентации
        D3DXVec3Add curPos, curPos, curOrt
    Case vbKeyA, vbKeyLeft
        ' Влево. Прибавляем к текущим координатам вектор влево
        D3DXVec3Add curPos, curPos, curLft
    Case vbKeyD, vbKeyRight
        ' Вправо. Вычитаем из текущих координат вектор влево
        D3DXVec3Subtract curPos, curPos, curLft
    Case vbKeyS, vbKeyDown
        ' Назад. Вычитаем из текущих координат ориентацию
        D3DXVec3Subtract curPos, curPos, curOrt
    End Select
   
    ' Устанавливаем измененную позицию
    dl.SetPosition curPos.X, curPos.Y, curPos.z, DS3D_IMMEDIATE
    ' Визуализация
    Render
   
End Sub

' // Процедура загрузки формы
Private Sub Form_Load()
    ' Создаем объект DirectX8
    Set dx = New DirectX8
    ' Создаем объект DirectSound
    Set dxs = dx.DirectSoundCreate(vbNullString)
    ' Настраиваем совместный доступ
    dxs.SetCooperativeLevel Me.hWnd, DSSCL_NORMAL
    ' Описатель буфера
    Dim bd  As DSBUFFERDESC
    ' Это первичный буфер и возможность контролировать положение и ориентацию в пространстве
    bd.lFlags = DSBCAPS_PRIMARYBUFFER Or DSBCAPS_CTRL3D
    ' Создаем первичный буфер
    Set dp = dxs.CreatePrimarySoundBuffer(bd)
    ' Получаем объект слушателя
    Set dl = dp.GetDirectSound3DListener()
    ' Для других буферов возможность контролировать положение и ориентацию в пространстве
    bd.lFlags = DSBCAPS_CTRL3D
    ' Задаем ориентацию вперед
    dl.SetOrientation 0, 0, 1, 0, 1, 0, DS3D_DEFERRED
   
    Dim i  As Long    ' Временная переменная
    Dim fil As Boolean  ' В IDE - загрузка из файла, в EXE из ресурсов
   
    ReDim ds(CountSources - 1)  ' Массив вторичных буферов (источников)
    ReDim db(CountSources - 1)  ' Массив 3D буферов
   
    Randomize
   
    For i = 0 To CountSources - 1
       
        Debug.Assert InIDE(fil)
       
        ' Загружаем из файла или из ресурса в зависимости от режима работы
        If fil Then
            Set ds(i) = dxs.CreateSoundBufferFromFile(Choose(i + 1, "Sound.wav", "Moto.wav", "Police.wav"), bd)
        Else
            Set ds(i) = dxs.CreateSoundBufferFromResource(App.EXEName & ".exe", Choose(i + 1, "#101", "#102", "#103"), bd)
        End If
        ' Получаем объект 3D буфера
        Set db(i) = ds(i).GetDirectSound3DBuffer()
        ' Задаем рандомную позицию
        db(i).SetPosition Rnd * 50 - 25, Rnd * 50, Rnd * 50 - 25, DS3D_DEFERRED
        ' Включаем воспроизведение
        ds(i).Play DSBPLAY_LOOPING
       
    Next
    ' Запуск просчета изменений
    dl.CommitDeferredSettings
   
    ' Для визуализации (не комментирую)
    ' //----//----//----//----//----//
    Dim pp  As D3DPRESENT_PARAMETERS
    Dim dm  As D3DDISPLAYMODE
   
    Set d3d = dx.Direct3DCreate()
   
    d3d.GetAdapterDisplayMode D3DADAPTER_DEFAULT, dm
   
    pp.BackBufferFormat = dm.Format
    pp.Windowed = 1
    pp.SwapEffect = D3DSWAPEFFECT_DISCARD
    pp.EnableAutoDepthStencil = 1
    pp.AutoDepthStencilFormat = D3DFMT_D16
   
    Set dev = d3d.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, pic.hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, pp)
   
    Dim mtx As D3DMATRIX
   
    D3DXMatrixPerspectiveFovLH mtx, 3.14 / 180 * 80, pic.ScaleHeight / pic.ScaleWidth, 0.1, 200
    dev.SetTransform D3DTS_PROJECTION, mtx
   
    dev.SetRenderState D3DRS_ZENABLE, D3DZB_TRUE
    dev.SetRenderState D3DRS_LIGHTING, 1
   
    Dim d3      As D3DX8
    Dim dat()  As Byte
   
    Set d3 = New D3DX8
    Set d3msh = d3.CreateSphere(dev, 1, 16, 8, Nothing)
    Set d3pln = d3.CreatePolygon(dev, 100, 4, Nothing)
   
    Dim lth As D3DLIGHT8
    Dim mat As D3DMATERIAL8
   
    lth.Type = D3DLIGHT_POINT
    lth.diffuse = col(1, 1, 1)
    lth.Position = vec3(0, 100, -100)
    lth.Attenuation1 = 0.01
    lth.Range = 400
   
    dev.SetLight 0, lth
    dev.LightEnable 0, 1
   
    mat.diffuse = col(1, 1, 1)
    dev.SetMaterial mat
    ' //----//----//----//----//----//
   
End Sub

' // Визуализация
Private Sub Render()
    Dim idx As Long
    Dim v1  As D3DVECTOR
    Dim v2  As D3DVECTOR
    Dim v3  As D3DVECTOR
    Dim mtx As D3DMATRIX
   
    dev.Clear 0, ByVal 0, D3DCLEAR_ZBUFFER Or D3DCLEAR_TARGET, &HAFFFFF, 1, 0

    dev.BeginScene

    dev.SetVertexShader d3msh.GetFVF
   
    dl.GetPosition v1:      dl.GetOrientation v2, v3
    D3DXVec3Add v2, v1, v2
    D3DXMatrixLookAtLH mtx, v1, v2, v3
    dev.SetTransform D3DTS_VIEW, mtx
   
    D3DXMatrixTranslation mtx, 0, -3, 0
    dev.SetTransform D3DTS_WORLD, mtx
    D3DXMatrixRotationX mtx, -3.14 / 2
    dev.MultiplyTransform D3DTS_WORLD, mtx
   
    d3pln.DrawSubset 0
   
    For idx = 0 To CountSources - 1
       
        db(idx).GetPosition v1
        D3DXMatrixTranslation mtx, v1.X, v1.Y, v1.z
        dev.SetTransform D3DTS_WORLD, mtx
        d3msh.DrawSubset 0
       
    Next
   
    dev.EndScene
   
    dev.Present ByVal 0, ByVal 0, 0, ByVal 0
   
End Sub

' // Функция сздания векторов
Private Function vec3(ByVal X As Single, ByVal Y As Single, ByVal z As Single) As D3DVECTOR
    vec3.X = X: vec3.Y = Y: vec3.z = z
End Function

' // Функция создания цветов
Private Function col(r As Single, g As Single, b As Single) As D3DCOLORVALUE
    col.r = r
    col.g = g
    col.b = b
    col.a = 1
End Function

' // Процедура выгрузки формы
Private Sub Form_Unload(Cancel As Integer)
    Dim i  As Long
    ' Проход по всем буферам
    For i = 0 To CountSources - 1
        ' Остановка
        ds(i).Stop
        ' Удаление и очистка
        Set ds(i) = Nothing
        Set db(i) = Nothing
       
    Next
   
    Set dl = Nothing
    Set dp = Nothing
   
    Set dxs = Nothing
    Set dx = Nothing
   
End Sub

' // Процедура обработки мыши
Private Sub pic_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    Static ox  As Single, oy As Single
    Dim mtx As D3DMATRIX
    Dim qt1 As D3DQUATERNION
    Dim qt2 As D3DQUATERNION
    Dim v1  As D3DVECTOR
    Dim v2  As D3DVECTOR
    Dim v3  As D3DVECTOR

    ' При движении с зажатой левой кнопкой изменяем ориентацию
    If Button = vbLeftButton Then
           
        dl.GetOrientation v1, v2
        D3DXVec3Cross v3, v1, v2
       
        D3DXQuaternionRotationAxis qt1, v2, (X - ox) / 50
        D3DXQuaternionRotationAxis qt2, v3, -(Y - oy) / 50
        D3DXQuaternionMultiply qt1, qt1, qt2
        D3DXMatrixRotationQuaternion mtx, qt1
       
        D3DXVec3TransformCoord v1, v1, mtx
        D3DXVec3TransformCoord v2, v2, mtx
        dl.SetOrientation v1.X, v1.Y, v1.z, v2.X, v2.Y, v2.z, DS3D_IMMEDIATE
       
        Render
    ' При правой кнопке - наклон (направление вверх)
    ElseIf Button = vbRightButton Then
   
        dl.GetOrientation v1, v2
       
        D3DXQuaternionRotationAxis qt1, v1, (X - ox) / 50
        D3DXMatrixRotationQuaternion mtx, qt1
       
        D3DXVec3TransformCoord v1, v1, mtx
        D3DXVec3TransformCoord v2, v2, mtx
        dl.SetOrientation v1.X, v1.Y, v1.z, v2.X, v2.Y, v2.z, DS3D_IMMEDIATE
       
        Render
       
    End If
   
    ox = X: oy = Y
   
End Sub

Private Sub pic_Paint()
    Render
End Sub

Private Function InIDE(z As Boolean) As Boolean
    z = True: InIDE = z
End Function

Download source code.

VB6 - Add to Hash using CNG

$
0
0
There are times when one needs to add to an existing hash, such as when calculating the HMAC hashes for TLS. With CAPI, the hash function could be easily split into separate functions; create the hash, add to the hash, and finish the hash. All you had to do was save the Hash Handle. Using CNG, it is a little more involved.

CNG uses objects extensively, and although the Hash Handle is the only element required for the BCryptFinishHash function, it is useless without the Hash Object. In the attached test program, the Hash Handle, the Hash Length, and the Hash Object are saved by the calling function. In reality, the Hash Object is the only thing that needs to be preserved, because the other two values are both contained within:
Code:

Hash Object:
14 00 00 00 53 55 55 55 F0 D7 20 00 24 9D 52 04
00 00 00 00 70 00 00 00 48 48 53 4D 02 00 02 00
14 00 00 00 00 00 00 00 54 65 73 74 20 73 74 72
69 6E 67 20 74 6F 20 48 61 73 68 2E 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 01 23 45 67 89 AB CD EF
FE DC BA 98 76 54 32 10 F0 E1 D2 C3 00 00 00 00
14 00 00 00 00 00 00 00 00 00

The Hash Length is in byte(0), and the Hash Handle starts at byte(12).

J.A. Coutts
Attached Files

[VB6] - Fireworks.

$
0
0

Code:

Option Explicit

Private Declare Function PlaySound Lib "winmm.dll" Alias "PlaySoundW" (ByVal lpszName As Long, ByVal hModule As Long, ByVal dwFlags As Long) As Long

Private Const SND_ASYNC = &H1
Private Const pi = 3.14

Private Function Draw(v As Long, cc As Long) As Boolean
    Dim dh As Single, c As Single, d As Single, x As Single, y As Single, w As Long, i As Long, dx As Single, dy As Single, _
        gr As Single, r As Single, g As Single, b As Single, n As String
    Rnd v: cc = cc + 2
    If cc <= 0 Then
        Exit Function
    ElseIf cc <= 100 Then
        If cc = 2 Then n = App.Path & "\1.wav": PlaySound StrPtr(n), 0, SND_ASYNC
        dh = 100 / cc: x = Rnd * 0.75 + 0.125 + (cc * ((v And 2) - 1)) / 1000: y = Sin((cc - 2) / 200 * pi) * 0.75
        w = 21 - cc * 0.2: d = 255 / w: c = 0
        Do: c = 255 / w: DrawWidth = w: PSet (x, y), RGB(c, c, 0): w = w - 1: Loop While w
    ElseIf cc < 300 Then
        If cc = 102 Then n = App.Path & "\0.wav": PlaySound StrPtr(n), 0, SND_ASYNC
        dh = (cc - 100) / 200: gr = (1 - Cos(dh * pi * 0.25)) * dh: dx = Rnd * 0.75 + 0.125 + ((v And 2) - 1) / 10
        dy = 0.75 - gr: i = Rnd * 100 + 200: gr = 1 - 0.2 ^ (dh * 5): dh = 1 - dh
        r = Rnd * 0.8 + 0.2: g = Rnd * 0.8 + 0.2: b = Rnd * 0.8 + 0.2
        If cc < 150 Then
            b = (1 - (cc - 100) / 50) * 3
            For w = (cc - 100) * 2 To 1 Step -1
                DrawWidth = w * 5: c = cc / w * b: PSet (dx, dy), RGB(c * r, c * g, c * b)
            Next
        End If
        Do While i
            c = Rnd * pi * 2: d = gr * (Rnd * 0.8 + 0.2) * 0.5: x = Cos(c) * d + dx: y = Sin(c) * d + dy
            w = (dh * 6) * Abs(Sin((cc + i) / 10 * pi)) + 1: c = 0
            Do: c = 512 / w * dh: DrawWidth = w: PSet (x, y), RGB(c * r, c * g, c * b): w = w - 1: Loop While w
            i = i - 1
        Loop
    Else: Draw = True: cc = 0: v = v - Rnd * 100
    End If
End Function
Private Sub Form_Click()
    Unload Me
End Sub
Private Sub Form_Load()
    Randomize
End Sub
Private Sub Form_Resize()
    Scale (0, 1)-(1, 0)
End Sub
Private Sub tmrTimer_Timer()
    Static a1 As Long, a2 As Long, c1 As Long, c2 As Long
    If a1 = 0 Then a1 = -(Rnd * 100) - 1: a2 = a1 - 2: c2 = -150
    Call Cls: Draw a1, c1: Draw a2, c2
End Sub

Download source code and resources.

[VB6] - Work with the pointers.

$
0
0
Often there are situations when you need to get data having only the address (for example, in WndProc, HookProc). Usually, simply copy the data via CopyMemory the structure after changing data and copy it back. If the structure is large, it will be a waste of resources to copy into structure and back. In languages such as C ++ is all done easily with the help of pointers, written something like newpos = (WINDOWPOS *) lparam. Nominally VB6 does not work with pointers, but there are a few workarounds.
For a start I will give the necessary declarations:
Code:

Public Declare Function GetMem4 Lib "msvbvm60" (src As Any, Dst As Any) As Long
Public Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (src() As Any) As Long

For example can access the the address of the variable in the stack are passed by reference. For example like this:
Code:

Private Type Vector
    X As Single
    Y As Single
End Type
Private Type TestRec
    Name As String
    Value As Long
    Position As Vector
    Money As Double
End Type
 
Private Sub Form_Load()
    Dim tr As TestRec
    Test tr
End Sub
 
Private Function Test(Pointer As TestRec, Optional ByVal nu As Long)
    Dim q As TestRec, z As TestRec
   
    q.Name = "The trick"
    q.Position.X = 5: q.Position.Y = 15
    q.Value = 12345: q.Money = 3.14
   
    z.Name = "Visual Basic 6.0"
    z.Position.X = 99: z.Position.Y = 105
    z.Value = 7643: z.Money = 36.6
   
    GetMem4 VarPtr(q), ByVal VarPtr(nu) - 4    ' Set pointer to q (Pointer = &q)
   
    PrintRec Pointer
   
    GetMem4 VarPtr(z), ByVal VarPtr(nu) - 4    ' Set pointer to z (Pointer = &z)
   
    PrintRec Pointer
   
End Function
 
Private Sub PrintRec(Pt As TestRec)
    Debug.Print "----------------"
    Debug.Print "Name = " & Pt.Name
    Debug.Print "Value = " & Pt.Value
    Debug.Print "Money = " & Pt.Money
    Debug.Print "Position.X = " & Pt.Position.X
    Debug.Print "Position.Y = " & Pt.Position.Y
End Sub

You can also create a pointer by using arrays. The idea is to create 2 arrays one element each, which will store the address of a variable, and the other will refer to the data. The first will always be Long, a second type of data desired. This is useful for example if you want to pass on lists, etc. It's no secret that the array in VB is simply an SafeArray. In the data structure of this array contains a lot of useful information, and a pointer to the data. What we do, we create two arrays:
  • 1st (with address) refers to a pointer to the second data array. As a result, changing the values in the first array, 2nd automatically refer to the desired data.*
  • 2nd is directly the data pointed to by the first.*

Also, after all the manipulations necessary to return all the pointers back to VB properly clear the memory.* For all manipulations I created auxiliary functions and structure for data recovery.* Address SafeArray is available through Not Not Arr, but IDE after such manipulations are glitches with floating point:
Code:

Public Type PtDat
    Prv1 As Long
    Prv2 As Long
End Type

' Create the pointer. 1st param is pointer, 2nd address.
Public Function PtGet(Pointer() As Long, ByVal VarAddr As Long) As PtDat
    Dim i As Long
    i = GetSA(ArrPtr(Pointer)) + &HC
    GetMem4 ByVal i, PtGet.Prv1
    GetMem4 VarAddr + &HC, ByVal i
    PtGet.Prv2 = Pointer(0)
End Function
' Release pointer
Public Sub PtRelease(Pointer() As Long, prev As PtDat)
    Pointer(0) = prev.Prv2
    GetMem4 prev.Prv1, ByVal GetSA(ArrPtr(Pointer)) + &HC
End Sub
' Obtaint address of SafeArray (same Not Not)
Public Function GetSA(ByVal addr As Long) As Long
    GetMem4 ByVal addr, GetSA
End Function

Example of use:
Code:

Private Sub Form_Load()
    Dim pt() As Long, var() As TestRec, prev As PtDat      ' Pointer, references data, release data.
    Dim q As TestRec, z As TestRec                          ' The structures, which we refer
   
    ReDim pt(0): ReDim var(0)
 
    q.Name = "The trick"
    q.Position.X = 5: q.Position.Y = 15
    q.Value = 12345: q.Money = 3.14
   
    z.Name = "Visual Basic 6.0"
    z.Position.X = 99: z.Position.Y = 105
    z.Value = 7643: z.Money = 36.6
   
    prev = PtGet(pt, GetSA(ArrPtr(var)))                    ' Create "pointer"
 
    pt(0) = VarPtr(q)                                      ' Refer to q (pt = &q)
    PrintRec var(0)
    pt(0) = VarPtr(z)                                      ' Refer to z (pt = &z)
    PrintRec var(0)
 
    PtRelease pt, prev                                      ' Release
 
End Sub

[VB6] - Get information about memory usage.

$
0
0
Code:

Option Explicit
 
Private Const MAX_PATH = 260
 
Private Type PROCESS_MEMORY_COUNTERS
    cb                          As Long
    PageFaultCount              As Long
    PeakWorkingSetSize          As Long
    WorkingSetSize              As Long
    QuotaPeakPagedPoolUsage    As Long
    QuotaPagedPoolUsage        As Long
    QuotaPeakNonPagedPoolUsage  As Long
    QuotaNonPagedPoolUsage      As Long
    PagefileUsage              As Long
    PeakPagefileUsage          As Long
End Type
Private Type PROCESSENTRY32
    dwSize                      As Long
    cntUsage                    As Long
    th32ProcessID              As Long
    th32DefaultHeapID          As Long
    th32ModuleID                As Long
    cntThreads                  As Long
    th32ParentProcessID        As Long
    pcPriClassBase              As Long
    dwFlags                    As Long
    szExeFile                  As String * MAX_PATH
End Type
Private Type OSVERSIONINFO
    dwOSVersionInfoSize        As Long
    dwMajorVersion              As Long
    dwMinorVersion              As Long
    dwBuildNumber              As Long
    dwPlatformId                As Long
    szCSDVersion                As String * 128
End Type
 
Private Declare Function GetVersionEx Lib "kernel32" _
                        Alias "GetVersionExA" ( _
                        ByRef lpVersionInformation As OSVERSIONINFO) As Long
Private Declare Function Process32First Lib "kernel32" ( _
                        ByVal hSnapshot As Long, _
                        ByRef lppe As PROCESSENTRY32) As Long
Private Declare Function Process32Next Lib "kernel32" ( _
                        ByVal hSnapshot As Long, _
                        ByRef lppe As PROCESSENTRY32) As Long
Private Declare Function OpenProcess Lib "kernel32" ( _
                        ByVal dwDesiredAccess As Long, _
                        ByVal bInheritHandle As Long, _
                        ByVal dwProcessId As Long) As Long
Private Declare Function CreateToolhelp32Snapshot Lib "kernel32" ( _
                        ByVal dwFlags As Long, _
                        ByVal th32ProcessID As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" ( _
                        ByVal hObject As Long) As Long
Private Declare Function GetProcessMemoryInfo Lib "psapi.dll" ( _
                        ByVal lHandle As Long, _
                        ByRef lpStructure As PROCESS_MEMORY_COUNTERS, _
                        ByVal lSize As Long) As Long
 
Private Const TH32CS_SNAPPROCESS                  As Long = 2
Private Const PROCESS_QUERY_LIMITED_INFORMATION  As Long = &H1000
Private Const PROCESS_QUERY_INFORMATION          As Long = &H400
Private Const INVALID_HANDLE_VALUE                As Long = -1
 
Dim IsVistaAndLater As Boolean
 
Private Sub Form_Load()
    Dim ver As OSVERSIONINFO
   
    ver.dwOSVersionInfoSize = Len(ver)
    GetVersionEx ver
    IsVistaAndLater = ver.dwMajorVersion >= 6
   
    Call tmrTimer_Timer
   
End Sub

Private Sub Form_Resize()
    If Me.ScaleWidth > 200 And Me.ScaleHeight > 200 Then lvwInfo.Move 100, 100, Me.ScaleWidth - 200, Me.ScaleHeight - 200
End Sub

Private Sub tmrTimer_Timer()
    Dim hSnap  As Long:                    Dim pe      As PROCESSENTRY32
    Dim hProc  As Long:                    Dim mi      As PROCESS_MEMORY_COUNTERS
    Dim i      As Long:                    Dim li      As ListItem
   
    hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
    If hSnap = INVALID_HANDLE_VALUE Then Exit Sub
   
    pe.dwSize = Len(pe)
   
    If Process32First(hSnap, pe) Then
   
        Do
            hProc = OpenProcess(IIf(IsVistaAndLater, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_QUERY_INFORMATION), _
                                False, pe.th32ProcessID)
            If hProc Then
                mi.cb = Len(mi)
                GetProcessMemoryInfo hProc, mi, Len(mi)
                If i >= lvwInfo.ListItems.Count Then
                    Set li = lvwInfo.ListItems.Add(, , Left$(pe.szExeFile, InStr(1, pe.szExeFile, vbNullChar)))
                Else: Set li = lvwInfo.ListItems(i + 1)
                End If
                li.SubItems(1) = pe.th32ProcessID
                li.SubItems(2) = LTrim(Format(mi.WorkingSetSize / 1024, "### ### ##0"))
                li.SubItems(3) = LTrim(Format(mi.PagefileUsage / 1024, "### ### ##0"))
                li.SubItems(4) = mi.PageFaultCount
                li.SubItems(5) = LTrim(Format(mi.PeakPagefileUsage / 1024, "### ### ##0"))
                li.SubItems(6) = LTrim(Format(mi.PeakWorkingSetSize / 1024, "### ### ##0"))
                li.SubItems(7) = LTrim(Format(Int(mi.QuotaNonPagedPoolUsage / 1024), "### ### ##0"))
                li.SubItems(8) = LTrim(Format(Int(mi.QuotaPagedPoolUsage / 1024), "### ### ##0"))
                li.SubItems(9) = LTrim(Format(mi.QuotaPeakNonPagedPoolUsage / 1024, "### ### ##0"))
                li.SubItems(10) = LTrim(Format(mi.QuotaPeakPagedPoolUsage / 1024, "### ### ##0"))
                CloseHandle hProc
                i = i + 1
            End If
           
        Loop While Process32Next(hSnap, pe)
       
    End If
   
    CloseHandle hSnap
   
    If i < lvwInfo.ListItems.Count Then
        Do Until lvwInfo.ListItems.Count = i
            lvwInfo.ListItems.Remove (lvwInfo.ListItems.Count)
        Loop
    End If
   
End Sub

Attached Files

[VB6] Simple, basic subclassing tutorial using the easier SetWindowSubclass method

$
0
0
So there's quite a few posts about specific questions where code like this is shown, but I really thought it would be better to have a subclassing tutorial type codebank entry for it to make it more easily searchable, and a better answer for when somebody hasn't used subclassing before.

Since Windows XP, there's been some great subclassing setups posted showing how to use SetWindowSubclass as a newer, better subclassing method than previous SetWindowLong-based methods. Karl E. Peterson's HookXP and LaVolpe's Subclassing With Common Controls Library being two top notch examples, and this method was first shown to me by Bonnie West over in my ITaskBarList3 demo. But before delving into using interfaces etc, (and even still for subclassing things other than forms and the like), it's helpful to show how to do the very simplest of subclassing: When you have any object, and just want to have a code to handle a message without much fuss.

The subclassing example I picked for this is validating whether text typed into a textbox is a valid filename, and blocking the input altogether if the keystroke or paste operation contains an illegal character or is too long. (You could do this without subclassing in most scenarios, but I thought it was a nice and simple way to get the idea across.)

All it requires on the form is a single textbox.

Once you have that, you'll need to create the function that handles its messages in a module. All such functions, usually referred to as the WndProc, have the same arguments. Also, they all must unsubclass when the window is being destroyed (or before) otherwise the program will crash. So before adding any code for handling messages, the basic prototype looks like this:

Code:

Public Function EditWndProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal uIdSubclass As Long, ByVal dwRefData As Long) As Long

Select Case uMsg

    '[other messages will go here later]

  Case WM_DESTROY
    Call UnSubclass(hWnd, PtrEditWndProc)

End Select
 
EditWndProc = DefSubclassProc(hWnd, uMsg, wParam, lParam)

End Function
Private Function PtrEditWndProc() As Long
PtrEditWndProc = FARPROC(AddressOf EditWndProc)
End Function

The DefSubclassProc call passes everything back to the default handler if you're not completely handling it. The PtrEditWndProc is there because you can't use the AddressOf operator to get the address of the function it's in.

Now that you have a function to handle the messages, you can add the code to start the subclass back in the form:

Code:

Private Sub Form_Load()
Call Subclass(Text1.hWnd, AddressOf EditWndProc)
End Sub

And now you have a basic subclass all set up and ready to go. You don't need an Unsubclass in Form_Unload.
Here's the message handlers used to validate input for a textbox looking for a valid file name (and appears right after Select Case uMsg:

Code:

  Case WM_CHAR
    Dim lLen As Long
    lLen = SendMessageW(hWnd, WM_GETTEXTLENGTH, 0, ByVal 0&) + 1
    If lLen > 260 Then
            Beep
            wParam = 0
            Call ShowBalloonTipEx(hWnd, "", "Maximum number of characters has been reached. The total length of the file name cannot exceed 260 characters.", TTI_NONE) ' TTI_ERROR)
            Exit Function
    End If
    Select Case wParam
        Case 47, 92, 60, 62, 58, 42, 124, 63, 34 'Illegal chars /\<>:*|?"
            Beep
            Call ShowBalloonTipEx(hWnd, "", "File names may not contain any of the following characters:" & vbCrLf & " / \ < > : ? * | " & Chr$(34), TTI_NONE) ' TTI_ERROR)
            wParam = 0
    End Select
   
    Case WM_PASTE
        Dim iCheck As Integer
        iCheck = IsClipboardValidFileName()
        If iCheck = 0 Then
            Beep
            Call ShowBalloonTipEx(hWnd, "", "File names may not contain any of the following characters:" & vbCrLf & " / \ < > : ? * | " & Chr$(34), TTI_NONE) ' TTI_ERROR)
            Exit Function
        ElseIf iCheck = -1 Then
            Beep
            Call ShowBalloonTipEx(hWnd, "", "The file name you have entered is too long. The total length of the file name cannot exceed 260 characters.", TTI_NONE) ' TTI_ERROR)
            Exit Function

        End If

Here's what the full project looks like with all the supporting codes and declares added in:

Form1
Code:

Option Explicit

Private Sub Form_Load()
Call Subclass(Text1.hWnd, AddressOf EditWndProc)
End Sub

mSubclass
Code:

Option Explicit

Private Declare Function DefSubclassProc Lib "comctl32.dll" Alias "#413" (ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function SetWindowSubclass Lib "comctl32.dll" Alias "#410" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long, Optional ByVal dwRefData As Long) As Long
Private Declare Function RemoveWindowSubclass Lib "comctl32.dll" Alias "#412" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long) As Long
Private Declare Function SendMessageW Lib "user32" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

Private Type EDITBALLOONTIP
    cbStruct As Long
    pszTitle As Long
    pszText As Long
    ttiIcon As BalloonTipIconConstants ' ; // From TTI_*
End Type
Private Enum BalloonTipIconConstants
  TTI_NONE = 0
  TTI_INFO = 1
  TTI_WARNING = 2
  TTI_ERROR = 3
End Enum

Private Const WM_CHAR = &H102
Private Const WM_PASTE = &H302
Private Const WM_DESTROY = &H2
Private Const WM_GETTEXTLENGTH = &HE

Private Const ECM_FIRST As Long = &H1500
Private Const EM_SHOWBALLOONTIP As Long = (ECM_FIRST + 3)

Public Function Subclass(hWnd As Long, lpfn As Long) As Long
Subclass = SetWindowSubclass(hWnd, lpfn, 0)
End Function
Public Function UnSubclass(hWnd As Long, lpfn As Long) As Long
'Only needed if you want to stop the subclassing code and keep the program running.
'Otherwise, the WndProc function should call this on WM_DESTROY
UnSubclass = RemoveWindowSubclass(hWnd, lpfn, 0)
End Function

Public Function EditWndProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal uIdSubclass As Long, ByVal dwRefData As Long) As Long
Select Case uMsg

  Case WM_CHAR
    Dim lLen As Long
    lLen = SendMessageW(hWnd, WM_GETTEXTLENGTH, 0, ByVal 0&) + 1
    If lLen > 260 Then
            Beep
            wParam = 0
            Call ShowBalloonTipEx(hWnd, "", "Maximum number of characters has been reached. The total length of the file name cannot exceed 260 characters.", TTI_NONE) ' TTI_ERROR)
            Exit Function
    End If
    Select Case wParam
        Case 47, 92, 60, 62, 58, 42, 124, 63, 34 'Illegal chars /\<>:*|?"
            Beep
            Call ShowBalloonTipEx(hWnd, "", "File names may not contain any of the following characters:" & vbCrLf & " / \ < > : ? * | " & Chr$(34), TTI_NONE) ' TTI_ERROR)
            wParam = 0
    End Select
   
    Case WM_PASTE
        Dim iCheck As Integer
        iCheck = IsClipboardValidFileName()
        If iCheck = 0 Then
            Beep
            Call ShowBalloonTipEx(hWnd, "", "File names may not contain any of the following characters:" & vbCrLf & " / \ < > : ? * | " & Chr$(34), TTI_NONE) ' TTI_ERROR)
            Exit Function
        ElseIf iCheck = -1 Then
            Beep
            Call ShowBalloonTipEx(hWnd, "", "The file name you have entered is too long. The total length of the file name cannot exceed 260 characters.", TTI_NONE) ' TTI_ERROR)
            Exit Function

        End If
       
  Case WM_DESTROY
    Call UnSubclass(hWnd, PtrEditWndProc)

End Select
 
EditWndProc = DefSubclassProc(hWnd, uMsg, wParam, lParam)

End Function
Private Function PtrEditWndProc() As Long
PtrEditWndProc = FARPROC(AddressOf EditWndProc)
End Function

Private Function FARPROC(pfn As Long) As Long
  FARPROC = pfn
End Function

Private Sub ShowBalloonTipEx(hWnd As Long, sTitle As String, sText As String, btIcon As BalloonTipIconConstants)
Dim lR As Long
Dim tEBT As EDITBALLOONTIP
tEBT.cbStruct = LenB(tEBT)
tEBT.pszText = StrPtr(sText)
tEBT.pszTitle = StrPtr(sTitle)
tEBT.ttiIcon = btIcon
lR = SendMessageW(hWnd, EM_SHOWBALLOONTIP, 0, tEBT)
End Sub
Public Function IsClipboardValidFileName() As Integer
Dim i As Long
Dim sz As String
Dim sChr As String
'there's a couple scenarios for invalid file names i've trimmed out
'to keep this example as simple as possible, look into them if you're
'going to use this code in an actual rename procedure
sz = Clipboard.GetText

If Len(sz) > 260 Then
    IsClipboardValidFileName = -1
    Exit Function
End If
IsClipboardValidFileName = 1

If InStr(sz, "*") Or InStr(sz, "?") Or InStr(sz, "<") Or InStr(sz, ">") Or InStr(sz, "|") Or InStr(sz, Chr$(34)) Then
    IsClipboardValidFileName = 0
End If
End Function

It's still a little complicated, but this is the very simplest way to get subclassing going.
Attached Files

Here's some code for calculating hashes.

$
0
0
It works with MD2, MD4, MD5, SHA1, SHA2-256, SHA2-384, and SHA2-512. Put the below code in a module (BAS file). It does everything that CAPICOM does regarding hashes, but without using any ActiveX DLL files. It depends entirely on the standard cryptographic API DLL files, using declare statements. There are several publicly accessible functions. These are
HashBytes
HashStringA
HashStringU
HashArbitraryData
BytesToHex

HashBytes computes a hash of a 1D byte array, who's lower bound is 0.

HashStringA computes the hash of an Ascii/Ansi (1 byte per character) string. As VB6 strings are actually Unicode (2 bytes per character) characters with an Ascii value above 127 will differ between locales. As such, LocaleID is a parameter for this function (it's needed to correctly convert the 2-byte-per-character values to 1-byte-per-character values via the StrConv function which is called inside this function). By default, the LocaleID used by the program is the LocaleID of the PC that the program is running on. This should be used in most situations, as this will generate a hash that will match the output of most other programs that generate a hash (such as the program called Easy Hash).

HashStringU computes the hash of a Unicode (2 bytes per character) string. As VB6 strings are actually Unicode, there is no conversion needed, and thus is no need to specify LocaleID. Therefore, this function doesn't have a LocaleID parameter.
Side-Note regarding Unicode in VB6: Despite this fact, that internally in VB6 all the strings are Unicode, the implementation of Unicode in VB6 is VERY LIMITED. That is, it won't display any Unicode character that can't also be displayed as an extended ascii character for the computer's current locale. Instead it will show it as a question mark. This won't effect how this function works (or the above function, as it's computing a hash, not displaying anything), but it will effect whether or not a given string will be properly displayed.

HashArbitraryData computes the hash of absolutely anything. It just needs to know where in memory the first byte of data is, and how many bytes long the data is. It will work with multidimensional byte arrays, arrays of other data types, arrays that start with with a lower bound other than zero, user defined types, sections of memory allocated with API functions, etc. There's nothing that it can't compute the hash of. Of course this gives you the added responsibility of needing to know where exactly in memory the data is, and the size of the data in bytes.

BytesToHex. This is a function intended to convert the raw bytes output from a hash function to a displayable hexadecimal string.




Code:

Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)

Private Declare Function CryptAcquireContext Lib "advapi32.dll" Alias "CryptAcquireContextA" (ByRef phProv As Long, ByVal pszContainer As String, ByVal pszProvider As String, ByVal dwProvType As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptCreateHash Lib "advapi32.dll" (ByVal hProv As Long, ByVal Algid As Long, ByVal hKey As Long, ByVal dwFlags As Long, ByRef phHash As Long) As Long
Private Declare Function CryptHashData Lib "advapi32.dll" (ByVal hHash As Long, ByRef pbData As Any, ByVal dwDataLen As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptGetHashParam Lib "advapi32.dll" (ByVal hHash As Long, ByVal dwParam As Long, ByRef pByte As Any, ByRef pdwDataLen As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptDestroyHash Lib "advapi32.dll" (ByVal hHash As Long) As Long
Private Declare Function CryptReleaseContext Lib "advapi32.dll" (ByVal hProv As Long, ByVal dwFlags As Long) As Long

Private Const PROV_RSA_AES As Long = 24
Private Const CRYPT_VERIFYCONTEXT As Long = &HF0000000

Public Enum HashAlgo
    HALG_MD2 = &H8001&
    HALG_MD4 = &H8002&
    HALG_MD5 = &H8003&
    HALG_SHA1 = &H8004&
    HALG_SHA2_256 = &H800C&
    HALG_SHA2_384 = &H800D&
    HALG_SHA2_512 = &H800E&
End Enum

Private Const HP_HASHSIZE As Long = &H4&
Private Const HP_HASHVAL As Long = &H2&


Public Function HashBytes(ByRef Data() As Byte, Optional ByVal HashAlgorithm As HashAlgo = HALG_MD5) As Byte()
Dim hProv As Long
Dim hHash As Long
Dim Hash() As Byte
Dim HashSize As Long

CryptAcquireContext hProv, vbNullString, vbNullString, 24, CRYPT_VERIFYCONTEXT
CryptCreateHash hProv, HashAlgorithm, 0, 0, hHash
CryptHashData hHash, Data(0), UBound(Data) + 1, 0
CryptGetHashParam hHash, HP_HASHSIZE, HashSize, 4, 0
ReDim Hash(HashSize - 1)
CryptGetHashParam hHash, HP_HASHVAL, Hash(0), HashSize, 0
CryptDestroyHash hHash
CryptReleaseContext hProv, 0

HashBytes = Hash()
End Function



Public Function HashStringA(ByVal Text As String, Optional ByVal LocaleID As Long, Optional ByVal HashAlgorithm As HashAlgo = HALG_MD5) As Byte()
Dim Data() As Byte
Data() = StrConv(Text, vbFromUnicode, LocaleID)
HashStringA = HashBytes(Data, HashAlgorithm)
End Function

Public Function HashStringU(ByVal Text As String, Optional ByVal HashAlgorithm As HashAlgo = HALG_MD5) As Byte()
Dim Data() As Byte
Data() = Text
HashStringU = HashBytes(Data, HashAlgorithm)
End Function

Public Function HashArbitraryData(ByVal MemAddress As Long, ByVal ByteCount As Long, Optional ByVal HashAlgorithm As HashAlgo = HALG_MD5) As Byte()
Dim Data() As Byte
ReDim Data(ByteCount - 1)
CopyMemory Data(0), ByVal MemAddress, ByteCount
HashArbitraryData = HashBytes(Data, HashAlgorithm)
End Function






Public Function BytesToHex(ByRef Bytes() As Byte) As String
Dim n As Long
Dim HexString As String
For n = 0 To UBound(Bytes)
    HexString = HexString & ByteToHex(Bytes(n))
Next n
BytesToHex = HexString
End Function


Private Function ByteToHex(ByVal Value As Byte) As String
Dim HexString As String
HexString = Hex$(Value)
ByteToHex = String$(2 - Len(HexString), "0") & HexString
End Function


Code Snippet: Getting folder settings with SHGetSettings

$
0
0
So there's not a single result for SHGetSettings, and this is an odd function, so I just wanted to post a working method of using it. SHGetSetSettings isn't supported above XP, so even if it's still working you might want to use this function.

The way you'd think to use it would be using the structure SHELLFLAGSTATE, but whether ByRef or VarPtr, that doesn't seem to work. Apparently it's a bitfield, and you can pass it an Integer. The Mask can be passed as normal.

Code:

Public Declare Function SHGetSettings Lib "shell32" (lpsfs As Integer, ByVal dwMask As SFS_MASK) As Long

Public Enum SFS_MASK
    SSF_SHOWALLOBJECTS = &H1
    SSF_SHOWEXTENSIONS = &H2
    SSF_SHOWCOMPCOLOR = &H8
    SSF_SHOWSYSFILES = &H20
    SSF_DOUBLECLICKINWEBVIEW = &H80
    SSF_SHOWATTRIBCOL = &H100
    SSF_DESKTOPHTML = &H200
    SSF_WIN95CLASSIC = &H400
    SSF_DONTPRETTYPATH = &H800
    SSF_SHOWINFOTIP = &H2000
    SSF_MAPNETDRVBUTTON = &H1000
    SSF_NOCONFIRMRECYCLE = &H8000
    SSF_HIDEICONS = &H4000
End Enum

The structure that's supposed to be returned looks like this:
Code:

Public Type SHELLFLAGSTATE
  fShowAllObjects  As Boolean
  fShowExtensions  As Boolean
  fNoConfirmRecycle  As Boolean
  fShowSysFiles  As Boolean
  fShowCompColor  As Boolean
  fDoubleClickInWebView  As Boolean
  fDesktopHTML  As Boolean
  fWin95Classic  As Boolean
  fDontPrettyPath  As Boolean
  fShowAttribCol  As Boolean
  fMapNetDrvBtn  As Boolean
  fShowInfoTip  As Boolean
  fHideIcons  As Boolean
  fAutoCheckSelect  As Boolean
  fIconsOnly  As Boolean
  fRestFlags  As Long
End Type

Instead, we're not going to use that structure at all, but its members represent bits (and fRestFlags isn't used for anything), so their order matters. fShowAllObjects is 2^0, fShowExtensions is 2^1, etc.

Code:

Public Function ExplorerSettingEnabled(lSetting As SFS_MASK) As Boolean
Dim lintg As Integer
Call SHGetSettings(lintg, lSetting)
Select Case lSetting
    Case SSF_SHOWALLOBJECTS
        ExplorerSettingEnabled = lintg And 2 ^ 0 'fShowAllObjects
    Case SSF_SHOWEXTENSIONS
        ExplorerSettingEnabled = lintg And 2 ^ 1 'fShowExtensions
    Case SSF_NOCONFIRMRECYCLE
        ExplorerSettingEnabled = lintg And 2 ^ 2 'fNoConfirmRecycle
    Case SSF_SHOWSYSFILES
        ExplorerSettingEnabled = lintg And 2 ^ 3 'fShowSysFiles
    Case SSF_SHOWCOMPCOLOR
        ExplorerSettingEnabled = lintg And 2 ^ 4 'fShowCompColor
    Case SSF_DOUBLECLICKINWEBVIEW
        ExplorerSettingEnabled = lintg And 2 ^ 5 'fDoubleClickInWebView
    Case SSF_DESKTOPHTML
        ExplorerSettingEnabled = lintg And 2 ^ 6 'fDesktopHTML
    Case SSF_WIN95CLASSIC
        ExplorerSettingEnabled = lintg And 2 ^ 7 'fWin95Classic
    Case SSF_DONTPRETTYPATH
        ExplorerSettingEnabled = lintg And 2 ^ 8 'fDontPrettyPath
    Case SSF_SHOWATTRIBCOL
        ExplorerSettingEnabled = lintg And 2 ^ 9 'fShowAttribCol
    Case SSF_MAPNETDRVBUTTON
        ExplorerSettingEnabled = lintg And 2 ^ 10 'fMapNetDrvButton
    Case SSF_SHOWINFOTIP
        ExplorerSettingEnabled = lintg And 2 ^ 11 'fShowInfoTip
    Case SSF_HIDEICONS
        ExplorerSettingEnabled = lintg And 2 ^ 12 'fHideIcons
   
End Select
End Function

VB6 Threading, using the small DirectCOM.dll-HelperLib

$
0
0
Just for those who want to try a proven approach, which works reliably (and comparably simple)
in spanning up STAs (Single-Threaded-Apartments) for over 10 years now (one can use his own
VB6-compiled AX-Dlls which provide a Class-Instance that runs on said STA - no Assembler-Thunking,
no TypeLibs and also no "special Dll-Exports" are needed - it's aside from a few API-calls just straight VB-Code).

The Thread-Class-Instances in question are always created Regfree in this case (so,
no Setup is needed - just ship your Thread-Dlls alongside DirectCOM.dll in a SubFolder
of your App).

The Demo here tries to show not only how to create the STA-threads with a Class-
Instance in it, but also how to share Memory with the Applications Main-Thread
(which is the fastest way of cross-thread-communication).

The implementation of the Main-App is using a separate (Private) Wrapper-Class
for handling the "Remote-Thread" (offering Properties to wrap the shared Memory-area,
and such a Class will also automatically close the thread it wraps, in case it is itself terminated).

That allows for cleaner Code in the "Thread-consuming-instance" (in our case the Apps fMain-Form).

The Code for the ThreadLib.dll on the other hand - (this is the AX-Dll which provides a Public
cThread-Class, which will finally run on its own threaded Apartment) - is contained in the
\Bin\... SubFolder of the Zip.

Just leave this Bin-SubFolder as it is, when you run the Main-Apps *.vbp File.

Here's the Code for the Demo:
VB6ThreadingDirectCOM.zip

And here is a ScreenShot (the colored Forms are created inside the 4 Threads - and perform
an endless loop, to show some kind of "Plasma-Effect" - just to put the threads under stress a bit).



Have fun (and just ask when you have any questions - though I tried to comment the Demo well enough, I hope).

Olaf
Attached Files

Register/Unregister both DLLs and OCXs with RightClick

$
0
0
I used a vbscript provided by Olaf to register vbRichClient5, changed it a bit, and added 4 entries to registry.
Now I'm able to register/unregister both DLLs and OCXs with a simple RightMouse click over the file.

This probably worth less than nothing, but it works for me, and might be useful for somebody else.
Just copy Register.vbs to C:\Windows and execute the file Register.reg

Register.zip
Attached Files

[VB6] - Module for working with COM-Dll without registration.

$
0
0
Hello. I give my module for working with COM-DLL without registration in the registry.
The module has several functions:
  1. GetAllCoclasses - returns to the list of classes and unique identifiers are extracted from a type library.
  2. CreateIDispatch - creates IDispatch implementation by reference to the object and the name of the interface.
  3. CreateObjectEx2 - creates an object by name from a type library.
  4. CreateObjectEx - creates an object by CLSID.
  5. UnloadLibrary - unloads the DLL if it is not used.

vb Code:
  1. ' The module modTrickUnregCOM.bas - for working with COM libraries without registration.
  2. ' © Krivous Anatolii Anatolevich (The trick), 2015
  3.  
  4. Option Explicit
  5.  
  6.  D E C L A R A T I O N
  7.  
  8. Dim iidClsFctr      As GUID
  9. Dim iidUnk          As GUID
  10. Dim isInit          As Boolean
  11.  
  12. ' // Get all co-classes described in type library.
  13. Public Function GetAllCoclasses( _
  14.                 ByRef path As String, _
  15.                 ByRef listOfClsid() As GUID, _
  16.                 ByRef listOfNames() As String, _
  17.                 ByRef countCoClass As Long) As Boolean
  18.                
  19.     Dim typeLib As IUnknown
  20.     Dim typeInf As IUnknown
  21.     Dim ret     As Long
  22.     Dim count   As Long
  23.     Dim index   As Long
  24.     Dim pAttr   As Long
  25.     Dim tKind   As Long
  26.    
  27.     ret = LoadTypeLibEx(StrPtr(path), REGKIND_NONE, typeLib)
  28.    
  29.     If ret Then
  30.         Err.Raise ret
  31.         Exit Function
  32.     End If
  33.    
  34.     count = ITypeLib_GetTypeInfoCount(typeLib)
  35.     countCoClass = 0
  36.    
  37.     If count > 0 Then
  38.    
  39.         ReDim listOfClsid(count - 1)
  40.         ReDim listOfNames(count - 1)
  41.        
  42.         For index = 0 To count - 1
  43.        
  44.             ret = ITypeLib_GetTypeInfo(typeLib, index, typeInf)
  45.                        
  46.             If ret Then
  47.                 Err.Raise ret
  48.                 Exit Function
  49.             End If
  50.            
  51.             ITypeInfo_GetTypeAttr typeInf, pAttr
  52.            
  53.             GetMem4 ByVal pAttr + &H28, tKind
  54.            
  55.             If tKind = TKIND_COCLASS Then
  56.            
  57.                 memcpy listOfClsid(countCoClass), ByVal pAttr, Len(listOfClsid(countCoClass))
  58.                 ret = ITypeInfo_GetDocumentation(typeInf, -1, listOfNames(countCoClass), vbNullString, 0, vbNullString)
  59.                
  60.                 If ret Then
  61.                     ITypeInfo_ReleaseTypeAttr typeInf, pAttr
  62.                     Err.Raise ret
  63.                     Exit Function
  64.                 End If
  65.                
  66.                 countCoClass = countCoClass + 1
  67.                
  68.             End If
  69.            
  70.             ITypeInfo_ReleaseTypeAttr typeInf, pAttr
  71.            
  72.             Set typeInf = Nothing
  73.            
  74.         Next
  75.        
  76.     End If
  77.    
  78.     If countCoClass Then
  79.        
  80.         ReDim Preserve listOfClsid(countCoClass - 1)
  81.         ReDim Preserve listOfNames(countCoClass - 1)
  82.    
  83.     Else
  84.    
  85.         Erase listOfClsid()
  86.         Erase listOfNames()
  87.        
  88.     End If
  89.    
  90.     GetAllCoclasses = True
  91.    
  92. End Function
  93.  
  94. ' // Create IDispach implementation described in type library.
  95. Public Function CreateIDispatch( _
  96.                 ByRef obj As IUnknown, _
  97.                 ByRef typeLibPath As String, _
  98.                 ByRef interfaceName As String) As Object
  99.                
  100.     Dim typeLib As IUnknown
  101.     Dim typeInf As IUnknown
  102.     Dim ret     As Long
  103.     Dim retObj  As IUnknown
  104.     Dim pAttr   As Long
  105.     Dim tKind   As Long
  106.    
  107.     ret = LoadTypeLibEx(StrPtr(typeLibPath), REGKIND_NONE, typeLib)
  108.    
  109.     If ret Then
  110.         Err.Raise ret
  111.         Exit Function
  112.     End If
  113.    
  114.     ret = ITypeLib_FindName(typeLib, interfaceName, 0, typeInf, 0, 1)
  115.    
  116.     If typeInf Is Nothing Then
  117.         Err.Raise &H80004002, , "Interface not found"
  118.         Exit Function
  119.     End If
  120.    
  121.     ITypeInfo_GetTypeAttr typeInf, pAttr
  122.     GetMem4 ByVal pAttr + &H28, tKind
  123.     ITypeInfo_ReleaseTypeAttr typeInf, pAttr
  124.    
  125.     If tKind = TKIND_DISPATCH Then
  126.         Set CreateIDispatch = obj
  127.         Exit Function
  128.     ElseIf tKind <> TKIND_INTERFACE Then
  129.         Err.Raise &H80004002, , "Interface not found"
  130.         Exit Function
  131.     End If
  132.  
  133.     ret = CreateStdDispatch(Nothing, obj, typeInf, retObj)
  134.    
  135.     If ret Then
  136.         Err.Raise ret
  137.         Exit Function
  138.     End If
  139.    
  140.     Set CreateIDispatch = retObj
  141.  
  142. End Function
  143.  
  144. ' // Create object by Name.
  145. Public Function CreateObjectEx2( _
  146.                 ByRef pathToDll As String, _
  147.                 ByRef pathToTLB As String, _
  148.                 ByRef className As String) As IUnknown
  149.                
  150.     Dim typeLib As IUnknown
  151.     Dim typeInf As IUnknown
  152.     Dim ret     As Long
  153.     Dim pAttr   As Long
  154.     Dim tKind   As Long
  155.     Dim clsid   As GUID
  156.    
  157.     ret = LoadTypeLibEx(StrPtr(pathToTLB), REGKIND_NONE, typeLib)
  158.    
  159.     If ret Then
  160.         Err.Raise ret
  161.         Exit Function
  162.     End If
  163.    
  164.     ret = ITypeLib_FindName(typeLib, className, 0, typeInf, 0, 1)
  165.    
  166.     If typeInf Is Nothing Then
  167.         Err.Raise &H80040111, , "Class not found in type library"
  168.         Exit Function
  169.     End If
  170.  
  171.     ITypeInfo_GetTypeAttr typeInf, pAttr
  172.    
  173.     GetMem4 ByVal pAttr + &H28, tKind
  174.    
  175.     If tKind = TKIND_COCLASS Then
  176.         memcpy clsid, ByVal pAttr, Len(clsid)
  177.     Else
  178.         Err.Raise &H80040111, , "Class not found in type library"
  179.         Exit Function
  180.     End If
  181.    
  182.     ITypeInfo_ReleaseTypeAttr typeInf, pAttr
  183.            
  184.     Set CreateObjectEx2 = CreateObjectEx(pathToDll, clsid)
  185.    
  186. End Function
  187.                
  188. ' // Create object by CLSID and path.
  189. Public Function CreateObjectEx( _
  190.                 ByRef path As String, _
  191.                 ByRef clsid As GUID) As IUnknown
  192.                
  193.     Dim hLib    As Long
  194.     Dim lpAddr  As Long
  195.    
  196.     hLib = LoadLibrary(StrPtr(path))
  197.     If hLib = 0 Then
  198.         Err.Raise 53, , Error(53) & " " & Chr$(34) & path & Chr$(34)
  199.         Exit Function
  200.     End If
  201.    
  202.     lpAddr = GetProcAddress(hLib, "DllGetClassObject")
  203.    
  204.     If lpAddr = 0 Then
  205.         Err.Raise 453, , "Can't find dll entry point DllGetClasesObject in " & Chr$(34) & path & Chr$(34)
  206.         Exit Function
  207.     End If
  208.  
  209.     If Not isInit Then
  210.         CLSIDFromString StrPtr(IID_IClassFactory), iidClsFctr
  211.         CLSIDFromString StrPtr(IID_IUnknown), iidUnk
  212.         isInit = True
  213.     End If
  214.    
  215.     Dim ret     As Long
  216.     Dim out     As IUnknown
  217.    
  218.     ret = DllGetClassObject(lpAddr, clsid, iidClsFctr, out)
  219.    
  220.     If ret = 0 Then
  221.  
  222.         ret = IClassFactory_CreateInstance(out, 0, iidUnk, CreateObjectEx)
  223.  
  224.     Else: Err.Raise ret: Exit Function
  225.     End If
  226.    
  227.     Set out = Nothing
  228.    
  229. End Function
  230.  
  231. ' // Unload DLL if not used.
  232. Public Function UnloadLibrary( _
  233.                 ByRef path As String) As Boolean
  234.                
  235.     Dim hLib    As Long
  236.     Dim lpAddr  As Long
  237.     Dim ret     As Long
  238.    
  239.     If Not isInit Then Exit Function
  240.    
  241.     hLib = GetModuleHandle(StrPtr(path))
  242.     If hLib = 0 Then Exit Function
  243.    
  244.     lpAddr = GetProcAddress(hLib, "DllCanUnloadNow")
  245.     If lpAddr = 0 Then Exit Function
  246.    
  247.     ret = DllCanUnloadNow(lpAddr)
  248.    
  249.     If ret = 0 Then
  250.         FreeLibrary hLib
  251.         UnloadLibrary = True
  252.     End If
  253.    
  254. End Function
  255.  
  256. ' // Call "DllGetClassObject" function using a pointer.
  257. Private Function DllGetClassObject( _
  258.                  ByVal funcAddr As Long, _
  259.                  ByRef clsid As GUID, _
  260.                  ByRef iid As GUID, _
  261.                  ByRef out As IUnknown) As Long
  262.                  
  263.     Dim params(2)   As Variant
  264.     Dim types(2)    As Integer
  265.     Dim list(2)     As Long
  266.     Dim resultCall  As Long
  267.     Dim pIndex      As Long
  268.     Dim pReturn     As Variant
  269.    
  270.     params(0) = VarPtr(clsid)
  271.     params(1) = VarPtr(iid)
  272.     params(2) = VarPtr(out)
  273.    
  274.     For pIndex = 0 To UBound(params)
  275.         list(pIndex) = VarPtr(params(pIndex)):   types(pIndex) = VarType(params(pIndex))
  276.     Next
  277.    
  278.     resultCall = DispCallFunc(0&, funcAddr, CC_STDCALL, vbLong, 3, types(0), list(0), pReturn)
  279.              
  280.     If resultCall Then Err.Raise 5: Exit Function
  281.    
  282.     DllGetClassObject = pReturn
  283.    
  284. End Function
  285.  
  286. ' // Call "DllCanUnloadNow" function using a pointer.
  287. Private Function DllCanUnloadNow( _
  288.                  ByVal funcAddr As Long) As Long
  289.                  
  290.     Dim resultCall  As Long
  291.     Dim pReturn     As Variant
  292.    
  293.     resultCall = DispCallFunc(0&, funcAddr, CC_STDCALL, vbLong, 0, ByVal 0&, ByVal 0&, pReturn)
  294.              
  295.     If resultCall Then Err.Raise 5: Exit Function
  296.    
  297.     DllCanUnloadNow = pReturn
  298.    
  299. End Function
  300.  
  301. ' // Call "IClassFactory:CreateInstance" method.
  302. Private Function IClassFactory_CreateInstance( _
  303.                  ByVal obj As IUnknown, _
  304.                  ByVal punkOuter As Long, _
  305.                  ByRef riid As GUID, _
  306.                  ByRef out As IUnknown) As Long
  307.    
  308.     Dim params(2)   As Variant
  309.     Dim types(2)    As Integer
  310.     Dim list(2)     As Long
  311.     Dim resultCall  As Long
  312.     Dim pIndex      As Long
  313.     Dim pReturn     As Variant
  314.    
  315.     params(0) = punkOuter
  316.     params(1) = VarPtr(riid)
  317.     params(2) = VarPtr(out)
  318.    
  319.     For pIndex = 0 To UBound(params)
  320.         list(pIndex) = VarPtr(params(pIndex)):   types(pIndex) = VarType(params(pIndex))
  321.     Next
  322.    
  323.     resultCall = DispCallFunc(obj, &HC, CC_STDCALL, vbLong, 3, types(0), list(0), pReturn)
  324.          
  325.     If resultCall Then Err.Raise resultCall: Exit Function
  326.      
  327.     IClassFactory_CreateInstance = pReturn
  328.    
  329. End Function
  330.  
  331. ' // Call "ITypeLib:GetTypeInfoCount" method.
  332. Private Function ITypeLib_GetTypeInfoCount( _
  333.                  ByVal obj As IUnknown) As Long
  334.    
  335.     Dim resultCall  As Long
  336.     Dim pReturn     As Variant
  337.  
  338.     resultCall = DispCallFunc(obj, &HC, CC_STDCALL, vbLong, 0, ByVal 0&, ByVal 0&, pReturn)
  339.          
  340.     If resultCall Then Err.Raise resultCall: Exit Function
  341.      
  342.     ITypeLib_GetTypeInfoCount = pReturn
  343.    
  344. End Function
  345.  
  346. ' // Call "ITypeLib:GetTypeInfo" method.
  347. Private Function ITypeLib_GetTypeInfo( _
  348.                  ByVal obj As IUnknown, _
  349.                  ByVal index As Long, _
  350.                  ByRef ppTInfo As IUnknown) As Long
  351.    
  352.     Dim params(1)   As Variant
  353.     Dim types(1)    As Integer
  354.     Dim list(1)     As Long
  355.     Dim resultCall  As Long
  356.     Dim pIndex      As Long
  357.     Dim pReturn     As Variant
  358.    
  359.     params(0) = index
  360.     params(1) = VarPtr(ppTInfo)
  361.    
  362.     For pIndex = 0 To UBound(params)
  363.         list(pIndex) = VarPtr(params(pIndex)):   types(pIndex) = VarType(params(pIndex))
  364.     Next
  365.    
  366.     resultCall = DispCallFunc(obj, &H10, CC_STDCALL, vbLong, 2, types(0), list(0), pReturn)
  367.          
  368.     If resultCall Then Err.Raise resultCall: Exit Function
  369.      
  370.     ITypeLib_GetTypeInfo = pReturn
  371.    
  372. End Function
  373.  
  374. ' // Call "ITypeLib:FindName" method.
  375. Private Function ITypeLib_FindName( _
  376.                  ByVal obj As IUnknown, _
  377.                  ByRef szNameBuf As String, _
  378.                  ByVal lHashVal As Long, _
  379.                  ByRef ppTInfo As IUnknown, _
  380.                  ByRef rgMemId As Long, _
  381.                  ByRef pcFound As Integer) As Long
  382.    
  383.     Dim params(4)   As Variant
  384.     Dim types(4)    As Integer
  385.     Dim list(4)     As Long
  386.     Dim resultCall  As Long
  387.     Dim pIndex      As Long
  388.     Dim pReturn     As Variant
  389.    
  390.     params(0) = StrPtr(szNameBuf)
  391.     params(1) = lHashVal
  392.     params(2) = VarPtr(ppTInfo)
  393.     params(3) = VarPtr(rgMemId)
  394.     params(4) = VarPtr(pcFound)
  395.    
  396.     For pIndex = 0 To UBound(params)
  397.         list(pIndex) = VarPtr(params(pIndex)):   types(pIndex) = VarType(params(pIndex))
  398.     Next
  399.    
  400.     resultCall = DispCallFunc(obj, &H2C, CC_STDCALL, vbLong, 5, types(0), list(0), pReturn)
  401.          
  402.     If resultCall Then Err.Raise resultCall: Exit Function
  403.      
  404.     ITypeLib_FindName = pReturn
  405.    
  406. End Function
  407.  
  408. ' // Call "ITypeInfo:GetTypeAttr" method.
  409. Private Sub ITypeInfo_GetTypeAttr( _
  410.             ByVal obj As IUnknown, _
  411.             ByRef ppTypeAttr As Long)
  412.    
  413.     Dim resultCall  As Long
  414.     Dim pReturn     As Variant
  415.    
  416.     pReturn = VarPtr(ppTypeAttr)
  417.    
  418.     resultCall = DispCallFunc(obj, &HC, CC_STDCALL, vbEmpty, 1, vbLong, VarPtr(pReturn), 0)
  419.          
  420.     If resultCall Then Err.Raise resultCall: Exit Sub
  421.  
  422. End Sub
  423.  
  424. ' // Call "ITypeInfo:GetDocumentation" method.
  425. Private Function ITypeInfo_GetDocumentation( _
  426.                  ByVal obj As IUnknown, _
  427.                  ByVal memid As Long, _
  428.                  ByRef pBstrName As String, _
  429.                  ByRef pBstrDocString As String, _
  430.                  ByRef pdwHelpContext As Long, _
  431.                  ByRef pBstrHelpFile As String) As Long
  432.    
  433.     Dim params(4)   As Variant
  434.     Dim types(4)    As Integer
  435.     Dim list(4)     As Long
  436.     Dim resultCall  As Long
  437.     Dim pIndex      As Long
  438.     Dim pReturn     As Variant
  439.    
  440.     params(0) = memid
  441.     params(1) = VarPtr(pBstrName)
  442.     params(2) = VarPtr(pBstrDocString)
  443.     params(3) = VarPtr(pdwHelpContext)
  444.     params(4) = VarPtr(pBstrHelpFile)
  445.    
  446.     For pIndex = 0 To UBound(params)
  447.         list(pIndex) = VarPtr(params(pIndex)):   types(pIndex) = VarType(params(pIndex))
  448.     Next
  449.    
  450.     resultCall = DispCallFunc(obj, &H30, CC_STDCALL, vbLong, 5, types(0), list(0), pReturn)
  451.          
  452.     If resultCall Then Err.Raise resultCall: Exit Function
  453.      
  454.     ITypeInfo_GetDocumentation = pReturn
  455.    
  456. End Function
  457.  
  458. ' // Call "ITypeInfo:ReleaseTypeAttr" method.
  459. Private Sub ITypeInfo_ReleaseTypeAttr( _
  460.             ByVal obj As IUnknown, _
  461.             ByVal ppTypeAttr As Long)
  462.    
  463.     Dim resultCall  As Long
  464.    
  465.     resultCall = DispCallFunc(obj, &H4C, CC_STDCALL, vbEmpty, 1, vbLong, VarPtr(CVar(ppTypeAttr)), 0)
  466.          
  467.     If resultCall Then Err.Raise resultCall: Exit Sub
  468.  
  469. End Sub
Attached Files

VB6 Regfree Handling of AX-Plugin-Dlls per DirectCOM

$
0
0
One necessity, to make the handling of VB6-produced ActiveX-Dlls more easy,
is the regfree loading of the Classes which are contained in the Plugin-Dlls.

Once that is out of the way (we use DirectCOM.dll for that task), another
problem comes to mind, which needs to be addressed:
How to "force" the potentially "foreign" contributors to the plugin-system,
to conform to a certain set of "rules" (with regards to the functions and
their signatures - type- and parameter-wise).

One way to do that would be "by convention which is documented somewhere".
Another (more strict - and more selfexplaining) way is, to define an interface
(or a set of interfaces, as used in this Demo) in a TypeLib, and force plugin-
authors to adhere to the "contract" which is given over those COM-Interfaces.

In VB6 this is quite easy - since we can just use:
Implements ISomeInterface
... then drop down the appropriate Methods we have to implement from the
VB6-IDEs ComboBoxes (Interface-methods are reachable quite similar to Event-
handler-Methods because they are available over the very same IDE-ComboBoxes,
but in case of an Interface we cannot choose to leave such a Method out -
we have to bring them all as code into the appropriate Module - and then
"fill them out" with our own implementations).

That said, the creation of such an interface might be considered "boring stuff"
(maybe because it really is ;)) - but it's a task which (when finished and well-done) -
kind of repays for the initial trouble... (implementing existing interfaces is a bit more fun,
because this can be done pretty fast usually, since "the structure is already a given" -
and the "fill-out" quite easy in most cases.

Maybe a nice TypeLib-Editor might help in this regard - I'm using the one from
Matt-Curland for my stuff (as e.g. for the 'PluginInterfaces.tlb', which is contained in the Demo).

The Demo comes with a Main-Application, which shows the Management of the Plugin-Dlls -
and is codewise quite lean - here's the complete Form-Code:
Code:

Option Explicit

Private Declare Function LoadLibraryW Lib "kernel32" (ByVal pFileName As Long) As Long  'to preload DirectCOM.dll
Private Declare Function GetInstanceEx Lib "DirectCOM" (spFName As Long, spClassName As Long, Optional ByVal UseAlteredSearchPath As Boolean = True) As stdole.IUnknown

Private CurPlugin As PluginInterfaces.IPluginInfo

Private Sub Form_Load()
  LoadLibraryW StrPtr(App.Path & "\Bin\DirectCom.dll")
  Set picSrc.Picture = LoadPicture(App.Path & "\Res\SrcImg.jpg")
  lstPlugins.Path = App.Path & "\Plugins"
End Sub

Private Sub lstPlugins_Click()
  If lstPlugins.ListIndex < 0 Then Exit Sub
  Set CurPlugin = LoadPlugin(lstPlugins.Path & "\" & lstPlugins.FileName)
 
  txtInfo(0) = CurPlugin.GetVersion
  txtInfo(1) = CurPlugin.GetName
  txtInfo(2) = CurPlugin.GetDescription
 
  lstActionClasses.Clear
  Dim AC
  For Each AC In Split(CurPlugin.GetActionClassNames, ",")
    lstActionClasses.AddItem Trim$(AC)
  Next
End Sub

Private Function LoadPlugin(FileNameDll As String) As PluginInterfaces.IPluginInfo
  Set LoadPlugin = GetInstanceEx(StrPtr(FileNameDll), StrPtr("Info")) 'instantiate the Info-Class regfree
End Function

Private Sub lstActionClasses_Click()
  If lstActionClasses.ListIndex < 0 Then Exit Sub
  DoAction lstActionClasses.Text
End Sub

Private Sub DoAction(ClassName As String)
  Dim ActionObj As PluginInterfaces.IPluginAction, Pxl() As Long
  Set ActionObj = CurPlugin.CreateActionInstance(ClassName)
 
  GetArrFromHdl Pxl, picSrc.Picture.Handle 'copy the Pixels from the Source-Picture into the Pxl-Array
    ActionObj.ProcessImgData UBound(Pxl, 1) + 1, UBound(Pxl, 2) + 1, VarPtr(Pxl(0, 0))
  DrawArr Pxl, picDst.hDC 'now that the action is finished, draw the resulting Pixels to the Destination-PicBox
  If picDst.AutoRedraw Then picDst.Refresh
End Sub

Here's a ScreenShot, what the Plugins do (I've decided to cover a "PhotoShop-like scenario"
(Plugins which cover different ImageProcessing-Algorithms).



Don't know - besides the already mentioned "hurdles":
- Regfree-Loading
- TypeLib-Creation
- getting familiar with VBs Implements-Keyword

There's not much more to say - if there's questions left - just ask...

The code for the Demo is here:
RegfreePluginHandlingVB6.zip

Have fun...

Olaf
Attached Files

[VB6] Reg-Free COM at runtime via Microsoft.Windows.ActCtx

$
0
0
One of the slick things Microsoft developed before they pulled the plug on VB development and threw everything behind .Net was registration-free COM. Sadly, the change in direction meant that they never added tools support to VB, either in a service pack, an add-on package, or a non-"managed" VB7.

Most of this didn't see the light of day until Windows XP, though it was a bit flaky until XP SP2, and a little less so in XP SP3. But it wasn't really completed until Windows Server 2003 and then in Windows Vista and beyond.

The missing piece was the Microsoft.Windows.ActCtx object.

This never shipped in Windows XP, though at one time there was supposedly a redist Installer merge module for it. I've never seen it though and if it did exist it doesn't seem to be hosted at Microsoft Downloads anymore.


Vista and Beyond

The good news is that we do have it now. So in addition to application manifests for "normal" reg-free COM we can also use "runtime" reg-free COM in VB6 as well as 32-bit VBA and VBScript.

There is one snag: the tooling.


Tools

To create instances of classes from an unregistered DLL requires information from somewhere. The Windows answer to this is application and assembly manifests. However nothing was provided for VB6 developers except for hacks using older versions of Visual Studio .Net or the MT.exe utility from the Windows Vista and later SDKs.

There certainly are 3rd party tools out there. The famous MMM comes to mind as well as the knock-off UMMM and the commercial Side-by-Side Manifest Maker.

However as far as I know only the latter of those can make the assembly manifest files we need for use with the Microsoft.Windows.ActCtx object.


DLLAsm

With that in mind, I knocked together a quick and dirty tool for just this task.

You can run DLLAsm.exe from a command prompt and supply the name of the DLL you need a manifest for, or you can drag the DLL's icon and drop it on the DLLAsm.exe icon in Explorer.

This creates an assembly manifest "next to" the DLL (in whatever folder it is in), overwriting an existing one if any.

Just open the Project in the attached archive and compile it. All of its dependencies are standard libraries included in Windows. While useless on XP it should still compile and run there just fine.
Attached Files

Transit Time Tester

$
0
0
Users sometimes want to know how accessible a certain site is and how long it takes to get to it. The "ping" command has traditionally been used for that, but there are problems using this utility. The difficulty is created by the way that some routers handle ICMP (Internet Control Message Protocol) packets. These routers give ICMP packets the lowest priority, so the round trip time displayed is highly questionable and variable. Some sites also disable "ping" to protect against Ping Flood attacks.

"Ping", (as well as "Tracert") utilize UDP packets, which do not establish a connection with the far end. Transit Time Tester uses TCP packets, which are initiated using a 3-way handshake. The client sends a SYN request, the server responds with a SYN-ACK, and the client completes the connection with an ACK. Transit Time Tester measures the time required to receive the SYN-ACK, and terminates the connection by forcing an error. It uses a cutdown version of NewSocket.cls/mWinsock.bas.

For the domain, you can use the domain name, the domain IP Address, or just copy and paste the URL. If the URL is used, the port is automatically adjusted to 80.

J.A. Coutts
Attached Images
 
Attached Files

DrawLine function with pixel count output

$
0
0
This is a function I wrote intended to replace the built-in VB6 Line method. With the internal Line method, in addition to all the intermediate pixels of a line, the first pixel is also drawn. But there's a problem, the last pixel is never drawn. So if you want a complete line between 2 points, you will need to use both the line and pset commands. Another problem is that it doesn't draw anything if the line has no length (the first pixel is the same as the last pixel). The problem with the internal Line method, it sees a line with the first and last points being the same as having a length of 0, and a line with a length of 1 being a line who's last pixel is just adjacent to the first pixel.

My function fixes these problems. A line with the first pixel being the same as the last pixel, using my function, is seen by my function as a line with a length of 1 (though it has a length, this line has no direction, because the first and last pixels are the same), so it draws a single pixel. If the last pixel is adjacent the first pixel, then the line has a length of 2. The length is simply equal to the number of pixels drawn, by my definition. Therefore, a single pixel has a length of 1 (seems counterintuitive, because in real life, a length must also have a direction, but real life is analog, while pixels are quantized, so that's why it's different here). The return value of the function is the length of the line, which is the number of pixels drawn by the function. This is another advantage of my function over the internal built-in Line method, which has no way to return the number of pixels drawn.


Here's the code for my DrawLine function.

Code:

Private Function DrawLine(ByVal x1 As Long, ByVal y1 As Long, ByVal x2 As Long, ByVal y2 As Long) As Long
Dim x As Long
Dim y As Long
Dim m As Single
Dim PixelCount As Long


If (x2 = x1) And (y2 = y1) Then
    PSet (x1, y1)
    PixelCount = 1
Else
    If Abs(x2 - x1) > Abs(y2 - y1) Then
        m = (y2 - y1) / (x2 - x1)
        If x2 < x1 Then
            For x = x1 To x2 Step -1
                y = (x - x1) * m + y1
                PSet (x, y)
                PixelCount = PixelCount + 1
            Next x
        Else
            For x = x1 To x2
                y = (x - x1) * m + y1
                PSet (x, y)
                PixelCount = PixelCount + 1
            Next x
        End If
    Else
        m = (x2 - x1) / (y2 - y1)
        If y2 < y1 Then
            For y = y1 To y2 Step -1
                x = (y - y1) * m + x1
                PSet (x, y)
                PixelCount = PixelCount + 1
            Next y
        Else
            For y = y1 To y2
                x = (y - y1) * m + x1
                PSet (x, y)
                PixelCount = PixelCount + 1
            Next y
        End If
    End If
End If
DrawLine = PixelCount
End Function

[VB6, Vista+] List all file properties, locale/unit formatted, by modern PROPERTYKEY

$
0
0
Previous VB6 methods for listing file properties haven't used the newer methods, which are especially handy if you're already working with IShellItem. This code is a tour of the modern property system, covering PROPERTYKEY, IPropertyStore, IPropertyDescription, and propsys.dll APIs to take raw values and format them according to the system locale; e.g. adding 'pixels' or 'dpi' to image properties, showing dates/times according to system settings, changing the unreadable number representing attributes into letters, etc. It also goes on to show the raw data, exposing an important method if you do need to work with PROPVARIANT in VB.

Requirements
-Requires oleexp 1.8 or higher (released Jun 1 2015) (for IDE only, add references to olelib.tlb and oleexp.tlb)
-Only works with Windows Vista and higher

Code
Code:

Public Declare Function ILCreateFromPathW Lib "shell32" (ByVal pwszPath As Long) As Long
Public Declare Function SHCreateItemFromIDList Lib "shell32" (ByVal pidl As Long, riid As UUID, ppv As Any) As Long
Public Declare Function CoInitialize Lib "ole32.dll" (ByVal pvReserved As Long) As Long
Public Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal pv As Long) ' Frees memory allocated by the shell
Public Declare Function SysReAllocString Lib "oleaut32.dll" (ByVal pBSTR As Long, Optional ByVal pszStrPtr As Long) As Long
Public Declare Function PSGetNameFromPropertyKey Lib "propsys.dll" (PropKey As PROPERTYKEY, ppszCanonicalName As Long) As Long
Public Declare Function PSGetPropertyDescription Lib "propsys.dll" (PropKey As PROPERTYKEY, riid As UUID, ppv As Any) As Long
Public Declare Function PSFormatPropertyValue Lib "propsys.dll" (ByVal pps As Long, ByVal ppd As Long, ByVal pdff As PROPDESC_FORMAT_FLAGS, ppszDisplay As Long) As Long
Public Declare Function PropVariantToVariant Lib "propsys.dll" (ByRef propvar As Any, ByRef var As Variant) As Long

Public Sub EnumFileProperties(sPath As String)
'sPath can be a file or a folder. Other objects that you might want properties
'for, a slight re-work can be used to start from its pidl or IShellItem directly
Dim isif As IShellItem2
Dim pidlt As Long
Dim pProp As IPropertyDescription
Dim pk As PROPERTYKEY
Dim pPStore As IPropertyStore
Dim lpe As Long
Dim lpProp As Long
Dim i As Long, j As Long
Dim vProp As Variant
Dim vrProp As Variant
Dim vte As VbVarType
Dim sPrName As String
Dim sFmtProp As String

Call CoInitialize(0)

'Create a reference to IShellItem2
pidlt = ILCreateFromPathW(StrPtr(sPath))
Call SHCreateItemFromIDList(pidlt, IID_IShellItem2, isif)
Call CoTaskMemFree(pidlt)
If (isif Is Nothing) Then
    Debug.Print "Failed to get IShellItem2"
    Exit Sub
End If

'Get the IPropertyStore interface
isif.GetPropertyStore GPS_DEFAULT, IID_IPropertyStore, pPStore
If (pPStore Is Nothing) Then
    Debug.Print "Failed to get IPropertyStore"
    Exit Sub
End If

'Get the number of properties
pPStore.GetCount lpe
Debug.Print "Total number of properties=" & lpe

On Error GoTo eper
For i = 0 To (lpe - 1)
    'Loop through each property; starting with information about which property we're working with
    pPStore.GetAt i, pk
    PSGetNameFromPropertyKey pk, lpProp
    sPrName = BStrFromLPWStr(lpProp)
    Debug.Print "Property Name=" & sPrName & ",SCID={" & Hex$(pk.fmtid.Data1) & "-" & Hex$(pk.fmtid.Data2) & "-" & Hex$(pk.fmtid.Data3) & "-" & Hex$(pk.fmtid.Data4(0)) & Hex$(pk.fmtid.Data4(1)) & "-" & Hex$(pk.fmtid.Data4(2)) & Hex$(pk.fmtid.Data4(3)) & Hex$(pk.fmtid.Data4(4)) & Hex$(pk.fmtid.Data4(5)) & Hex$(pk.fmtid.Data4(6)) & Hex$(pk.fmtid.Data4(7)) & "}, " & pk.pid


   
    'Some properties don't return a name; if you don't catch that it leads to a full appcrash
    If Len(sPrName) > 1 Then
        'PSFormatPropertyValue takes the raw data and formats it according to the current locale
        'Using these APIs lets us completely avoid dealing with PROPVARIANT, a huge bonus.
        'If you don't need the raw data, this is all it takes
        PSGetPropertyDescription pk, IID_IPropertyDescription, pProp
        PSFormatPropertyValue ObjPtr(pPStore), ObjPtr(pProp), PDFF_DEFAULT, lpProp
        sFmtProp = BStrFromLPWStr(lpProp)
        Debug.Print "Formatted value=" & sFmtProp
    Else
        Debug.Print "Unknown Propkey; can't get formatted value"
    End If
   
    'Now we'll display the raw data
    isif.GetProperty pk, vProp
    PropVariantToVariant vProp, vrProp 'PROPVARIANT is exceptionally difficult to work with in VB, but at
                                      'least for file properties this seems to work for most
   
    vte = VarType(vrProp)
    If (vte And vbArray) = vbArray Then 'this always seems to be vbString and vbArray, haven't encountered other types
        For j = LBound(vrProp) To UBound(vrProp)
            Debug.Print "Value(" & j & ")=" & CStr(vrProp(j))
        Next j
    Else
    Select Case vte
        Case vbDataObject, vbObject, vbUserDefinedType
            Debug.Print "<cannot display this type>"
        Case vbEmpty, vbNull
            Debug.Print "<empty or null>"
        Case vbError
            Debug.Print "<vbError>"
        Case Else
            Debug.Print "Value=" & CStr(vrProp)
    End Select
    End If
Next i
Exit Sub
eper:
    Debug.Print "Property conversion error->" & Err.Description
    Resume Next

End Sub

'Supporting functions
Public Function IID_IShellItem2() As UUID
'7e9fb0d3-919f-4307-ab2e-9b1860310c93
Static IID As UUID
If (IID.Data1 = 0) Then Call DEFINE_UUID(IID, &H7E9FB0D3, CInt(&H919F), CInt(&H4307), &HAB, &H2E, &H9B, &H18, &H60, &H31, &HC, &H93)
IID_IShellItem2 = IID
End Function

Public Function IID_IPropertyDescription() As UUID
'(IID_IPropertyDescription, 0x6f79d558, 0x3e96, 0x4549, 0xa1,0xd1, 0x7d,0x75,0xd2,0x28,0x88,0x14
Static IID As UUID
 If (IID.Data1 = 0) Then Call DEFINE_UUID(IID, &H6F79D558, CInt(&H3E96), CInt(&H4549), &HA1, &HD1, &H7D, &H75, &HD2, &H28, &H88, &H14)
  IID_IPropertyDescription = IID
 
End Function

Public Function IID_IPropertyStore() As UUID
'DEFINE_GUID(IID_IPropertyStore,0x886d8eeb, 0x8cf2, 0x4446, 0x8d,0x02,0xcd,0xba,0x1d,0xbd,0xcf,0x99);
Static IID As UUID
 If (IID.Data1 = 0) Then Call DEFINE_UUID(IID, &H886D8EEB, CInt(&H8CF2), CInt(&H4446), &H8D, &H2, &HCD, &HBA, &H1D, &HBD, &HCF, &H99)
  IID_IPropertyStore = IID
 
End Function
Public Sub DEFINE_UUID(Name As UUID, L As Long, w1 As Integer, w2 As Integer, B0 As Byte, b1 As Byte, b2 As Byte, B3 As Byte, b4 As Byte, b5 As Byte, b6 As Byte, b7 As Byte)
  With Name
    .Data1 = L
    .Data2 = w1
    .Data3 = w2
    .Data4(0) = B0
    .Data4(1) = b1
    .Data4(2) = b2
    .Data4(3) = B3
    .Data4(4) = b4
    .Data4(5) = b5
    .Data4(6) = b6
    .Data4(7) = b7
  End With
End Sub
Public Function BStrFromLPWStr(lpWStr As Long, Optional ByVal CleanupLPWStr As Boolean = True) As String
SysReAllocString VarPtr(BStrFromLPWStr), lpWStr
If CleanupLPWStr Then CoTaskMemFree lpWStr
End Function

Sample output:
Code:

Property Name=System.FileAttributes,SCID={B725F130-47EF-101A-A5F1-2608C9EEBAC}, 13
Formatted value=A
Value=32

Also, if your user is selecting which properties to display, which is still done by column IDs, you can map a column id to a PROPERTYKEY like this, where isfPar is the IShellFolder2 the properties are selected from:
Code:

            isfPar.MapColumnToSCID lColumn, SHColEx
            pk.fmtid = SHColEx.fmtid
            pk.pid = SHColEx.pid

[VB6] Yet another simple and versatile Tray Icon code with subclassing

$
0
0
Yesterday, I didn't know what 'subclassing' is.
Today I made a complete solution to serve all your tray icon needs.

Easy to use: just create a cSysTray object in your form, pass the hWnd and you're set.
You can add and remove icons at will, change tooltips, create baloons and catch events from every icon.
Unfortunately the code needs 3 files, 2 class files and a module to do all the subclassing stuff, but it's very easy to use.
I grabbed some code from this post by Ellis Dee, hence the wrench icon on the form. And included some ideas from this post on subclassing by fafalone.

There are 2 BAS files on the zip:
modSysTray.bas uses SetWindowSubclass, RemoveWindowSubclass, DefSubclassProc
modSysTray_old.bas uses SetWindowLong, CallWindowProc
You can use any of them but the 1st one is better.


The code is not finished yet. It works perfectly on windows xp and win 7, but there are a lot of things to include like add other events, add support for baloon alternative icons, msn style baloons, and some other minor changes.
Note that I didn't include any routine to create icons from bitmaps or anything else. You need a 16x16 ICON picture. No mask creation, no scaling, no nothing.

Here's a screenshot:
Name:  pic.jpg
Views: 39
Size:  16.4 KB

And a minimal form code sample:
Code:

Option Explicit
Private WithEvents sysTray As cSysTray

Private Sub Form_Load()
    Set sysTray = New cSysTray
    sysTray.Init Me.hWnd
    sysTray.AddIcon (pic.Picture, "Hola mundo").ShowBalloon "my baloon", "baloon title", NIIF_NOSOUND Or NIIF_ERROR
    sysTray.AddIcon Me.Icon, "Hola mundo 2"
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Set sysTray = Nothing
End Sub

Private Sub sysTray_DoubleClick(index As Integer)
    Debug.Print "dblclick"; index
End Sub

Private Sub sysTray_RightClick(index As Integer)
    Debug.Print "rtclick"; index
End Sub

Attached Images
 
Attached Files

VB6 - NewSocket (updated)

$
0
0
NewSocket.cls/mWinsock.bas has received several small updates.

1. All references to StrConv/vbUnicode have been removed and replaced by StrToByte/ByteToStr. This was necessitated to support some upper ANSI characters above &H7F, commonly encountered when using cryptography.

2. The code to load the assembler hex code in "Subclass_Initialize" has been simplified by introducing a routine called "HexToByte", and this routine has been made Public to allow for general use.

3. The memory allocated for the assembler code has been changed from "GlobalAlloc" to "VirtualAlloc". Most Desktop computers enable DEP (Data Exection Prevention) for essential programs and services only, but most servers and a few desktop computers will enable it for all programs. Because we are running assembler code in the data area, an abrupt and unexplained failure is experienced when running the executable using GlobalAlloc with DEP enabled for all programs. The same failure will not occur in the IDE because the IDE runs in Virtual Memory.

4. The functions "InitiateService/FinalizeService" has been eliminated and the code from them included in "InitiateProcesses/FinalizeProcesses".

5. The name for the Log File was inadvertently left at the first program that it was tested with (IPv6Chat.Log), and has been changed to "Socket.Log". This log file can be used to log debug statements while running the executable, by enabling "DbgFlg".

The ActiveX control (NewSocket.ocx) does require registration. If you previously used this control, it was automtically registered the first time you used it. To replace it, deregister the old one first using "regsvr32.exe", and delete the files "NewSocket.ocx/NewSocket.oca" from the \Windows\System32\ directory (\Windows\Syswow64\ on 64 bit systems). After compiling the control, copy the new "ocx" file to the same directory. The "oca" file will be automatically generated. Full instructions can be found in the "Readme.txt" file.

I have included 2 small test programs, as well as a simple ocxTest program to aid with the registration process. PrjTest downloads a small HTML file from our server using NewSocket.cls/mWinSock.bas. WebTest2 does the same thing using using the NewSocket Control.

J.A. Coutts
Attached Files

modZlib.bas

$
0
0
This is my module file for using zlibwapi.dll in VB6. To use this code, simply copy the text in the code box at the bottom of this post, and paste it into an empty module in VB6. Note that you must have the DLL file in question in either the windows\system32 folder (windows\syswow64 on x64 Windows), or in the folder where your VB6 project files are for the project you are working on (the same folder where your EXE file will be compiled to). Normally Zlib's only easy to use compression/decompression functions are compress, compress2, and uncompress. Unfortunately those functions expect the compressed data to exist within the a Zlib container (has a 2 byte header, and a 4 byte footer that is an Adler32 checksum of the uncompressed data). However, a number of various file formats expect raw "deflate" data to be in use (I believe that the Zip file format is one), without any Zlib container surrounding the compressed data. Deflate is the name of the algorithm that Zlib uses. Now Zlib does have functions for directly accessing raw deflate streams, but they are VERY difficult to use, and require initializing special structures associated with the streams, requiring a massive amount of overhead in any program implementing it. Zlib also has builtin functions for working with GZip files directly, but what if you want to handle an in-memory copy of a GZip container? Well once again, you can use the stream commands for that (and again use a HUGE amount of overhead in writing what could otherwise be a very simple program).

That's where my module comes in. It completely gets around the need for stream handling, by ultimately always using the compress2, uncompress, compressBound, and crc32 Zlib methods, and then handling the container formats as needed directly in VB6 code (and also using the Windows API CopyMemory method where needed). It contains methods for handling not only Zlib containers, but also raw deflate streams, and GZip containers. And it does it all using memory. The methods for the raw deflate streams work by calling the Zlib functions, and then adding or removing the Zlib container from the compressed data as needed. Of course, when it recreates the Zlib container, it doesn't have access to the uncompressed data until it decompresses it, so there's no way for it to recreate the Adler32 checksum, and without the correct checksum the Zlib decompressor returns an error, even though it does correctly decompress the data. As a result, error checking for decompression of a raw deflate stream is impossible, and therefore the Inflate method (Inflate is what they call decompressing Deflated data), is a "sub" rather than a "function", as it can't return any usable error, as otherwise it would always be signalling that it failed. I recommend that if you use raw deflate streams, that you use some other error checking method outside of the compression functions, such as storing a checksum or CRC separately (either in the header of your file, or in a separate file that your program will also load in addition to the file containing compressed data). My GZip compress and decompress functions call my Inflate and Deflate methods, and add or remove the GZip container from the data as needed. GZip uses CRC32 rather than a checksum, and since it can check for errors, the decompress method for GZip once again is a function. I have verified that my GZip compress function generates a valid GZip container, by saving it to a file and then opening it in the program 7Zip. My Zlib functions are included just to simplify the use of Zlib, as no special preprocessing or postprocessing of container formats is required here. These simplify handling of Zlib containers, by using byte arrays, rather than arbitrary data, so you don't need to know the size of the data that's being fed to it. These functions internally automatically determine the size of the input data by using the UBound VB6 function on the arrays. The only thing you will need to know is upon decompressing a Zlib stream or a raw Deflate stream, you will will need to know the original uncompressed size. This can be determined easily by your own use of the UBound function in your own code, and then this info can be saved into whatever structure or file format you use to pass information to and from this program. Only difference is with a GZip container, which already stores the original uncompressed size as part of the container (it's a 4byte Long value, which is the last 4 bytes of the 8byte footer at the end of the container, according to the official specs for GZip).

All my functions use the Boolean type for the return value, and output True for success, and False for failure. All input and output data are byte arrays. All byte arrays are to be 1D arrays, with the first index at 0 (zero). My GZip functions also handle a stored filename. For compressing, supplying a filename is optional. For decompressing, even if you don't have a filename stored, since it is passed byref, a filename variable MUST be supplied, even if it's only acting as a dummy/filler variable if you have no intent to use that info. All other optional fields that may be present in a GZip container are ignored by my decompression function, and are simply skipped if they are present. If the header indicates they exist they do get processed to find their length, but only for the purpose of skipping them to get to the deflate stream, as no info stored in them is returned by my GZip decompress function. Likewise , the only optional field that can be saved by my GZip compress function is the filename field.

Code:

Private Declare Function crc32 Lib "zlibwapi.dll" (ByVal OldCRC As Long, ByRef Data As Any, ByVal DataLen As Long) As Long
Private Declare Function compress2 Lib "zlibwapi.dll" (ByRef Dest As Byte, ByRef DestLen As Long, ByRef Src As Byte, ByVal SrcLen As Long, ByVal CompLevel As Long) As Long
Private Declare Function uncompress Lib "zlibwapi.dll" (ByRef Dest As Byte, ByRef DestLen As Long, ByRef Src As Byte, ByVal SrcLen As Long) As Long
Private Declare Function compressBound Lib "zlibwapi.dll" (ByVal SrcLen As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)



Public Function ZlibCompress(ByRef Dest() As Byte, ByRef Src() As Byte, Optional ByVal CompLevel As Long = 9) As Boolean
Dim SrcLen As Long
Dim DestLen As Long
Dim ErrorNum As Long

SrcLen = UBound(Src) + 1
DestLen = compressBound(SrcLen)
ReDim Dest(DestLen - 1)
ErrorNum = compress2(Dest(0), DestLen, Src(0), SrcLen, CompLevel)
If ErrorNum Then Exit Function
ReDim Preserve Dest(DestLen - 1)
ZlibCompress = True
End Function



Public Function ZlibDecompress(ByRef Dest() As Byte, ByRef Src() As Byte, ByVal UncompLen As Long) As Boolean
Dim SrcLen As Long
Dim DestLen As Long
Dim ErrorNum As Long

SrcLen = UBound(Src) + 1
DestLen = UncompLen
ReDim Dest(DestLen - 1)
ErrorNum = uncompress(Dest(0), DestLen, Src(0), SrcLen)
If ErrorNum Then Exit Function
ReDim Preserve Dest(DestLen - 1)
ZlibDecompress = True
End Function



Public Function Deflate(ByRef Dest() As Byte, ByRef Src() As Byte, Optional ByVal CompLevel As Long = 9) As Boolean
Dim ZlibCompData() As Byte
Dim Success As Boolean

Success = ZlibCompress(ZlibCompData, Src, CompLevel)
If Success = False Then Exit Function
ReDim Dest(UBound(ZlibCompData) - 6)
CopyMemory Dest(0), ZlibCompData(2), UBound(Dest) + 1
Deflate = True
End Function



Public Sub Inflate(ByRef Dest() As Byte, ByRef Src() As Byte, ByVal UncompLen As Long)
Dim ZlibCompData() As Byte
Dim CheckSumInput As Long
Dim n As Long
   
ReDim ZlibCompData(UBound(Src) + 6)
ZlibCompData(0) = &H78
ZlibCompData(1) = &H80
CheckSumInput = &H7880&
For n = 0 To 31
    If (CheckSumInput Or n) Mod 31 = 0 Then
        ZlibCompData(1) = ZlibCompData(1) Or n
        Exit For
    End If
Next n
CopyMemory ZlibCompData(2), Src(0), UBound(ZlibCompData) + 1
ZlibDecompress Dest(), ZlibCompData(), UncompLen
End Sub



Public Function GzipCompress(ByRef Dest() As Byte, ByRef Src() As Byte, Optional ByVal CompLevel As Long = 9, Optional ByVal FileName As String) As Boolean
Const HeaderLen As Long = 10
Const FooterLen As Long = 8
Dim DeflatedData() As Byte
Dim DeflateLen As Long
Dim FNameBytes() As Byte
Dim FNameLen As Long
Dim CRC As Long
Dim UncompLen As Long
Dim Success As Boolean

Success = Deflate(DeflatedData, Src, CompLevel)
If Success = False Then Exit Function
DeflateLen = UBound(DeflatedData) + 1
FNameBytes() = StrConv(FileName, vbFromUnicode)
FNameLen = Len(FileName)
If FNameLen > 0 Then
    FNameLen = FNameLen + 1
    ReDim Preserve FNameBytes(FNameLen - 1)
End If
UncompLen = UBound(Src) + 1
CRC = crc32(0, Src(0), UncompLen)

ReDim Dest(HeaderLen + FNameLen + DeflateLen + FooterLen - 1)
Dest(0) = 31
Dest(1) = 139
Dest(2) = 8

If FNameLen Then
    Dest(3) = 8
    CopyMemory Dest(HeaderLen), FNameBytes(0), FNameLen
End If

If CompLevel < 5 Then Dest(8) = 4 Else Dest(8) = 2
Dest(9) = 0

CopyMemory Dest(HeaderLen + FNameLen), DeflatedData(0), DeflateLen
CopyMemory Dest(HeaderLen + FNameLen + DeflateLen), CRC, 4
CopyMemory Dest(HeaderLen + FNameLen + DeflateLen + 4), UncompLen, 4

GzipCompress = True
End Function



Public Function GzipDecompress(ByRef Dest() As Byte, ByRef Src() As Byte, ByRef FileName As String) As Boolean
Const HeaderLen As Long = 10
Const ID1 As Byte = 31
Const ID2 As Byte = 139
Const CM As Byte = 8
Const FooterLen As Long = 8
Dim DataPtr As Long
Dim SrcLen As Long
Dim FLG As Byte
Dim XLEN As Integer
Dim DeflatedData() As Byte
Dim DeflateLen As Long
Dim TempStr As String
Dim FNameLen As Long
Dim FCommentLen As Long
Dim LenBeforeData As Long
Dim UncompLen As Long
Dim CRC As Long
Dim CRC2 As Long

SrcLen = UBound(Src) + 1
LenBeforeData = HeaderLen

If Src(0) <> ID1 Then Exit Function
If Src(1) <> ID2 Then Exit Function
If Src(2) <> CM Then Exit Function
FLG = Src(3)
If FLG And 2 Then LenBeforeData = LenBeforeData + 2
If FLG And 4 Then
    CopyMemory XLEN, Src(HeaderLen), 2
    LenBeforeData = LenBeforeData + 2 + XLEN
    DataPtr = HeaderLen + 2 + XLEN
Else
    DataPtr = HeaderLen
End If

If (FLG And 8) Or (FLG And 16) Then
    Do Until Src(DataPtr) = 0
        TempStr = TempStr & Chr$(Src(DataPtr))
        DataPtr = DataPtr + 1
    Loop
    If FLG And 8 Then
        FNameLen = Len(TempStr) + 1
        FileName = Left$(TempStr, FNameLen - 1)
        LenBeforeData = LenBeforeData + FNameLen
        If FLG And 16 Then
            DataPtr = DataPtr + 1
            TempStr = ""
            Do Until Src(DataPtr) = 0
                TempStr = TempStr & Chr$(Src(DataPtr))
                DataPtr = DataPtr + 1
            Loop
            FCommentLen = Len(TempStr) + 1
            LenBeforeData = LenBeforeData + FCommentLen
        End If
    Else
        FCommentLen = Len(TempStr) + 1
        LenBeforeData = LenBeforeData + FCommentLen
    End If
End If

DeflateLen = SrcLen - LenBeforeData - 8
ReDim DeflatedData(DeflateLen - 1)

CopyMemory CRC, Src(LenBeforeData + DeflateLen), 4
CopyMemory UncompLen, Src(LenBeforeData + DeflateLen + 4), 4
CopyMemory DeflatedData(0), Src(LenBeforeData), DeflateLen
ReDim Dest(UncompLen - 1)
Inflate Dest(), DeflatedData(), UncompLen
CRC2 = crc32(0, Dest(0), UncompLen)
If CRC2 <> CRC Then Exit Function

GzipDecompress = True
End Function

Viewing all 1480 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>