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

[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: 55
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

modCRC.bas

$
0
0
This is my code for CRC calculating. It calculates CRC32 using the standard polynomial 0x04C11DB7, and also 2 different 16bit CRCs (one uses the standard CRC16 polynomial 0x8005, and the other uses the CCITT polynomial 0x1021). Both the CRC32 and CRC16 functions allow the following parameters to be configured that affect the calculation of the CRC:
InvertInitCRC (if true, the initial CRC value has all 1 bits, otherwise it is has all 0 bits)
MirrorInputBits (if true, the bit order in each byte of input data is reversed before being used to calculate the CRC)
MirrorOutputBits (if true, the order of the bits in the output CRC is reversed, which is a 32bit reversal for CRC32 and a 16bit reversal for CRC16)
InvertFinalCRC (if true, the output bits of the CRC are all inverted, where 1 becomes 0, and 0 becomes 1)

These above parameters are all required parameters in both functions. That is, they must be explicitly set to true or false.

Also, both CRC functions have an optional parameter called SwapOutputBytes. This simply affects the "endianness" of the output CRC (the order in which the CRC's bytes are stored in memory or in a file).

The CRC16 function, has an extra required parameter called UsePolyCCITT. If true, it uses the CCITT polynomial (often used in various communications protocols), which is 0x1021. If false, it uses the standard CRC16 polynomial, which is 0x8005.

Note that for the CRC32 function to perform the standard CRC32 calculation, the 4 required parameters must be set as shown here:
InvertInitCRC = True
MirrorInputBits = True
MirrorInputBits = True
InvertFinalCRC = True

Note that for the CRC16 function to perform the standard CRC16 calculation, the 5 required parameters must be set as shown here:
UsePolyCCITT = False
InvertInitCRC = False
MirrorInputBits = True
MirrorInputBits = True
InvertFinalCRC = False




Here's the complete code for this module. Just copy and paste it into a module in VB6, and then you will be able to use the CRC32 and CRC16 functions from anywhere else in your code.

Code:

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



Public Function CRC32(ByRef Data() As Byte, _
                      ByVal InvertInitCRC As Boolean, _
                      ByVal MirrorInputBits As Boolean, _
                      ByVal MirrorOutputBits As Boolean, _
                      ByVal InvertFinalCRC As Boolean, _
                      Optional ByVal SwapOutputBytes As Boolean) As Long
                       
Dim ByteNumber As Long
Dim BitNumber As Long
Dim CurrentByte As Long
Dim CRC As Long
Const Poly As Long = &H4C11DB7

If InvertInitCRC Then CRC = &HFFFFFFFF Else CRC = 0

For ByteNumber = 0 To UBound(Data)
    CurrentByte = Data(ByteNumber)
    If MirrorInputBits Then CurrentByte = ReverseBits8(CurrentByte)
    CurrentByte = SwapBytes4(CurrentByte)
    CRC = CRC Xor CurrentByte
    For BitNumber = 0 To 7
        If CRC And &H80000000 Then
            CRC = ShiftLeft32(CRC) Xor Poly
        Else
            CRC = ShiftLeft32(CRC)
        End If
    Next BitNumber
Next ByteNumber
If MirrorOutputBits Then CRC = ReverseBits32(CRC)
If InvertFinalCRC Then CRC = CRC Xor &HFFFFFFFF
If SwapOutputBytes Then CRC = SwapBytes4(CRC)
CRC32 = CRC
End Function



Public Function CRC16(ByRef Data() As Byte, _
                      ByVal UsePolyCCITT As Boolean, _
                      ByVal InvertInitCRC As Boolean, _
                      ByVal MirrorInputBits As Boolean, _
                      ByVal MirrorOutputBits As Boolean, _
                      ByVal InvertFinalCRC As Boolean, _
                      Optional ByVal SwapOutputBytes As Boolean) As Integer
                     
Dim ByteNumber As Long
Dim BitNumber As Long
Dim CurrentByte As Long
Dim CRC As Integer
Dim Poly As Integer
Const PolyStandard As Integer = &H8005
Const PolyCCITT As Integer = &H1021

If UsePolyCCITT Then Poly = PolyCCITT Else Poly = PolyStandard
If InvertInitCRC Then CRC = &HFFFF Else CRC = 0

For ByteNumber = 0 To UBound(Data)
    CurrentByte = Data(ByteNumber)
    If MirrorInputBits Then CurrentByte = ReverseBits8(CurrentByte)
    CurrentByte = SwapBytes2(CurrentByte)
    CRC = CRC Xor CurrentByte
    For BitNumber = 0 To 7
        If CRC And &H8000 Then
            CRC = ShiftLeft16(CRC) Xor Poly
        Else
            CRC = ShiftLeft16(CRC)
        End If
    Next BitNumber
Next ByteNumber
If MirrorOutputBits Then CRC = ReverseBits16(CRC)
If InvertFinalCRC Then CRC = CRC Xor &HFFFF
If SwapOutputBytes Then CRC = SwapBytes2(CRC)
CRC16 = CRC
End Function



Private Function ReverseBits8(ByVal Value As Byte) As Byte
Dim Value2 As Byte
Dim n As Long

Value2 = (Value And 1) * &H80
For n = 1 To 7
    Value2 = Value2 + ShiftLeft32(ShiftRight32(Value, n) And 1, 7 - n)
Next n
ReverseBits8 = Value2
End Function



Private Function ShiftLeft32(ByVal Value As Long, Optional ByVal BitCount As Long = 1) As Long
Dim temp As Currency
Dim temp2 As Long

CopyMemory temp, Value, 4
temp = temp * (2 ^ BitCount)
CopyMemory temp2, temp, 4
ShiftLeft32 = temp2
End Function



Private Function ShiftRight32(ByVal Value As Long, Optional ByVal BitCount As Long = 1) As Long
Dim temp As Currency
Dim temp2 As Long

CopyMemory temp, Value, 4
temp = Int((temp * 10000) / (2 ^ BitCount)) / 10000
CopyMemory temp2, temp, 4
ShiftRight32 = temp2
End Function



Private Function ReverseBits32(ByVal Value As Long) As Long
Dim Value2 As Long
Dim n As Long

Value2 = (Value And 1) * &H80000000
For n = 1 To 31
    Value2 = Value2 + ShiftLeft32(ShiftRight32(Value, n) And 1, 31 - n)
Next n
ReverseBits32 = Value2
End Function



Private Function SwapBytes4(ByVal Value As Long) As Long
Dim Value2 As Long

CopyMemory ByVal VarPtr(Value2) + 0, ByVal VarPtr(Value) + 3, 1
CopyMemory ByVal VarPtr(Value2) + 1, ByVal VarPtr(Value) + 2, 1
CopyMemory ByVal VarPtr(Value2) + 2, ByVal VarPtr(Value) + 1, 1
CopyMemory ByVal VarPtr(Value2) + 3, ByVal VarPtr(Value) + 0, 1
SwapBytes4 = Value2
End Function



Private Function ShiftRight16(ByVal Value As Integer, Optional ByVal BitCount As Long = 1) As Integer
Dim temp As Long
Dim temp2 As Integer

CopyMemory temp, Value, 2
temp = temp \ (2 ^ BitCount)
CopyMemory temp2, temp, 2
ShiftRight16 = temp2
End Function



Private Function ShiftLeft16(ByVal Value As Integer, Optional ByVal BitCount As Long = 1) As Integer
Dim temp As Long
Dim temp2 As Integer

CopyMemory temp, Value, 2
temp = temp * (2 ^ BitCount)
CopyMemory temp2, temp, 2
ShiftLeft16 = temp2
End Function



Private Function ReverseBits16(ByVal Value As Integer) As Integer
Dim Value2 As Integer
Dim n As Long

Value2 = (Value And 1) * &H8000
For n = 1 To 15
    Value2 = Value2 + ShiftLeft32(ShiftRight32(Value, n) And 1, 15 - n)
Next n
ReverseBits16 = Value2
End Function



Private Function SwapBytes2(ByVal Value As Integer) As Integer
Dim Value2 As Integer

CopyMemory ByVal VarPtr(Value2) + 0, ByVal VarPtr(Value) + 1, 1
CopyMemory ByVal VarPtr(Value2) + 1, ByVal VarPtr(Value) + 0, 1
SwapBytes2 = Value2
End Function

[VB6, Vista+] Undocumented ListView feature: Footer items

$
0
0
Ran across this nifty thing on codeproject, and successfully got it working in VB.

Tested and working with 5.0 ListView and API ListView (it will also work on krool's Common Control Replacement ListView), have not tried with 6.0 ListView and presumably it wouldn't work (Windows Common Controls 5.0 is actually the more modern control due to linkage with the real comctl32.dll, and required for a lot of modern features like this and group view). The items are present and displayed the same way in all views, including tile and group view modes. It does appear you can only add up to 4 buttons, if you add more than that NONE of them appear.

This one is a little complicated to set up, but straightforward to use. First, it requires a type library with the undocumented interfaces IListViewFooter and IListViewFooterCallback, then the latter has to be implemented by a class module. From there, more undocumented goodness: LVM_SETIMAGELIST with a wParam of 4 will set the icons used in the footer, and LVM_QUERYINTERFACE retrieves an instance of IListViewFooter.
For the purposes of this code, I'll assume you have a ListView set up already. I use the system imagelist, but you can assign any imagelist (well, api imagelist):

Code:

Public Const IID_IListViewFooter = "{F0034DA8-8A22-4151-8F16-2EBA76565BCC}"
Public Const LVM_QUERYINTERFACE = (LVM_FIRST + 189)
Public Declare Function CLSIDFromString Lib "ole32" (ByVal lpszGuid As Long, pGuid As Any) As Long
Public Type GUIDA
  Data1 As Long
  Data2 As Integer
  Data3 As Integer
  Data4(7) As Byte
End Type
Public m_himlSysSmall As Long
Public Function GetFileTypeIconIndex(ext As String) As Long
  Dim sfi As SHFILEINFO
  Dim pidl As Long
If SHGetFileInfo(ext, FILE_ATTRIBUTE_NORMAL, sfi, Len(sfi), SHGFI_SYSICONINDEX Or SHGFI_SMALLICON Or SHGFI_USEFILEATTRIBUTES) Then
    GetFileTypeIconIndex = sfi.iIcon
  End If
End Function

The code to insert items can be placed wherever, but it won't show until there's items in the ListView.
Code:

  m_himlSysSmall = GetSystemImagelist(SHGFI_SMALLICON)
    Call SendMessage(ListView1.hWnd, LVM_SETIMAGELIST, 4, ByVal m_himlSysSmall)

Dim pLVF As IListViewFooter
Dim pFtrCB As cLVFooterCallback
Set pFtrCB = New cLVFooterCallback
Dim iidLVF As GUIDA
Call CLSIDFromString(StrPtr(IID_IListViewFooter), iidLVF)

Call SendMessage(hLVS, LVM_QUERYINTERFACE, VarPtr(iidLVF), pLVF)
If (pLVF Is Nothing) Then
    Debug.Print "Failed to get LV Footer interface"
    Exit Sub
End If
Dim lFtrIco As Long
lFtrIco = GetFileTypeIconIndex(".jpg") 'just an example, it's a standard index for the assigned image list.
With pLVF
    .SetIntroText "Intro text - hello!"
    .InsertButton 0, "Test Item 1", "where does this go", lFtrIco, 2000
    .Show pFtrCB
End With

'2000' - the lParam - has no special meaning, you can store whatever Long you want there. NOTE: It must not be 0, otherwise the buttonclick/buttondelete callback events won't fire.

The attached ZIP contains the typelib, the typelib source code, a batch file to compile it from a standard VS6 install, and the class module implementing the callback. I didn't bother will a full fledged example because presumably anyone interested in this would be adding it onto an already well set-up ListView, but if really needed let me know.

Coming up next in the world of undocumented ListView: subsetted groups (link for "Display all x items"), subitem label editing, and if I'm particularly ambitious.. apparently you can use groups in full virtual mode.
Attached Files

[VB6, Vista+] Undocumented ListView feature: Subsetted Groups (simple, no TLB)

$
0
0

Compatibility: Like other modern features, this should work with API-created ListView's including krools, as well as the 5.0 Common Controls ListView in an IDE and/or compiled EXE manifested for the latest comctl32.dll version; and will almost certainly not work with the VB "Common Controls 6.0" ocx. Works with Windows Vista and higher.

Subsetted groups allow you to show only a limited number of rows, and have a link at the bottom to show the hidden items. Works in any view where group view is supported (e.g. large icon and details, not list, etc). Not only is all the info needed to do it undocumented, but MSDN provides some of the constants then explicitly says it can't be done. Not sure what their deal is... I mean yeah there's some issues (see warning) but no reason they couldn't have fixed it between Vista and 10).
So I had been converting this project to VB, and after I had already implemented the full IListView class, I went back and decided to try LVM_SETGROUPSUBSETCOUNT anyway, having originally thought the project author had tried that first since it was mentioned where he got the idea from. Lo and behold, it worked. So now you can subsetted groups with just a couple lines, and no TLB, no subclassing, nothing.


Code:

Public Const LVM_FIRST = &H1000
Public Const LVM_SETGROUPSUBSETCOUNT = (LVM_FIRST + 190)
Public Const LVM_GETGROUPSUBSETCOUNT = (LVM_FIRST + 191)

 'is included in standard group def despite MSDN saying not supported:
    LVGF_SUBSET = &H8000
    LVGS_SUBSETED = &H40
    LVGS_SUBSETLINKFOCUSED = &H80

Now that you have your constants, when you're adding a group you want to be subsetted, add LVGF_SUBSET to .mask, and LVGS_SUBSETED to .State and .StateMask.
Next add the subset link text,
.pszSubsetTitle = StrPtr(sSubSetText)
.cchSubsetTitle = Len(sSubSetText) + 1 'MSDN says this needs its own flag, but this combo of flags and properties works for both me and the codeproject sample

Then, after the group has been added, to set the number of rows simply use:
Call SendMessage(hLVS, LVM_SETGROUPSUBSETCOUNT, 0, ByVal 2&)
where 2 can be anything, it's the number of rows you want. Note that in VB programs, all groups will have the link if one does, even without the style set. The link doesn't seem to go away, although in the c++ sample is does, so it might vary.

And that's all it takes!

WARNING:
Note that this is an undocumented message, and as such has SERIOUS issues: MSDN explicitly says subset text cannot be set. They lied, but changing the variable holding it after running your program without restarting the IDE can cause damage your project, leading to crashes and having to re-enter control settings. If Group View is not enabled, or no groups are added, or no groups are marked as subsetted, the ListView window will lock up and nothing can be drawn to that area of the screen until the program is ended.

[VB6, XP+] Code snippet: Show combined file properties window- SHMultiFileProperties

$
0
0
It's easy to show the file property window for a single file with ShellExecuteEx, but what if you wanted to also show a property window for multiple files in multiple paths as you can do in Explorer? The ShellExecuteEx method provides no option to pass an array of files. So you have to turn to SHMultiFileProperties. The reason this has never been done in VB before (at least as far as I could find with Google), is that it requires an IDataObject to describe the files, and that's traditionally been a tough thing to do. But thanks to some shell32 API's, it's not as bad as you'd think.

There's two APIs we can use to get the needed IDataObject, SHCreateDataObject and SHCreateFileDataObject. The former is only available on Vista and higher, and the latter is undocumented and exported by ordinal only. However, it's been at the same ordinal from XP through 8.1 (haven't checked 10), so I'll use that in the sample code. If you don't need to support XP, switch it out- they're extremely similar.

Requirements
Windows XP or higher
For the IDE only, a type library containing the definition for IDataObject. Some versions of OLEGuids might work, but I recommend using my Modern Interfaces Type Library, although just the original version of olelib would be sufficient. Simply download and add a reference to olelib.tlb to your project.

Code
Code:

Public Declare Function SHCreateFileDataObject Lib "shell32" Alias "#740" (ByVal pidlFolder As Long, ByVal cidl As Long, ByVal apidl As Long, pDataInner As Any, ppDataObj As olelib.IDataObject) As Long
'For Vista+ if you wanted:
'Public Declare Function SHCreateDataObject Lib "shell32" (ByVal pidlFolder As Long, ByVal cidl As Long, ByVal apidl As Long, pdtInner As Any, riid As UUID, ppv As Any) As Long
Public Declare Function SHMultiFileProperties Lib "shell32" (ByVal pdtobj As Long, ByVal dwFlags As Long) As Long
Public Declare Function ILCreateFromPathW Lib "shell32" (ByVal pwszPath As Long) As Long
Public Declare Sub ILFree Lib "shell32" (ByVal pidl As Long)

Public Sub ShowMultiFileProperties(sFiles() As String)
'Displays merged file properties window
'Will also display normal property window if a single file is passed
'Unicode is supported

Dim pData As olelib.IDataObject 'always explicitly type this with the parent
Dim apidl() As Long
Dim cpidl As Long
Dim i As Long
ReDim apidl(UBound(sFiles))

If (UBound(sFiles) = 0) And (sFiles(0) = "") Then Exit Sub

For i = 0 To UBound(sFiles)
    apidl(i) = ILCreateFromPathW(StrPtr(sFiles(i))) 'create a fully qualified pidl for each file
Next i
cpidl = UBound(apidl) + 1
Call SHCreateFileDataObject(VarPtr(0), cpidl, VarPtr(apidl(0)), ByVal 0&, pData) 'VarPtr(0) is always equal to the desktop's pidl
If (pData Is Nothing) Then
    Debug.Print "ShowMultiFileProperties: Could not create data object"
    Exit Sub
End If

Call SHMultiFileProperties(ObjPtr(pData), 0) 'passing IDataObject ByRef like you'd think from MSDN results in a crash, so the declare is changed to Long and we send the object pointer

Set pData = Nothing
For i = 0 To UBound(apidl)
    ILFree apidl(i) 'never forget to set your pidls free
Next i
End Sub


Duktape JS engine for vb6

$
0
0
Hi guys, thought i would share a project I have been working on.

I wanted to find a newer javascript engine that I could use with vb6. All in all the MS script control is very capable and easy to use, but it has some nuances that makes it not work with some javascript and does not support all newer constructs. Also there is no built in debugging support unless you try to host the IActiveScript interfaces yourself. (i never could get the debug interfaces working with vb6 either)

I started looking around and found the duktape javascript engine and got it working with vb6. I also devised a way to give the scripts access to COM objects.

An example use could be as simple as:
Code:

  Dim duk As New CDukTape
  msgbox duk.Eval("1+2")

Below are my current supported test cases:

Code:

'    js = "1+2"
'    js = "alert(1+2)"
'    js = "while(1){;}"                'timeout test
'    js = "prompt('text')"
'    js = "a='testing';alert(a[0]);"

'------------- vbdevkit tests ---------------------
'    js = "fso2.ReadFile('c:\\lastGraph.txt')"
'    js = "alert(dlg.OpenDialog(4))"
'    js = "pth = dlg.OpenDialog(4,'title','c:\\',0); fso2.ReadFile(pth)"
'--------------------------------------------------

'    js = "form.Text1.Text = 'test'"
'    js = "form.Text1.Text + ' read back in from javascript!'"
'    js = "form.caption = 'test!';alert(form.caption)"
'    js = "for(i=0;i<10;i++)form.List2.AddItem('item:'+i);alert('clearing!');form.List2.Clear()"
'    js = "var ts = fso.OpenTextFile('c:\\lastGraph.txt',1,true,0);v = ts.ReadAll(); v"        'value of v is returned from eval..
'    js = "var ts = fso.OpenTextFile('c:\\lastGraph.txt',1); v = ts.ReadAll();alert(v)"        '(default args test)

Its not as automatic as the MS script control, you do have to generate JS class wrappers for the COM object you want to use, but there is also a generator for it. In the future this stage could be automated but not yet.

This project is at a good point right now and generally usable so thought I would share at this point.

https://github.com/dzzie/duk4vb

The duktape engine also supports a debugger protocol, which is going to be my next step.

[VB6, Vista+] Host Windows Explorer on your form: navigation tree and/or folder

$
0
0
IExplorerBrowser

IExplorerBrowser is an easy to use, more complete version of IShellView that lets you have a complete Explorer frame on your form, with very little code. You can either have just a plain file view, or with a navigation tree and toolbar. It uses all the same settings and does all the same things as Explorer, and your program can interact with those actions to do things like browse for files, or be the basis of a namespace extension.
The only complication is that there's no event notifying of an individual file selection within the view, and getting a list of selected files is fairly complex- however there is a function to do it in the demo project.
Here's how it looks if you're just using folder view without the frames:


INamespaceTreeControl

If all you want is the navigation tree, you have the INamespaceTreeControl. It's got a decent amount of options for however you want to display things, including checkboxes. There is a wide range of events that you're notified of via the event sink, and most of these use IShellItem- the demo project does show to to convert that into a path, but it's a very useful interface to learn if you're going to be doing shell programming. The selection is reported through IShellItemArray, which is slightly easier than IDataObject.
It's got one little quirk though... you have the option to set the folder icons yourself, but if you don't want to do that and just use the default icon that you see in Explorer, you have to return -1, which requires a v-table swap. The demo project shows how to go both ways, no thanks to MSDN and their complete lack of documentation of this.
But this is by far the easiest to create way of having a full-featured Explorer-like navigation- I've made a regular TreeView into this, and it took hundreds of lines and heavy subclassing. This is a simple object. (Note that it does support some advanced features through related interfaces, like custom draw, drop handling, and accessibility... these interfaces are included in oleexp, but have not been brought to the sample project here, perhaps in the future I'll do a more in-depth one if there's any interest)

Requirements
Windows Vista or higher required as these interfaces did not exist in earlier OS versions
oleexp.tlb: Modern Interfaces Type Library v2.0 or higher (17 Jun 2015) - Only required in the IDE. Add/fix in demo references to olelib.tlb and oleexp.tlb.

These 'controls' create themselves- all you need is a blank form, and here's the creation code for a basic idea of how these things work (code to initialize some variables omitted):
Code:

Set pNST = New NamespaceTreeControl
pNST.Initialize Me.hWnd, prc, lFlag
Set pAdv = New cNSTEvents
Set pUnkAdv = pAdv
pNST.TreeAdvise pUnkAdv, lpck
pNST.InsertRoot 0, isiDesk, SHCONTF_FOLDERS, NSTCRS_EXPANDED Or NSTCRS_VISIBLE, pif

Attached Files

[vbRichClient] - How to create tabs ?

$
0
0
Hi,

I would like to avoid to use the tabstrip control or the bugged SStabs. So I was looking for a code example of tabs with vbRichClient and I don't find exactly what I looking for.

I'm about to use the vbRichClient Toolbar demo like tabs. Is it a good idea or there is something more adapted ?


Thank you.

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 = GetModuleHandle(StrPtr(path))
  197.    
  198.     If hLib = 0 Then
  199.    
  200.         hLib = LoadLibrary(StrPtr(path))
  201.         If hLib = 0 Then
  202.             Err.Raise 53, , Error(53) & " " & Chr$(34) & path & Chr$(34)
  203.             Exit Function
  204.         End If
  205.        
  206.     End If
  207.    
  208.     lpAddr = GetProcAddress(hLib, "DllGetClassObject")
  209.    
  210.     If lpAddr = 0 Then
  211.         Err.Raise 453, , "Can't find dll entry point DllGetClasesObject in " & Chr$(34) & path & Chr$(34)
  212.         Exit Function
  213.     End If
  214.  
  215.     If Not isInit Then
  216.         CLSIDFromString StrPtr(IID_IClassFactory), iidClsFctr
  217.         CLSIDFromString StrPtr(IID_IUnknown), iidUnk
  218.         isInit = True
  219.     End If
  220.    
  221.     Dim ret     As Long
  222.     Dim out     As IUnknown
  223.    
  224.     ret = DllGetClassObject(lpAddr, clsid, iidClsFctr, out)
  225.    
  226.     If ret = 0 Then
  227.  
  228.         ret = IClassFactory_CreateInstance(out, 0, iidUnk, CreateObjectEx)
  229.  
  230.     Else: Err.Raise ret: Exit Function
  231.     End If
  232.    
  233.     Set out = Nothing
  234.    
  235. End Function
  236.  
  237. ' // Unload DLL if not used.
  238. Public Function UnloadLibrary( _
  239.                 ByRef path As String) As Boolean
  240.                
  241.     Dim hLib    As Long
  242.     Dim lpAddr  As Long
  243.     Dim ret     As Long
  244.    
  245.     If Not isInit Then Exit Function
  246.    
  247.     hLib = GetModuleHandle(StrPtr(path))
  248.     If hLib = 0 Then Exit Function
  249.    
  250.     lpAddr = GetProcAddress(hLib, "DllCanUnloadNow")
  251.     If lpAddr = 0 Then Exit Function
  252.    
  253.     ret = DllCanUnloadNow(lpAddr)
  254.    
  255.     If ret = 0 Then
  256.         FreeLibrary hLib
  257.         UnloadLibrary = True
  258.     End If
  259.    
  260. End Function
  261.  
  262. ' // Call "DllGetClassObject" function using a pointer.
  263. Private Function DllGetClassObject( _
  264.                  ByVal funcAddr As Long, _
  265.                  ByRef clsid As GUID, _
  266.                  ByRef iid As GUID, _
  267.                  ByRef out As IUnknown) As Long
  268.                  
  269.     Dim params(2)   As Variant
  270.     Dim types(2)    As Integer
  271.     Dim list(2)     As Long
  272.     Dim resultCall  As Long
  273.     Dim pIndex      As Long
  274.     Dim pReturn     As Variant
  275.    
  276.     params(0) = VarPtr(clsid)
  277.     params(1) = VarPtr(iid)
  278.     params(2) = VarPtr(out)
  279.    
  280.     For pIndex = 0 To UBound(params)
  281.         list(pIndex) = VarPtr(params(pIndex)):   types(pIndex) = VarType(params(pIndex))
  282.     Next
  283.    
  284.     resultCall = DispCallFunc(0&, funcAddr, CC_STDCALL, vbLong, 3, types(0), list(0), pReturn)
  285.              
  286.     If resultCall Then Err.Raise 5: Exit Function
  287.    
  288.     DllGetClassObject = pReturn
  289.    
  290. End Function
  291.  
  292. ' // Call "DllCanUnloadNow" function using a pointer.
  293. Private Function DllCanUnloadNow( _
  294.                  ByVal funcAddr As Long) As Long
  295.                  
  296.     Dim resultCall  As Long
  297.     Dim pReturn     As Variant
  298.    
  299.     resultCall = DispCallFunc(0&, funcAddr, CC_STDCALL, vbLong, 0, ByVal 0&, ByVal 0&, pReturn)
  300.              
  301.     If resultCall Then Err.Raise 5: Exit Function
  302.    
  303.     DllCanUnloadNow = pReturn
  304.    
  305. End Function
  306.  
  307. ' // Call "IClassFactory:CreateInstance" method.
  308. Private Function IClassFactory_CreateInstance( _
  309.                  ByVal obj As IUnknown, _
  310.                  ByVal punkOuter As Long, _
  311.                  ByRef riid As GUID, _
  312.                  ByRef out As IUnknown) As Long
  313.    
  314.     Dim params(2)   As Variant
  315.     Dim types(2)    As Integer
  316.     Dim list(2)     As Long
  317.     Dim resultCall  As Long
  318.     Dim pIndex      As Long
  319.     Dim pReturn     As Variant
  320.    
  321.     params(0) = punkOuter
  322.     params(1) = VarPtr(riid)
  323.     params(2) = VarPtr(out)
  324.    
  325.     For pIndex = 0 To UBound(params)
  326.         list(pIndex) = VarPtr(params(pIndex)):   types(pIndex) = VarType(params(pIndex))
  327.     Next
  328.    
  329.     resultCall = DispCallFunc(obj, &HC, CC_STDCALL, vbLong, 3, types(0), list(0), pReturn)
  330.          
  331.     If resultCall Then Err.Raise resultCall: Exit Function
  332.      
  333.     IClassFactory_CreateInstance = pReturn
  334.    
  335. End Function
  336.  
  337. ' // Call "ITypeLib:GetTypeInfoCount" method.
  338. Private Function ITypeLib_GetTypeInfoCount( _
  339.                  ByVal obj As IUnknown) As Long
  340.    
  341.     Dim resultCall  As Long
  342.     Dim pReturn     As Variant
  343.  
  344.     resultCall = DispCallFunc(obj, &HC, CC_STDCALL, vbLong, 0, ByVal 0&, ByVal 0&, pReturn)
  345.          
  346.     If resultCall Then Err.Raise resultCall: Exit Function
  347.      
  348.     ITypeLib_GetTypeInfoCount = pReturn
  349.    
  350. End Function
  351.  
  352. ' // Call "ITypeLib:GetTypeInfo" method.
  353. Private Function ITypeLib_GetTypeInfo( _
  354.                  ByVal obj As IUnknown, _
  355.                  ByVal index As Long, _
  356.                  ByRef ppTInfo As IUnknown) As Long
  357.    
  358.     Dim params(1)   As Variant
  359.     Dim types(1)    As Integer
  360.     Dim list(1)     As Long
  361.     Dim resultCall  As Long
  362.     Dim pIndex      As Long
  363.     Dim pReturn     As Variant
  364.    
  365.     params(0) = index
  366.     params(1) = VarPtr(ppTInfo)
  367.    
  368.     For pIndex = 0 To UBound(params)
  369.         list(pIndex) = VarPtr(params(pIndex)):   types(pIndex) = VarType(params(pIndex))
  370.     Next
  371.    
  372.     resultCall = DispCallFunc(obj, &H10, CC_STDCALL, vbLong, 2, types(0), list(0), pReturn)
  373.          
  374.     If resultCall Then Err.Raise resultCall: Exit Function
  375.      
  376.     ITypeLib_GetTypeInfo = pReturn
  377.    
  378. End Function
  379.  
  380. ' // Call "ITypeLib:FindName" method.
  381. Private Function ITypeLib_FindName( _
  382.                  ByVal obj As IUnknown, _
  383.                  ByRef szNameBuf As String, _
  384.                  ByVal lHashVal As Long, _
  385.                  ByRef ppTInfo As IUnknown, _
  386.                  ByRef rgMemId As Long, _
  387.                  ByRef pcFound As Integer) As Long
  388.    
  389.     Dim params(4)   As Variant
  390.     Dim types(4)    As Integer
  391.     Dim list(4)     As Long
  392.     Dim resultCall  As Long
  393.     Dim pIndex      As Long
  394.     Dim pReturn     As Variant
  395.    
  396.     params(0) = StrPtr(szNameBuf)
  397.     params(1) = lHashVal
  398.     params(2) = VarPtr(ppTInfo)
  399.     params(3) = VarPtr(rgMemId)
  400.     params(4) = VarPtr(pcFound)
  401.    
  402.     For pIndex = 0 To UBound(params)
  403.         list(pIndex) = VarPtr(params(pIndex)):   types(pIndex) = VarType(params(pIndex))
  404.     Next
  405.    
  406.     resultCall = DispCallFunc(obj, &H2C, CC_STDCALL, vbLong, 5, types(0), list(0), pReturn)
  407.          
  408.     If resultCall Then Err.Raise resultCall: Exit Function
  409.      
  410.     ITypeLib_FindName = pReturn
  411.    
  412. End Function
  413.  
  414. ' // Call "ITypeInfo:GetTypeAttr" method.
  415. Private Sub ITypeInfo_GetTypeAttr( _
  416.             ByVal obj As IUnknown, _
  417.             ByRef ppTypeAttr As Long)
  418.    
  419.     Dim resultCall  As Long
  420.     Dim pReturn     As Variant
  421.    
  422.     pReturn = VarPtr(ppTypeAttr)
  423.    
  424.     resultCall = DispCallFunc(obj, &HC, CC_STDCALL, vbEmpty, 1, vbLong, VarPtr(pReturn), 0)
  425.          
  426.     If resultCall Then Err.Raise resultCall: Exit Sub
  427.  
  428. End Sub
  429.  
  430. ' // Call "ITypeInfo:GetDocumentation" method.
  431. Private Function ITypeInfo_GetDocumentation( _
  432.                  ByVal obj As IUnknown, _
  433.                  ByVal memid As Long, _
  434.                  ByRef pBstrName As String, _
  435.                  ByRef pBstrDocString As String, _
  436.                  ByRef pdwHelpContext As Long, _
  437.                  ByRef pBstrHelpFile As String) As Long
  438.    
  439.     Dim params(4)   As Variant
  440.     Dim types(4)    As Integer
  441.     Dim list(4)     As Long
  442.     Dim resultCall  As Long
  443.     Dim pIndex      As Long
  444.     Dim pReturn     As Variant
  445.    
  446.     params(0) = memid
  447.     params(1) = VarPtr(pBstrName)
  448.     params(2) = VarPtr(pBstrDocString)
  449.     params(3) = VarPtr(pdwHelpContext)
  450.     params(4) = VarPtr(pBstrHelpFile)
  451.    
  452.     For pIndex = 0 To UBound(params)
  453.         list(pIndex) = VarPtr(params(pIndex)):   types(pIndex) = VarType(params(pIndex))
  454.     Next
  455.    
  456.     resultCall = DispCallFunc(obj, &H30, CC_STDCALL, vbLong, 5, types(0), list(0), pReturn)
  457.          
  458.     If resultCall Then Err.Raise resultCall: Exit Function
  459.      
  460.     ITypeInfo_GetDocumentation = pReturn
  461.    
  462. End Function
  463.  
  464. ' // Call "ITypeInfo:ReleaseTypeAttr" method.
  465. Private Sub ITypeInfo_ReleaseTypeAttr( _
  466.             ByVal obj As IUnknown, _
  467.             ByVal ppTypeAttr As Long)
  468.    
  469.     Dim resultCall  As Long
  470.    
  471.     resultCall = DispCallFunc(obj, &H4C, CC_STDCALL, vbEmpty, 1, vbLong, VarPtr(CVar(ppTypeAttr)), 0)
  472.          
  473.     If resultCall Then Err.Raise resultCall: Exit Sub
  474.  
  475. End Sub

Download.

Vb6 - cng test

$
0
0
Attached is a test program for various CNG (Cryptography Next Generation) functions.
1. Create Key Pair
2. Sign Data
3. Verify Signature
4. Test Hashes (AES-GMAC not functional yet)
5. Generate Random
6. Enumerate Algorithms
7. Test Encryption
8. Test Forward Secret (Eliptical DH keys not functional yet)
9. Create TLS 1.0 Master Keys

Tested on Windows Vista and Windows 8.1.

J.A. Coutts
Attached Images
 
Attached Files

Hook system wide with DLL in C++

$
0
0
Hello,

Even we are in 2015 and vb6 is old, it’s still great. As I had been in difficult to find a simple and efficient method to do hooking system wide, so I share my source code, here in zip attachment.

This project allow you to do global hook, system wide in Windows. The principle is to put the hook with a DLL, then get the message by subclassing our program. This hook system wide work only with 32 bits applications.

The DLL in attachment was compiled in C++. Which allow you to put hook system wide in Windows, then send to our program, with SendMessage, the message WM_USER and the hook code (nCode). With subclassing of our program, we can get the hook code by subtract WM_USER.

The hook provided from the DLL is not specific to our program but global in all Windows (system wide).

The DLL support these hooks types :
' WH_CALLWNDPROC = CallWndProc;
' WH_CALLWNDPROCRET = CallWndRetProc;
' WH_CBT = CBTProc;
' WH_DEBUG = DebugProc;
' WH_FOREGROUNDIDLE = ForegroundIdleProc;
' WH_GETMESSAGE = GetMsgProc;
' WH_JOURNALPLAYBACK = JournalPlaybackProc;
' WH_JOURNALRECORD = JournalRecordProc;
' WH_KEYBOARD = KeyboardProc;
' WH_KEYBOARD_LL = LowLevelKeyboardProc;
' WH_MSGFILTER = MessageProc;
' WH_MOUSE = MouseProc;
' WH_MOUSE_LL = LowLevelMouseProc;
' WH_SHELL = ShellProc;
' WH_SYSMSGFILTER = SysMsgProc;

This project provide a demo of these hooks system wide :
CBT / CreateWnd : get the name of the handle parent of the window to be created.
Keyboard = get the code of the keystroke.
Mouse = get the name of the handle pointed by the left click.

I’m not the author of the DLL, neither hooking and subclassing methods.
I took these three elements and make a simple project.
The DLL was coded in C++ by Renfield – 2007
Source code of subclassing by Renfield – 2010
Hooking routines by vbAccelerator – 2003

Have fun ;-)

-ZIP Removed By Moderator-

[VB6] Color Management - Different Approach

$
0
0
The class included in the attached zip file is intended for those that want to add some color management to their VB projects with minimal effort. The color management class (cICMLite) uses GDI higher level color management built-in functions and returns the image as a stdPicture object suitable to assigning to picture box, image control, etc, or selecting the picture handle into a DC for BitBlt and other rendering functions.

Pros:
1. Easy to use. Call the cICMLite.LoadPIctureICM function to return the image as a stdPicture
2. Unicode supported. Can optionally use the class as a unicode-friendly version of VB's LoadPicture
3. Can load PNG and TIFF files that GDI+ can read/process
4. CMYK jpgs handled without any additional requirements when run on Win7 or better
5. Can load alpha bitmaps, both premultiplied and not
6. Can load bitmaps using versions 4 & 5 of the BitmapInfoHeader format

Cons:
1. CMYK jpgs are not supported on XP but requires GDI+ v1.1 manifest when run on Vista
2. Transparency in PNG, TIFF, & bitmap images is filled with a backcolor your provide to cICMLite. This is because VB stdPictures do not support transparency except for icons & gifs.
3. Minor limitation. Cannot use the class for soft proofing printer ICM profiles.
4. Since icons don't support ICM, they are not specifically handled and passed to VB's LoadPicture. We all know that VB is quite limited with support for modern icons. However, you do not have to use the class to load any image files. You could also add your own custom handling routine to the class to handle modern icons.
5. When running on Vista or XP, GDI+ versions have some bugs that can prevent color managment profiles from being read

Some notes
1. In the class, you may find the pvValidateAlphaChannel logic useful for other graphic routines
2. GIFs, containing ICM profiles, are processed based on theory. I have not found any in the wild. The logic is unique
3. BMPs, containing ICM profiles, are processed based on theory. I've found only one in the wild & it was a test image
Since GDI+ does not honor alpha channels in bitmaps, and VB cannot load versions 4/5 of the BitmapInfoHeader, all bitmaps are processed manually. When possible, passed off to VB. The logic in the handling routines perform minimal sanity checks. Feel free to beef it up if desired.
4. GDI+ is used to extract ICM profiles from JPG, PNG, TIFF. Not guaranteed to find these if they exist in meta data tags vs. known ICM tags.

Since forum rules limit amount of stuff we attach, I'll include a link to my hotmail's one-drive where you can download additional images to play with. Googling for ICM Profiles can also yield more images to play with.
Attached Files

[VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager

$
0
0
Using the KnownFolderManager Object

oleexp 2.0 includes the IKnownFolderManager and IKnownFolder interfaces.

If plan on doing any work with the Known Folders that replaced CSIDL Special Locations and you're working exclusively with Vista and higher, there's now the IKnownFolderManager interface, for which Windows provides a default instance of, which makes your job much easier.

Code:

Dim pKFM as KnownFolderManager
Set pKFM = New KnownFolderManager

Now you have a ready-to-use manager that gives you the following:

.FindFolderFromPath /IDList - Have the path of a special folder and want to get its IKnownFolder interface to find out information about it? You can specify a full or partial path. If you work with PIDLs, e.g. the result from a folder browser that you could use here directly without converting back and forth to a string path, there's a function to get a known folder directly from that as well.


.FolderIdFromCsidl - Still working with CSIDLs? This will ease the transition into support Known Folders.

.GetFolder / .GetFolderByName - You can use either the GUID or canonical name to return a Known Folder object.

Code:

Dim pikf As IKnownFolder
pKFM.FindFolderFromPath "C:\Users\Jon\Downloads", FFFP_EXACTMATCH, pikf

Once you have a Known Folder, in the form of a IKnownFolder object, you can get tons of information about it:

From the main IKnownFolder object, you can get all its file system information, like its PROPERTYKEY, path, pidl, or even an IShellItem interface for it (you can also change the path with SetPath), then there's a significant subset of information in the description:
Code:

pikf.GetFolderDefinition desc
pikf.GetId pid
PrintGUID pid
Debug.Print "Icon=" & BStrFromLPWStr(desc.pszIcon, False)
Debug.Print "Name=" & BStrFromLPWStr(desc.pszName, False)
Debug.Print "Description=" & BStrFromLPWStr(desc.pszDescription, False)
Debug.Print "LocalizedName=" & BStrFromLPWStr(desc.pszLocalizedName, False)
Debug.Print "ToolTip=" & BStrFromLPWStr(desc.pszToolTip, False)
Debug.Print "Category=" & desc.category 'peruser, common, etc
Debug.Print "Attributes=" & desc.dwAttributes

This is by far the easiest way to work with these special folders on newer versions of Windows.

Most of the oleexp projects use this, but again:
Code:

Public Declare Function SysReAllocString Lib "oleaut32.dll" (ByVal pBSTR As Long, Optional ByVal pszStrPtr As Long) As Long
Public Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal pv As Long) ' Frees memory allocated by the shell

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

'also handy,
Public Declare Function StringFromGUID2 Lib "ole32.dll" (ByRef rguid As Any, ByVal lpsz As String, ByVal cchMax As Long) As Long

Public Sub PrintGUID(TempGUID As UUID)
Dim GuidStr As String
Dim lLen As Long

GuidStr = Space(80)
lLen = StringFromGUID2(TempGUID, GuidStr, 80)

If (lLen) Then
    GuidStr = StrConv(Left$(GuidStr, (lLen - 1) * 2), vbFromUnicode)
    Debug.Print GuidStr
End If
End Sub

[VB6] Color Management and VB6 How-Tos

$
0
0
The intent of this thread is to explain how color management can be used in VB6. There may be code samples included in some posts and/or links where code samples can be found. This thread will be updated as needed. Per the posting rules of the codebank, 'tutorial' like threads are ok here.

Some definitions/terms used throughout

Color Management: The primary goal is to obtain a good match across color devices; for example, the colors of one frame of a video should appear the same on a computer LCD monitor, on a plasma TV screen, and as a printed poster. Color management helps to achieve the same appearance on all of these devices. For Microsoft's definition, description & justification, see this link. Another reference can be found here that attempts to explain color management in layman terms but also goes indepth

Color transformation: The transformation of the representation of a color from one color space to another.

Color Profile: A file that contains matrices and/or look up tables that are used for color transformations.

Color Gamut: A subset of all possible colors relative to a specific color space. Basically a range of colors supported by a color space

Device-Dependent: Expresses colors relative to some other reference space, a subset of colors which can be displayed using a particular monitor or printer, or can be captured using a particular digital camera or scanner.

Device-Independent: Expresses colors in absolute terms. sRGB (standard RGB) is such a color space.

Why do I want to support color management and how much effort is needed? "Why" is simple enough. If you are displaying graphics that should always be seen by the user/customer as perfect as possible, then you should support color management. How much of a headache is this? Can be a lot initially. At a minimum, these issues need to be resolved:

- Whatever monitor your application runs on should be calibrated. For the average user, this is not trivial.
- Whatever monitor your application runs on should also have installed device-specific profiles to ensure best color matching
- You need to be able to extract/apply embedded color profiles/data from any image you display, if those profiles exist
- In VB, you need to activate color management for your applications.This is NOT done by default

Even if calibration is not performed nor a manufacturer-supplied color profile is being used, supporting color management for images can produce more accurate colors relative to the source image than would otherwise be displayed on the screen without it. Extraction of color profiles is made easier with the use of GDI+. But some versions have bugs that can prevent some data extraction and GDI+ does not support extraction on all image formats that support color profile embedding.

Activating color management within VB is the easiest thing you can do, but does very little without the ability of applying embedded image color profiles. Generally speaking, if no profile has been assigned to the monitor, then Windows assumes sRGB color space is in use. And if color profiles are not applied to images, then rendering done by Windows assumes it is between sRGB source and sRGB destination. Bottom line is that color management is prevented. If there is a color profile assigned to the monitor, then activating color management in VB for your display contexts (DCs, hDC properties) may improve color display a bit. Most monitor color profiles are not equivalent to sRGB, so color transformations are likely to occur.

In the next sections, I'll discuss how you can extract color profiles from images and various ways to apply those profiles to the images for a more accurate display. Also, VB6 users may or may not use GDI+ for the lion's share of rendering images, so I'll also try to show how these profiles can be used with both GDI and GDI+. But for simplicity sake, let's just say that enabling color management for embedded profiles will require the use of both GDI and GDI+

Some examples on this site:
Color Management with GDI+
Color Management - Different Approach
Color Management (ICC Profile) support in VB6: guide and sample project

Algorithm - Detect Alpha Usage and Type From Pixel Data

$
0
0
For those of us that use GDI+, we know that it has a major issue loading bitmaps that contain alpha data. Maybe not a major concern, because GDI basically ignores the alpha channel in most every function in its arsenal. But unfortunately, the GdipGetImageFlags function may not be useful in all cases. What if you render to a DIB and supply a pixel color to be made transparent? Maybe that color exists, maybe not; therefore, maybe transparency exists in the DIB, maybe it doesn't. This routine is not restricted for GDI+ users only. GDI users may find it useful as well.

So, let's say we have access to 32bpp pixel data and want to know if it contains transparency and if so, how is that transparency interpreted. The function below can be of big help. Read the comments I've sprinkled about the function and you should have no issues.

There are very specific, rare cases, when the logic used can fail. Those are addressed in the function's comments. This is a function I plan on using in new projects moving forward. Here are some scenarios that can be avoided. Offer an option to ignore the alpha channel in these cases:
- Steganography: If known to exist & alpha channel used for data only, image will be displayed correctly, but hidden data is lost
- All black image: Image displayed correctly
- VB picture object. Unless known it contains a valid alpha channel, ignore it. VB picture objects can produce dirty alpha channels. VB has no direct support of alpha channels. And unless a valid channel was purposely added to the picture object, it is otherwise invalid.

Simple Transparency as used here. Alpha values are either 0 or 255. Those that are 0 have RGB values also zero. Can also be considered premultiplied RGB compnents against the alpha channel
Complex Transparency as used here. Mix of alpha values that range from 0 to 255. Alpha values of zero can have non-zero RGB values. RGB components may or may not be premultiplied against the alpha channel.

The function is provided below
Code:

Public Function ValidateAlphaChannelEx( _
        ByVal PixelPointer As Long, _
        ByVal PixelCount As Long, _
        Optional ByVal MaskPointer As Long, _
        Optional ByVal MaskCount As Long) As AlphaChannelUsage
       
    ' Method determines if the alhpa channel is used and how it is used
    ' Only supports 32 bpp pixel data. Passing any other format will result in a crash
    ' Assumption is that you will only call this routine to test 32 bpp data
    ' Notes:
    '  1. If function returns values less than 2 (acuOpaque). The result should be handled manually:
    '      - acError indicates bad mask information passed
    '      - acuAllBlack indicates all color values are exactly zero. 100% transparent or 100% black?
    '      - acuOpaqueAssumed indicates all alpha values are zero, but all RGB values are not zero
    '  2. If dirty alpha values are passed, the return value may not be valid. Dirty alpha
    '      values can occur when images are drawn on a 32bpp surface and the alpha bytes are
    '      ignored. Can also occur if steganography is used in the image and the alpha byte
    '      is used for that purpose.
    '  3. It is possible that acuPremultipliedRGB can be wrong in a specific scenario: every RGB
    '      component is less than it's alpha value on purpose but the image's RGB components
    '      were NOT premultiplied against the alpha values. This specific case should be very
    '      rare and the routine assumes premultiplication, as no way of knowing thru code
   
    ' Parameters...
    ' PixelPointer :: i.e., VarPtr(pixelArray(0)), pointer returned from GdipBitmapLockBits, CreateDIBSection
    '  Note: The pixel data pointed to by PixelPointer must be 32 bit per pixel format
    '      and the pixel data must be contiguous from PixelPointer to PixelPointer+PixelCount-1
    '      the stride must be positive not negative. If negative, ensure pointer adjusted for a
    '        positive stride. Function does not care if pixel data is right-side up or not
    ' PixelCount :: amount of pixel data to process, i.e., Width * Height
    ' MaskPointer :: optional and if not provided, bytes are assumed to be in DIB format
    '  Note: BitmapInfoHeader & its later versions allow masks to be included
    '  If provided, a minimum of 3 masks (R,G,B) expected and maximum of 4 masks (Alpha)
    '  Expect the pointer to the masks to be consecutive 4 byte values: R,G,B,Alpha
    ' MaskCount :: must be one of these values: 0, 3, 4
   
    ' Mask information is generally valid only if the image has not yet been loaded into a GDI
    '  bitmap/DIB or a GDI+ image object, i.e., you are manually parsing a bitmap file.
    '  If it has already been loaded correctly, then the format of the PixelPointer you passed
    '  will already be in what this routine considers default:
    '  Defaults: Red=&HFF0000, Green=&HFF00, Blue=&HFF, Alpha=&HFF000000
    ' Unless masks use less than 8 bits each, the only important mask is the Alpha mask, the
    '  routine below does not care if pixel format is RGB,BGR,GBR,GRB,etc
   
    ValidateAlphaChannelEx = acuError                      ' default return value
    If PixelPointer = 0& Or PixelCount < 1& Then Exit Function

    Dim lMasks(0 To 3) As Long, lShifts(0 To 3) As Long      ' BGRA masks
    Dim lPtr As Long, bAlpha As Long, lFormat As Long
    Dim lColor As Long, lPrevColor As Long
    Dim bData() As Long, tSA As SafeArray
    Const ZEROES As Long = 256&
   
    ' ///// Step 1: validate passed masks are valid and/or apply default masks
    If MaskPointer Then
        If (MaskCount = 3& Or MaskCount = 4&) Then
            CopyMemory lMasks(0), ByVal MaskPointer, MaskCount * 4& ' get RGB masks
            lColor = (lMasks(0) Or lMasks(1) Or lMasks(2))          ' see if any are actually set
            If lColor Then
                If lMasks(3) = 0& Then                      ' apply default alpha if needed
                    lMasks(3) = lColor Xor -1&
                ElseIf (lMasks(3) And lColor) Then          ' see if alpha overlaps RGB mask
                    Exit Function
                End If
            End If
        End If
    End If
    ' if no mask information provided, default values will be used
    If lColor = 0& Then
        lMasks(0) = &HFF0000: lMasks(1) = &HFF00&: lMasks(2) = &HFF: lMasks(3) = &HFF000000
    End If
    For lPtr = 0& To 3&                                    ' validate masks within 8 bit boundary
        lShifts(lPtr) = lMasks(lPtr) And -lMasks(lPtr)
        If ((lMasks(lPtr) \ lShifts(lPtr)) And &HFF) > 255 Then Exit Function ' invalid mask
    Next
   
    ' ///// Step 2: setup an overlay onto the passed pixel pointer
    With tSA
        .cbElements = 4&
        .cDims = 1
        .pvData = PixelPointer
        .pvBounds.cElements = PixelCount
    End With
    CopyMemory ByVal VarPtrArray(bData), VarPtr(tSA), 4&
    On Error GoTo ExitRoutine
   
    ' ///// Step 3: test the alpha channel
    lPrevColor = bData(0) Xor 1&            ' force a no-match at start of loop
    For lPtr = 0& To PixelCount - 1&
        lColor = bData(lPtr)                ' get 32bit color
        If Not lColor = lPrevColor Then    ' and extact the alpha byte
            If lColor = 0& Then
                lFormat = lFormat Or ZEROES ' entire value is zero
                ' all zeroes indicates 100% transparent or 100% black image
                ' mix of zero & non-zero alpha values indicates transparency
            Else
                bAlpha = (lColor And lMasks(3)) \ lShifts(3) And &HFF
                If bAlpha = 0& Then
                    If (lColor And Not lMasks(3)) Then  ' RGB value is non-zero
                        If (lFormat And Not ZEROES) > acuOpaque Then
                            ' at least one other alpha value was > 0 and < 255
                            ' since this alpha is zero & RGB non-zero. Done:
                            lFormat = acuComplexTransparency: Exit For
                        End If
                        lFormat = lFormat Or acuOpaqueAssumed ' keep going, maybe all alphas are zero
                    End If
                ElseIf bAlpha = 255& Then
                    If (lFormat And acuOpaqueAssumed) Then
                        ' already seen alpha zero & non-zero RGB. Here we have 255 alpha. Done:
                        lFormat = acuComplexTransparency: Exit For
                    End If
                    lFormat = lFormat Or acuOpaque
                   
                ' else if any RGB values > alpha then not-premultiplied
                ElseIf bAlpha < (lColor And lMasks(0)) \ lShifts(0) And &HFF Then
                    lFormat = acuComplexTransparency: Exit For ' definitly ARGB
                ElseIf bAlpha < (lColor And lMasks(2)) \ lShifts(2) And &HFF Then
                    lFormat = acuComplexTransparency: Exit For ' definitly ARGB
                ElseIf bAlpha < (lColor And lMasks(1)) \ lShifts(1) And &HFF Then
                    lFormat = acuComplexTransparency: Exit For ' definitly ARGB
                Else
                    lFormat = lFormat Or acuPremultipliedRGB ' likely pARGB, but not sure yet
                End If
            End If
            lPrevColor = lColor
        End If
    Next
   
    ' ///// Step 4: Analyze result
    If (lFormat And acuPremultipliedRGB) Then
        ValidateAlphaChannelEx = acuPremultipliedRGB
    ElseIf lFormat = ZEROES Then
        ValidateAlphaChannelEx = acuAllBlack
    ElseIf lFormat = (ZEROES Or acuOpaque) Then
        ValidateAlphaChannelEx = acuSimpleTransparency
    Else
        ValidateAlphaChannelEx = (lFormat And Not ZEROES)
    End If
   
ExitRoutine:
    ' ///// Step 5: Clean up
    CopyMemory ByVal VarPtrArray(bData), 0&, 4&
   
End Function

The declarations are here
Code:

Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (ByRef ptr() As Any) As Long
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal length As Long)
Private Type SafeArrayBound          ' OLE structure
    cElements As Long
    lLbound As Long
End Type
Private Type SafeArray
    cDims As Integer
    fFeatures As Integer
    cbElements As Long
    cLocks As Long
    pvData As Long
    pvBounds As SafeArrayBound  ' single dimension usage
End Type

Public Enum AlphaChannelUsage
    acuError = &H80000000      ' invalid Mask information passed to ValidateAlphaChannelEx
    acuAllBlack = 0            ' image can be interpreted as 100% black or 100% transparent
    acuOpaqueAssumed = 1        ' all alpha values are zero, assuming image is not meant to be 100% transparent
    acuOpaque = 2              ' alpha channel is used, but all alpha values are 255
    acuSimpleTransparency = 4  ' alpha channel is used and contains simple transparency only
    acuComplexTransparency = 8  ' alpha channel is used and contains complex transparency
    acuPremultipliedRGB = 16    ' R,G,B components are multiplied against the alpha channel
    acuMask_HasTransparency = acuSimpleTransparency Or acuComplexTransparency Or acuPremultipliedRGB
    acuMask_AlphaBlendFriendly = acuOpaque Or acuSimpleTransparency Or acuPremultipliedRGB
    acuMask_Opaque = acuOpaque Or acuOpaqueAssumed
End Enum

Edited: Different interpretations you may want to consider.

1. Let's say you passed mask bits to the function and the function returns acuError. That indicates the mask was interpreted as invalid. This can happen for 3 primary reasons: a) it is invalid, any of the masks use more than 8 bits, b) the R,G,B masks combined use less than 24 bits & no alpha mask provided, leaving the routine to assume the alpha mask uses 9+ bits, or c) the alpha mask spans over one of the other masks. In a really malformatted file, I guess it would be possible to supply a mask for just one component and none for the others. In any of these cases, the likelihood that an alpha channel is used is slim to none. May want to interpret acuError in these cases as acuOpaque or acuOpaqueAssumed. That is my plan.

2. Though acuOpaqueAssumed is not included in the acuMask_AlphaBlendFriendly, this does not mean AlphaBlend cannot be used with the pixel data. acuOpaqueAssumed simply means that the pixel data has all zero alpha values along with non-zero RGB values. Alphablend can be used as long as the blend function of that API doesn't include the alpha channel (AC_SRC_ALPHA)

3. Interpreting acuAllBlack depends on whether it is expected or not. Is it possible to create a 100% transparent image? Sure. Can an all black 32bpp image exist? Sure. Are either any use? Highly doubtful. But maybe your app wants an invisible image to be applied to some control? Maybe acuAllBlack can indicate a empty DIB (no image data)? If acuAllBlack is unexpected, really a simple decision: is it better to display an invisible image or a black image or report it as 'invalid'? Your choice

Just FYI: If you don't care how the alpha channel is used, only that it is used, then you can create your own function that would be extremely fast. And it is easy to do. Simply look at the alpha channel only and it should be in use if one of these conditions apply. Once known, abort your loop.
a. Any alpha value is in the range: 1-254 inclusively
b. Any mix of 0 & 255 alpha values. Assumption: All zero alpha values <> 100% transparent image
Pseudo-code follows. Same gotchas apply as above, i.e., steganography, dirty alpha channel, etc.
Code:

Dim bSimple As Byte, p As Long
For p = 0 To [nrPixels] - 1
... extract AlphaValue with appropriate mask (if applicable)
    Select Case [AlphaValue]
        Case 0: If (bSimple And 2) Then Exit For
                bSimple = bSimple Or 1
        Case 255: If (bSimple And 1) Then Exit For
                bSimple = bSimple Or 2
        Case Else: Exit For
    End Select
Next p
If p = [nrPixels] Then ' no alpha else alpha is used

M2000 now can handle Word

$
0
0
After two days I found a way to call methods with named arguments. The problem was in typelib "IDispatch Interface - Eduardo Morcillo"
So I use the ole/com object user to extract the idl file.

Code:

        long _stdcall GetIDsOfNames(
                        [in] IID* riid,
                        [in] LPSTR* rgszNames,
                        [in] long cNames,
                        [in] long lcid,
                        [in, out] long* rgDispId);

I do a big search to find a way to pass a sting array for rgszNames, because from second element we have the named arguments. So after searching all possible variations (like safe arrays), I found the most easy solution.
Code:

        long _stdcall GetIDsOfNames(
                        [in] IID* riid,
                        [in] long* rgszNames,
                        [in] long cNames,
                        [in] long lcid,
                        [in, out] long* rgDispId);

I use mktyplib Idispatch.IDL to make the tlb

So how I can pass a string array? I think that the array is a simple long array with pointers to actual bstr (that use an array string). So i do a copy of StrPtr(stringvar or string_element_of_array) to a long type array and I pass that array. No need to convert to unicode, is ready in unicode. Secondly the array is read only for the GetIDsOfNames and i count that the job happen too fast, for vb to rearrange bstr (but maybe this is a fault). I do the same for rgDispId but here only we pass the first item.
This is a line from mdlIDispatch module in M2000 ver 8 rev 11, where fixnamearg is the number of named arguments. We just pass the first element of each array. and the others are valid from 3rd parameter, the number of elements.
lngRet = IDsp.GetIDsOfNames(rIid, myptr(0), fixnamearg + 1, cLid, varDISPID(0))

This is an example in M2000 using named arguments in Method command. Because SET is used for other purpose, I use Declare to set new objects. We can set objects as result from Method. We see that in Add method in Documents object of Word.Application.
I do some test with no named arguments, with mix and with one or two named arguments...and work fine.
Declare statement used for libraries also.
Here is the unfinished language definition
Here is the code - there is also a signed executable. Only the executable M2000.exe and the help2000.mdb needed to run the program.

Code:

declare global alfa "Word.Application"
declare global doc  use alfa, "Documents"
global wdDoNotSaveChanges=0
Global WdNewBlankDocument = 0 \\Blank document
Global WdNewEmailMessage = 2 \\E-mail message
Global WdNewFrameset = 3 \\Frameset
Global WdNewWebPage = 1 \\Web page
Global WdNewXMLDocument = 4 \\XML document
test
module kappa {
      With alfa, "visible" as anyname
      try ok {
            anyname=true
      }
      a$=key$
            try {
            with alfa, "top",100,"left",0
           
            try ok_doc { method doc, "add", "", DocumentType:=WdNewWebPage as doc1 }
            if not ok_doc then print "no doc" : exit
            method doc1, "activate"
            declare global selection  use alfa, "selection"  \\ now we can make a selection
            method selection, "TypeText","This is my text in Word Document using M2000"
            a$=key$
            try saved { method doc1,"close" }
         
            if not saved then {
            print "document not saved, press any key"
              refresh
              a$=key$
              method doc1,"close", SaveChanges:=wdDoNotSaveChanges  \\closed without saving
              }
            flush error
      }
      a$=key$
      \\ now we hide word
      if ok then {try { anyname=false }}
      try { declare doc1 nothing }     
      try { declare selection nothing }     
}
kappa
wait 10
try {method alfa, "quit"
declare doc nothing
declare alfa nothing }

Viewing all 1487 articles
Browse latest View live