Here is a small program that demonstrate a random number generator. There are two buttons and a listbox. We can see the frequencies of each of 100 numbers (from 0 to 99). We produce 2000000 random numbers. With the left button is mine generator the other is the vb generator. In Ide vb generetor is faster, but in compiled code mine generator is 2 times quicker. Why? First I would like more span to the frequencies, and the function give a number form 0 to 9999. In mine computer needed 1 seconds for 2 million random numbers, and 2 seconds using the vb function.
↧
Random number generator
↧
[VB6] Call Functions By Pointer
I was recently made aware that a function I've used from time to time for calling virtual functions of COM objects was perfectly adept at calling functions from just about any standard DLL out there. So, I whipped up a 'generic' class that can call both standard DLL functions & COM VTable functions. No thunks are used, just a couple of supporting API calls in the main class, including the low-level core API function: DispCallFunc
What does this mean for you? Well, it does allow you to call DLL functions from nearly 10 different calling conventions, including CDecl. It also allows you to call virtual functions from COM objects.
I'd consider this topic advanced for one reason only. This is very low level. If you provide incorrect parameter information to the class, your project is likely to crash. For advanced coders, we have no problem doing the research to understand what parameter information is required, be it variable type, a pointer, a pointer to a pointer, function return types, etc, etc. Not-so-advanced coders just want to plug in values & play, but when playing at such a low level, that usually results in crashes, in frustration.
The attachment includes very simple examples of calling DLL functions and calling a COM virtual function. You will notice that the form has no API function declarations, though several DLL functions are called & executed correctly.
For DLL calls, the class takes the DLL name and function name to be called. Technically, you aren't passing the function pointer to the class. However, the class does make the call to the pointer, not via declared API functions. Just thought I'd throw this comment in, should someone suggest we aren't really calling functions by pointer. The class is, the user calling the class is not, but can be if inclined to modify the code a bit.
Tip: If you really like this class, you may want to instantiate one for each DLL you will be calling quite often. This could speed things up a bit when making subsequent calls. As is, the class will load the requested DLL into memory if it isn't already. Once class is called again, for a different DLL, then the previous DLL is unloaded if needed & the new DLL loaded as needed. So, if you created cUser32, cShell32, cKernel32 instances, less code is executed in the class if it doesn't have to drop & load DLLs.
What does this mean for you? Well, it does allow you to call DLL functions from nearly 10 different calling conventions, including CDecl. It also allows you to call virtual functions from COM objects.
I'd consider this topic advanced for one reason only. This is very low level. If you provide incorrect parameter information to the class, your project is likely to crash. For advanced coders, we have no problem doing the research to understand what parameter information is required, be it variable type, a pointer, a pointer to a pointer, function return types, etc, etc. Not-so-advanced coders just want to plug in values & play, but when playing at such a low level, that usually results in crashes, in frustration.
The attachment includes very simple examples of calling DLL functions and calling a COM virtual function. You will notice that the form has no API function declarations, though several DLL functions are called & executed correctly.
For DLL calls, the class takes the DLL name and function name to be called. Technically, you aren't passing the function pointer to the class. However, the class does make the call to the pointer, not via declared API functions. Just thought I'd throw this comment in, should someone suggest we aren't really calling functions by pointer. The class is, the user calling the class is not, but can be if inclined to modify the code a bit.
Tip: If you really like this class, you may want to instantiate one for each DLL you will be calling quite often. This could speed things up a bit when making subsequent calls. As is, the class will load the requested DLL into memory if it isn't already. Once class is called again, for a different DLL, then the previous DLL is unloaded if needed & the new DLL loaded as needed. So, if you created cUser32, cShell32, cKernel32 instances, less code is executed in the class if it doesn't have to drop & load DLLs.
Code:
' top of form
Private cUser32 As cUniversalDLLCalls
Private cKernel32 As cUniversalDLLCalls
Private cShell32 As cUniversalDLLCalls
' in form load
Set cUser32 = New cUniversalDLLCalls
Set cKernel32 = New cUniversalDLLCalls
Set cShell32 = New cUniversalDLLCalls
' now use cUser32 for all user32.dll calls, cKernel32 for kernel32, cShell32 for shell32, etc
↧
↧
[VB6] Lickety - an alternative to Slurp'n'Split
We're in a time of PCs with vast amounts of RAM and little going on in the background, despite money being spent on multi-core CPUs. What was once considered a crude and naive practice of slurping entire text files into a String and then calling Split() to make an array is now actually touted by many programmers.
But much as the practice of Slurp'n'Split was impractical when far smaller amounts of RAM were available, it can break down when you deal with large files.
First you have the problem of an ANSI text file doubling in size in RAM as soon as your slurp it. Then you double that again by calling the Split() function, at least until you discard the original intact String. Both of these together conspire against you for the maximum file size you can process no matter how much RAM you bought on Mommy's credit card: VB6 programs can only use just so much due to 32-bit and other design limitations.
To add insult to injury, Split() was taken over almost without modification from VBScript and never designed for high performance on large amounts of data. That just wasn't one of its design goals.
So What's A Slurp'n'Splitter To Do?
VB5 didn't get a Split() function, and this led the fans at VBspeed to create several alternative equivalents. And of course being VBspeed they worked out some clever optimizations.
Lickety Class
My Lickety.cls was based on some of those Split() alternatives, adding in the reading of the file in chunks for better memory efficiency.
The performance is pretty good, and as soon as your input file is much over 10KB of ANSI text it quickly overtakes a Split() call in both performance and memory requirements. This advantage just grows with the size of the input file.
It has options to read ANSI or Unicode (UTF-16LE) files, optionally skip the Unicode BOM, and whatever line delimiter you choose.
It also takes care of discarding the "dangler" empty line at the end of the file, unlike a Split() call.
I haven't looked it over to be sure but it may work in VB5 as well if you work around the returning of the array (VB5 can't have array-valued functions).
Demo
The demo runs through several rounds of using Lickety and Split() to load the text lines from files of several record lengths and record counts, reporting elapsed time for each test.
![Name: sshot.png
Views: 3
Size: 7.7 KB]()
Your mileage may vary.
But much as the practice of Slurp'n'Split was impractical when far smaller amounts of RAM were available, it can break down when you deal with large files.
First you have the problem of an ANSI text file doubling in size in RAM as soon as your slurp it. Then you double that again by calling the Split() function, at least until you discard the original intact String. Both of these together conspire against you for the maximum file size you can process no matter how much RAM you bought on Mommy's credit card: VB6 programs can only use just so much due to 32-bit and other design limitations.
To add insult to injury, Split() was taken over almost without modification from VBScript and never designed for high performance on large amounts of data. That just wasn't one of its design goals.
So What's A Slurp'n'Splitter To Do?
VB5 didn't get a Split() function, and this led the fans at VBspeed to create several alternative equivalents. And of course being VBspeed they worked out some clever optimizations.
Lickety Class
My Lickety.cls was based on some of those Split() alternatives, adding in the reading of the file in chunks for better memory efficiency.
The performance is pretty good, and as soon as your input file is much over 10KB of ANSI text it quickly overtakes a Split() call in both performance and memory requirements. This advantage just grows with the size of the input file.
It has options to read ANSI or Unicode (UTF-16LE) files, optionally skip the Unicode BOM, and whatever line delimiter you choose.
It also takes care of discarding the "dangler" empty line at the end of the file, unlike a Split() call.
I haven't looked it over to be sure but it may work in VB5 as well if you work around the returning of the array (VB5 can't have array-valued functions).
Demo
The demo runs through several rounds of using Lickety and Split() to load the text lines from files of several record lengths and record counts, reporting elapsed time for each test.
Your mileage may vary.
↧
[VB6] Call Functions By Pointer (Universall DLL Calls)
I was recently made aware that a function I've used from time to time for calling virtual functions of COM objects was perfectly adept at calling functions from just about any standard DLL out there. So, I whipped up a 'generic' class that can call both standard DLL functions & COM VTable functions. No thunks are used, just a couple of supporting API calls in the main class, including the low-level core API function: DispCallFunc
What does this mean for you? Well, it does allow you to call DLL functions from nearly 10 different calling conventions, including the two most common: StdCall & CDecl. It also allows you to call virtual functions from COM objects.
I'd consider this topic advanced for one reason only. This is very low level. If you provide incorrect parameter information to the class, your project is likely to crash. For advanced coders, we have no problem doing the research to understand what parameter information is required, be it variable type, a pointer, a pointer to a pointer, function return types, etc, etc. Not-so-advanced coders just want to plug in values & play, but when playing at such a low level, that usually results in crashes, frustration.
The attachment includes very simple examples of calling DLL functions and calling a COM virtual function. You will notice that the form has no API function declarations, though several DLL functions are called & executed correctly.
For DLL calls, the class takes the DLL name and function name to be called. Technically, you aren't passing the function pointer to the class. However, the class does make the call to the pointer, not via declared API functions. Just thought I'd throw this comment in, should someone suggest we aren't really calling functions by pointer. The class is, the user calling the class is not, but can be if inclined to modify the code a bit.
Tip: If you really like this class, you may want to instantiate one for each DLL you will be calling quite often. This could speed things up a bit when making subsequent calls. As is, the class will load the requested DLL into memory if it isn't already. Once class is called again, for a different DLL, then the previous DLL is unloaded if needed & the new DLL loaded as needed. So, if you created cUser32, cShell32, cKernel32 instances, less code is executed in the class if it doesn't have to drop & load DLLs.
Tip: When using the STR_ANSI flag to indicate the passed parameters include string values destined for ANSI functions, the class will convert the passed string to ANSI before calling the function. Doing so, default Locale is used for string conversion. If this is a problem, you should ensure you convert the string(s) to ANSI before passing it to the class. If you do this conversion, use STR_NONE & pass the string via StrPtr(). FYI: strings used strictly as a buffer for return values should always be passed via StrPtr() and the flag STR_NONE used; regardless if destined for ANSI or unicode functions.
What does this mean for you? Well, it does allow you to call DLL functions from nearly 10 different calling conventions, including the two most common: StdCall & CDecl. It also allows you to call virtual functions from COM objects.
I'd consider this topic advanced for one reason only. This is very low level. If you provide incorrect parameter information to the class, your project is likely to crash. For advanced coders, we have no problem doing the research to understand what parameter information is required, be it variable type, a pointer, a pointer to a pointer, function return types, etc, etc. Not-so-advanced coders just want to plug in values & play, but when playing at such a low level, that usually results in crashes, frustration.
The attachment includes very simple examples of calling DLL functions and calling a COM virtual function. You will notice that the form has no API function declarations, though several DLL functions are called & executed correctly.
For DLL calls, the class takes the DLL name and function name to be called. Technically, you aren't passing the function pointer to the class. However, the class does make the call to the pointer, not via declared API functions. Just thought I'd throw this comment in, should someone suggest we aren't really calling functions by pointer. The class is, the user calling the class is not, but can be if inclined to modify the code a bit.
Tip: If you really like this class, you may want to instantiate one for each DLL you will be calling quite often. This could speed things up a bit when making subsequent calls. As is, the class will load the requested DLL into memory if it isn't already. Once class is called again, for a different DLL, then the previous DLL is unloaded if needed & the new DLL loaded as needed. So, if you created cUser32, cShell32, cKernel32 instances, less code is executed in the class if it doesn't have to drop & load DLLs.
vb Code:
' top of form Private cUser32 As cUniversalDLLCalls Private cKernel32 As cUniversalDLLCalls Private cShell32 As cUniversalDLLCalls ' in form load Set cUser32 = New cUniversalDLLCalls Set cKernel32 = New cUniversalDLLCalls Set cShell32 = New cUniversalDLLCalls ' now use cUser32 for all user32.dll calls, cKernel32 for kernel32, cShell32 for shell32, etc
vb Code:
' how to have a VB string contain ANSI vs Unicode myString = StrConv(myString, vbFromUnicode, [Locale ID]) ' how to convert the returned ANSI string to a proper VB string myString = StrConv(myString, vbUnicode, [Locale ID])
↧
[VB6] Clipboard/DataObject Extension Class
The attached class extends VB's Clipboard and Data objects. Change its extension from .txt to .cls after downloading.
1) Support for dragging and pasting virtual files. Virtual files exist in Windows compressed folders. They also can be transfered from any application that wishes to use the registered clipboard format. Outlook, for example, uses it when dragging emails & attachments. VB does not natively support this format
2) Support for unicode file names. The clipboard's & data object's Files collection is not unicode compatible. This class replicates the Files collection that is unicode compatible.
3) Adding custom formats and standard formats to the clipboard that VB does not support natively. This class offers two methods to add data to the clipboard and data object.
The attached class also includes, for convenience, all the methods of VB's clipboard and data objects. If you use this class, you should not need to call some methods from it and some methods from VB's objects. However, you are not prevented from using VB's methods for its objects.
Virtual files used here, do not refer to files dragged out of a WinZip file. WinZip extracts the file to a temporary folder and then provides the full path/file name of the file. The dragged data was unzipped and written to file and the file(s) are accessible to VB normally. Windows compressed folders do not do this when dropping on your form.
How does the class enable virtual file access? It uses a low-level API to communicate with VB's IDataObject. That object allows us to ask the source for specific information that VB doesn't expose. No existing APIs, that I'm aware of, expose that data either. With this IDataObject, we have full control for retrieval of anything pasted to the clipboard or dropped via OLE.
One more note about virtual files. As mentioned above, don't think of these as actual files. Think of them as simply blobs of data. The file names provided by the source application should be considered for information only. Do not consider those actual file names, they may not be. Could be an Outlook attachment name. Do not assume they do not contain invalid file name characters. To view or save these data blobs, the class includes methods for saving the blobs to file or array.
Examples of a few "file names" dragged out of a Windows compressed folder follows. Notice there is no qualified path. These are not traditional files, just blobs of data.
And here's an email dragged out of Outlook Express:
Will update the class from time to time to repair errors and/or enhance.
The code is well commented. Take the time to read thru the comments at the top of the class and those comments provided with the public methods/properties.
1) Support for dragging and pasting virtual files. Virtual files exist in Windows compressed folders. They also can be transfered from any application that wishes to use the registered clipboard format. Outlook, for example, uses it when dragging emails & attachments. VB does not natively support this format
2) Support for unicode file names. The clipboard's & data object's Files collection is not unicode compatible. This class replicates the Files collection that is unicode compatible.
3) Adding custom formats and standard formats to the clipboard that VB does not support natively. This class offers two methods to add data to the clipboard and data object.
The attached class also includes, for convenience, all the methods of VB's clipboard and data objects. If you use this class, you should not need to call some methods from it and some methods from VB's objects. However, you are not prevented from using VB's methods for its objects.
Virtual files used here, do not refer to files dragged out of a WinZip file. WinZip extracts the file to a temporary folder and then provides the full path/file name of the file. The dragged data was unzipped and written to file and the file(s) are accessible to VB normally. Windows compressed folders do not do this when dropping on your form.
How does the class enable virtual file access? It uses a low-level API to communicate with VB's IDataObject. That object allows us to ask the source for specific information that VB doesn't expose. No existing APIs, that I'm aware of, expose that data either. With this IDataObject, we have full control for retrieval of anything pasted to the clipboard or dropped via OLE.
One more note about virtual files. As mentioned above, don't think of these as actual files. Think of them as simply blobs of data. The file names provided by the source application should be considered for information only. Do not consider those actual file names, they may not be. Could be an Outlook attachment name. Do not assume they do not contain invalid file name characters. To view or save these data blobs, the class includes methods for saving the blobs to file or array.
Examples of a few "file names" dragged out of a Windows compressed folder follows. Notice there is no qualified path. These are not traditional files, just blobs of data.
Code:
license.txt
readme.txt
inc\jpegdecoder.h << sub-folder items in compressed folder
inc\jpegdecoder.inl
inc\main.h
Code:
Welcome to Outlook Express 6.eml
The code is well commented. Take the time to read thru the comments at the top of the class and those comments provided with the public methods/properties.
↧
↧
mistaken post
please save my sorry dumb butt from this and allow us to delete our own threads
↧
VB6 Mercator-Projection (or - how to handle lots of small objects on a Picturebox)
Just a small Demo, which demonstrates a Mapping for Latitude/Longitude based Coords into
a Mercator-Projected MapImage (the Background-image of a VB-PictureBox).
What's often preferred (in case one has to handle a lot of "Sub-Objects" or "Sub-Regions"
on such a Container-Control) - is normal VB-Labels or VB-Shape-Objects - usually "inherited"
from an Index-Zero Member of VBs Control-Arrays.
Though Control-Arrays behave a bit clumsy as far as dynamic removing/adding in-between
an existing List of Objects goes... so - that's the reason why I made this post, to break a lance
for a Collection-based approach which takes up and holds the instances of small alternative
ShapeClasses (any of VBs Shape-Like Controls can be replaced by a Class and a few Drawing-Commands).
Here's the complete Class-Code for the alternative Label-Shape which is used in the Demo.
The approach in the Zip below is fast, behaves flickerfree - it reports the current MousePos
in both Coord-Systems (Pixels - and Lat/Lng) - it supports hovered highlighting of the current
Shape-Object under the Cursor - as well as dynamic ToolTips for each of the Shape-Objects separately.
It does that in a quite small CodeBase, so for those who have Label-based approaches out there -
compare carefully if that's not something to switch over - even if it's a bit out of your comfort-zone. ;)
![]()
Mercator.zip
Olaf
a Mercator-Projected MapImage (the Background-image of a VB-PictureBox).
What's often preferred (in case one has to handle a lot of "Sub-Objects" or "Sub-Regions"
on such a Container-Control) - is normal VB-Labels or VB-Shape-Objects - usually "inherited"
from an Index-Zero Member of VBs Control-Arrays.
Though Control-Arrays behave a bit clumsy as far as dynamic removing/adding in-between
an existing List of Objects goes... so - that's the reason why I made this post, to break a lance
for a Collection-based approach which takes up and holds the instances of small alternative
ShapeClasses (any of VBs Shape-Like Controls can be replaced by a Class and a few Drawing-Commands).
Here's the complete Class-Code for the alternative Label-Shape which is used in the Demo.
Code:
Option Explicit
Private Declare Function Rectangle& Lib "gdi32" (ByVal hDC&, ByVal X1&, ByVal Y1&, ByVal X2&, ByVal Y2&)
Public Key As String 'a unique Key for identification (we are added into a Collection under it)
Public Longitude As Single, Latitude As Single 'centerpoint of the Label in Lat/Lng-coords
Public cx As Single, cy As Single 'Pixel-centerpoint of the Label on the PicBox
Public Radius As Long, Color As Long, ToolTipText As String, Hovered As Boolean
Friend Sub Init(Key, cx, cy, Lat, Lng, Optional ByVal Radius& = 3, Optional ByVal Color&, Optional ToolTipText$)
Me.Key = Key
Me.cx = cx: Longitude = Lng
Me.cy = cy: Latitude = Lat
Me.Radius = Radius
Me.Color = Color
Me.ToolTipText = ToolTipText
End Sub
Public Sub DrawOn(Cont As PictureBox)
Cont.ForeColor = vbYellow
Cont.FillColor = IIf(Hovered, vbCyan, Color)
Rectangle Cont.hDC, cx - Radius, cy - Radius, cx + Radius, cy + Radius 'GDI is a bit faster than VBs Line-Method
End Sub
in both Coord-Systems (Pixels - and Lat/Lng) - it supports hovered highlighting of the current
Shape-Object under the Cursor - as well as dynamic ToolTips for each of the Shape-Objects separately.
It does that in a quite small CodeBase, so for those who have Label-based approaches out there -
compare carefully if that's not something to switch over - even if it's a bit out of your comfort-zone. ;)

Mercator.zip
Olaf
↧
VB6 Render PDF-Files directly (and generate a Preview from the same DrawingRoutine)
Since the PDF-Printing-Topic comes up occasionally - sometimes with recommendations and links to
VB-Modules which try to write a PDF-file directly - the PDF-support of the cairo-lib (wrapped conveniently
in vbRichClient5.dll) is "miles ahead" of all these approaches in terms of functionality (the supported
Drawing-Commands) - and reliability (correctness of the generated PDF-Output).
So here's a small VB6-Demo which shows how to use a vbRichClient5.cCairoSurface instance, which
can also be created as "being of cairo-SurfaceType PDF" instead of the usual "ImgSurface-Type" which
is pixelbased and used for OnScreen-Output (BTW, another true VectorSurface-type is e.g. SVG,
which other than PDF can not only be written directly, but also read).
Anyways, in the end we are able to perform the very same Drawing-Commands against a CairoSurface
of type PDF, the same way as we are wont, when we take a CairoSurface of Type "Image" (which then
just renders antialiased against a Pixel-area to bring out the results of the VectorDrawing-Commands).
That transparently usable multi-surface-support of Cairo is one of the greatest advantages of this library IMO.
The demo will print two Pages (only the first one is currently rendered and shown as an OnScreen-Preview) -
but in the PDF-Document we will end up with a Portrait-Page - followed by a Landscape-Page with somewhat
different (simpler) content ( just wanted to show how to handle the swapping of Page-Orientations "on the go",
so that's the purpose of the simpler Landscape-Page).
Ok, here's the Demo-Code (which references the vbRichClient5 dependency, you will
have to download and install first, to be able to run the Example... from vbRichClient.com).
SimplePDF.zip
And here's a ScreenShot of the VB-OnScreen-Preview of the first Page - accompanied to the right
with the Preview of the PDF-Viewer (adjusted to show the two contained PDF-Document-Pages).
![]()
Edit: added a missing info to the RichClient-Library, this PDF-Demo depends on.
Olaf
VB-Modules which try to write a PDF-file directly - the PDF-support of the cairo-lib (wrapped conveniently
in vbRichClient5.dll) is "miles ahead" of all these approaches in terms of functionality (the supported
Drawing-Commands) - and reliability (correctness of the generated PDF-Output).
So here's a small VB6-Demo which shows how to use a vbRichClient5.cCairoSurface instance, which
can also be created as "being of cairo-SurfaceType PDF" instead of the usual "ImgSurface-Type" which
is pixelbased and used for OnScreen-Output (BTW, another true VectorSurface-type is e.g. SVG,
which other than PDF can not only be written directly, but also read).
Anyways, in the end we are able to perform the very same Drawing-Commands against a CairoSurface
of type PDF, the same way as we are wont, when we take a CairoSurface of Type "Image" (which then
just renders antialiased against a Pixel-area to bring out the results of the VectorDrawing-Commands).
That transparently usable multi-surface-support of Cairo is one of the greatest advantages of this library IMO.
The demo will print two Pages (only the first one is currently rendered and shown as an OnScreen-Preview) -
but in the PDF-Document we will end up with a Portrait-Page - followed by a Landscape-Page with somewhat
different (simpler) content ( just wanted to show how to handle the swapping of Page-Orientations "on the go",
so that's the purpose of the simpler Landscape-Page).
Ok, here's the Demo-Code (which references the vbRichClient5 dependency, you will
have to download and install first, to be able to run the Example... from vbRichClient.com).
SimplePDF.zip
And here's a ScreenShot of the VB-OnScreen-Preview of the first Page - accompanied to the right
with the Preview of the PDF-Viewer (adjusted to show the two contained PDF-Document-Pages).

Edit: added a missing info to the RichClient-Library, this PDF-Demo depends on.
Olaf
↧
VB6 Regfree-Usage of your own VB- and other COM-Dlls per DirectCOM-Helper
Think, that this topic is an important one - and deserves its own article.
Hmm, how to begin...
Once there was a time,
in the dark ages,
long before "Manifestos" became available to human beings,
that VB-Developers fought all kinds of Dll-Hell...
Erm, all of them? ...No... ;)
So this is about an approach which is an alternative to the Manifest-triggered SxS-Services
we can use today, reliably and succesfully in usage for nearly two decades now (it was working
already on Win95/Win98 - and still works up to the recent Win8.1 or Win10).
It is IMO the more flexible approach - and relative easy to incorporate into your Apps,
by dropping-in a simple *.bas Module which can remain constant, codewise (no Manifests
to keep in sync with your COM-Dlls TypeLib-informations, in case one recompiles these
Dlls without Binary-Compatibility).
Ok, let's come to the point:
(will try to roll this out a bit, in a style which is hopefully more Newbie-friendly to read).
You plan, to ship your own 'MyActiveX.dll' alongside your 'MyApp.exe' -
in addition to 'ForeignActiveX.dll', which you made use of in your App too.
Well, first thing I would suggest when you plan to ship your App regfree is, to do it "orderly" - by
ensuring a certain Folder-Structure (in your Zip, or whatever you will use for packaging in the end).
What I usually go with is a Folder-Structure as:
\MyApp\
.... \Bin\
.... \Res\
.... MyApp.exe
Aside from MyApp.exe (and maybe a MyAppSettings.ini) not much more in the Apps Root-Folder.
This way the user (after unpacking your regfree deployed App into his own Target-Foder)
can immediately see, "where the Startup-Point is" (and what to click).
Well, forgot to congratulate you first, because when you read this article, you already did the mental step from:
"I need to compile everything into a single Executable"
to:
"I'm far more flexible, when I have certain things in my own, dedicated Dll-Binaries"
accompanied hopefully by:
"I'm also more efficient, when I re-use good work of others, avoiding re-inventions of the wheel"
So, what would reside in your relative Subfolder \Bin\ now? All the Dlls of course:
\MyApp\
.... \Bin\
........ MyActiveX.dll
........ ForeignActiveX.dll
.... \Res\
.... MyApp.exe
To make the approach I'm talking about work, what you need in addition is a Helper-Dll, which is a
Standard-Dll that needs no registering: DirectCOM.dll - so, adding it - your Folder-Structure should look then:
\MyApp\
.... \Bin\
........ DirectCOM.dll
........ MyActiveX.dll
........ ForeignActiveX.dll
.... \Res\
.... MyApp.exe
With such a Folder-Structure in place (and the Bin-Folder filled with the right Binaries),
what you need now in your App is a *.bas Module with the following content:
(in my Demo-Zip for this article, I named this Module: modRegfreeDlls.bas
With that module in place, you have now two globally reachable (Public) Functions available:
- GetInstanceFromBinFolder(...)
- GetInstance(...)
The first one is the more conveniently usable one, because it saves you from giving
"Full explicit Paths to your Dll-Binaries", so for ActiveX-Dlls in your own Bin-Folder,
all you need to instantiate a Class from one of them is e.g.:
Note, how the above Form resembles the well-known ProgID-based instancing per CreateObject:
And in fact, both versions accomplish the same thing - both create a new Object-Instance - just that
CreateObject needs a registered version of 'MyActiveX.dll', whilst GetInstanceFromBinFolder does not.
The second available Function from the *.bas Module (GetInstance) is just the explicit form,
which you can give a full absolute path into the FileSystem, to specify a certain Dll ...
otherwise the behaviour (and results) are the same as those from GetInstanceFromBinFolder.
Well, that's it already - a Helper-*.bas Module (in conjunction with a Helper-Dll in your Bin-Folder)
can ensure regfree loading, over a relative easy to use function (GetInstanceFromBinFolder).
You have to make sure though, that you use this Function now consequently throughout
your whole App, when it comes to the instantiation of Classes from your regfree shipped Dlls.
To ensure that, you should scan through your whole App, using a project-wide Code-search for
the String: [New ] (.... leaving out the brackets of course - but include the space-char at the end).
This will stop at all code-lines where you do an Object-Instantiation - change all New MyClass
occurences then into the appropriate GetInstanceFromBinFolder(...) replacements - of course
only for Classes which are defined in those Dlls - one doesn't need to replace the instantiation
of a normal VB-Collection - or an ADO-Recordset, since those are always available directly
(contained in the VB-Runtime or in case of ADO - coming preinstalled on a given System).
I know - if your Project is not a small one, this is quite a boring "Identify-And-Paste"-task
but not that time-consuming as one might think (I never needed more than 3-10 minutes for
those replacements, even in larger projects and with "double-checking").
So, that's it with regards to describing the usage of a regfree-alternative to SxS-Manifests.
The latest version of DirectCOM.dll is contained in the vbRichClient5-BaseDlls package on:
http://vbrichclient.com/#/en/Downloads.htm
What remains is a few words to the following Demo, in case you want to use this as a first
Code-base for your own tests:RegfreeDeployment.zip
I've tried to make it a real-world example, which involves also your own compiled ActiveX-Dll.
So, before starting the Demo Project-File: RegfreeDeploymentOfDlls.vbp ...
You should go one Folder-Deeper into: \TestDllProject\ ...
Start-up the ActiveX-Dll-Project: MyTest.vbp there ...
And compile the appropriate Dll from it into the \Bin\ Folder we already talked about above.
What this Demo shows in addition, is a scenario including some these "ForeignActiveX.dlls" which
were mentioned earlier already (using the 3 Base-Dlls of the vbRichClient-framework as an example -
since one of these Dlls, DirectCOM.dll, is needed anyways for the approach to work).
So, after downloading the vbRC5BaseDlls.zip - make sure you copy all 3 Dlls it contains:
DirectCOM.dll
vbRichClient5.dll
vb_cairo_sqlite.dll
Into the Demos \Bin\ Folder too.
So, what you will need to end up with, before starting the Main-Project: RegfreeDeploymentOfDlls.vbp
is the following FolderStructure (reusing the Schema I've introduced above):
\RegfreeDeployment\
.... \Bin\
........ DirectCOM.dll
........ MyTest.dll
........ vbRichClient5.dll
........ vb_cairo_sqlite.dll
.... \Res\
........ BackGround.jpg
........ SomeSvgIcon.svg
........ SomePngIcon.png
.... RegfreeDeploymentOfDlls.vbp
.... modRegfreeDlls.bas
.... modMain.bas
.... fTest.frm
If that above Folder-Structure is given, then you will succeed in running the Demo, which
then should come up this way:
![]()
Olaf
Hmm, how to begin...
Once there was a time,
in the dark ages,
long before "Manifestos" became available to human beings,
that VB-Developers fought all kinds of Dll-Hell...
Erm, all of them? ...No... ;)
So this is about an approach which is an alternative to the Manifest-triggered SxS-Services
we can use today, reliably and succesfully in usage for nearly two decades now (it was working
already on Win95/Win98 - and still works up to the recent Win8.1 or Win10).
It is IMO the more flexible approach - and relative easy to incorporate into your Apps,
by dropping-in a simple *.bas Module which can remain constant, codewise (no Manifests
to keep in sync with your COM-Dlls TypeLib-informations, in case one recompiles these
Dlls without Binary-Compatibility).
Ok, let's come to the point:
(will try to roll this out a bit, in a style which is hopefully more Newbie-friendly to read).
You plan, to ship your own 'MyActiveX.dll' alongside your 'MyApp.exe' -
in addition to 'ForeignActiveX.dll', which you made use of in your App too.
Well, first thing I would suggest when you plan to ship your App regfree is, to do it "orderly" - by
ensuring a certain Folder-Structure (in your Zip, or whatever you will use for packaging in the end).
What I usually go with is a Folder-Structure as:
\MyApp\
.... \Bin\
.... \Res\
.... MyApp.exe
Aside from MyApp.exe (and maybe a MyAppSettings.ini) not much more in the Apps Root-Folder.
This way the user (after unpacking your regfree deployed App into his own Target-Foder)
can immediately see, "where the Startup-Point is" (and what to click).
Well, forgot to congratulate you first, because when you read this article, you already did the mental step from:
"I need to compile everything into a single Executable"
to:
"I'm far more flexible, when I have certain things in my own, dedicated Dll-Binaries"
accompanied hopefully by:
"I'm also more efficient, when I re-use good work of others, avoiding re-inventions of the wheel"
So, what would reside in your relative Subfolder \Bin\ now? All the Dlls of course:
\MyApp\
.... \Bin\
........ MyActiveX.dll
........ ForeignActiveX.dll
.... \Res\
.... MyApp.exe
To make the approach I'm talking about work, what you need in addition is a Helper-Dll, which is a
Standard-Dll that needs no registering: DirectCOM.dll - so, adding it - your Folder-Structure should look then:
\MyApp\
.... \Bin\
........ DirectCOM.dll
........ MyActiveX.dll
........ ForeignActiveX.dll
.... \Res\
.... MyApp.exe
With such a Folder-Structure in place (and the Bin-Folder filled with the right Binaries),
what you need now in your App is a *.bas Module with the following content:
(in my Demo-Zip for this article, I named this Module: modRegfreeDlls.bas
Code:
'A readymade "PlugIn-Module" you can include into your Projects, to be able to load Classes from COM-Dlls
'without registering them priorily - and just to be clear - there is no "dynamic re-registering" involved -
'DirectCOM.dll will load the appropriate Class-Instances without touching the Systems Registry in any way...
'
'There's 3 Functions exposed from this Module:
'- GetInstanceFromBinFolder ... loads Classes from Dlls located in a SubFolder, relative to your Exe-Path
'- GetInstance ... same as above - but allowing absolute Dll-Paths (anywhere in the FileSystem)
'- GetExePath ... just a small helper, to give the correct Exe-Path, even when called from within Dlls
'
'the approach is *very* reliable (in use for nearly two decades now, it works from Win98 to Windows-10)
'So, happy regfree COM-Dll-loading... :-) (Olaf Schmidt, in Dec. 2014)
Option Explicit
'we need only two exports from the small DirectCOM.dll Helper here
Private Declare Function GetInstanceEx Lib "DirectCOM" (spFName As Long, spClassName As Long, Optional ByVal UseAlteredSearchPath As Boolean = True) As Object
Private Declare Function GETINSTANCELASTERROR Lib "DirectCOM" () As String
Private Declare Function LoadLibraryW& Lib "kernel32" (ByVal lpLibFileName&)
Private Declare Function GetModuleFileNameW& Lib "kernel32" (ByVal hMod&, ByVal lpFileName&, ByVal nSize&)
'a convenience-function which loads Classes from Dlls (residing in a SubFolder below your Exe-Path)
'just adjust the Optional RelBinFolderName-Param to your liking (currently specified as "Bin")
Public Function GetInstanceFromBinFolder(ByVal ShortDllFileName As String, ClassName As String, _
Optional RelBinFolderName$ = "Bin") As Object
Select Case LCase$(Right$(ShortDllFileName, 4))
Case ".dll", ".ocx" 'all fine, nothing to do
Case Else: ShortDllFileName = ShortDllFileName & ".dll" 'expand the ShortFileName about the proper file-ending when it was left out
End Select
Set GetInstanceFromBinFolder = GetInstance(GetExePath & RelBinFolderName & "\" & ShortDllFileName, ClassName)
End Function
'the generic Variant, which needs a full (user-provided), absolute Dll-PathFileName in the first Param
Public Function GetInstance(FullDllPathFileName As String, ClassName As String) As Object
If Len(FullDllPathFileName) = 0 Or Len(ClassName) = 0 Then Err.Raise vbObjectError, , "Empty-Param(s) were passed to GetInstance"
EnsureDirectCOMDllPreLoading FullDllPathFileName 'will raise an Error, when DirectCOM.dll was not found in "relative Folders"
On Error Resume Next
Set GetInstance = GetInstanceEx(StrPtr(FullDllPathFileName), StrPtr(ClassName), True)
If Err Then
On Error GoTo 0: Err.Raise vbObjectError, Err.Source & ".GetInstance", Err.Description
ElseIf GetInstance Is Nothing Then
On Error GoTo 0: Err.Raise vbObjectError, Err.Source & ".GetInstance", GETINSTANCELASTERROR()
End If
End Function
'always returns the Path to the Executable (even when called from within COM-Dlls, which resolve App.Path to their own location)
Public Function GetExePath(Optional ExeName As String) As String
Dim S As String, Pos As Long: Const MaxPath& = 260
Static stExePath As String, stExeName As String
If Len(stExePath) = 0 Then 'resolve it once
S = Space$(MaxPath)
S = Left$(S, GetModuleFileNameW(0, StrPtr(S), Len(S)))
Pos = InStrRev(S, "\")
stExeName = Mid$(S, Pos + 1)
stExePath = Left$(S, Pos) 'preserve the BackSlash at the end
Select Case UCase$(stExeName) 'when we run in the VB-IDE, ...
Case "VB6.EXE", "VB5.EXE": stExePath = App.Path & "\" 'we resolve to the App.Path instead
End Select
End If
ExeName = stExeName
GetExePath = stExePath
End Function
Private Sub EnsureDirectCOMDllPreLoading(FullDllPathFileName As String)
Static hDirCOM As Long
If hDirCOM Then Exit Sub 'nothing to do, DirectCOM.dll was already found and pre-loaded
If hDirCOM = 0 Then hDirCOM = LoadLibraryW(StrPtr(GetExePath & "DirectCOM.dll"))
If hDirCOM = 0 Then hDirCOM = LoadLibraryW(StrPtr(GetExePath & "Bin\DirectCOM.dll"))
If hDirCOM = 0 Then hDirCOM = LoadLibraryW(StrPtr(GetExePath & "RC5Bin\DirectCOM.dll"))
If hDirCOM = 0 Then hDirCOM = LoadLibraryW(StrPtr(App.Path & "\DirectCOM.dll"))
If hDirCOM = 0 Then hDirCOM = LoadLibraryW(StrPtr(Left$(FullDllPathFileName, InStrRev(FullDllPathFileName, "\")) & "DirectCOM.dll"))
If hDirCOM = 0 Then Err.Raise vbObjectError, Err.Source & ".GetInstance", "Couldn't pre-load DirectCOM.dll"
End Sub
- GetInstanceFromBinFolder(...)
- GetInstance(...)
The first one is the more conveniently usable one, because it saves you from giving
"Full explicit Paths to your Dll-Binaries", so for ActiveX-Dlls in your own Bin-Folder,
all you need to instantiate a Class from one of them is e.g.:
Code:
Dim oMyClass
Set oMyClass = GetInstanceFromBinFolder("MyActiveX", "cMyClass")
oMyClass.DoSomething
Code:
Dim oMyClass
Set oMyClass = CreateObject("MyActiveX.cMyClass")
oMyClass.DoSomething
CreateObject needs a registered version of 'MyActiveX.dll', whilst GetInstanceFromBinFolder does not.
The second available Function from the *.bas Module (GetInstance) is just the explicit form,
which you can give a full absolute path into the FileSystem, to specify a certain Dll ...
otherwise the behaviour (and results) are the same as those from GetInstanceFromBinFolder.
Well, that's it already - a Helper-*.bas Module (in conjunction with a Helper-Dll in your Bin-Folder)
can ensure regfree loading, over a relative easy to use function (GetInstanceFromBinFolder).
You have to make sure though, that you use this Function now consequently throughout
your whole App, when it comes to the instantiation of Classes from your regfree shipped Dlls.
To ensure that, you should scan through your whole App, using a project-wide Code-search for
the String: [New ] (.... leaving out the brackets of course - but include the space-char at the end).
This will stop at all code-lines where you do an Object-Instantiation - change all New MyClass
occurences then into the appropriate GetInstanceFromBinFolder(...) replacements - of course
only for Classes which are defined in those Dlls - one doesn't need to replace the instantiation
of a normal VB-Collection - or an ADO-Recordset, since those are always available directly
(contained in the VB-Runtime or in case of ADO - coming preinstalled on a given System).
I know - if your Project is not a small one, this is quite a boring "Identify-And-Paste"-task
but not that time-consuming as one might think (I never needed more than 3-10 minutes for
those replacements, even in larger projects and with "double-checking").
So, that's it with regards to describing the usage of a regfree-alternative to SxS-Manifests.
The latest version of DirectCOM.dll is contained in the vbRichClient5-BaseDlls package on:
http://vbrichclient.com/#/en/Downloads.htm
What remains is a few words to the following Demo, in case you want to use this as a first
Code-base for your own tests:RegfreeDeployment.zip
I've tried to make it a real-world example, which involves also your own compiled ActiveX-Dll.
So, before starting the Demo Project-File: RegfreeDeploymentOfDlls.vbp ...
You should go one Folder-Deeper into: \TestDllProject\ ...
Start-up the ActiveX-Dll-Project: MyTest.vbp there ...
And compile the appropriate Dll from it into the \Bin\ Folder we already talked about above.
What this Demo shows in addition, is a scenario including some these "ForeignActiveX.dlls" which
were mentioned earlier already (using the 3 Base-Dlls of the vbRichClient-framework as an example -
since one of these Dlls, DirectCOM.dll, is needed anyways for the approach to work).
So, after downloading the vbRC5BaseDlls.zip - make sure you copy all 3 Dlls it contains:
DirectCOM.dll
vbRichClient5.dll
vb_cairo_sqlite.dll
Into the Demos \Bin\ Folder too.
So, what you will need to end up with, before starting the Main-Project: RegfreeDeploymentOfDlls.vbp
is the following FolderStructure (reusing the Schema I've introduced above):
\RegfreeDeployment\
.... \Bin\
........ DirectCOM.dll
........ MyTest.dll
........ vbRichClient5.dll
........ vb_cairo_sqlite.dll
.... \Res\
........ BackGround.jpg
........ SomeSvgIcon.svg
........ SomePngIcon.png
.... RegfreeDeploymentOfDlls.vbp
.... modRegfreeDlls.bas
.... modMain.bas
.... fTest.frm
If that above Folder-Structure is given, then you will succeed in running the Demo, which
then should come up this way:

Olaf
↧
↧
WinpkFilter VB6 Samples
The WinpkFilter development kit no longer includes VB6 samples. Version 3.2.3 has added support for IPv6, resulting in a number of changes to the data formats. I have taken the liberty to update the older samples to support the newer formats, and with permission, I am posting them here.
Unfortunately I still have not got the low level filter table to load, but I will post the results upon completion.
J.A. Coutts
Unfortunately I still have not got the low level filter table to load, but I will post the results upon completion.
J.A. Coutts
↧
VB6 Handling of Alpha-Layered Windows with vbRichClient5
Since it was asked in another thread, if there is an easy way to deal with Layered-Windows
in conjunction with the cairo-Drawing of the RC5....
Yes, of course there is - not provided by cairo itself, but by the new Form-Engine (Classes of type cWidgetForm).
If the task is, to use a Layered-Window in conjunction with a VB-Form (on top of a hosted Control on that
VB-Form, which could be e.g. a Video-Control which is playing a Stream or something), then one would
have to provide a synchronizing between the current VBForm- or VB-Control-Position - and the Position of the
Layered Window (the System doesn't support Child-Windows for layering - they currently need to be TopLevel ones).
In this Demo (to avoid SubClassing - thus making it a bit more robust) I've used a Timer, to keep the layered
Window following the desired position on top of the VB-Form (which acts as the "leading Mother-Goose").
Ok, so the following code-snippet shows already, what we need exactly (within a normal VB-Form):
- fOverlay (of type cWidgetForm)
- wOverlay (a small User-Widget, which renders the Png-Images (and a small Animation)
- and the timer for the Position-Syncing
At the top of the above Form-Load procedure, we loaded and stored two Png-Images in the global Imagelist.
These are the two "Bubbles", you see at the left and right ends in the following ScreenShot:
![]()
And aside from the Reposition-Code...:
There's not much more in our hosting VB-Form.
That we deal with a real Top-Level-Window here, becomes more apparent when you look at this Shot:
![]()
Ok, so the RC5-cWidgetForm-Classes (when hosting a TopLevel-Window) can handle Layering without
any problem - the philosophy there being, that "anything you don't draw is truly invisible".
So, that something *became* visible was ensured by the small cwOverlay-Widget (less than 30 lines of code):
The above Widget is itself Parent of an additional Widget (the one which ensures the "Sinuid Animation").
But I will leave out the 40 lines of Code for that Widget here - just take a look at the Zip, which contains
the complete Demo: AlphaLayering.zip
Happy layering... :)
Olaf
in conjunction with the cairo-Drawing of the RC5....
Yes, of course there is - not provided by cairo itself, but by the new Form-Engine (Classes of type cWidgetForm).
If the task is, to use a Layered-Window in conjunction with a VB-Form (on top of a hosted Control on that
VB-Form, which could be e.g. a Video-Control which is playing a Stream or something), then one would
have to provide a synchronizing between the current VBForm- or VB-Control-Position - and the Position of the
Layered Window (the System doesn't support Child-Windows for layering - they currently need to be TopLevel ones).
In this Demo (to avoid SubClassing - thus making it a bit more robust) I've used a Timer, to keep the layered
Window following the desired position on top of the VB-Form (which acts as the "leading Mother-Goose").
Ok, so the following code-snippet shows already, what we need exactly (within a normal VB-Form):
- fOverlay (of type cWidgetForm)
- wOverlay (a small User-Widget, which renders the Png-Images (and a small Animation)
- and the timer for the Position-Syncing
Code:
Private WithEvents fOverlay As cWidgetForm, wOverlay As cwOverlay, WithEvents tmrPosSync As cTimer
Private Sub Form_Load()
Cairo.ImageList.AddImage "P1", App.Path & "\P1.png"
Cairo.ImageList.AddImage "P2", App.Path & "\P2.png"
Set fOverlay = Cairo.WidgetForms.CreateChild(Me.hWnd, True, False)
Set wOverlay = fOverlay.Widgets.Add(New cwOverlay, "Overlay")
wOverlay.Init "P1", "P2"
Set tmrPosSync = New_c.Timer(20, True)
End Sub
These are the two "Bubbles", you see at the left and right ends in the following ScreenShot:

And aside from the Reposition-Code...:
Code:
Private Sub Reposition(Optional ByVal IsResize As Boolean)
Dim x&: x = Left / Screen.TwipsPerPixelX - 56
Dim y&: y = (Top + Height / 2) / Screen.TwipsPerPixelY - 56
fOverlay.Move x, y, Width / Screen.TwipsPerPixelX + wOverlay.ImgWidth - 7, wOverlay.ImgHeight
With wOverlay.Widget
If Not IsResize And GetActiveWindow = hWnd And .Alpha < 1 Then .Alpha = .Alpha + 0.03: .Refresh
If Not IsResize And GetActiveWindow <> hWnd And .Alpha > 0 Then .Alpha = .Alpha - 0.08: .Refresh
End With
End Sub
That we deal with a real Top-Level-Window here, becomes more apparent when you look at this Shot:

Ok, so the RC5-cWidgetForm-Classes (when hosting a TopLevel-Window) can handle Layering without
any problem - the philosophy there being, that "anything you don't draw is truly invisible".
So, that something *became* visible was ensured by the small cwOverlay-Widget (less than 30 lines of code):
Code:
Option Explicit
Public ImgWidth As Long, ImgHeight As Long, wAnim As cwAnimation
Private mImgKey1 As String, mImgKey2 As String
'****---- Start of cwImplementation-Conventions ----****
Private WithEvents W As cWidgetBase
Private Sub Class_Initialize()
Set W = Cairo.WidgetBase '<- this is required in each cwImplementation...
Set wAnim = Widgets.Add(New cwAnimation, "Anim")
End Sub
Public Property Get Widget() As cWidgetBase: Set Widget = W: End Property
Public Property Get Widgets() As cWidgets: Set Widgets = W.Widgets: End Property
'****---- End of cwImplementation-Conventions ----****
Public Sub Init(ImgKey1, ImgKey2)
mImgKey1 = ImgKey1: ImgWidth = Cairo.ImageList(mImgKey1).Width
mImgKey2 = ImgKey2: ImgHeight = Cairo.ImageList(mImgKey2).Height
End Sub
Private Sub W_ContainerResize()
W.Alpha = 0
W.Move 0, 0, W.Parent.Width, W.Parent.Height
wAnim.Widget.Move 0.8 * ImgWidth, 0, W.Width - 1.77 * ImgWidth, W.Height * 0.9
End Sub
Private Sub W_Paint(CC As cCairoContext, ByVal xAbs As Single, ByVal yAbs As Single, ByVal dx_Aligned As Single, ByVal dy_Aligned As Single, UserObj As Object)
CC.RenderSurfaceContent mImgKey1, 0, 0, , , , W.Alpha
CC.RenderSurfaceContent mImgKey2, dx_Aligned - ImgWidth, 0, , , , W.Alpha
End Sub
But I will leave out the 40 lines of Code for that Widget here - just take a look at the Zip, which contains
the complete Demo: AlphaLayering.zip
Happy layering... :)
Olaf
↧
Simple "Clone Stamp Tool" Demo
Put 2 PictureBoxes on your form. Make sure they both have these properties:
Appearance = Flat
AutoRedraw = True
Border Style = None
Picture = a picture that is small enough to fit in a picture box that is relatively small (320x240 is good for this demo)
ScaleMode = Pixel
Set these properties on your form:
ScaleMode = Pixel
Put a command button on your form.
Put all this code in your form:
Create a class, and call it ImgObj. Then put this code in that class:
Appearance = Flat
AutoRedraw = True
Border Style = None
Picture = a picture that is small enough to fit in a picture box that is relatively small (320x240 is good for this demo)
ScaleMode = Pixel
Set these properties on your form:
ScaleMode = Pixel
Put a command button on your form.
Put all this code in your form:
Code:
Dim a As ImgObj
Dim StartX As Long
Dim StartY As Long
Private Sub Command1_Click()
Set a = Nothing
End Sub
Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button <> 1 Then Exit Sub
If (a Is Nothing) = False Then Exit Sub
Set a = New ImgObj
StartX = X
StartY = Y
End Sub
Private Sub Picture1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
Dim X0 As Long
Dim X1 As Long
Dim Y0 As Long
Dim Y1 As Long
If Button <> 1 Then Exit Sub
If a Is Nothing Then Exit Sub
If a.ImageInited Then Exit Sub
If X > StartX Then
X0 = StartX
X1 = X
Else
X0 = X
X1 = StartX
End If
If Y > StartY Then
Y0 = StartY
Y1 = Y
Else
Y0 = Y
Y1 = StartY
End If
If a.InitImage(X1 - X0 + 1, Y1 - Y0 + 1) = False Then
Set a = Nothing
Exit Sub
End If
a.BltFromDC Picture1.hDC, X0, Y0
End Sub
Private Sub Picture2_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button <> 1 Then Exit Sub
If a Is Nothing Then Exit Sub
If a.ImageInited = False Then Exit Sub
a.BltToDC Picture2.hDC, X, Y
Picture2.Refresh
End Sub
Create a class, and call it ImgObj. Then put this code in that class:
Code:
Private Declare Function GetDC Lib "user32.dll" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32.dll" (ByVal hwnd As Long, ByVal hDC As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32.dll" (ByVal hDC As Long) As Long
Private Declare Function DeleteDC Lib "gdi32.dll" (ByVal hDC As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hDC As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hDC As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
Private Declare Function BitBlt Lib "gdi32.dll" (ByVal hDestDC As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Dim ImageHandle As Long
Dim DCHandle As Long
Dim OldImageHandle As Long
Dim ImgInited As Boolean
Dim PicW As Long
Dim PicH As Long
Public Event GetImageParams(ByRef Width As Long, ByRef Height As Long)
Public Property Get hImage()
hImage = ImageHandle
End Property
Public Property Get hDC()
hDC = DCHandle
End Property
Public Property Get Width()
Width = PicW
End Property
Public Property Get Height()
Height = PicH
End Property
Public Property Get ImageInited() As Boolean
ImageInited = ImgInited
End Property
Private Sub Class_Initialize()
Dim tempDC As Long
tempDC = GetDC(0)
DCHandle = CreateCompatibleDC(tempDC)
ReleaseDC 0, tempDC
End Sub
Public Function InitImage(ByVal Width As Long, ByVal Height As Long) As Boolean
Dim tempDC As Long
If (PicW > 0) Or (PicH > 0) Then Exit Function
If Width <= 0 Then Exit Function
If Height <= 0 Then Exit Function
PicW = Width
PicH = Height
tempDC = GetDC(0)
ImageHandle = CreateCompatibleBitmap(tempDC, PicW, PicH)
ReleaseDC 0, tempDC
OldImageHandle = SelectObject(DCHandle, ImageHandle)
InitImage = True
ImgInited = True
End Function
Private Sub Class_Terminate()
SelectObject DCHandle, OldImageHandle
DeleteObject ImageHandle
DeleteObject OldImageHandle
DeleteDC DCHandle
End Sub
Public Sub BltToDC(ByVal DestDC As Long, Optional ByVal X As Long, Optional ByVal Y As Long)
BitBlt DestDC, X, Y, PicW, PicH, DCHandle, 0, 0, vbSrcCopy
End Sub
Public Sub BltFromDC(ByVal SrcDC As Long, Optional ByVal X As Long, Optional ByVal Y As Long)
BitBlt DCHandle, 0, 0, PicW, PicH, SrcDC, X, Y, vbSrcCopy
End Sub
↧
[VB6] Faster RTB Code Formatting
The scenario here is a program that allows user scripting in VBScript. This program needs a script IDE to make it easier for users to write, test, and modify their scripts. Instead of a simple text editor, we want to format entered VBScript code in a manner similar to the way VB6.EXE does it. This means more than simple "colorization" but also includes correction of whitespace and punctuation as well as re-formatting statements as they are manually altered.
Performance is a factor because in the real code far more is going on and it occurs during user editing as well as upon loading.
Demo
Stripped down implementation for illustration. Only about 5% of the real thing is included here.
Does not take into account predefined constants, comments, line continuations, or string literals. Does not attempt to correct whitespace and punctuation usage. Also does not include the logic to handle statement by statement user edits.
Merely does some one-pass simplified colorizing of the entire script after loading. Just enough to look interesting.
This is done using four different methods in four different Forms varying only as needed to add optimizations in steps:
The four Forms are MDI child Forms within a parent Form. They are run in sequence and timings are displayed for each child Form upon completion. They all use the same large sample script (testcase.txt).
![Name: sshot-sm.png
Views: 87
Size: 16.4 KB]()
![Name: sshot-timings.png
Views: 61
Size: 2.0 KB]()
Requirements
To use TOM with the VB6 RichTextBox, Windows 2000 or later is required. This causes RichTextBox to use Riched20.dll in Microsoft Rich Edit 1.0 emulation mode, thus giving us the TOM interfaces to use. TOM is not supported by Riched32.dll.
Links
BUG: Cannot Set RichTextBox Visible Property to False
Text Object Model
Performance is a factor because in the real code far more is going on and it occurs during user editing as well as upon loading.
Demo
Stripped down implementation for illustration. Only about 5% of the real thing is included here.
Does not take into account predefined constants, comments, line continuations, or string literals. Does not attempt to correct whitespace and punctuation usage. Also does not include the logic to handle statement by statement user edits.
Merely does some one-pass simplified colorizing of the entire script after loading. Just enough to look interesting.
This is done using four different methods in four different Forms varying only as needed to add optimizations in steps:
- RTB.Visible = True (default).
- Let RTB.Visble = False at start of formatting, then True again when done. Flawed, see MS KB 189483.
- Let RTB.Visible = False at start and make ShowWindow calls per KB article.
- Use TOM with Freeze and ITextRange objects.
The four Forms are MDI child Forms within a parent Form. They are run in sequence and timings are displayed for each child Form upon completion. They all use the same large sample script (testcase.txt).
Requirements
To use TOM with the VB6 RichTextBox, Windows 2000 or later is required. This causes RichTextBox to use Riched20.dll in Microsoft Rich Edit 1.0 emulation mode, thus giving us the TOM interfaces to use. TOM is not supported by Riched32.dll.
Links
BUG: Cannot Set RichTextBox Visible Property to False
Text Object Model
↧
↧
VB6 how to capture Sound, using CoreAudio (Vista and onwards)
Here's a simple Demo, which is using the CoreAudio-Interface wrapper-classes of the vbRichClient5-framework,
to select a Capture-Device - followed by entering "Stream-Capture-Mode".
Whilst the Stream_Data is rolling in continously - we use an Event of the Class cAudioCaptureClient,
to retrieve Peak- and FFT-Data only... (no MP3-conversion of the SoundData, nor any FileWriting is done,
only visualizing takes place)
Here's what's needed in a normal VB-Form:
In Form_Load above, the line:
... MMDeviceEnumerator.GetDefaultAudioEndpoint(eCapture, eCommunications)
will usually resolve to the Microphone-Device which is built into your NoteBook or Tablet
(or connected over Mike-In).
There's different combinations of the Enums (the first one responsible for the direction, the second for the "Role") as e.g.:
- (eCapture, eMultimedia) ... will typically resolve to a device on your LineIn-port, if connected
- (eRender, eConsole) typically resolves to the Default-SoundOutput (Speakers or Phones)
- (eRender, eMultimedia) often resolving to (digital) 5.1 Output-Ports e.g. when watching Video over HDMI
The Roles are (since Win7) supported in a way, that the User can define different devices for
those different "Role-Tasks".
However, it's also possible to enumerate Devices, to select one later - e.g. using this code:
I got the following output here on my Machine:
The interfaces are (in comparison to the old Mixer/WaveIn/WaveOut-APIs) quite clear -
but nevertheless would require some learning on your part, here's the MSDN-link to the
CoreAudio-Interface-List in all its glory - you will find the most important ones in the RC5,
prefixed with a small 'c' instead of an 'I': http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx
Here's the complete Demo, which visualizes Peak- as well as "FFTed"-Spectrum-Values (containing,
in addition to the Form-Code above only one additional small Drawing-Class, cDrawPeakAndFFT...
AudioCapture.zip
Here's a screenshot, what the Demo will put out with the help of the Drawing-Class:
![]()
Olaf
to select a Capture-Device - followed by entering "Stream-Capture-Mode".
Whilst the Stream_Data is rolling in continously - we use an Event of the Class cAudioCaptureClient,
to retrieve Peak- and FFT-Data only... (no MP3-conversion of the SoundData, nor any FileWriting is done,
only visualizing takes place)
Here's what's needed in a normal VB-Form:
Code:
Option Explicit
Private CapDev As cMMDevice, WithEvents ACC As cAudioCaptureClient, PeakAndFFT As New cDrawPeakAndFFT
Private Sub Form_Load()
ScaleMode = vbPixels
Set CapDev = New_c.MMDeviceEnumerator.GetDefaultAudioEndpoint(eCapture, eCommunications)
Caption = CapDev.OpenPropertyStore.GetDevice_FriendlyName & " (" & CapDev.GetStateName & ")"
Set ACC = New_c.AudioCaptureClient
ACC.InitOn CapDev, 44100
ACC.RaiseMeterEvents = True
ACC.RaiseFFTEvents = True
ACC.FFTInputLevelFac = 0.8
ACC.StartStream
End Sub
Private Sub ACC_MeterInfo(ByVal LeftPeak As Single, ByVal RightPeak As Single, LeftFFT() As Single, RightFFT() As Single)
'let's give the Peak-Values a logarithmic scaling (also between 0 and 1), before passing them on...
LeftPeak = Log(1.00000000000001 + LeftPeak * 9) / 2.303
RightPeak = Log(1.00000000000001 + RightPeak * 9) / 2.303
PeakAndFFT.Draw LeftPeak, RightPeak, LeftFFT, RightFFT
PeakAndFFT.Srf.DrawToDC hDC, 0, 0, ScaleWidth, ScaleHeight
End Sub
Private Sub Form_Unload(Cancel As Integer)
If Not ACC Is Nothing Then ACC.StopStream: Set ACC = Nothing
Set CapDev = Nothing
Set PeakAndFFT = Nothing
End Sub
Private Sub Form_Terminate()
If Forms.Count = 0 Then New_c.CleanupRichClientDll
End Sub
... MMDeviceEnumerator.GetDefaultAudioEndpoint(eCapture, eCommunications)
will usually resolve to the Microphone-Device which is built into your NoteBook or Tablet
(or connected over Mike-In).
There's different combinations of the Enums (the first one responsible for the direction, the second for the "Role") as e.g.:
- (eCapture, eMultimedia) ... will typically resolve to a device on your LineIn-port, if connected
- (eRender, eConsole) typically resolves to the Default-SoundOutput (Speakers or Phones)
- (eRender, eMultimedia) often resolving to (digital) 5.1 Output-Ports e.g. when watching Video over HDMI
The Roles are (since Win7) supported in a way, that the User can define different devices for
those different "Role-Tasks".
However, it's also possible to enumerate Devices, to select one later - e.g. using this code:
Code:
Dim i As Long, Dev As cMMDevice
Debug.Print "Capture-Devices"
With New_c.MMDeviceEnumerator.EnumAudioEndpoints(eCapture, DEVICE_STATEMASK_ALL)
For i = 0 To .GetCount - 1
Set Dev = .Item(i)
Debug.Print , Dev.GetStateName, Dev.OpenPropertyStore.GetDevice_FriendlyName
Next
End With
Debug.Print vbLf; "Render-Devices"
With New_c.MMDeviceEnumerator.EnumAudioEndpoints(eRender, DEVICE_STATEMASK_ALL)
For i = 0 To .GetCount - 1
Set Dev = .Item(i)
Debug.Print , Dev.GetStateName, Dev.OpenPropertyStore.GetDevice_FriendlyName
Next
End With
Code:
Capture-Devices
ACTIVE Mikrofon (High Definition Audio-Gerät)
NOTPRESENT CD-Audio (2- High Definition Audio-Gerät)
NOTPRESENT Mikrofon (Sennheiser USB Headset)
NOTPRESENT Mikrofon (2- High Definition Audio-Gerät)
NOTPRESENT Mikrofon (Sennheiser USB Headset)
Render-Devices
NOTPRESENT Lautsprecher (Sennheiser USB Headset)
ACTIVE Lautsprecher (High Definition Audio-Gerät)
NOTPRESENT Kopfhörer (2- High Definition Audio-Gerät)
NOTPRESENT Lautsprecher (2- High Definition Audio-Gerät)
NOTPRESENT Lautsprecher (2- Logitech USB Speaker)
but nevertheless would require some learning on your part, here's the MSDN-link to the
CoreAudio-Interface-List in all its glory - you will find the most important ones in the RC5,
prefixed with a small 'c' instead of an 'I': http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx
Here's the complete Demo, which visualizes Peak- as well as "FFTed"-Spectrum-Values (containing,
in addition to the Form-Code above only one additional small Drawing-Class, cDrawPeakAndFFT...
AudioCapture.zip
Here's a screenshot, what the Demo will put out with the help of the Drawing-Class:

Olaf
↧
VB6 a simple wrapper for the VBA.Collection with (much) faster Indexed-Access
The title says it all - this is a simple Collection-Wrapper which can act as a compatible
Replacement for the VBA.Collection - the fast Hash-Access of the VBA.Collection is used further -
but all the disadvantages with indexed access are avoided for the most typical use-cases.
At this occasion there were also some other enhancements made as:
- an Exists Method to check for Keys
- a RemoveAll-method
- can hand out Value- and Key-Arrays in appropriate Variant- or String-Arrays (with userdefinable LBound)
- in addition to the compatible Item-Method we have ItemByIndex and KeyByIndex too
- The unnerving behaviour to not allow Add(..., BeforeIndex) with an Index at constantly 1 (in case of Count=0) is gone
It should be also possible, to implement the Item-property also in Write-Direction (with Property Let/Set),
but I leave that for interested colleagues to explore - currently the internally used Collection-Instance-Types,
which were pointed out to me in this thread for the first time (structs posted by jbarnett74 - then refined with a few more
explanations by Bonnie West) - these Structures are currently only used within this wrapper in "safe-read-only-fashion".
Those who try themselves at implementing Write-Access for the Values over the Item-Property,
should test this accordingly (back and forward), because these Structs contain quite a few
"still unknown members", which partly are used for the Hashing-functionality of the VB-Collection,
but some of them could also store additional information, which is related to the Value-member
of the Element-Struct - so changing the Value-Member is alluringly simple - since it seems easily
accessible also for the Write-Direction - but one doesn't know yet, if changing its Value requires
also changes in a few so far unknown members (which the Add-Method of the VB-Collection might
automatically ensure under the covers).
So, as the implementation comes currently - it is "playing things safe" - no real risk in using it, especially
when you compile it into a dedicated ActiveX-Dll-Binary, to give the Class more IDE-safety with regards
to cleanup, even when the Stop-Button was pressed in the IDE (it needs to run over its Class_Terminate-
Event to clear things up properly).
Ok, so here is the Implementation- and Demo-Code (with a small performance-test):
CollectionEx.zip
And here the appropriate ScreenShot:
![]()
An additional advice for those who plan to compile it into an ActiveX-Dll-Binary,
all extended native Compiler-Options are allowed *aside* from the "Aliasing-Option"
(which should remain unchecked in this case of SafeArray-Pointer-usage).
Olaf
Replacement for the VBA.Collection - the fast Hash-Access of the VBA.Collection is used further -
but all the disadvantages with indexed access are avoided for the most typical use-cases.
At this occasion there were also some other enhancements made as:
- an Exists Method to check for Keys
- a RemoveAll-method
- can hand out Value- and Key-Arrays in appropriate Variant- or String-Arrays (with userdefinable LBound)
- in addition to the compatible Item-Method we have ItemByIndex and KeyByIndex too
- The unnerving behaviour to not allow Add(..., BeforeIndex) with an Index at constantly 1 (in case of Count=0) is gone
It should be also possible, to implement the Item-property also in Write-Direction (with Property Let/Set),
but I leave that for interested colleagues to explore - currently the internally used Collection-Instance-Types,
which were pointed out to me in this thread for the first time (structs posted by jbarnett74 - then refined with a few more
explanations by Bonnie West) - these Structures are currently only used within this wrapper in "safe-read-only-fashion".
Those who try themselves at implementing Write-Access for the Values over the Item-Property,
should test this accordingly (back and forward), because these Structs contain quite a few
"still unknown members", which partly are used for the Hashing-functionality of the VB-Collection,
but some of them could also store additional information, which is related to the Value-member
of the Element-Struct - so changing the Value-Member is alluringly simple - since it seems easily
accessible also for the Write-Direction - but one doesn't know yet, if changing its Value requires
also changes in a few so far unknown members (which the Add-Method of the VB-Collection might
automatically ensure under the covers).
So, as the implementation comes currently - it is "playing things safe" - no real risk in using it, especially
when you compile it into a dedicated ActiveX-Dll-Binary, to give the Class more IDE-safety with regards
to cleanup, even when the Stop-Button was pressed in the IDE (it needs to run over its Class_Terminate-
Event to clear things up properly).
Ok, so here is the Implementation- and Demo-Code (with a small performance-test):
CollectionEx.zip
And here the appropriate ScreenShot:

An additional advice for those who plan to compile it into an ActiveX-Dll-Binary,
all extended native Compiler-Options are allowed *aside* from the "Aliasing-Option"
(which should remain unchecked in this case of SafeArray-Pointer-usage).
Olaf
↧
Tooltips, Balloon Tips, Tray Notofications [vbRichClient]
Here's a dll I've created that provides visually nicer and enhanced (functionality-wise) tooltips. It is dependent on Olaf's vbRichClient (the nice drawing comes via Cairo functionality) but is intended for use with regular VB6 projects, as illustrated in the demo.
Features:
- Regular tooltips (can be mult-line so that's an improvement right there!)
- Balloon Tips (Caps Lock is on kind of information)
- Tray Notifications (the pop-up kind)
- Custom Content (display anything you like; pictures, drawings, text, etc)
All the features above are illustrated in the demo. Here's a few images:
![Name: custom.JPG
Views: 78
Size: 53.0 KB]()
![Name: balloons.JPG
Views: 75
Size: 26.4 KB]()
![Name: tray.JPG
Views: 66
Size: 10.2 KB]()
The demo is uploaded as a Project Group; just compile the dll for use in your own projects.
Attachment 121873
Features:
- Regular tooltips (can be mult-line so that's an improvement right there!)
- Balloon Tips (Caps Lock is on kind of information)
- Tray Notifications (the pop-up kind)
- Custom Content (display anything you like; pictures, drawings, text, etc)
All the features above are illustrated in the demo. Here's a few images:
The demo is uploaded as a Project Group; just compile the dll for use in your own projects.
Attachment 121873
↧
VB6 - Packet Ananlyzer
PacketVB Version 2 is a Network Packet Analyzer. It utilizes Windows Packet Filter Kit 3.2.3 from NT Kernel Resources, which is available for download free of charge for personal use. This version of the Filter Kit supports IPv6, and loads on all supported versions of Windows (including 64 bit). After upgrading Version 1 to work with this latest version of "NDISRD.sys", I discovered that it did not work properly on Win 8.1. The data view window requires a fixed width font, for which I chose the Terminal Font due to it's limited width. This font exists on Win 8.1 (dosapp.fon), but is not enabled by default. The font that Windows chose to use in it's place exceeded the width allowed for in the data view window.
So I chose to use the Courier New Font instead. This caused a major rewrite of the program because I had to change all the allowances for width.
PacketVB also uses COMDLG32.OCX, MSFLXGRD.OCX, and mscomctl.ocx, which the user must have available.
PacketVB supports IPv6, but as usual it has undergone very limited testing for this particular protocol because of the lack of a native IPv6 network other than local. If someone has access to such a Wide Area Network, I would appreciate feedback.
J.A. Coutts
So I chose to use the Courier New Font instead. This caused a major rewrite of the program because I had to change all the allowances for width.
PacketVB also uses COMDLG32.OCX, MSFLXGRD.OCX, and mscomctl.ocx, which the user must have available.
PacketVB supports IPv6, but as usual it has undergone very limited testing for this particular protocol because of the lack of a native IPv6 network other than local. If someone has access to such a Wide Area Network, I would appreciate feedback.
J.A. Coutts
↧
↧
VB6 create DDL-Schema-instructions from JET-MDBs (to apply to a new created *.mdb)
As the title says - the Demo will show, how to create DDL-SQL-instructions from a given *.mdb-File,
using two small Classes (cJETDDL and cJETDDLTable).
The DDL is created for existing:
- Table-Definitions
- Index-Definitions
- Foreign-Key relations
- Views and Queries
In the example I'm using (also coming included in the Demo-Zip) the "official JET-Demo-DB" (NWind.mdb).
There's a little GUI around the functionality of the two above mentioned Classes, which shows
the Text-Output they did produce for the different kinds of DDL-Statements.
Here's a ScreenShot:
![]()
And here is the Demo-zip.
I think the whole thing is educational for those who want to learn DDL, to define - or alter
their DB-Schemas "per Code".
Olaf
using two small Classes (cJETDDL and cJETDDLTable).
The DDL is created for existing:
- Table-Definitions
- Index-Definitions
- Foreign-Key relations
- Views and Queries
In the example I'm using (also coming included in the Demo-Zip) the "official JET-Demo-DB" (NWind.mdb).
There's a little GUI around the functionality of the two above mentioned Classes, which shows
the Text-Output they did produce for the different kinds of DDL-Statements.
Here's a ScreenShot:

And here is the Demo-zip.
I think the whole thing is educational for those who want to learn DDL, to define - or alter
their DB-Schemas "per Code".
Olaf
↧
VB6 - Math Utility: Clipping polygons including degenerate cases
This is an implementation of Kim & Kim's extension of the Greiner-Hormann's polygon clipping algorithm to include degenerate cases.
Degenerate cases occur e.g. when a polygon's vertex lies on an edge or vertex of the other polygon.
The method works based on linked lists -implemented in the vertex, contour and polygon classes- and follows the guidelines given in this paper (used to be available in the web and I have a pdf copy):
Dae Hyun Kim and Myoung-Jun Kim
"An extension of polygon clipping to resolve degenerate cases"
Computer-Aided Design and Applications, Vol. 3, pp 447–456 (2006)
The 2 polygons are referred to as the subject and the clipping polygon. Either one can have more than one branch (contour). Contours can be nested and the points constituting them may be given in any orientation, clockwise or counterclockwise.
However, the contours must be not self intersecting.
In this demo, the user can input new polygons or load a number of convenient demo cases provided.
Degenerate cases occur e.g. when a polygon's vertex lies on an edge or vertex of the other polygon.
The method works based on linked lists -implemented in the vertex, contour and polygon classes- and follows the guidelines given in this paper (used to be available in the web and I have a pdf copy):
Dae Hyun Kim and Myoung-Jun Kim
"An extension of polygon clipping to resolve degenerate cases"
Computer-Aided Design and Applications, Vol. 3, pp 447–456 (2006)
The 2 polygons are referred to as the subject and the clipping polygon. Either one can have more than one branch (contour). Contours can be nested and the points constituting them may be given in any orientation, clockwise or counterclockwise.
However, the contours must be not self intersecting.
In this demo, the user can input new polygons or load a number of convenient demo cases provided.
↧
Using Line Input for Unicode (UTF-8 included), the fast way
For some days ago I was involved in the word count routines. I realize that a speed contest for some extra ms was not what we call productive. Productive can be a searching utility for those words. So I decide to make a search utility.
For that task I make some functions. One set of functions used for loading the text. Olaf’s Schmidt routine ReadUnicodeOrANSI was perfect to read any kind of text (as that text follows some rules). But I want the old fashion Line Input to fill my document class. So I make a combination. I use line input, for ANSI and Unicode LE16, BE16, UTF8, with my own buffer. I realize that using LOF or SEEK in each reading cost a huge delay. For using BINARY files VB didn’t give buffers, and that is right if you have to read and write in one session. But here we use binary file for read only, so we need a buffer. But this buffer maybe not as those from vb. We can use buffers with more length, here I use 327680 bytes for buffer.
For ANSI reading we need to read one byte from buffer. For LE16 or BE16 we need to read one word (2 bytes). For UTF8 is a little complicated, but we can found the end of line without parse the code that define if a char has one or two or more bytes length. Exactly we use a small parser that read bit 8 (&h80). If that is clear then this is a one byte char. Any other byte from any multi byte char has this bit set to 1. Because we have to get bytes and translate them to LE16, we place any char to a second buffer, and then we do the translation.
The second set of functions is for INSTR and INSTRREV with any LCID (locale ID). Because we want to use VBtextcompare with any LCID, we have to make our own routines (For OS better from Windows XP there are API for that but we can easy make what we want…as you can see in the source)
You need the bible13.txt, (it is in ANSI fromat but you can open it and save again from notepad, using any unicode fromat)
Info for other wordcount routines here
![Name: find.jpg
Views: 57
Size: 41.3 KB]()
In the text box we see a preview of a maximum of 9 lines (we don't feed textbox with all text)
A docpara = 0 needed if we want to place new document in the doc object
So change this Sub in Document object
Sub EmptyDoc()
delDocPara = 0
docpara = 0
docmax = 20 '
ReDim para(1 To docmax)
ReDim DocParaBack(0 To docmax)
ReDim DocParaNext(0 To docmax)
End Sub
For that task I make some functions. One set of functions used for loading the text. Olaf’s Schmidt routine ReadUnicodeOrANSI was perfect to read any kind of text (as that text follows some rules). But I want the old fashion Line Input to fill my document class. So I make a combination. I use line input, for ANSI and Unicode LE16, BE16, UTF8, with my own buffer. I realize that using LOF or SEEK in each reading cost a huge delay. For using BINARY files VB didn’t give buffers, and that is right if you have to read and write in one session. But here we use binary file for read only, so we need a buffer. But this buffer maybe not as those from vb. We can use buffers with more length, here I use 327680 bytes for buffer.
For ANSI reading we need to read one byte from buffer. For LE16 or BE16 we need to read one word (2 bytes). For UTF8 is a little complicated, but we can found the end of line without parse the code that define if a char has one or two or more bytes length. Exactly we use a small parser that read bit 8 (&h80). If that is clear then this is a one byte char. Any other byte from any multi byte char has this bit set to 1. Because we have to get bytes and translate them to LE16, we place any char to a second buffer, and then we do the translation.
The second set of functions is for INSTR and INSTRREV with any LCID (locale ID). Because we want to use VBtextcompare with any LCID, we have to make our own routines (For OS better from Windows XP there are API for that but we can easy make what we want…as you can see in the source)
You need the bible13.txt, (it is in ANSI fromat but you can open it and save again from notepad, using any unicode fromat)
Info for other wordcount routines here
In the text box we see a preview of a maximum of 9 lines (we don't feed textbox with all text)
A docpara = 0 needed if we want to place new document in the doc object
So change this Sub in Document object
Quote:
Sub EmptyDoc()
delDocPara = 0
docpara = 0
docmax = 20 '
ReDim para(1 To docmax)
ReDim DocParaBack(0 To docmax)
ReDim DocParaNext(0 To docmax)
End Sub
↧