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

Resizeable VB6 UserForms (and Unicode Form Captions)

$
0
0
This system uses a small amount of code in a form module plus a small class module shared by all of your forms to enable you and the user to be able to move and size any form and have all of the controls on the form resize properly. In summary:


  • Form and control resizing is available (including use of the maximize button) all of the time for all new and existing forms. This requires 2 variable declarations and 4 lines of code in each form.
  • The programmer has control over whether resize is allowed, what controls will resize, whether the form retains its height to width ratio as it resizes, etc.
  • Form sizing routines are available to make a form a certain percentage of the screen size, regardless of the screen resolution and size.
  • A form can be maximized without getting distorted.
  • A form’s last size and position can be saved and then restored the next time the form is used. This data can be saved to a file or to the registry. This takes only 10 lines of code for each form.
  • As a bonus, you can now easily set the form title (caption) with any Unicode string you want.
  • Minimal use of Windows API (2 calls).



Class Module

In a form that you want to be resizable you should put the following code (cut and paste if necessary). There are more optional things you can add that will be discussed later but the simplest code to provide comprehensive resizing requires only the following a few lines of code in your form (not clResizer):

First, ensure that the BorderStyle property of the form is set to “2 – Sizable”.

In the declaration section which is below the Option Explicit statement (which you definitely should be using):

Code:

Private frmResize As New clResizer
Public UniCaption As String

Then if you have a Form_Load procedure (if not, make one), put this line in it:

Code:

frmResize.NewSetup Me ' put after any form setup code in this subroutine
That’s all that is required to have form resizing that also makes all of the controls on your form resize along with the form and to prevent everything on the form from being distorted by keeping the form’s height/width aspect ratio the same as the original form.

Normally you will want the form to appear in the same size and position on the user’s screen each time the form is displayed. We will cover various other options later but to have automatic save and restore of your form’s size and position, modify the Form_Load routine as shown below and modify or add the Form_Unload routine as shown below.

Code:

Private Sub Form_Load()
frmResize.NewSetup Me ' put after any form setup code in this subroutine
If frmResize.GetFormParmsFromFile(App.Path) = 0 Then ' specify "" to read from the registry
  ' either first time through (file does not exist) or had read error
  frmResize.CenterForm 30, 30, True ' center form on screen and make 30% the width of screen
  frmResize.MoveForm 5, 5, False
  End If
End Sub
 
 
Private Sub Form_Unload(Cancel As Integer)
' put any other code you need to save data from the form here
If Not Cancel Then
  ' This is our last shot at the form before unloading. It is possible that you
  '  have code to just hide the form and in that case we don't need to save the
  '  form settings because sometime later before the program ends this Form_Unload
  '  routine will be called.
 
  ' Don't write to App.Path in a real EXE, Windows no longer allows writing files
  ' to the Program Files folders
  frmResize.SaveFormParms2File App.Path ' specify "" to write to the registry
  End If
End Sub


Displaying a Unicode Caption

There are several peope and companies who provide Unicode controls to put onto a Form but there is no native way of putting a Unicode title on the base Form itself. This is especially frustrating since VB6 deals with Unicode directly. The problem is that the IDE editor doesn’t do Unicode nor does the code that sets up the form when it is displayed. There is now a public variable in each form called UniCaption. If you set this variable to any string then when the form is displayed UniCaption will be the form caption instead of whatever was used previously as Form.Caption. If you leave this variable blank then whatever you had set as the Form.Caption is used for the Form caption.

Suppose you have a form named fmTestMe. Suppose you want the string “あいうえお Caption” to be displayed as the caption of fmTestMe. If it never changed you could put the following line in the Form_Load sub of the form:

Code:

UniCaption = ChrW$(&H3042) & ChrW$(&H3044) & ChrW$(&H3046) & ChrW$(&H3048) & _
ChrW$(&H304A) & " Caption"

Alternatively, you can set the variable from a normal module or another class module or form by specifying the form name (the following snippet assumes the form name is TestForm):

Code:

TestForm.UniCaption = ChrW$(&H3042) & ChrW$(&H3044) & ChrW$(&H3046) & ChrW$(&H3048) & _
ChrW$(&H304A) & " Caption"

If the value of UniCaption is set before anything else is done with the form then the code you put in the Form_Load routine that calls NewSetup not only sets the size and location of your form, it also takes the value for UniCaption and sets the form caption with it.

But suppose you want to change the caption one or more times after it has been displayed. First, put the following simple routine in your form code so you can get to the variable and procedure in the class module:

Code:

Public Sub SetUnicodeCaption(Caption As String)
UniCaption = Caption
frmResize.ShowUniCap
End Sub

And then whenever you want to change it after the form has been displayed you would call it like this (if the form name was TestForm and the new Caption string was the variable MyNewCaption):

TestForm.SetUnicodeCaption(myNewCaption)


Form Design Considerations

Fonts

Since the objective of this system is to enable making your forms larger and smaller with corresponding change in the size and fonts of each control, you should avoid the use of raster fonts in your forms since these scale extremely poorly. Typical Windows raster fonts include:

8514oem Regular
ADMUI3Lg Regular
ADMUI3Sm Regular
Courier Regular (there is a TrueType version called Courier New that is okay)
Fixedsys Regular
Modern Regular
MS San Serif Regular (this is the VB6 standard font)
MS Serif Regular
Roman Regular
Script Regular
Small Fonts Regular
System Bold
Terminal

List/Combo Boxes
Drop-down boxes appear to size properly. If you use a list or combo box that shows more than one items at a time, it is possible that as you resize the form the text at the bottom gets dropped off and a vertical scrollbar appears. That’s because these controls size their own fonts based on the vertical size of the box. I have rarely seen this behavior be any problem but when I did I just set the control’s IntegralHeight property to False and gave a tiny bit more room at the bottom of the control.

Setting the Form’s Initial Size and Position

Below are some techniques for setting the initial position of the form. My recommendation is to initially put the form on the same screen as VB and since it is resizable and moveable the user will put it wherever it works best and then we will save and restore that size and position for future re-use.

Because of the way forms work, once it is displayed the programmer has little control over the position of the form. Generally you will be more concerned about what the user does to items on the form and you won’t be too worried about where the form is or how large it is as long as the user can put it wherever he/she likes and can make it as large or small as desired.

Programatically we can respond to the Resize event (which we already do) but that is largely driven by the user who is resizing the form. I suppose you could catch this re-sizing event and do something different but I don’t know what. There is no easy way to catch a Move event and the whole purpose of this system is to let the user move the form and re-size as he/she sees fit. So this means that in general we would want to move and or re-size the form just before it is being displayed via the Form.Show command.

You can put code in the Initialize event for the form but keep in mind that at this point we have not yet had Windows make the form resizable so any attempt to resize the form will not work. Also, if you try something like the following in another module it will not work either:

Code:

fmTestMe.CenterForm(50, 50, True)
fmTestMe.Show

Anything in a normal module before the Show command basically causes the Initialize event to fire and our code will be executing before the Windows call to enable resizing. The resizing code is called in the Load event which is after Initialize and just before the form is displayed.

The only way I know of to get code to affect the form after the Show statement is if the form has been Hidden instead of Unloaded.

My recommendation is to decide what you want to do regarding the form size and location and put the code to do this in the Load event procedure in the code for the Form. You have 3 routines you in the Class module for form location that enable you set the size and position to be centered or anywhere on the screen and with little effort you can derive many others. The code to access these 3 routines will need to be in your Form module code.

Code:

Sub CenterForm(WidthPerCent As Single, HeightPerCent As Single, Limit2Screen As Boolean)
This class module sub enables you to center and optionally resize the form.


  • To size the form based on the available screen width and height
    • WidthPerCent and HeightPerCent are the %'s of the screen width and height respectively
    • To make a form fill up half of the screen width regardless of the screen size and resolution you would specify the following in the form’s Load procedure:


Code:

frmResize.CenterForm 50, 50, True
  • Note – As long as Zoomable is True (default), the setting for HeightPerCent is ignored because the code determines the required height to keep the height/width ratio constant.





  • To size the form based on the original size of the form
    • WidthPerCent and HeightPerCent are based on the original form size but negative
    • To make a form be twice the size of the original form you would specify the following in the Load procedure of your form:


Code:

frmResize.CenterForm -200, -200, True  ' for 200% but negative
  • Note – As long as Zoomable is True (default), the HeightPerCent parameter is ignored.


  • If limit2Screen is True then the form size is adjusted as necessary to keep it all onscreen.



Code:

Sub MoveForm(WidthPerCent As Single, HeightPerCent As Single, Limit2Screen As Boolean)
This class module sub enables you to move the form and optionally keep it onscreen.


  • To move the form based on the available screen width and height
    • WidthPerCent and HeightPerCent are the %'s of the screen width and height respectively
    • To make a form’s upper left corner go to the middle of the screen regardless of the screen size and resolution you would specify the following in the form’s Load procedure:


Code:

frmResize.MoveForm 50, 50, True
  • Note – As long as Zoomable is True (default), the setting for HeightPerCent is ignored because the code determines the required height to keep the height/width ratio constant.



  • To move the form to the specific left and top coordinates
    • WidthPerCent and HeightPerCent are the specific form position values for Left and Top
    • To make a form go to the top left of the screen you would specify this in the Load procedure:


Code:

Frmresize.MoveForm 0, 0, True
  • Note – As long as Zoomable is True, the HeightPerCent parameter is ignored.


  • If limit2Screen is True then the form size is adjusted as necessary to keep it all onscreen.


So if we wanted to make our form be 65% the width of the screen (whatever that may be) and also displayed with the upper left corner 5% of the screen width and height from the screen’s upper left corner we could have a Form_Load routine that looks like this:

Code:

Private Sub Form_Load()
frmResize.NewSetup Me
frmResize.CenterForm 65, 65, True ' center form on screen and make 65% the width of screen
frmResize.MoveForm 5, 5, False
End Sub


Continued below...
Attached Files

VB6 - NAT Traversal

$
0
0
A NAT router is an excellent way to protect your computer network from outside hackers. The normal way to allow an outside host to connect with an internal host sitting behind a NAT router is to manually add a port forwarding address to the router setup. Setting up a router is not a simple task for the average user, and some routers have restricted access (especially public WiFi routers). To understand how to allow a host to connect with your program through a NAT router without adjusting the router setup, you must first understand how a NAT router works. Since we are interested in TCP connections, we will restrict our discussion to this type of connection.

All outbound connection requests (SYN request) are allowed through the router. At this point, the connection and it's translation are added to a NAT table. Your computer initiated the request using it's private IP address (eg. 192.168.1.5) and it's first available port (eg. 50342). The NAT router does the same thing. It uses the public address (eg. 201.34.87.52) and it's first available port (eg. 54671) and translates the outbound request to use these values. The other end only sees the router values. It never sees the values that your computer used. The values added to the NAT table on a SYN request include all 4 values:
Private IP Private Port Public IP Public Port
192.168.1.5 50342 201.34.87.52 54671
Most routers will allow about 60 seconds for this initial connection request to be acknowledged. Otherwise, it times out and is removed from the table. The connection acknowledgement (SYN-ACK) from the other end is received by the router, translated back to the private values, and forwarded to your computer. Once the connection is established, the inactivity timeout is much longer (say 24 hours), but will vary with the router.

So to traverse a NAT router without changing it's settings, we must create the NAT table entry and then connect to it before it times out. To accomplish this, we use a third party server which supplies the connecting IP address and port. Seems simple enough, but there is a complication. TCP standards do not allow us to share a port. So we will just close the existing connection and open it again with the same port number. There are 2 problems with this approach. One is that there is a TIME_WAIT after a connection is closed, which is to allow for straggling packets to be received.The other issue is that the newer versions of Microsoft sockets don't allow us to pick and choose the internal port number on a TCP connection request. It automatically chooses the first available port. So I set out to find a resolution to these issues.

To enable each side to connect to each other, we must kill the connection to the server without leaving the socket in a TIME_WAIT state. To do this, we set the "SO_LINGER" & "SO_REUSEADDR" options when we connect to the server. When we receive instructions from the server with the IP address & port number of the other end, we kill the existing socket and initiate a connection request using the same local IP address and port to the remote IP address and port. That creates a temporary NAT Table entry in the router.

When both sides are connected to the server, the server sends to each side the IP and port from the other side. Both ends will kill the existing socket that connects it to the server, create a new socket on the same local IP & port, and send a connection request to the other end. Don't ask me how this works, but the first one to receive the SYN request sends a SYN-ACK to the other end. This is enough to establish a connection and extend the timeout on the NAT router. Both ends will just have a connected socket (no listening socket).

So why are we going to this length to establish a connection? We want to allow a direct connection between 2 parties without the necessity of having the server forward all the information from both ends (as in a proxy server). The only purpose of the server is to supply the necessary information to establish that connection. Once directly connected, the entire session can be encrypted, and the server has no record of it.

The attached programs (NAT.vbp & Server.vbp) allow us to test this functionality. The server is set up to listen on port 24 using "SimpleServer.cls". Being a server, firewalls and routers must be setup to allow outside connections on port 24. To test "NAT.vbp", I added a NAT router between one of my computers and the local network, and a different NAT router to a second computer. This created a double NAT situation between these 2 particular computers, but a single NAT between each computer and the rest of the local network. The server program (Server.vbp) I set up on the local network. That left all 3 computers on separate networks. As each Nat.vbp connects to the server program, the server will display the connecting IP address and port. Machines operating from behind a NAT router will display the IP address and port of the NAT router public interface, instead of the computer's local IP address and port. Then the Trigger button on the server was clicked to send instructions to the 2 test computers. It took about 1 second for the connection to be established between the 2 test computers.

I had a great deal of difficulty getting this to work because of the vintage of one of the routers. Most routers will assign the same public port to a connection when it is using the same Private IP address and port. The older router however incremented the Public port number with each connection. I got around this problem by adding 1 to "sAddr" in the "SndConnect" routine of the machine behind the newer router.
Code:

mSocket.TCPConnect(sAddr, lPort + 1, PortListen)
You can test how your own router behaves by connecting to the server program from behind the router and noting the port number displayed in the TextBox on the server. Then disconnect and connect again. It should be the same each time.

J.A. Coutts
Attached Images
 
Attached Files

Compression in VB6: modern solutions

$
0
0
Compression has always been a cumbersome task in VB. Microsoft provides very weak support in their standard libraries, and while there are good 3rd-party solutions for VB developers, they are either expensive (http://www.dart.com/zip-activex-library-api.aspx) or very difficult to use correctly (http://www.7-zip.org/sdk.html).

So for many years, VB6 developers have fallen back on the classic zLib compression library (http://zlib.net/). zLib is an open-source compression library with a very permissive license, and it is "good enough" for most tasks: decent compression ratios, but with relatively slow compression and decompression speeds.

But in recent years, even zLib has become problematic for VB6 users. The stdcall variant of zLib hasn't been updated in over a decade, and it contains serious known security bugs. You can always compile your own version of zLib from the latest source code, but the core library definitions are bugged, so this requires a fairly deep knowledge of C and a lot of patience. (Also, zLib's source code hasn't been updated in over three years, and there are a huge number of bug fixes that have yet to be incorporated.)

And even if you do manage to survive all this and successfully build a recent version of zLib, you're still left with compression technology that is 20+ years old. A ton of compression research has been done since 1995 (when zLib first released), and we now have libraries that are both much faster, and with even better compression ratios.

So here's what this small project does: it provides a small "Compression" module that wraps four different open-source compression libraries: zLib, zstd, lz4, and lz4_hc. The compression/decompression functions are unified so you simply call a function like "Compress", and pass a "compression library enum" that specifies which compression engine you want to use.

To simplify this demo, precompiled DLLs are provided for each library. Because these are all based off open-source projects (links to code below), I believe these still meet the vbforums requirements for precompiled binaries. You are of course free to compile these yourself, from the latest source code, but you will need a modern copy of Visual Studio, some knowledge of compiling C code, and you must manually modify the project files to build stdcall variants. (They all default to cdecl, as-is.)

These are all bare C libraries, so they do not need to be registered on target PCs. Simply ship them in a subfolder of your project - for example, this demo project uses a "\Plugins\" subfolder, and the DLLs are all loaded at run-time via LoadLibrary.

Here is a brief overview of the provided compression libraries, all of which are 100% open-source and free to use in personal or commercial projects (with attribution - see the included license files for details).

- zLib is the classic library you know and love. I've freshly compiled the newest build (v1.2.8) for this demo. Despite its age, zLib remains a solid general-purpose compression library, with good compression ratios across a wide variety of data, but with slow compression speeds compared to the competition. zLib supports a "compression level" parameter that allows you to choose a trade-off between faster but worse compression, or slower but better compression. Generally speaking, there is no longer much reason to use zLib, unless you specifically need the DEFLATE algorithm it provides (e.g. to work with .gz files).

- zstd (or "zstandard") is a modern replacement for zLib. It was originally developed by Yann Collet, and its ongoing development is now sponsored by Facebook. It is 100% open-source and BSD licensed. zstd is significantly faster than zLib at both compression and decompression, and it also achieves better compression ratios. It provides a "compression level" parameter just like zLib, but with a much wider range, including extremely slow speeds but extremely good compression ratios if you need that sort of thing. For most users, zstd could replace zLib in their existing projects, and they'd immediately get a "free" performance boost from it.

- lz4 is a real-time compression engine that emphasizes performance above all else. It was also developed by Yann Collet, and it is also 100% open-source and BSD licensed. lz4 is so fast that it is now used for OS-level compression (Linux), file system compression (OpenZFS, SquashFS), database compression (MySQL), RAM caching (Emscripten, ZRam), and a whole bunch of video games (Battlefield 4, Black Ops 3, etc). LZ4's speed comes at a trade-off, however - it does not compress as well as zLib or zstd on most data. It also provides an adjustable "compression level" parameter, but instead of providing "slower but better" compression as you increase this value, lz4 provides "faster but worse" compression. It is the best solution when speed is paramount. (For example, lz4 is one of the few algorithms fast enough to provide a performance benefit vs raw uncompressed data when reading/writing to a hard drive.)

- lz4_hc comes "for free" with lz4. It is a "high-compression" variant of lz4, with much better compression ratios but much slower compression speeds. Decompression speed remains the same. It is a good solution if you have all the time in the world for compression, but you still require very fast decompression. (This is the version that video games use, for example.)

The included demo project allows you to compare compression speed, decompression speed, and compression ratio across all libraries. A baseline comparison of "no compression" is also provided, which measures timing against bare RtlMoveMemory calls. I've included a few multilanguage XML files for comparison (because they're small enough to fit inside vbforum size limits), but for best results, you should test some of your own files. Just drag-and-drop a file onto the project window to run an automated test across all libraries.

Note that - by design - the Compression module operates entirely on byte arrays and/or bare pointers (passed using VarPtr()). This makes it trivial to compress source data of any size or type. Specialized functions for Strings or other data types could always be added, but for now, those are left as an exercise to the reader.

Bug reports and feedback welcome, of course.

Download here:
Compression.zip
Attached Files

Custom Scrollbar (vbRichClient)

$
0
0
As requested here http://www.vbforums.com/showthread.p...using-Pictures

This is a custom scrollbar class that requires only to be given a reference to a picture box, as illustrated in this demo. The same class supports both horizontal and vertical orientations.

It is 99% compliant to a regular scrollbar; the ony real exception is that it's Change and Scroll events also report the current value of the scrollbar. In the case of the latter, it actually reports an exact value (e.g. 6.72, rather than 7), which can be useful for smooth-scrolling type effects. Or you can just use Round(ExactValue) in the event handler if you prefer to not have this level of precision.

Requires a reference to Olaf's vbRichClient5.dll
Attached Files

Send email in a simple VB6 project using a command1 button gmail smtp

$
0
0
i want a project code example using command1 button to submit a email using gmail smtp to submit a email to my destinated gmail email address using a single click

[VB6] ListView / TreeView Extended and Custom Checkboxes

$
0
0

So I've mentioned this and posted snippets in a few threads, but thought it would be good to do a formal sample project on this, especially since I've never seen one done before.

By default, the ListView and TreeView controls, whether it's from the OCX or manually created, only has the basic checked or unchecked state. But what if you want to add the Partial check state? Or even more? Or customize the regular checked and unchecked look? It turns out it's actually quite easy: checkboxes are simply an imagelist, so all you have to do is create your own and assign it just like you do for the regular icons. The ListView/TreeView even manages the number of checkboxes for you; no special code is required to cycle through all the checkboxes then loop back to the beginning. There's 8 different checkboxes in the sample project, I'm not sure what the limit is but you almost certainly won't hit it.

The only thing that makes this even a little complex is that you have to drop down to the API level to set the imagelist, and subclass it just to prevent VB from accidentally removing the imagelist. The good news though is that it's entirely possible to do it with the regular Common Controls 5.0 ListView/TreeView control, which is what the sample project uses.

The new checkboxes are stored in a resource file and accessed from there, but I've also included the .ico's as normal files in the zip.

How it works

First we create a new API ImageList with our new checkboxes:
Code:

Dim hIco As Long

himlCheck = ImageList_Create(32, 32, ILC_COLOR32 Or ILC_ORIGINALSIZE, 1, 1)
ImageList_SetIconSize himlCheck, 16, 16
hIco = ResIconTohIcon("CHK_STD_UNCHKD", 16, 16)
Call ImageList_AddIcon(himlCheck, hIco)
Call DestroyIcon(hIco)
'rinse and repeat for all other checkboxes. Note that if you're doing this with a TreeView,
'you need a blank icon (not unchecked, entirely blank) as the first image, but with the ListView
'you just start with the first box in the series- usually unchecked.

The checkbox imagelist is the State ImageList, so when setting up the ListView, it's assigned as such:
ListView_SetImageList hLVS, himlCheck, LVSIL_STATE

That's all you have to do to get started- all items will default to the first checkbox in the list, then cycle through in order with each click, then after the last one returns to the beginning.

If you want to set the check state through code, you need to use API since True/False isn't good enough,
Code:

Dim li As ListItem
Dim lvi As LVITEM

    lvi.iItem = li.Index - 1 'get your li from ListView.Items.Add() and similar
    lvi.Mask = LVIF_STATE
    lvi.StateMask = LVIS_STATEIMAGEMASK
    lvi.State = IndexToStateImageMask(k) 'where k is the 1-based index of the checkbox you want
    ListView_SetItem ListView1.hWnd, lvi

True/False also doesn't work for retrieving the check state either, so you just have to reverse how it was done when added,
CheckIndex = StateImageMaskToIndex(ListView_GetItemState(hLVS, iItem, LVIS_STATEIMAGEMASK)) 'where iItem is zero-based

The procedure for the TreeView is virtually identical, with the important step of adding the blank image mentioned earlier, and needing to get the hItem since the APIs don't use the index (TVITEM.hItem = pvGetHItem(Comctllib.Node))

That covers the basic concept, all the other code is just standard setup.

Requirements
-Windows XP or higher
-Common Controls 6.0 Manifest - The sample project has the cc6.0 manifest embedded in its resource file so it will work when compiled, but to work in the IDE your VB6.exe must also be set up to use the 6.0 controls. See LaVolpe's excellent manifest creator project to generate the manifest and startup code for your own projects.
Attached Files

[VB6, Vista+] Core Audio - Change the system default audio device

$
0
0

Changing the system-wide default input and output audio devices

WARNING: This feature is not designed to be accessible to programs and uses a COM interface that is undocumented and unsupported by Microsoft. As such, it may not function in future versions of Windows.

Several times I've come across people asking how to change the default input/output devices through code, and usually the reply is that it isn't possible. Changing the device per-app is well documented, but many people want to be able to set the system-wide default like the Sound control panel applet does. Tonight I was looking into that a little deeper, and the applet does it through an undocumented private COM interface called IPolicyConfig. So naturally I immediately found the definition and added it to oleexp.

There's two versions of the interface included, one for Windows Vista (IPolicyConfigVista / CPolicyConfigVistaClient) and one for Windows 7 and higher (IPolicyConfig / PolicyConfigClient).
Using this interface to set the defaults is very easy:
Code:

Private pPolicyCfg As PolicyConfigClient

If (pPolicyCfg Is Nothing) Then
    Set pPolicyCfg = New PolicyConfigClient
End If
pPolicyCfg.SetDefaultEndpoint StrPtr(sDeviceID), eMultimedia
pPolicyCfg.SetDefaultEndpoint StrPtr(sDeviceID), eCommunications

It's actually far more complicated to figure out the device ID string that you need, as it's not name, it's a string like {0.0.1.00000000}.{b12f40bc-c3ec-4a74-afcc-4b6d0eb6914a}. The good news is enumerating all the devices and their IDs (as well as enabling them if you need to, as they need to be active to be set as default) was covered in my Core Audio Basics demo. The enumeration code is copied right out of that project.

Requirements
-Windows Vista or higher
-oleexp.tlb v4.11 or higher (new release for this demo)
-oleexp addon mIID.bas (included in oleexp download)
-oleexp addon mCoreAudio.bas (included in oleexp download)
-oleexp addon mPKEY.bas (included in oleexp download)
Attached Files

Determining when two numeric ranges overlap

$
0
0
The code below will report True if 2 ranges (x1-x2 & y1-y2) overlap. You can put the two range parameters in either ascending or descending order. The function will rearrange them to ascending order if necessary.

At the bottom of the function are the two lines that will determine whether the ranges overlap at any point. If the ranges merely 'touch' each other, partially overlap or completely overlap, the IsOverlapped function will, in any of these cases, report back True. Only one of the lines at the bottom of the function is needed. The other one needs to stay rem'ed out. Use either line according to your whim. :)

The line that starts out with "Not (. . .)" actually performs its 'deed' by determining if the ranges are not overlapping, and then reverses the result with "Not".

I can't take credit for the code. I found it on the stackoverflow site in a C forum looking for an efficient answer to this problem. I simply converted their solution to a VB format.

Both lines look pretty efficient, but my gut thinks the first line might have a little speed edge due to using mostly boolean operators, whereas, the second line uses mostly math. But the difference is probably splitting hairs.


Code:

Public Function IsOverlapped(ByVal x1 As Long, ByVal x2 As Long, _
                            ByVal y1 As Long, ByVal y2 As Long) As Boolean
Dim TmpSwap As Long

  ' the procedures below depend on both ranges being in
  ' ascending order so we have to test for that to make sure
  '
  If x1 > x2 Then
      TmpSwap = x1
      x1 = x2
      x2 = TmpSwap
  End If
 
  If y1 > y2 Then
      TmpSwap = y1
      y1 = y2
      y2 = TmpSwap
  End If

  ' either of these two lines will work
  ' I kinda think the 1st one is a wee bit faster
  '
  IsOverlapped = Not ((y1 > x2) Or (x1 > y2))
  'IsOverlapped = ((y2 - x1) * (x2 - y1)) >= 0
End Function

I've done some testing with various types of overlap and non-overlap and, so far, have not found any flaw in the two lines that determine overlap. If you find an example that provides an incorrect return I would appreciate hearing about it.

rdee

Multi Bit Manipulation Class

$
0
0
This submission is a class that handles bit manipulation in the following VB data types: Byte, Integer, Long, Single, Double, Currency, Date and One dimensional Long Arrays. (These are VarTypes 2 thru 7, 17, and 8195.)

One of the nice features is that all these data types use the same interface. You can find the class here (on PSC). I keep it there so that there is only one source that I need to keep track of.

Some of the features include:

Sign bit is manipulated same as other bits.

Long arrays are in place of Decimals.
Any size long array can be made to act like a single bitfield (even shifting and rotating them).

You can insert numeric values into a bit field making for a 'hybrid' bitfield.

The class uses GetMem/PutMem & CopyBytes for various memory movements.

The class also optionally uses direct casting for some numeric types and strings. (The idea came from a Bonnie West class.)

The class is intended as a sort of "Swiss-Army-Knife" for bit manipulation. A more complete description can be found at the link and in the remarks in the class.

Any advice, ideas, or improvements would be more than welcome so as to improve it.

rdee

[VB6] ColorList - Another example of a custom dropdown list control

$
0
0
ColorList:

A "dropdown list box" control for choosing among a list of predefined colors.

This is a sort of drop-down list box written in VB6 that accepts a series of color text/value pairs. Users may choose a color via the mouse or keyboard.


Name:  sshot.png
Views: 117
Size:  11.5 KB

When too near the bottom of the screen the
list "drops up" instead of down.


Selection is by "pair" i.e. even though text "names" are displayed the name can't be typed in to choose.

Instead to make a choice via keyboard the user presses the spacebar to drop down the list, arrows up and down within the list, then types space or enter to select a text/value pair. The user can also type escape or tab to "cancel." Both actions "retract" the drop-down list.

By mouse the user can click the drop-down triangle symbol to drop the list, then scroll as necessary and click on an item to choose it. Clicking "away from" the dropdown list retracts it.

There is just one Event implemented: Click. This Click Event is raised when the user selects a new value, but not when the user cancels.

Properties Color and Text can then be fetched to obtain the choice.


Property Colors:

The color list is defined via the Colors Property, a String value. This is a list of "pairs" separated by pipe (|) characters. Each pair is a text name, a comma (,), and an 8-digit hex color value of OLE_COLOR type (i.e. system color values can be used).

Example:

"Blue,00FF0000|Green,0000FF00|Red,000000FF|Button Face,8000000F"

The text ("names") can be any text that doesn't contain a comma or a pipe.

Until Colors has a valid value assigned (design-time or run-time) the dropdown action does not occur.


Property Color:

This is a Long (OLE_COLOR) value. RGB colors are in "BGR" sequence as usual, and values with bit 31 set are OLE system color values.

The initial value may be assigned at design-time or run-time.

When this is assigned at design-time or programmatically, the Color is looked up in Colors and Text is assigned (if not found Text gets the UNKNOWN_COLOR_TEXT Const value).

The Colors Property should be assigned first.


Property Text:

This is a String value.

The initial value may be assigned at design-time or run-time.

When this is assigned at design-time or programmatically, the Text is looked up in Colors and Color is assigned (if not found Color gets the UNKNOWN_COLOR Const value).

The Colors Property should be assigned first.


Requirements:

VB6

32-bit Windows, or 64-bit Windows with WOW64 subsystem.

Only tested on Windows 10, but should work on Win9x all the way back to Windows 95.


Pieces and Parts:

ColorList.ctl
ColorList.ctx
ColorListPopup.frm
Subclasser.bas

Subclasser.bas can be removed along with calls to it and some exception handling code if you don't need ther "dropdown" to get retracted automatically when the application loses focus. See comments within ColorListPopup.frm for more details.


Remarks:

This seems to be working fine on Windows 10, but more testing is needed on downlevel versions of Windows.

In particular I'd be concerned about proper positioning of the popup/dropdown Form. It should be right under the ColorList instance (or if positioned low on the screen, right ABOVE the ColorList). "Fat" Aero borders might be a factor that could throw this off.

This UserControl might also serve as a model for creating other kinds of custom "dropdown list" controls.

ColorList (and supporting modules) compile down pretty small, adding much less size than any standard OCX we might use for this.
Attached Images
 

Statistics in VB6

$
0
0
Alright, I'm embarking on this endeavor as one of those "spare time" projects. I've long thought that there should be a good VB6 open-source program for doing various statistical analyses, and I've actually got VB6 pieces to a great many of them. I'm going to take my time with this though, and paste updates to this #1 (OP) post as things progress. Also, thoughts and ideas are more than welcome.

My philosophy is going to be that the actual data massaging will take place in Excel. And then, when the data are ready to analyze, it'll be moved into this VB6 program and analyzed. This obviates the need for data massaging/manipulation routines. Excel already does all of this quite well. (Just as an FYI, yes, I know that Excel also does primitive hypothesis testing, but I hope that this will expand well beyond the abilities of Excel, possibly approaching the abilities of something like SPSS. Also, many may want to incorporate these routines into their existing VB6 projects.)

Here's a brainstorm list of what I'd like to see:
  • parametric hypotheses:
    • two-way ANOVA
    • Pearson's correlation
    • simple linear regression
    • multiple linear regression
    • general linear model analysis
      • overall analysis
      • forward, backward, and stepwise best fit models
    • Kolmogorov Smirnov test
    • Shapiro-Wilk test
    • Levene test
    • Satterwaite t-test
  • logistic regression hypotheses
  • non-parametric hypotheses:
    • Mann-Whitney
    • Wilcoxon sign-rank
    • Kruskal-Wallis
    • Cochran test
    • Friedman test


Here's a list of statistics it currently does:
  • Descriptives
    • none at moment
  • Assumptions
    • none at moment
  • Inferential
    • one group z-test
    • one group t-test
    • two group t-test
    • one-way ANOVA


Clearly, there's much to do. However, even if a subset of the above could be accomplished, this may very well become a teaching tool for intro-stats classes, possibly giving VB6 a bit more clout. I'm not saying I'll complete the above list, but I do have sizable chunks of that code lying around, and I do have a strong knowledge of what's going on with this stuff.

Reverse chronological log:
1/14/17: Update version 1.03. Added two group t-test and one-way ANOVA.
1/13/17: First update, version 1.02. A great deal of clean-up and organization for going forward. Just briefly, organized the statistical distribution modules, added a Save/Open for datasets, added an Output for which includes printing and saving, made forms sizable and work well, and added drag-n-drop for selecting variables to analyze. Also, the one group z-test was added just to keep some headway on the actual statistical analysis.
1/11/17: Made first post, a start. It includes mechanisms for moving things back and forth from Excel, and it provides output (to Immediate window) for a one sample t-test.

The main dataset form:

Name:  main.gif
Views: 5
Size:  18.3 KB

The one group t-test form:

Name:  t.gif
Views: 5
Size:  13.8 KB

Sample of Output form:

Name:  Image1o.jpg
Views: 5
Size:  38.6 KB

If you know anything about statistics, hopefully you can see that this could be useful.

Again, I welcome assistance with anyone who would like to participate in this project. Here are some ideas that I'd welcome help on:
  • A bulletproof routine that would allow double-click editing of the dataset grid (including variable names, but excluding the record number column). Just editing of existing data. We'll take a look at adding rows/columns later.
  • A hard look at the routine that converts the Kolmogorov-Smirnov D to a p-value. This can be improved.
  • Beta testing, and reporting back with any errors/suspicions/suggestions.
  • For the Kolmogorov-Smirnov, I had to sort an array. I just threw together a primitive sort. Someone is welcome to slip in a quick-sort or some other better sort.

If you do decide to participate, possibly post your changes in posts below in-between [code][/code] with your changes highlighted in red. If they make sense, I'll absolutely incorporate them.

Just as an FYI, I know that my passion will be adding additional hypothesis test procedures to the project.

I'm going to save a couple of subsequent posts, just in case this thing actually takes off.

Best Regards to All,
Elroy
Attached Images
   
Attached Files

Linking a File with a Folder so they Copy (or Drag) Together

$
0
0
crud, this was suppose to be in the main VB6 threads. Attn moderators, please delete. I've re-posted over there.

[VB6] Port for use DX9 in VB6.

[VB6] SHFolderDlg class: BrowseForFolder

$
0
0
A VB6 Class with one Method: BrowseForFolder()

It raises a Shell dialog for browsing to a folder. You can specify the "browse root" as well as an optional StartPath under that for the dialog to show selected.

Requirements

Some features require Windows 2000/Me or later, or Win9x with IE 5. Others require Windows XP or later. See comments for the Shell32.dll version requirements of some of the features.

It may be portable to Office VBA, though for 64-bit versions it could need tweaks to the data type of variables holding pointer values.


Features

The Class wraps a call to SHBrowseForFolder() in Shell32.dll, but with a callback supporting a number of other features via Properties you assign values to before calling the Method:

  • StartPath, a path below the browse root.
  • Root, the browse root directory. This accepts CSIDL values.
  • RootPath supports a String path as an alternative to Root.
  • Instructions can be used to set the dialog's "Title" text (this is not the dialog window's "title bar" caption).
  • ExpandStartPath instructs the dialog to open with the StartPath expanded (or not).
  • OkCaption allows you to specify the "Ok" button's caption text.


The Instructions property only displays as ANSI characters but everything else should be fully Unicode.


Advantages

The main advantage over a simple call is that your programs can start the user browsing from some given location under the root. For example where they last browsed to, or perhaps a value you store in a user settings file.

This can also be used to browse for files.


Demo Project

'This demo starts with an empty FolderPath, then loops until "cancel" reusing the most recently selected FolderPath. This just shows that your programs can start the user browsing from some given location, such as where they last browsed to.



There is nothing exotic here, it just makes use of a few more features than most VB6 examples. It isn't anything we haven't seen before in other sample code, but it adds a few features. I think all of the potential memory leaks have been resolved and it should be stable compared to most older samples.
Attached Files

[VB6, Vista+] Core Audio - Monitor for disabled/active, default, and property changes

$
0
0

Monitoring audio hardware for changes

At first glance, the IMMNotificationClient interface looks like it's simple and straightforward. And in the IDE it is, but it turns out when compiled all normal ways of accessing the passed values results in a crash. Getting around this involves putting the variables into a public structure that can only be accessed and processed by subclassing the main form and using PostMessage to send the message to process things.

Setting up the notifications is easy enough:
Code:

Private Sub Command1_Click()
Command1.Enabled = False
hFrm = Me.hwnd
Subclass2 Me.hwnd, AddressOf F1WndProc, Me.hwnd
SetNotify
End Sub

Private Sub SetNotify()
Dim hr As Long

If (mDeviceEnum Is Nothing) Then
    Set mDeviceEnum = New MMDeviceEnumerator
End If

If (cMMNotify Is Nothing) Then
    Set cMMNotify = New cMMNotificationClient
    hr = mDeviceEnum.RegisterEndpointNotificationCallback(cMMNotify)
    AddMsg "Registered cMMNotify, hr=" & Hex$(hr)
End If
End Sub

Then the notification class, cMMNotify, is set up just to assign the data and fire off a window message:
Code:

Private Sub IMMNotificationClient_OnDeviceStateChanged(ByVal pwstrDeviceId As Long, ByVal dwNewState As DEVICE_STATE)
tPVC.lpszID = pwstrDeviceId
tPVC.pid = dwNewState
PostMessage hFrm, WM_MMONDEVICESTATECHANGE, 0&, ByVal 0&
End Sub

OnPropertyChanged, which uses a PROPERTYKEY, is even weirder. See the class module comments for more details.

Our custom WM_MMx messages then get handled in a normal subclassing WndProc:
Code:

Select Case uMsg
    Case WM_MMONDEVICESTATECHANGE
        sPtr = LPWSTRtoStr(tPVC.lpszID, False)
        Form1.AddMsg "StateChange: DeviceID: " & sPtr
        Form1.AddMsg "Name=" & Form1.GetDeviceName(sPtr)
        Form1.AddMsg "New status=" & GetStatusStr(tPVC.pid)

As the code suggests, this project also shows how to convert a device ID to the friendly name.

Since all messages are handled with API posting, the project should be free of instabilities that cause a crash. I haven't had crashes in testing both for the IDE and once compiled. Always unregister the notification callback before unloading the form containing an instance of the class, or the app crashes on exit (the demo project handles this automatically in Form_Unload)

Requirements
-Windows Vista or newer
-oleexp.tlb v4.0 or higher (only needed for the IDE)
-mPKEY.bas (included in oleexp download)
Attached Files

[VB6] ADO Recordset data to custom XML

$
0
0
This demo uses SAX to write an XML document in a custom format.

Even though an ADO Recordset.Save method can write XML output to disk, this uses a proprietary serialization schema your program has no control over. Sometimes you want a more compact or just a more specific output format. Sometimes you don't want to write to disk.

People who learned VB6 from copy/pasting Classic ASP VBScript snippets tend to be familiar with MSXML's DOM to some extent. However in large data scenarios or when performance and control are important using a SAX ("Simple API for XML") approach can be a handy alternative. MSXML also contains Microsoft's SAX2 implementation in addition to DOM and other objects.


Demo

There are two programs provided in VB6 source code form: CreateMDB and ExportXML. There is also some sample data provided in CSV format.

CreateDB is used to create an empty Jet 4.0 database and import supplied CSV data into it. If you try running this with some locale settings you may have trouble (for example the data contains decimal points as periods, and the commas are commas). However there is an included schema.ini file that may overcome this for you.

Name:  sshot1.png
Views: 22
Size:  1.5 KB

ExportXML performs a query against the tables in the previously-created Movies.mdb and exports this as XML via SAX as an Exported.xml file.

Name:  sshot2.png
Views: 13
Size:  1.8 KB

Result:

Code:

<ratings>
        <movie average="5.0" min="5.0" max="5.0">10 Attitudes (2001)</movie>
        <movie average="5.0" min="5.0" max="5.0">16 Wishes (2010)</movie>
        <movie average="5.0" min="5.0" max="5.0">29th and Gay (2005)</movie>
:
:
        <movie average="2.5" min="2.5" max="2.5">Zoom (2006)</movie>
</ratings>


Requirements

VB6 on a version of Windows that includes MSXML 6.0.

You could easily change the MSXML Library reference to MSXML 3.0 and adjust the data types to match (look for "60" suffixes and change them to "30" suffixes). That can help on unsupported downlevel OSs like Windows XP or even Windows 95 with IE 5.x installed.


Wrapping Up

While there is nothing spectacular about this, it shows a way to quickly export large rowsets from a database query in custom XML formats. It also serves as a demonstration of one use of the SAX2 portion of MSXML.

The attachment is large, but this is mainly due to the included sample data. See the ReadMe.txt file it contains.
Attached Images
  
Attached Files

[VB6] OWM: How lean is my program?

$
0
0
A tool meant to help answer the question "How lean is my program?"

OWM runs a specified program and samples its performance until completion. Then the final measurements are displayed, and may optionally be saved as a textual report or CSV data.

OWM is not a profiler. It only reports final bulk statistics. These are not precise since after process termination values are not available. Instead it samples roughly 10 times per second until statistics are no longer retrievable.


Saving

When data is saved to an existing report or CSV file it is appended to the end.

Effort is made to be locale-aware when writing reports and CSV data. This means respecting "comma as decimal point" and delimiting output columns with semicolons in such cases. Other locale formats should work automatically as well. In the report text the appropriate "thousands separators" should be used too.

CSV text values are always wrapped in quotes (") with quote characters escaped by doubling (as ""). No other escaping is
attempted.

CSV output files always use the ANSI encoding of the current locale.


Requirements

Requires at least Windows 2000, but some things might fail before Windows Vista.

Only tested on Windows 10.


Sample report (U.S. English locale)

Code:

                        Program ExportQuery.exe
              Run date and time 2/2/2017 1:09:14 PM
                          Note
                      Exit code 0
                    Termination Normal
                  Elapsed time 2,074 ms
              CPU: Kernel time 62 ms
                CPU: User time 953 ms
                CPU: Total time 1,015 ms
          I/O: Read operations 119
          I/O: Write operations 149
          I/O: Other operations 295
          I/O: Total operations 563
    I/O: Read bytes transferred 2,408,900 bytes
  I/O: Write bytes transferred 607,823 bytes
  I/O: Other bytes transferred 4,012 bytes
  I/O: Total bytes transferred 3,020,735 bytes
            Memory: Page faults 6,127
  Memory: Peak total workingset 22,450,176 bytes
  Memory: Avg total workingset 22,437,374 bytes
 Memory: Peak shared workingset 11,223,040 bytes
  Memory: Avg shared workingset 11,210,238 bytes
Memory: Peak private workingset 11,227,136 bytes
 Memory: Avg private workingset 11,227,136 bytes
  Memory: Peak pagedpool usage 166,912 bytes
Memory: Peak nonpagedpool usage 8,960 bytes
    Memory: Peak pagefile usage 13,578,240 bytes


There is more information in the included ReadMe.txt file.
Attached Files

Multi-Monitor Routines (and "Better" routines even for single monitor systems)

$
0
0
Well, I recently helped someone solve the problem of centering a form on a secondary monitor. At that time, I had the necessary routines, but they weren't cleaned up as much as I'd like. And a couple of you encouraged me to do so. So here they are, all cleaned up.

Also, it's important to make note that, in some ways, these are better than the built-in VB6 properties for doing the same thing. Specifically, if you intend to program for multi-monitor systems, or even for a single monitor system that has its monitor in portrait orientation. VB6 struggles when monitors aren't in the typical landscape orientation.

More specifically, you should abandon all use of Screen.Width, Screen.Height, Screen.TwipsPerPixelX, & Screen.TwipsPerPixelY. Those functions just return bad data in certain situations, and mine don't.

I'll let you peruse the functions available. With very little study, they should start making perfect sense. To use them, just throw them in a standard (BAS) module, and that's it.

The code is right at 15,000 characters (the VBForums limit) so I'll post it in the second post.

To test, you can do something as simple as throw a couple of command buttons on a Form1, and then put this code in the form:

Code:


Option Explicit

Private Sub Command1_Click()
    CenterWindowOnMonitor Me.hWnd, MonitorHandle(1), True
End Sub

Private Sub Command2_Click()
    CenterWindowOnMonitor Me.hWnd, MonitorHandle(2), True
End Sub

There's much more to it than that, but that'll give you the idea. It's all been thoroughly tested. In fact, the vast majority of it has been in production for many years.

Enjoy,
Elroy

vbRichClient5 SQLite Database Definition & Creation Helper Classes

$
0
0
This is some older code of mine that I've just updated to work with vbRichClient5 and to support Serialization/Deserialization. It may even have some bug fixes over the last publicly available version since I think it was updated a bit over the years.

It came up in the following thread: http://www.vbforums.com/showthread.p...=1#post5135705

So I thought I would post it here for posterity: JPBDbFactory.zip

UPDATE February 6, 2017

Added preliminary support for Foreign Keys. Let me know if I've missed any use cases with my object model.

Currently you can add foreign keys to any table (including Deferrable FKs with optional ON DELETE and ON UPDATE declarations).

What Is This?

It is a collection of helper classes that let you easily define Tables, Fields, Indexes, and Foreign Keys in code, then create SQLite databases and connection objects in memory or on disk. For example:

Code:

  Dim lo_Db As JPBDbFactory.CDbFactory
  Dim lo_Table As JPBDbFactory.CDbTableDef
  Dim la_DbSerial() As Byte
 
  ' Create the main DB definition helper class
  Set lo_Db = New JPBDbFactory.CDbFactory
 
  ' Add a table
  Set lo_Table = lo_Db.TableDefinitions.Add("my_table")
 
  With lo_Table
      ' Add fields to the table
      .Fields.Add "field_1", vbInteger, True  ' Create a primary field
      .Fields.Add "field_2", vbString, , , fielddefault_CurrentDateAndTime  ' Add a field that defaults to the current date and time (UTC ISO 8601)
      .Fields.Add "field_3", vbString, , False, fielddefault_Literal, "NEW ITEM"  ' Add a field that defaults to the text "NEW ITEM" and does notdoes not allow NULL values
     
      ' Index a field on the table
      With .Indexes.Add("idx_my_table_field3")
        .Add lo_Table.Fields.Item("field_3"), fieldsortorder_Ascending
      End With
  End With
 
  ' Build the schema and save the DB to disk (overwriting any existing file)
  lo_Db.CreateFileDatabase App.Path & "\test.sqlite", , True

I've also included 3 optional "useful" fields with associated TRIGGERs that you can activate with Boolean properties - AutoRowGuid (this will generate a GUID for every newly created row), AutoCreatedDate (this will be set to the current UTC date and time in ISO8601 format on record INSERT), and AutoModifiedDate (this will be set to the current UTC data and time in ISO8601 format on INSERT and UPDATE). The field names for the above "auto" fields are jpbdbf_rowguid, jpbdbf_created, and jpbdbf_lastmodified repspectively (for use with your SELECT statements).

As per CarlosRocha's requirements in the thread linked above, the classes as now fully serializable/deserializable. Just call the Serialize method on the CDbFactory class to get a byte array for storing state, and call Deserialize on a new copy of the CDbFactory class passing the results of a previous Serialize call to restore the state.

The code should be fairly self-explanatory, but I'm happy to answer any questions about it here.
Attached Files

[VB6] List and view Alternate Data Streams using GetFileInformationByHandleEx

$
0
0

So I was playing around with this last night and thought it would make a good demo, since while it seems like a single API would be straightforward it turns out this is fairly complex.

Alternate Data Streams are a hidden part of regular files that you can't normally see. They can be any length, but only the first one is reported in Windows size counts, so a 1KB file could actually be hiding 1GB in an alternate stream. The most common use of alternate streams is web browsers marking files downloaded from the internet, which is how Windows knows to ask you to confirm if you really want to run something you downloaded-- this is shown in the picture above, and getting/setting that was the subject of [VB6] Code Snippet: Get/set/del file zone identifier (Run file from internet? source). There's already code samples about these streams, notably Karl Peterson's, but I still wanted to post this since it's highly simplified and uses a different API- haven't seen any others that do it with GetFileInformationByHandleEx.


Code:

Option Explicit


Public Type FileStream
    StrmName As String
    StrmSize As Currency
    StrmAllocSize As Currency
End Type

Public Declare Function GetFileInformationByHandleEx Lib "kernel32" (ByVal hFile As Long, ByVal FileInformationClass As FILE_INFO_BY_HANDLE_CLASS, ByVal lpFileInformation As Long, ByVal dwBufferSize As Long) As Long
Public Declare Function CreateFileW Lib "kernel32" (ByVal lpFileName As Long, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Public Const GENERIC_READ    As Long = &H80000000
Public Const FILE_SHARE_READ = &H1&
Public Const OPEN_EXISTING = 3&
Public Const FILE_FLAG_BACKUP_SEMANTICS = &H2000000
Public Type LARGE_INTEGER
    lowpart As Long
    highpart As Long
End Type
Public Type FILE_STREAM_INFO
  NextEntryOffset As Long
  StreamNameLength As Long
  StreamSize As LARGE_INTEGER
  StreamAllocationSize As LARGE_INTEGER
  StreamName(0) As Integer
End Type
Public Enum FILE_INFO_BY_HANDLE_CLASS
    FileBasicInfo = 0
    FileStandardInfo = 1
    FileNameInfo = 2
    FileRenameInfo = 3
    FileDispositionInfo = 4
    FileAllocationInfo = 5
    FileEndOfFileInfo = 6
    FileStreamInfo = 7
    FileCompressionInfo = 8
    FileAttributeTagInfo = 9
    FileIdBothDirectoryInfo = 10 ' 0xA
    FileIdBothDirectoryRestartInfo = 11 ' 0xB
    FileIoPriorityHintInfo = 12 ' 0xC
    FileRemoteProtocolInfo = 13 ' 0xD
    FileFullDirectoryInfo = 14 ' 0xE
    FileFullDirectoryRestartInfo = 15 ' 0xF
    FileStorageInfo = 16 ' 0x10
    FileAlignmentInfo = 17 ' 0x11
    FileIdInfo = 18 ' 0x12
    FileIdExtdDirectoryInfo = 19 ' 0x13
    FileIdExtdDirectoryRestartInfo = 20 ' 0x14
    MaximumFileInfoByHandlesClass = 2
End Enum
Public Declare Sub ZeroMemory Lib "NTDLL.DLL" Alias "RtlZeroMemory" (dest As Any, ByVal numBytes As Long)
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Public Const INVALID_HANDLE_VALUE = -1&
Public Function LargeIntToCurrency(li As LARGE_INTEGER) As Currency
    CopyMemory LargeIntToCurrency, li, LenB(li)
    LargeIntToCurrency = LargeIntToCurrency * 10000
End Function
Public Function GetFileStreams(sFile As String, tStreams() As FileStream) As Long
ReDim tStreams(0)

Dim hFile As Long
hFile = CreateFileW(StrPtr(sFile), GENERIC_READ, FILE_SHARE_READ, ByVal 0&, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)
If hFile <> INVALID_HANDLE_VALUE Then
    Dim tFSI As FILE_STREAM_INFO
    Dim byBuf() As Byte
    Dim byName() As Byte
    Dim nErr2 As Long
    Dim dwNameOffset As Long
    Dim dwDirOffset As Long
    Dim nEntryNum As Long
   
    ReDim byBuf((LenB(tFSI) + CLng(260 * 2 - 3)) * CLng(&H10000))
    ReDim byName(0)
   
    If GetFileInformationByHandleEx(hFile, FileStreamInfo, VarPtr(byBuf(0)), UBound(byBuf) + 1) Then
'    nErr2 = GetLastError()
'    Debug.Print "lasterr=0x" & Hex$(nErr2)
    dwDirOffset = 0
    Do While 1
        ReDim Preserve tStreams(nEntryNum)
        ZeroMemory tFSI, LenB(tFSI)
        CopyMemory tFSI, ByVal VarPtr(byBuf(dwDirOffset)), LenB(tFSI)
        Erase byName
       
        dwNameOffset = dwDirOffset + &H18
        dwNameOffset = VarPtr(byBuf(dwNameOffset))
        ReDim byName(tFSI.StreamNameLength - 1)
        CopyMemory byName(0), ByVal dwNameOffset, tFSI.StreamNameLength

        tStreams(nEntryNum).StrmSize = LargeIntToCurrency(tFSI.StreamSize)
        tStreams(nEntryNum).StrmAllocSize = LargeIntToCurrency(tFSI.StreamAllocationSize)
        tStreams(nEntryNum).StrmName = CStr(byName)
        nEntryNum = nEntryNum + 1
       
        If tFSI.NextEntryOffset = 0 Then Exit Do
        dwDirOffset = dwDirOffset + tFSI.NextEntryOffset
    Loop
    GetFileStreams = nEntryNum
    End If
clhn:
    CloseHandle hFile
End If
End Function

Once you know the stream name, you can address it with normal file functions to open, save, and delete it. The sample project uses VB's Open:
Code:

Private Sub List1_Click()
If List1.ListIndex <> -1 Then
    Text2.Text = LoadFile(Text1.Text & tStrm(List1.ListIndex).StrmName)
End If
End Sub


Public Function LoadFile(ByVal FileName As String) As String
  Dim hFile As Long
  On Error GoTo Hell
  hFile = FreeFile
  Open FileName For Binary As #hFile
      LoadFile = Space$(LOF(hFile))
      Get #hFile, , LoadFile
  Close #hFile
  Exit Function
Hell:
    Debug.Print "LoadFile::" & Err.Description
End Function

Attached Files
Viewing all 1321 articles
Browse latest View live




Latest Images