Quantcast
Viewing all 1478 articles
Browse latest View live

This allows you to convert any normal array into a byte array.

It uses a Variant for the input parameter so that you don't need to have a specific array type (Byte, Integer, etc) when putting the array into the parameter. It then uses API functions, rather than VB6 functions, for handling the SafeArray so as to be usable regardless of the data type of the array, regardless of the number of dimensions, and regardless of the lower bounds of the dimensions. It copies the entire content of the array (as long as it's a fairly normal type, not something with variable length entries like an array of strings or variants) to a byte array. This is very useful if you want to treat the array as a single chunk of data, such as for input to various functions that act on a single large piece of data. These might be checksum, CRC, or hash type functions, or even an encryption function. This should work with any arrays of any of the numeric data types (Byte, Integer, Long, Currency, Single, or Double).

Below is the code for this function, as well as the declare statements that you will need to make it work.
Code:

Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Private Declare Sub SafeArrayAccessData Lib "oleaut32.dll" (ByVal psa As Long, ByRef ppvData As Any)
Private Declare Sub SafeArrayUnaccessData Lib "oleaut32.dll" (ByVal psa As Long)
Private Declare Function SafeArrayGetDim Lib "oleaut32.dll" (ByVal psa As Long) As Long
Private Declare Function SafeArrayGetElemsize Lib "oleaut32.dll" (ByVal psa As Long) As Long



Private Function AnyArrayToBytes(ByVal SafeArray As Variant) As Byte()
Dim SArrayPtr As Long
Dim ElemSize As Long
Dim DimCount As Long
Dim ElemsInDim As Long
Dim TotalElems As Long
Dim DataSize As Long
Dim DataPtr As Long
Dim Bytes() As Byte
Dim n As Long

CopyMemory SArrayPtr, ByVal VarPtr(SafeArray) + 8, 4
If SArrayPtr = 0 Then Exit Function
DimCount = SafeArrayGetDim(SArrayPtr)
ElemSize = SafeArrayGetElemsize(SArrayPtr)

TotalElems = 1
For n = 0 To DimCount - 1
    CopyMemory ElemsInDim, ByVal SArrayPtr + 16 + n * 8, 4
    TotalElems = TotalElems * ElemsInDim
Next n

DataSize = TotalElems * ElemSize
ReDim Bytes(DataSize - 1)
SafeArrayAccessData SArrayPtr, DataPtr
CopyMemory Bytes(0), ByVal DataPtr, DataSize
SafeArrayUnaccessData SArrayPtr

AnyArrayToBytes = Bytes()
End Function



Here's some code to test it out. Make sure that your Form1 has the property AutoRedraw set to True.
Code:

Private Sub Form_Load()
Dim a(3, 1) As Currency
Dim b() As Byte

b() = AnyArrayToBytes(a)
Print UBound(b)
End Sub


The value printed on Form1 should be 63.
Here's why. There are 2 dimensions. The upper bounds are 3 and 1. The lower bounds are both 0. So this is a size of 4 in the first dimension, and 2 in the second dimension. This makes 8 elements. Since each element is of Currency type, which occupies 8 bytes, this gives 8*8=64 bytes. Since the byte array returned from the function has 0 as the lower bound, the upper bound is 63.

StdDataFormat as a data parser

People often rely on String to binary data type coercion, but it can fall down (hard) in many scenarios.

There is always the cross-locale issue with many data types (Booleans, numbers, and date/time in particular). Then there are custom values e.g. Yes/No.


I'd hoped that this ParseTypes class, awkward as it is, would help parse and convert string text into strongly typed data. ParseTypes is based on the StdDataFormat object used by data binding.

I can't find the interfaces of this object that get used by data binding sources and sinks. In any case they don't seem to be exposed by the typelib of MSSTDFMT.DLL at all.

So as an aid to experimenting with this I am using a one-row fabricated ADO Recordset with DataFormat properties set on its fields. One field for each data type. Then I create a clone of this Recordset without DataFormat objects so I can get the strongly typed data out.

Ideally if this was going to be useful we'd find a way to work with StdDataFormat objects more directly and omit the use of these Recordsets. However after testing this doesn't seem anywhere near as useful as I had originally hoped anyway. :mad:

See the screen shot:

Image may be NSFW.
Clik here to view.
Name:  sshot.png
Views: 45
Size:  18.6 KB

Some of these are using Format strings, others are using additional StdDataFormat properties.

Case 4 might appear to be working but it isn't. The Format gets ignored and default CDate() conversion gets used instead, yielding an incorrect value. Case 5 addresses this by using the UnFormat event and "manual" code to perform the parsing and conversion.

Cases 6 to 8 are also flawed and are working only because those are the default CCur() conversions. The Format strings do not get used.

Caes 9 and 10 are a little weird, but working as desired. See Form1's code to see how ParseTypes1 was set up for TypeName = "String" data.


So while this isn't complete (as in completely working) it still works for some cases, so I'm posting it here as sample code. Hopefully somebody will be able to point out improvements... or ideally some other library that can do this very thing in a lighter manner with broader case coverage (for example actually parsing date/time string values according to a format string).

Dealing with date/time parsing in the UnFormat event works, but at that point you may as well skip all of this plumbing and just write a simpler date/time parsing function with the same logic in it. My hope was that even if relatively low-performance StdDataFormat would make it easy to change formats simply by changing the Format string instead of rewriting parsing code. Oh well!

I'm not sure where the "magic" happens here, at least the magic of the working cases. This seems to be mostly within MSSTDFMT.DLL even though the documentation for StdDataFormat suggests this is done by "the database" (which probably means by ADO or DAO depending on the case).
Attached Images
Image may be NSFW.
Clik here to view.
 
Attached Files

VB6 Threading-Examples using the vbRichClient5 ThreadHandler

As the Title says, two Threading-Examples which make use of the vbRichClient5-cThreadHandler-Class.
(as requested in this thread here: http://www.vbforums.com/showthread.p...=1#post4991011)

Make sure (in addition to downloading the SourceCode), that you download and install a new RC5-version (>= 5.0.40)
first before running the second, more advanced example (since it requires SQLites support for "FileURIs",
which were not yet recognized in the cConnection.CreateNewDB-method in RC5-versions below 5.0.40).

Here's the SourceCode for the two examples:
ThreadingRC5.zip

The Zip contains two Project-Folders (_Hello World and ThreadedDirScan) -
please make sure, before running the Main-VB-Projects in those Folders,
to compile the appropriate ThreadLib-Dlls from their own LibProject-SubFolders - then
placing the compiled Thread-Dll-Files in the ParentFolder (where the Main-Projects reside).

Ok, how does it work - best we start with the simpler Example, the one in the _Hello World-Folder:

VB6-Threading works best and most stable (since it was designed for that), when the
"threaded Routines" reside in a compiled ActiveX-Dll(Class) - that's the one thing which
is a bit of a "hurdle" for those who never used or compiled ActiveX-Dll-Projects so far.

But it's really quite simple... When you start out fresh - and plan to use threading
(because you have a routine which is a long-runner, blocking your UI) - then the
first step should be, to move that critical Function (and its Sub-Helper-Routines) into:
1) a Private Class in your Main-Project first
- test it there, so that you're sure everything works Ok
- also check that this Class gets everything over Function-Parameters and doesn't rely on "global Variables" outside of it

if you already have such a Class in your Main-Project - all the better - you can now move this Class:
2) as a Public Class into a new ActiveX-Dll-Project (setting its Class-Instancing-Property to 5 - MultiUse)

In case of the _Hello World-Demo, this ThreadClass' is named cThread and its Code-Content looks entirely normal:
Code:

Option Explicit

Public Function GetThreadID() As Long
  GetThreadID = App.ThreadID
End Function

Public Function StringReflection(S As String) As String
  StringReflection = StrReverse(S)
End Function

Just two simple Routines, you plan to execute on the new Thread, which
your compiled SimpleThreadLib.dll is later instantiated on (by the Main-Thread).

As alread mentioned, you can now compile this ActiveX-Dll Project into its ParentFolder (_Hello World),
where the Main-StdExe-Project resides.

This Project (ThreadCall.vbp in _Hello World) contains only a single Form, which in turn has this code:

For instantiation of the above ThreadDll-ThreadClass (cThread)
Code:

Option Explicit
 
Private WithEvents TH As cThreadHandler 'the RC5-ThreadHandler will ensure the communication with the thread-STA

Private Sub Form_Load() 'first let's instantiate the ThreadClass (regfree) on its own thread, returning "a Handler" (TH)
  Set TH = New_c.RegFree.ThreadObjectCreate("MyThreadKey", App.Path & "\SimpleThreadLib.dll", "cThread")
End Sub

And for Execution of the two Thread-Functions (from within Form_Click) it contains:
Code:

Private Sub Form_Click()
Dim StrResult As String, ThreadID As Long
  Cls
  Print Now; " (ThreadID of the Main-Thread: " & App.ThreadID & ")"; vbLf
  Print "Let's perform a few calls against the ThreadClass which now runs on its own STA "; vbLf
 
  'first we demonstrate synchronous Calls against the Thread-Instance, which was created regfree in Form_Load
  StrResult = TH.CallSynchronous("StringReflection", "ABC")
  Print "Direct (synchronous) StringReflection-Call with result: "; StrResult
 
  ThreadID = TH.CallSynchronous("GetThreadID")
  Print "Direct (synchronous) GetThreadID-Call with result: "; ThreadID; vbLf
 
  'now the calls, which are more common in threading-scenarios - the asynchronous ones, which don't
  'make the caller wait for the result (instead the results will be received in the Event-Handler below)

  TH.CallAsync "StringReflection", "ABC"
  TH.CallAsync "GetThreadID"
 
  Print "The two async calls were send (now exiting the Form_Click-routine)..."; vbLf
End Sub
 
'Our TH-Object is the clientside ThreadHandler, who's able to communicate with the Thread
'raising appropriate Events here, when results come back (in case of the async-calls)

Private Sub TH_MethodFinished(MethodName As String, Result As Variant, ErrString As String, ErrSource As String, ByVal ErrNumber As Long)
  If ErrNumber Then Print "TH-Err:"; MethodName, ErrString, ErrSource, ErrNumber: Exit Sub
 
  Print "MethodFinished-Event of TH for: "; MethodName; " with Result: "; Result
End Sub

That's all - I hope the above code-comments are sufficient - feel free to ask, when something is not clear.

Forgot to attach a ScreenShot of the Output produced by the Form_Click-Event above:
Image may be NSFW.
Clik here to view.


Will describe the second, more advanced example in a follow-up post in this thread.

Olaf
Attached Files

Visual Basic Advance Timer

Did you coded or experienced a timer with more then 60 seconds interval
or a timer with maximum cap limit ?

Here it all now :

Functions:-
  1. Intervals 1 , 5 , 10 and 15 minutes
  2. Maximum Timer Life 1 , 2 ,3 and infinity hours


Image may be NSFW.
Clik here to view.
Name:  TimerCodePreview.jpg
Views: 80
Size:  45.5 KB


Code:
Code:

Public Function StartTimer(bInterval As Double, Optional bMax As Double)
Dim MiliCounter  As Double, Infinity As Boolean
If bMax = 0 Then
Infinity = True
End If
mMax = bMax ‘Maximum Second , -1 for infinity
mInterval = bInterval ‘Interval Second
MiliCounter = -1 ‘bypass millisecods (timer is for seconds only)
StartTime = GetTickCount() ‘Get currunt system timer tick
Do While TimerEnabled = True
TimePassed = Int((GetTickCount() – StartTime) / 1000) ‘Convert to seconds
If Infinity = False Then
If TimePassed > mMax Then TimerEnabled = False ‘look for Maximum Second
End If
If (TimePassed Mod mInterval = 0) And (TimePassed > MiliCounter) Then
‘TimePassed Mod mInterval = 0  ‘look for Interval Second
‘TimePassed > MiliCounter      ‘look for next second
MiliCounter = TimePassed
‘/////////////Main Interval Code////////////
Debug.Print “time consumed “; TimePassed
Call InvokeIntervalEvent
‘///////////////////////////////////////////
End If
DoEvents
Loop
End Function

Call the function as This:
call StartTimer( 1 * 60, 1 * 3600)
i.e StartTimer for 60 sec intervel and 1 hour life

Happy Coding…:)


Example is coded in following Post
Attached Images
Image may be NSFW.
Clik here to view.
 

mdlSSE: Using SSE instructions (floating point related) in VB6 (in ASM)

Hello all!
I've developed this module to allow the use of SSE (SSE2 and 3) operations, to compute floating point operations directly by the CPU (and in 1 clock!).
SSE support the sum, sub, mul, div, and some other functions, applied in "matrices". Those are just arrays of floats. SSE (one) supports 8 operands (single precision), and it will apply the same operation to all. For example, we can have 2 arrays A and B, with their items labeled as A1, A2, etc. So, if we apply the sum operation, the result would be RESULT = A + B, so RESULT1 = A1 + B1, and so on until RESULT4 = A4 + B4.
SSE2 handles double precision, but it does use just 4 operands (A1, A2, B1, B2).

Overall I've seen some nice stuff done with SSE, like obtaining the size (projection) of a vector into other vector; mostly 3D related. But this module it's just a simple wrapper for the simpler operations.

This module allocates some memory which later is loaded with some assembly. Since the "operations" that can be done with SSE are stored in just 1 byte of the assembly, every time you call the module to do some operation, the code changes that byte accordingly.
When there is no need to use the SSE module anymore, call the free function, which releases the memory allocated previously. Source ASM file inclueded (compiled with fasm).

However, I thought that it would be nice to share it with all the devs.
The attached file includes a crude example.

Licence: Do whatever you want with the source, but a shout in your "about" would be great :bigyello:
Attached Files
  • Image may be NSFW.
    Clik here to view.
    File Type: zip
    sse.zip (5.6 KB)

[VB6] GetAdaptersInfo Example

See GetAdaptersInfo function for the details.

This sample program makes some basic use of the API call and the results it returns to report on network adapters in your system. If you need more information you can easily expand upon it to extract multiple IP addresses where they exist, etc.

There are some limitations when using early versions of Windows, so see the MSDN link above if you need support for Windows XP or earlier.

Sample output, Adapter 0 is not connected to a network:

Code:

2 adapter(s) found.

Adapter 0:
        Description = Realtek RTL8191SU Wireless LAN 802.11n USB 2.0 Network Adapter
        AdapterIndex = 23
        Name = {E9D4C1E7-8714-4545-A74B-A2FF60453A00}
        Type = 71
        Address = 00-12-71-BA-C4-34
        IP = 0.0.0.0
        GatewayIP = 0.0.0.0

Adapter 1:
        Description = Realtek PCIe GBE Family Controller
        AdapterIndex = 11
        Name = {F4747718-7BF6-4369-97C0-76A31249F698}
        Type = 6
        Address = 00-14-71-21-A3-11
        IP = 192.168.0.100
        GatewayIP = 192.168.0.1

This works even when WMI is not installed, the WMI Service is stopped, or WMI has "gone bye bye" and started returning bogus results.

Note that Address varies by adapter type. For Ethernet, WiFi, and similar network media adapters this is the MAC Address.


This might look like a lot of code, but much of it consists of structure definitions (UDTs). For specific purposes you can trim out things you do not need which can reduce it further.
Attached Files

VB6 - NewSocket 2.5

The orginal CSocket & cSocket2 were designed to emulate the Microsoft Winsock Control. Data would be sent and recovered as variants. On input, the variant was converted to byte array, converted to string using StrConv, added and subtracted to a string buffer, converted back to byte array, and finally handed over to the Socket API. Receipt of data was similar. Verion 2 of NewSocket eliminated all the back and forth conversion, and interfaced using a single conversion of string to byte. Because the StrConv function caused problems with systems using a non-latin character set, it also was eliminated.

Version 2.5 sends and accepts both ASCII strings and byte arrays. Since the Socket API requires byte strings, it made sense to allow direct input and output of byte arrays. This meant converting the string buffers (m_strSendBuffer and m_strRecvBuffer) to byte arrays and writing routines to replace the simple string concatenation and string search functions. At the same time, the functions to send and receive data were changed from a single call which included the data as a string, to 2 separate calls; one to place the data in a buffer, and one to perform the actual function. This enabled the option of using ASCII string data or byte array data.
Code:

Old Way                                  New Way
Socket.SendData strData                  Socket.sOutBuffer = strData '(ASCII String)
                                    or    Socket.bOutBuffer = bData '(Byte Array)
                                          Socket.SendData

Socket.GetData strData                    Socket.RecoverData
                                          strData = Socket.sInBuffer '(ASCII String)
                                    or    bData = Socket.bInBuffer '(Byte Array)

This posting just includes the "NewSocket.cls" and it's companion "mWinsock.bas", as well as a small program (prjTest.vbp) to test the pair. Once these have proved themselves, I will publish the corresponding OCX.

J.A. Coutts
Attached Files

[vb6] AddressOf for Class, Form, UC methods

This will be of little use to the masses, but can be of good use for thunk creators/users and the curious.

VB gives us the AddressOf function which can return the function address of any method within a bas module. We have no such function to return addresses within non-module code pages like classes, forms, usercontrols, etc. This can be a suitable substitute.

Why would you even need these addresses? Normally, you wouldn't. The most obvious case would be if you wanted to call a class function, particularly a private one, from a thunk. Can apply if wanting to do same thing from a bas module, but there are easier workarounds for calling private class methods from a module.

Here is code that you can use, both are well commented. Note that the passed VbCodeObject parameter to the ClassAddressOf function ideally would be from within the code object itself. For example, ObjPtr(myUserControlInstance) from a form is not the same as ObjPtr(Me) from within the usercontrol itself. The logic below utilizes the VB typelib info generated from VB code objects. Please read the comments within the code.

Code:

Private Declare Function DispCallFunc Lib "oleaut32.dll" (ByVal pvInstance As Long, ByVal offsetinVft As Long, ByVal CallConv As Long, ByVal retTYP As Integer, ByVal paCNT As Long, ByVal paTypes As Long, ByVal paValues As Long, ByRef retVAR As Variant) 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 ClassAddressOf(VbCodeObject As Object, Optional ByVal isPrivateMethod As Boolean = True, Optional ByVal Ordinal As Long = -1&) As Long

    ' Notes within context of this function...
    '  Method is any sub, function or property
    '  Statement is a method statement. Property Get & Property Let are two statements not one
    '  Private is not public. Friend and Private are considered Private
    ' It does not matter if your public and private method statements are intermixed
   
    ' Warnings...
    ' You should NOT include public variables at the top of your code page, before the 1st public/private method statement.
    '  Why? VB will add a Public Get,Let (and maybe a Set) statement when the project is run.
    '      if the variable is type object or variant, a public Set statement is added else it is not
    '  If public variables exist, the passed Ordinal parameter must take this into consideration else wrong address returned.
    '  To avoid this problem, properly create the public Get,Let property statements for the variable and then change
    '      the variable from public to private.
    ' If you add, remove, or move any statements be sure to double check the passed Ordinal is still correct.
    ' It is your responsibility to ensure the ordinal is not outside your total public/private statement counts
    ' If the code page only contains private methods, no public methods, you must pass the Ordinal as negative,
    '  working from the end of the page. In such a case, the only reliable offset is the end of the vTable.
   
    ' Parameters...
    ' VbCodeObject is a non-Module, i.e., class, form, usercontrol, property page, etc
    ' isPrivateMethod is True if you want function pointer for a private/friend method else False for public
    ' Ordinal is a positive number when starting from the first private/public statement
    '      else a negative number when starting from the last private/public statement
    '  Ordinal cannot be zero, must be +/-(1 to number of private/public statements)
    ' Return value of zero indicates function failed
   
    ' Examples:
    ' 1) Return the final private method address: ClassAddressOf(someObject, True, -1)
    '      Note: function defaults to this, so this works too: ClassAddressOf(someObject)
    ' 2) Return the first private method address: ClassAddressOf(someObject, True, 1)
    ' 3) Return the 2nd public method address: ClassAddressOf(someObject, False, 2)
    ' 4) Return the next-to-last public method address: ClassAddressOf(someObject, False, -2)

    If VbCodeObject Is Nothing Then Exit Function
    If Ordinal = 0& Then Exit Function
   
    Dim ITInfo As IUnknown  ' ITypeInfo interface
    Dim lPointer As Long, fOffset As Integer
    Dim fCount As Integer, vTblSize As Integer
   
    ' offset 16 = IDispatch.GetTypeInfo
    pvCallObjFunction ObjPtr(VbCodeObject), 16&, vbLong, Empty, 0&, 0&, VarPtr(ITInfo)
    If ITInfo Is Nothing Then Exit Function
       
    ' offset 12 = ITypeInfo.GetTypeAttr
    pvCallObjFunction ObjPtr(ITInfo), 12&, vbLong, Empty, VarPtr(lPointer)
    If lPointer = 0& Then Exit Function
   
    ' https://msdn.microsoft.com/en-us/library/windows/desktop/ms221003%28v=vs.85%29.aspx
    ' offset 44 = TYPEATTR structure's cFuncs member
    CopyMemory fCount, ByVal lPointer + 44&, 2&
    ' offset 50 = TYPEATTR structure's cbSizeVft member
    CopyMemory vTblSize, ByVal lPointer + 50&, 2&
   
    ' offset 76 = ITypeInfo.ReleaseTypeAttr
    pvCallObjFunction ObjPtr(ITInfo), 76&, vbEmpty, Empty, lPointer
       
    If isPrivateMethod = True Then
        If Ordinal < 0& Then fOffset = vTblSize + (Ordinal * 4&)
    ElseIf Abs(Ordinal) > fCount Then
        Exit Function  ' result would be > number of public method statements
    End If
    If fOffset = 0& Then
        lPointer = 0&
        If isPrivateMethod = True Or Ordinal < 0& Then fOffset = fCount - 1
        ' offset 20& = ITypeInfo.GetFuncDesc
        pvCallObjFunction ObjPtr(ITInfo), 20&, vbLong, Empty, fOffset, VarPtr(lPointer)
        If lPointer = 0 Then Exit Function
           
        ' https://msdn.microsoft.com/en-us/library/windows/desktop/ms221425%28v=vs.85%29.aspx
        ' offset 28 = FUNCDESC structure's oVft member
        CopyMemory fOffset, ByVal lPointer + 28&, 2&
        If isPrivateMethod Then
            fOffset = fOffset + Ordinal * 4
        ElseIf Ordinal < 0& Then
            fOffset = fOffset + (Ordinal + 1&) * 4
        Else
            fOffset = fOffset + (Ordinal - 1&) * 4
        End If
        ' offset 80 = ITypeInfo.ReleaseFuncDesc
        pvCallObjFunction ObjPtr(ITInfo), 80&, vbEmpty, Empty, lPointer
    End If
    Set ITInfo = Nothing
    ' 28 is absolute smallest offset one can have with a VB codepage object: IUnknown+IDispatch vTable size
    If Not (fOffset > 27 And fOffset < vTblSize) Then Exit Function
   
    CopyMemory lPointer, ByVal ObjPtr(VbCodeObject), 4&
    CopyMemory ClassAddressOf, ByVal lPointer + fOffset, 4&

End Function

Private Function pvCallObjFunction(ByVal InterfacePointer As Long, ByVal VTableOffset As Long, _
                            ByVal FunctionReturnType As Integer, ByRef FunctionReturnValue As Variant, _
                            ParamArray Args() As Variant) As Long
                           
' Used to call active-x or COM objects, not standard dlls

    ' InterfacePointer passed as ObjPtr(someObj) or a Long value representing a COM interface
    ' VTableOffset is zero-bound multiple of 4. Ex: IUnknown:Release is 3rd method, offset = 8 = (3-1)*4
    ' FunctionReturnType is the variable/variant type returned by called virtual function; generally it is vbLong
    '    if method returns VOID, pass vbEmpty or 0. Note: VB subs do not return VOID, so use vbLong
    ' FunctionReturnValue is return result from the called virtual function. Can pass EMPTY if not wanted.
    ' Args(): Pass ByRef using VarPtr(someVariable). Strings passed via StrPtr()
 
  'Const CC_STDCALL = 4 << 3rd parameter passed to DispCallFunc
    Dim hResult As Long, pCount As Long, vParams As Variant
    Dim vPtr() As Long, vType() As Integer

    FunctionReturnValue = Empty
    pCount = UBound(Args) + 1&
    If pCount = 0& Then                                ' no parameters
        pvCallObjFunction = DispCallFunc(InterfacePointer, VTableOffset, 4&, FunctionReturnType, _
                          pCount, 0&, 0&, FunctionReturnValue)
    Else
        vParams = Args()
        ReDim vPtr(0 To pCount - 1&)                    ' need matching array of parameter types
        ReDim vType(0 To pCount - 1&)                  ' and pointers to the parameters
        For pCount = 0& To pCount - 1&
            vPtr(pCount) = VarPtr(vParams(pCount))
            vType(pCount) = VarType(vParams(pCount))
        Next                                            ' call the function now
        pvCallObjFunction = DispCallFunc(InterfacePointer, VTableOffset, 4&, FunctionReturnType, _
                          pCount, VarPtr(vType(0)), VarPtr(vPtr(0)), FunctionReturnValue)
    End If
End Function

The return value from ClassAddressOf, when non-zero, would be the public or private method's address. Because we are talking about COM objects here, you should be aware that the 1st parameter sent to a COM method directly is the ObjPtr() of the object instance whose method is being called.

If you'd like to play, here is a test class to play with:
Code:

Option Explicit

Public Sub pbTest1()
    MsgBox "Got public method #1"
End Sub
Public Sub pbTest2()
    MsgBox "Got public method #2"
End Sub

Private Sub pvTest1()
    MsgBox "Got private method #1"
End Sub

Private Sub pvTest2()
    MsgBox "Got private method #2"
End Sub

In a new project, add a form (with 1 button) and the test class above. Add the above functions to the form. Behind the button's click event, add this & play:
Code:

    Dim clsObj As Class1
    Dim bPrivate As Boolean, lOffset As Long, lAddress As Long
   
    Set clsObj = New Class1
    Select Case MsgBox("Want Private method triggered?", vbYesNoCancel)
        Case vbYes: bPrivate = True
        Case vbCancel: Exit Sub
    End Select
    Select Case MsgBox("Do you want the Last method triggered?", vbYesNoCancel)
        Case vbYes: lOffset = -1
        Case vbNo: lOffset = 1
        Case Else: Exit Sub
    End Select
    lAddress = ClassAddressOf(clsObj, bPrivate, lOffset)
    If lAddress = 0& Then
        MsgBox "Failed to find the address of the target method", vbExclamation + vbOKOnly
    Else
        pvCallObjFunction 0&, lAddress, vbLong, Empty, ObjPtr(clsObj)
    End If

Another note & tidbit. The ClassAddressOf returns the address of the function. If it were modified to also return the offset of the function (fOffset within the code), the method could be called another way... Notice we do not pass the ObjPtr() in the param array since we are passing it as the 1st parameter.
Code:

pvCallObjFunction ObjPtr(clsObj), fOffset, vbLong, Empty, [any parameters]

[vb6] - Filter Listview User control

I will attach a zip here for archival, any updates will be on github, free for any use.

https://github.com/dzzie/libs/tree/master/filterList

Description:

small usercontrol that gives you a listview control
with a built in filter textbox on the bottom.

very similar to use as original, couple bonus functions thrown
in on top.

simple but very useful.

set integer FilterColumn to determine which column it searchs
1 based. You can set this any time, including before setting
column headers. You can also specify it in the call to
SetColumnHeaders by including an * in the column you want.

The user can also change FilterColumn on the fly from the popup
menu, or through entering /[index] in filter textbox and hitting
return.

The filter popup has a help message with more details.

When the control is locked no events will be generated or
processed. filter textbox locked and grayed.

If allowDelete property is set, user can hit delete key to
remove items from list box. This supports removing items
from the filtered results as well. (Even if the user resorted
the columns with the built in column click sort handler)

When you resize the control, the last listview item column
header will grow. You specify initial column widths in set header
call. When running in the IDE, there is a debug menu item available
that will give you the current column width settings to copy out for
the set column header call. So just manually adjust them, then use
the menu item, then you can easily set them as startup defaults.

the current list count is always available on the popup menu along
with some basic macros to allow the user to copy the whole table,
copy a specific column, copy selected entries etc.


examples:

lvFilter.SetColumnHeaders "test1,test2,test3*,test4", "870,1440,1440"

Set li = lvFilter.AddItem("text" & i)
li.subItems(1) = "taco1 " & i

Set li = lvFilter.AddItem("text", "item1", "item2", "item3")
lvFilter.SetLiColor li, vbBlue

Image may be NSFW.
Clik here to view.
Name:  filterList.jpg
Views: 45
Size:  38.6 KB
Attached Images
Image may be NSFW.
Clik here to view.
 
Attached Files

vb6 - OCX: Javascript Engine, debugger, IDE

source: https://github.com/dzzie/duk4vb

I have been tinkering with this project for a while now. I think it should be ready to share in the codebank now. binary compatibility has not been set yet but is probably about ready to. Figured I would open it up for feedback before finalizing the interface.

Image may be NSFW.
Clik here to view.
Name:  screenshot.jpg
Views: 94
Size:  30.6 KB


The project contains all of the logical stages of development in its various sub folders, from first getting the C javscript engine working with VB, to implementing basic COM integration for vb form elements, to integrating with the js engine debugger api as a standalone executable, to the final ocx which wraps it all into an easy to use component.

The Javascript engine is the C DukTape engine ( http://duktape.org/ )

The syntax highlight edit control is Scintilla (again done in C http://www.scintilla.org/ ) wrapped in another OCX done in vb6. The scivb ocx was originally done by Stewart Collier and Stu and released open source. I have also spent some time in there jiggling it around a bit. its repo is here:

https://github.com/dzzie/scivb2

The COM integration is done on the vb6 side but is not automatic like in teh script control. You have to generate javascript wrappers to represent the objects in js. This may be an intermediate stage, it is possible to generate these on the fly but will be allot of work and a ton of testing. Static files is a good safe route for now. There is a standalone vb6 generator for them. The intellisense lists also work off of parsing these generated class wrappers.

It supports breakpoints, step in/over/out, and mouse over variable values in tooltips.

I have already started using this control in a test project video below:
(This page also contains an installer that can setup all the dependencies for you.)

http://sandsprite.com/blogs/index.php?uid=7&pid=361
Attached Images
Image may be NSFW.
Clik here to view.
 

I just found this really cool code sample for getting the MAC address.

I have been looking for something like this for a while now, and somebody on stackoverflow.com had this piece of code that gets the MAC address of the default network card, and also the current IP address assigned to that network card, and then displays these 2 pieces of info in message boxes. A slight modification of this code could easily turn it into a function that returns the MAC address as the function's return value, instead of popping up message boxes. This would be very useful for designing copyprotection that is locked to hardware, by using an activation key that is tied to the computer's main network card's MAC address.

Note that even though I'm posting this code here, I'm not the one who figured it out (I don't even know exactly how it works). That credit goes to Jerome Teisseire on stackoverflow.com. I'm just posting it here for the sake of archiving it (so it will be present on more than just one website) and also helping to redistribute it to others who might come to vbforums.com looking for how to do this. More places this code is on the net, the more likely somebody who's looking to figure out how to do it will be able to find it. And the nice thing is it's only a few lines of code, not some huge thing with dozens of API calls.

Code:

Dim myWMI As Object, myObj As Object, Itm

Set myWMI = GetObject("winmgmts:\\.\root\cimv2")
Set myObj = myWMI.ExecQuery("SELECT * FROM " & _
                "Win32_NetworkAdapterConfiguration " & _
                "WHERE IPEnabled = True")
For Each Itm In myObj
    MsgBox (Itm.IPAddress(0))
    MsgBox (Itm.MACAddress)
    Exit For
Next

[VB6] ProgramData for common files

The Wild West

Back in those DOS and Win9x days you could pretty much dump files anywhere since there was no real filesystem security. On Windows 2000 and then on its minor update Windows XP, people carried on working in "DOS Mentality" by just making all users members of an elevated rights group such as Administrators or Power Users.

This gave Microsoft a black eye because users logged on with such accounts who used the Internet had opened a gaping hole for malware to come in and wreak havoc. A lot of email spam comes from zombied Windows XP machines even today.

In response this was modified beginning in Windows Vista through user Account Control (UAC). With UAC even if you were silly enough to have users log on as an Administrators account (Power Users was removed entirely) your session was no longer elevated. Instead elevated rights require special actions that raise a dialog on a Secure Desktop that malware can't just hijack and blat messages at to "click approval."

However when combined with NT security (pretty much the same model since at least NT 4.0) users can't just dump files and folders willy-nilly anymore. Lots of secured filesystem locations became off limits. This meant installed programs (in Program Files) began to either fail or run afoul of appcompat filesystem virtualization.


What To Do?

Well, there are lots of writeable locations. Each user has a Desktop, a Documents, and even AppData locations in which he can create, modify, delete, and do other things with folders and files. These work fine if the programmer takes any time to make use of them. But these don't work well for files "shared" among different users of the same PC.

Instead Windows has a CommonAppData special folder with special security on it. The DOS-visible name of this file can vary: on recent versions of Windows an English-language system calls this ProgramData.

The security on CommonAppData/ProgramData is such that a folder or file created within it has a special "Owner Access" applied to it. If user Joe creates a folder there he has full access, and all other users have basically read access. However since Joe "owns" it he can change the folder's security without elevation, and this altered security will be inherited by any folders or files created within it.

In order to avoid collisions between applications using ProgramData the convention is to create a "company" subfolder there, and within that create "product" or "application" subfolders to contain the folders and files of a given application that all users need access to.


How To Do It?

Windows Explorer, also known as Shell32, knows how to locate ProgramData by invariant code and can return the path to your programs. These "codes" are numeric values, assigned names prefixed ssf/CSIDL_ such as ssfCOMMONAPPDATA.

That gets you to the folder's path, and you can use MkDir to create subfolders, so you're nearly there!

To alter the security I've posted SetSec.bas before, but nobody seems to be using it. In an attempt to simplify this I have written ProgramData.bas as a "wrapper" for it. This gets things down to a simple function call.


Demo

The attached archive contains a simple program AnyUserUpdate.vbp, which doesn't do much.

The program looks for an application common data path and creates it as required, altering security at each created level to "Full Access" for members of the "Users" group. This is very liberal access and not correct for all situations, but it emulates the Wild West of those DOS/Win9x days to simplify programming for people. "Users" differs from "Everyone" in the post-XP era (starting in Windows Vista, "Everyone" no longer includes "Guest" accounts).

Once the demo program has this common folder and its path, it loads Text.txt into a TextBox for possible user editing.

When the program ends it checks for changes to the TextBox. If changes have been made it writes the altered Text.txt back out to disk, then logs the change to Log.txt (timestamp and user name) and exits.

Image may be NSFW.
Clik here to view.
Name:  FolderStructure.png
Views: 158
Size:  12.3 KB

All of the work required is now down to a one-liner:

Code:

Path = ProgramData.Path(App.CompanyName & "\" & App.ProductName)

Beyond ProgramData

A ProgramData subfolder is a good place for data common to all users. That might be program settings INI files, Jet MDB databases, or any other files common to all users that your application's program(s) need to be able to create, alter, or delete.

However you often have per-user settings and such too. These should go into a similar folder structure underneath ssfLOCALAPPDATA, but that's easy enough since there is no need to use SetSec.bas to alter security there. However you might want to add a new function to ProgramData.bas to look-up/create such a path too.


Installed Programs

If you use an installer to put your program(s) into Program Files you can still use ProgramData. For example maybe your program ships with an initial empty Jet MDB that all users will be updating.

Just have your program use ProgramData.Path() as above. Then check to see whether your XXX.MDB exists there, and if not copy XXX.MSB from App.Path to this ProgramData path. Then open the database as you normally would, but in the ProgramData path instead of in App.Path.

It's as easy as that!
Attached Images
Image may be NSFW.
Clik here to view.
 
Attached Files

[VB6] MapStitcher.cls - Create imagemaps from images

Sometimes it can be handy to make use of the PictureClip control or similar approaches to grab and use small images "cut out" from a larger image set up in a grid layout. But usually we have separate images to work with, and stitching them together into a larger "imagemap" image can be tedious.

MapStitcher.cls is a VB6 class that makes use of WIA 2.0 to create such "maps." A really simple thing but you might find it useful.


Limitations

Since WIA 2.0 only offers limited capabilities for handling transparency, this works best when you have images that make use of mask color transparency. If you give it source images with PNG or GIF transparency it can "stamp" these onto a backdrop making use of the transparency but there isn't any way to retrieve a composite image with any transparency itself.

This isn't all bad, since so many VB6 controls and operations make use of mask color transparency anyway. But note that to be effective your source images should use the same mask color, and you'd want to set the composite image's backdrop color to match that.


Demo

I've attached the class in a demo Project "StitchMap."

StitchMap loads the images that it finds in the directory "Samples" and assumes a magenta mask/backdrop color (255, 0, 255). It looks at the first loaded image and places its dimensions in pixels into the Cell width and Cell Height TextBoxes, and puts a 4 into the Cells per row TextBox. You can change those values.

When you click the Stitch button it passes these values to MapStitcher, which creates a stitched image. The demo displays this:


Image may be NSFW.
Clik here to view.
Name:  sshot1.png
Views: 76
Size:  14.0 KB


Oops! One image got cut off.


Image may be NSFW.
Clik here to view.
Name:  sshot2.png
Views: 70
Size:  14.5 KB


Changed dimensions to match the large image


Then you could click the Save as BMP button and it will save this to Stitched.bmp and reset.

It also works with a longer list of input images, though this will run a little longer:

Image may be NSFW.
Clik here to view.
Name:  sshot3.png
Views: 68
Size:  45.1 KB


Summary

Sometimes you need to create imagemaps from small images, but positioning them using Paint, etc. can be a pain.

MapStitcher shows a way to do this more easily. You could wrap this in a program with more options: browse to the input folder, list the input files and their dimensions, a backdrop color-picker, save-as dialog, etc.

You could also modify MapStitcher, replacing WIA 2.0 by GDI+ flat API calls to obtain more options such as saving the composite image as a PNG with a transparent backdrop.
Attached Images
Image may be NSFW.
Clik here to view.
 Image may be NSFW.
Clik here to view.
 Image may be NSFW.
Clik here to view.
 
Attached Files

Using a VB6 Collection in C++

This is vb6 specific so I am guessing this is probably the best place for it.

Sometimes you need to do a task in C/C++ and call it from VB6. Maybe its a library only available in C or you found some ready to use code. Transfer of simple types like strings or numbers is pretty straight forward, but what if you need to return a variable sized array of data elements? Things get tricky and go beyond standard C types.

The easiest solution is to dump the data to a file, or use vb6 call backs and have vb build up the array/collection for you. What i really wanted though was a way to pass a vb6 collection into a C function so that it could add arbitrary amounts of data. Searching the internet I couldnt find any examples of this. Luckily I finally had some spare time this weekend and was able to sort it out.

The sample looks deceptively simple. Figuring out the setup on what was required was a bit tricky. While there is allot going on behind the scenes automatically for us through the use of the smart pointers and the _variant_t type it actually ends up being very easy to use.

Vb example:
Code:

'we cant create a vba.collection in C, so we use a reference passed in from vb
Private Declare Sub addItems Lib "col_dll" (ByRef col As Collection)

Private Sub Command1_Click()

    Dim c As New Collection
    Dim x, tmp
   
    addItems c
    Me.Caption = c.Count & " items returned"
   
    For Each x In c
        tmp = tmp & x & vbCrLf
    Next
   
    Text1 = tmp
   
End Sub

C++ Dll Code
Code:

#include "msvbvm60.tlh"

void addStr(_CollectionPtr p , char* str){
        _variant_t vv;
        vv.SetString(str);
        p->Add(&vv.GetVARIANT());
}

void __stdcall addItems(_CollectionPtr *pColl)
{
#pragma EXPORT
       
        addStr(*pColl, "this is my string1");
        addStr(*pColl, "this is my string2");
        addStr(*pColl, "this is my string3");
        addStr(*pColl, "this is my string4");
}

So for the C portion, the tlh and tli files were generated by

#import "msvbvm60.dll" no_namespace

we then manually modified them to only include the collection object and we now import them manually. (they gave compile error as auto generated)

These files give us the smart pointer definition for the VB6 _CollectionPtr object. The VBA.Collection type can not be created by C++ code. So in order to use it we just pass in a live collection instance from our VB6 caller.

The demo app includes several methods designed to test for memory leaks, dangling pointers, corruption, and object ownership. This technique is passing on all fronts and looks good. The C project files are for VS 2008.

col_dll.zip
Attached Files
  • Image may be NSFW.
    Clik here to view.
    File Type: zip
    col_dll.zip (25.7 KB)

Using VB6 Collections, Variants, & Byte Arrays in C++

This is vb6 specific so I am guessing this is probably the best place for it.

Sometimes you need to do a task in C/C++ and call it from VB6. Maybe its a library only available in C or you found some ready to use code. Transfer of simple types like strings or numbers is pretty straight forward, but what if you need to return a variable sized array of data elements? Things get tricky and go beyond standard C types.

The easiest solution is to dump the data to a file, or use vb6 call backs and have vb build up the array/collection for you. What i really wanted though was a way to pass a vb6 collection into a C function so that it could add arbitrary amounts of data. Searching the internet I couldnt find any examples of this. Luckily I finally had some spare time this weekend and was able to sort it out.

The sample looks deceptively simple. Figuring out the setup on what was required was a bit tricky. While there is allot going on behind the scenes automatically for us through the use of the smart pointers and the _variant_t type it actually ends up being very easy to use.

Vb example:
Code:

'we cant create a vba.collection in C, so we use a reference passed in from vb
Private Declare Sub addItems Lib "col_dll" (ByRef col As Collection)

Private Sub Command1_Click()

    Dim c As New Collection
    Dim x, tmp
   
    addItems c
    Me.Caption = c.Count & " items returned"
   
    For Each x In c
        tmp = tmp & x & vbCrLf
    Next
   
    Text1 = tmp
   
End Sub

C++ Dll Code
Code:

#include "msvbvm60.tlh"

void addStr(_CollectionPtr p , char* str){
        _variant_t vv;
        vv.SetString(str);
        p->Add(&vv.GetVARIANT());
}

void __stdcall addItems(_CollectionPtr *pColl)
{
#pragma EXPORT
       
        addStr(*pColl, "this is my string1");
        addStr(*pColl, "this is my string2");
        addStr(*pColl, "this is my string3");
        addStr(*pColl, "this is my string4");
}

So for the C portion, the tlh and tli files were generated by

#import "msvbvm60.dll" no_namespace

we then manually modified them to only include the collection object and we now import them manually. (they gave compile error as auto generated)

These files give us the smart pointer definition for the VB6 _CollectionPtr object. The VBA.Collection type can not be created by C++ code. So in order to use it we just pass in a live collection instance from our VB6 caller.

The demo app includes several methods designed to test for memory leaks, dangling pointers, corruption, and object ownership. This technique is passing on all fronts and looks good. The C project files are for VS 2008.
Attached Files
  • Image may be NSFW.
    Clik here to view.
    File Type: zip
    col_dll.zip (25.7 KB)

ChaCha streaming cipher

this is a C chacha library as a vb compatible dll
also an example on working with SafeArrays between C and VB

symmetric so you just call it again to decrypt.
I compiled with vs2008, release size 42k

repo here: https://github.com/dzzie/libs/tree/master/libChaCha

Example usage:
Code:

Private Declare Sub chainit Lib "libchacha" (ByVal key As String, ByVal klen As Long, ByVal counter As Long)
Private Declare Function chacha Lib "libchacha" (ByRef buf() As Byte) As Byte()

    Dim b() As Byte
    Dim bOut() As Byte
    dim cnt as long

    'todo: load b() with your data..

    chainit txtPass, Len(txtPass), cnt
    bOut() = chacha(b)
   
    If AryIsEmpty(bOut) Then
        txtCrypt = "Encryption Failed!"
        Exit Sub
    End If

ChaCha implementation license
Code:

/*
Copyright (C) 2014 insane coder (http://insanecoding.blogspot.com/, http://chacha20.insanecoding.org/)

Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

This implementation is intended to be simple, many optimizations can be performed.
*/

Attached Files

VB6 - Unnamed Encryption Routine

Working with CNG (Cryptography Next Generation) can sometimes be a little frustrating, and often times MS is slow to add support for newer algorithms. So I came up with my own streaming Encryption routine that operates in VB6. Comments are welcome.

CNG is used to create the random key used for testing. The bytes within this random key are then shuffled using information obtained from the key itself. The shuffled bytes are then hashed to prevent matching of the shuffled bytes to the original key, and this is then XOR'd in a rotating fashion with the Plain Text to produce the Encrypted Text. The final key used in the XOR operation is never held in memory, thereby preventing a hacker from recovering it from memory.

J.A. Coutts
Attached Images
Image may be NSFW.
Clik here to view.
 
Attached Files
  • Image may be NSFW.
    Clik here to view.
    File Type: zip
    Encrypt.zip (3.5 KB)

Tar, tar.gz, tar.bz2 - Create/Extract

Wrapper for Yoshioka Tsuneo's open source C tar32.dll

http://openlab.ring.gr.jp/tsuneo/tar32/index-e.html

Code:

TAR32.DLL is a compression and archive library.
This library can compress/decompress/archive/extract/list archive files.
This library have Common Archivers Library Project API interface.
This library can manipulate under formats.
This library is opensource, and you can use for any purpose.

    .gz (gzip format)
    .Z (compress utility format) / extrace only
    .bz2 (bzip2 format)
    .tar (Tape ARchiver format)
    .cpio (cpio archive format)
    .a, .lib (ar tool format, COFF/a.out/b.out) / extract only
    .rpm (RPM package) (=leading + signature + header + .cpio.gz) /extract only
    .deb (Debian Package) (=ar of ("debian-binary" + "control.tar.gz" + "data.tar.gz")) /extract only
    .tar.gz(.tgz), .tar.bz2
    .tar.Z(.taz) / extract only
    .cpio.gz, .cpio.Z, .cpio.bz2 /extract only
    .a.gz, .a.Z, .a.bz2, lib.gz, lib.Z, lib.bz2, lib.gz / extract only

Project repo:
https://github.com/dzzie/libs/tree/master/tar32
Attached Files
  • Image may be NSFW.
    Clik here to view.
    File Type: cls
    CTarFile.cls (10.7 KB)

Closing File Select Dialog

closing a dialog window called from a process can have problems as the code will stop running while the dialog is open

a simple solution is a second exe file to find and close the dialog
compile this into a formless executable
Code:

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long

Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
(ByVal HWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long

Private Declare Function GetWindowTextLength Lib "user32" Alias _
"GetWindowTextLengthA" (ByVal HWnd As Long) As Long

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal HWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Dim Ret As Long, childret As Long, openret As Long
Dim strBuff As String, ButCap As String
Dim combo As Long

Const WM_SETTEXT = &HC
Const BM_CLICK = &HF5

Sub Main()
Dim wcap As String, bcap As String, upfile As String, params() As String, s As String, timeout As Long
params = Split(Command, "-")
wcap = Replace(Trim(params(0)), """", "")  ' window caption
bcap = Replace(Trim(params(1)), """", "")  ' button caption
upfile = Replace(Trim(params(2)), """", "") ' file path\name.ext to upload
' any of the above parameters that could contain spaces, must be enclosed in quotes by the calling application
' separate the parameters using - character
'timeout value can be altered if required as optional extra parameter
If UBound(params) = 3 Then timeout = Trim(params(3)) Else timeout = 15  ' 15 seconds
'MsgBox wcap & vbNewLine & bcap & vbNewLine & upfile
t = Timer

Do While Ret = 0 And Timer < t + timeout
DoEvents
    Ret = FindWindow(vbNullString, wcap)

    s = ""
    If Ret <> 0 Then
    Sleep 1000  ' let dialog load fully was not required in older OS

        '~~> Get the handle of the TextBox Window where we need to type the filename
        combo = FindWindowEx(Ret, ByVal 0&, "ComboBoxEx32", vbNullString)

        combo = FindWindowEx(combo, ByVal 0&, "Combobox", vbNullString)

        childret = FindWindowEx(combo, ByVal 0&, "Edit", vbNullString)

        If childret <> 0 Then
            'MsgBox "TextBox's Window Found"
            '~~> This is where we send the filename to the Text Window
            SendMess upfile, childret

            DoEvents

            '~~> Get the handle of the Button's "Window"
           
            childret = FindWindowEx(Ret, ByVal 0&, "Button", vbNullString)

            '~~> Check if we found it or not
            If childret <> 0 Then
                'MsgBox "Button's Window Found"

                '~~> Get the caption of the child window
                strBuff = String(GetWindowTextLength(childret) + 1, Chr$(0))
                GetWindowText childret, strBuff, Len(strBuff)
                ButCap = strBuff

                '~~> Loop through all child windows
                Do While childret <> 0
                    '~~> Check if the caption has the word "OK"
                    If InStr(1, ButCap, bcap) Then
                        '~~> If this is the button we are looking for then exit
                        openret = childret
                        Exit Do
                    End If

                    '~~> Get the handle of the next child window
                    childret = FindWindowEx(Ret, childret, "Button", vbNullString)
                    '~~> Get the caption of the child window
                    strBuff = String(GetWindowTextLength(childret) + 1, Chr$(0))
                    GetWindowText childret, strBuff, Len(strBuff)
                    ButCap = strBuff
                Loop

                '~~> Check if we found it or not
                If openret <> 0 Then
                    '~~> Click the OK Button
                    SendMessage childret, BM_CLICK, 0, vbNullString
                    s = "Window closed"
                Else
                    s = "The Handle of OK Button was not found"
                End If
            Else
                s = "Button's Window Not Found"
            End If
        Else
            s = "The Edit Box was not found"
        End If
    Else
        s = "Dialog Window was not Found"
    End If
Loop
'MsgBox s
End Sub

Sub SendMess(Message As String, HWnd As Long)
    Call SendMessage(HWnd, WM_SETTEXT, False, ByVal Message)
End Sub

this can then be shelled from any other code VB6, VBA and possibly VBS
using code like
Code:

Private Sub Command1_Click()
Dim wb As Object, params As String
Set wb = CreateObject("internetexplorer.application")
wb.navigate2 somesite  ' with file input element
wb.Visible = True
upfile = "c:\temp\list1.txt"  file path to upload
params = """C:\Documents and Settings\user\timerclosedialog\closefiledialog2.exe"" ""Choose File to Upload-&Open-" & upfile & """"
'change path\filename above to the exe where you compile it
' change dialog caption and button caption if required
Shell params
DoEvents
wb.document.All("uploadedfile").Click    ' change to file input element on form
' end of testing, no further code used here
End Sub

you must shell the executable before displaying the dialog
the arguments passed when shelling the exe are separated by a - (spaces are not required, but will be ignored if present), 3 arguments are required, with an optional time out value
arguments are:-
dialog caption
button caption
filename to pass to dialog
timeout (optional)

dialog and button captions are language dependent and may possibly vary on other factors, which is why i made them to be passed as arguments

originally from http://www.vbforums.com/showthread.p...t=close+dialog

Increase Stack Size in a Vb6 exe

I want to share my method to increase stack suze for VB6. (I need that for M2000 Interpreter, so now I can calll 14500 times the same function...in m2000 (so more in vb6 inside))
I made a bat file, and all we need is the editbin.exe from Masm32.
I checked also in a signed version of m2000.exe, and the sign is ok after the stack increase.
Here we get more than 100Mbyte (but not all is used, only commited, as achunk of continues memory.

Echo off
Cls
Echo Set Stack Size for M2000 - press enter
C:\masm32\bin\editbin /stack:108000000 m2000.exe
Viewing all 1478 articles
Browse latest View live


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