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

VB6 - Scan file (text format) in Server and Print it to Printer Directly

0
0
Dear All,

Can you share the programme for scaning text file in Server folder and continue with print it (the contain of file) to printer

Thanks:)

VB6 Render PDF-Files directly (and generate a Preview from the same DrawingRoutine)

0
0
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
Attached Files

VB6 Regfree-Usage of your own VB- and other COM-Dlls per DirectCOM-Helper

0
0
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
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

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.:

Code:

Dim oMyClass
Set oMyClass = GetInstanceFromBinFolder("MyActiveX", "cMyClass")
    oMyClass.DoSomething

Note, how the above Form resembles the well-known ProgID-based instancing per CreateObject:
Code:

Dim oMyClass
Set oMyClass = CreateObject("MyActiveX.cMyClass")
    oMyClass.DoSomething

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
Attached Files

VB6 Base-Widget for tree-like (nested) Query-Design

0
0
A small Base-Demo (dependent on vbRichClient5), to show how a seemingly complex,
hierarchical Control can be implemented with only two Base-Widgets.

The visual appearance is quite similar to the jQuery-based implementation here:
http://mistic100.github.io/jQuery-QueryBuilder/

But the Codebase is much smaller (about 250 lines currently, whereas the jQuery-
implementation is about 1700 lines ... when fully completed with the yet missing ComboBoxes
and Field-Value-Widgets, I guess that the VB6-version will end up with about 550 lines).

Here's a ScreenShot what it currently looks like:


- cwQueryGroup (about 160 lines of code - this is the red-bordered Widget with the Buttons)
- cwQueryRule (about 70 lines of code - this is the "Stripe-Widget" with the green Border)

What remains is now only the introducion of 2 cwDropDown-Widgets (one for the FieldNames
and one for the logic-Operators) - and a Handler-Widget for the comparison-value -
which in the simplest case could be a cwTextBox) - the Demo as it currently is, depends only
on RC5 (to make it easier for a first fast test) - the just mentioned additional Widgets for the
Field-Comparison-Logic are located in vbWidgets.dll, which is available as a Zip-Download on:
http://vbrichclient.com/#/en/Downloads.htm
(3rd section - Zip-Download from the GitHub-Repo).

Let me know when there's interest, to complete the current Base-implementation (e.g. later handing over
the Field-List and Field-Types of an ADO- or SQLite-Recordset, then dynamically creating the DropDown-Contents
and dynamically adding the appropriate Field-Value-Input-Widgets, which are matching the selected Fields Type)...

I could help with suggestions and code-snippets, where to plug the missing functionality in, and with
general "how-to-do-RC5-Widgets"... :)

Here's the Download for the Demo-Code:
QueryDesigner.zip

Hoping this Base-Implemenation is nice and encouraging enough for others, to complete what was
started here - (the Demo already does the nested Tree-Rendering, adding, removing, MouseHovering,
and MouseWheel-based Scrolling when the Widgets run out of Space of their Root-Container -
so the heavy lifting with regards to the graphics-rendering is already "out of the way")...

Olaf
Attached Files

[VB6] Read SMBIOS info w/o WMI

0
0
Summary

WMI is a bulky service with a COM-based API that was meant to help computer technicians write administrative maintenance scripts. It isn't meant for application use and if a user stops the service for security reasons relying on it can leave your program high and dry.

One of the things people often fall back on WMI for is system information, particularly information retrieved from the SMBIOS data block. That's usually just fine, since it seldom makes sense for applications to use such information anyway, so WMI admin scripts usually more than suffice.

But some people still want to do this, as unreliable as the SMBIOS info is. So here is an approach that bypasses WMI to get the same information.


The Code

This is a VB6 collection class SmbiosInformation and a small item class SmbiosItem. It requires no 3rd party libraries and makes use of GetSystemFirmwareTable() in Kernel32.dll to retrieve the raw data. Once retrieved it browses (some would say "parses" but we really aren't) the compound structure for data items.

It is important to note that this API entrypoint did not become available until Windows Server 2003 SP1 and Windows XP Professional 64-bit SP3. So effectively none of this applies to pre-Vista versions of Windows, all of which are officially unsupported now anyway.


Limitations, Opportunity

SmbiosInformation is not exhaustive in its browsing and reporting, returning only a subset of the available information.

You could choose to add more items, delete items you do not care about, or change the way some data items are reported. For example the "ChassisType" item is currently reported as a Byte value from 0-127 as stored in the raw data block, but you could translate those to friendly String values instead.

None of this seems very close to "perfect." Depending on the machine you might get various serial number values or you might get an empty String or a String of all 0 characters. You might get "BaseBoardxxxx" items or you might get none of them if the machine doesn't provide them. There is a theoretical way to detect HyperThreading capability, but it fails on some systems, probably because the BIOS is reporting many values according to how it has them set during boot. On some machines the BIOS version comes back as 0.0, which isn't correct.

Lots of String items come back as "To Be Filled By O.E.M." instead of a useful value, others come back as empty Strings.

Some kinds of data can be complex. CPUs are reported by socket, and a given machine may have more than one socket and CPU chip, so SmbiosInformation reports those as sub-collections of data, one per socket. There can be multiple Memory Array entries in the raw data, so SmbiosInformation just sums them.


Helpful References

I found SMBIOS Demystified helpful but really ended up even more confused. It could have been written far better, but the topic really is confusing so I can't fault the author too much. I'm sure not volunteering to do so!

But he did provide a link to System Management BIOS where you can download PDFs of different versions of the actual specs. This is critical information if you want to expand on what the provided code does.


Demo

I have attached a ZIP archive containing these classes and a simple Project1 program that uses them.

The demo gets the information, and then reports results two ways:

  • An initial MsgBox presenting a few values retrieved by key (Name).
  • A full dump in a sortable ListView, retrieved by enumeration.


Name:  sshot.png
Views: 21
Size:  23.4 KB

Full Dump


Useful?

You be the judge. Results are highly variable from machine to machine that I tested. If it is any comfort, if you can't get it this way you can't get it via WMI either.

Of course WMI covers lots of other things too - such as letting people get remote access to your machine! It is not a safe thing to have running at all, but Windows itself is becoming more and more dependent on it. Even some of the administrative Control Panel applets need the WMI Service running to work now.
Attached Images
 
Attached Files

[VB6] Working with Libraries (Win7+)

0
0

Windows 7 introduced the Library to the filesystem. But it's no ordinary folder. There are a few reasons you'd want to be able to work directly with these objects in VB... the issue first came up for me when I was writing a search program, and wanted to let the user select a library to search and then automatically search all folders in the library. Another time I came across the issue was for saving... if you pick a library from a save box, what folder should it go to? You need the library default save location. Further down the road, it's possible to actually write a program to manage libaries and even create new ones in VB. For that, I'll create a complete wrapper of IShellLibrary in a class module.

For now, however, I wanted to share a small sample program illustrating the basics of interacting with Library objects through the IShellLibrary interface. This program shows how to display basic Library information, and how to add a folder to an existing library.

As a bonus, it shows how to use a couple of important interfaces in VB- IShellItem and IEnumShellItems.

Note: You'll need to update the project reference to whereever you extract olelib.tlb to.
Alternative: Create a new project, add the reference, then add Form1.frm and modLib.bas.
(although I've never heard of a .tlb capable of being used as a malware vector since it's not executable per-se, the paranoid among you will find the source code that can be put through MKTYPLIB to create a hash-identical olelib.tlb)

Preview function - Getting all folders included in a library:
Code:

Public Function Library_GetFolders(pidlLib As Long, sout() As String) As Long
'WINDOWS 7 AND ABOVE ONLY:
'Given the fully qualified pidl for a library (including new ones besides music/videos/docs/pics)
'returns an array of the folders that are included in the library
Dim isi As IShellItem, isiChild As IShellItem, isiDef As IShellItem
Dim isl As IShellLibrary
Dim iesi As IEnumShellItems
Dim isia As IShellItemArray
Dim sinpt As Long, sinsz As String
Dim sinpt2 As Long, sinsz2 As String
Dim objClsid As GUIDA
Dim hRes As Long, hr As Long
Dim i As Long
ReDim sout(0)


hRes = SHCreateShellItem(0, 0, pidlLib, isi)
  'create a GUID from each CLSID string
  Call CLSIDFromString(StrPtr(CLSID_ShellLibrary), objClsid)
   
  'obtain an interface pointer for idClsid
  hr = CoCreateInstanceISL(objClsid, 0&, _
                      CLSCTX_INPROC_SERVER, _
                      IID_IShellLibrary, _
                      isl)

    Call isl.LoadLibraryFromItem(isi, STGM_READ)

        isl.GetFolders LFF_ALLITEMS, IID_IShellItemArray, isia
       
        isia.EnumItems iesi

        Do While (iesi.Next(1, isiChild, 0) = 0)
            ReDim Preserve sout(i)
            isiChild.GetDisplayName SIGDN_FILESYSPATH, sinpt
            sinsz = BStrFromLPWStr(sinpt, True)
            If sinsz <> "" Then
                sout(i) = sinsz
                i = i + 1
            End If
           
            Set isiChild = Nothing

        Loop
       
If sinpt Then Call CoTaskMemFree(sinpt)
Set isl = Nothing
Set isi = Nothing
Set isia = Nothing
Set iesi = Nothing

End Function

Attached Files

Image Carousel Class [vbRichClient]

0
0
Here's a class that you can use to create Image Carousel controls as either standard VB6 User Controls or vbRichClient widgets. This demo shows how to do the former; the UC is just a small wrapper around the class in order to implement the classes functionality and (of course) display it to the user.

This project has a dependency on Olaf's vbRichClient5 dll (http://www.vbrichclient.com)





Note: The demo contains an Icons sub-folder which is empty due to the size of its would-be contents being too large to include in the above zip. You can get all the icons that should be in that folder from here:

http://www.veehive.x10.mx/CarouselDemoIcons.zip

A screenshot:
Attached Images
 
Attached Files

VB6 - Netgear Telnet

0
0
I upgraded my Internet connection and discovered that my old Netgear router just didn't have the capacity to keep up with it. I kept that old one around because I had several, and I really appreciated the command mode via Telnet. After some research, I purchased a new Netgear router (WNDR3400). When I checked for open ports, I discovered that port 23 was active on the LAN interface, but when I connected with Telnet, I got nothing. With a little more research, I found that Netgear allows you to enable the Telnet interface by sending it a special packet via a small program called "TelnetEnable.exe". The description given is that it takes the MAC address of the LAN Interface and combines it with the UserID and Password. It then does an MD5 Hash on it along with some byte swapping, and encrypts it all using Blowfish and the key "AMBIT_TELNET_ENABLE+".

I was tempted to try and duplicate the encryption, but Blowfish isn't supported by Microsoft and it is quite an old standard. As well, Telnet is not enabled by default on all modern Microsoft Operating Systems. So I created a small program to duplicate the Telnet Interface called TelNetgear. The TelnetEnable program requires the MAC address of the LAN interface, so the program automatically sends an ARP request to the configured IP address to determine the MAC address residing at that address. This is only good for IPv4, as ARP is not used in IPv6. Instead of a broadcast IP address, IPv6 uses a multicast MAC address (33:33:00:01:00:02).

When TelNetgear attempts to connect with the router, it first uses ShellExecute to call the "TelnetEnable" program. If successful, it will then connect with port 23. The router should respond with the "#" prompt as illustrated below. This Netgear router uses Unix type commands, and is very powerful for it's compact size. You can enter those commands in the lower Textbox, or you can use the commands listed in the yellow Listbox. Those commands are loaded from a text file called "Commands.txt", which can be edited with any standard Text Editor. I have included only a few of the many commands available. My knowledge of the Unix commands is quite limited. The "help" command lists a few, and the "busybox" command lists a few more. But there are more than that (eg. RouterInfo), and viewers are free to offer suggestions.

Some routers require UserID/Password after enabling Telnet, so that logic will have to be added if required. My particular router does not, and that means there is no restriction to access on the local network. If there is any doubt about the security of your local network (for example WiFi access on the same subnet), you should reboot the router after accessing, as this reverts the Telnet interface back to the default condition.

J.A. Coutts
Attached Images
 
Attached Files

[VB6] Common Dialog Replacement - IFileDialog Vista+

0
0
Attached is a working example of using the IFileDialog interface introduced in Vista. It is the replacement for the common dialog used for opening/saving files and browsing for folders. This code is not compatible with XP and lower. You should ensure your project has needed logic to verify system is Vista or better. This version does not currently support enhancements provided by Win7 and above. I do plan on adding them in the near future.

The classes are plug & play. No TLB references are required. The main class is the FileDialog class and can be used-as is. The optional IFileDialogEvents class is provided so that you can receive callbacks while the FileDialog is displayed. Before Vista, this would have required subclassing the dialog, but Vista provided a way to do this through simple callback events. This optional events class is not required to display the dialog. But that class is required if you want callbacks, wish to filter what is displayed in the dialog, or want to add your own controls to the dialog. That class must be implemented if used. This means adding a line of code in your declarations section: Implements IFileDialogEvents. Lets talk about the three types of events. You can opt to have any of them or none of them.

Each event sent from the class includes a reference to the Dialog, a user-defined key for the instance of the Dialog, and appropriate event-driven parameters. Some events expect an answer back from you. Though all events will be added when you Implement the class, you only have to code for the ones you asked for & want to respond to.

1. Standard Callback Events. The FileDialog can forward events to whatever is displaying the dialog. The most common events are: DialogOnInit, OnFolderChanging, OnSelectionChange, OnFieOk. The OnFileOk event occurs just before the user is about to close the dialog and offers you a chance to prevent closing the dialog and/or cache any custom control properties set by the user while the dialog was displayed. The OnFolderChanging event offers you a way to prevent the user from navigating to what they clicked on. There are a few other events that are forwarded and can be reviewed on MSDN. Note: DialogOnInit is a custom event the class sends. It is intended for you to finalize any custom controls you may have added, i.e., setting an option button, selecting a combobox item, etc, before the dialog is displayed. Any actions you take will not be forwarded back through events until DialogOnInit returns. Should you need to actually subclass the dialog, you can do it at this time since the hWnd of the dialog is known.

2. Filter Events. We all know we can assign filters to a file dialog like *.exe, *.txt, *.dll, etc. The dialog offers a more advanced filter where you can individually choose whether what is returned during navigation is actually displayed. When the filter is active, Windows sends you, one by one, every item that would be displayed. You can opt to say yes or no. Note: This was removed in Win7 & may not be added back.

3. Custom Control Events. You have the option to add controls to the dialog. These include combo/check/text boxes, option/command buttons, labels, & menu-like controls. There is little point in adding controls if you have no way of knowing if the user is clicking on them. So the dialog can send you some events. However, these events are lacking in my opinion. They do offer basic stuff, like something was clicked, something was selected, but little else. Any added textbox controls, for example, have no events passed. There are no got/lost focus events for the controls. Locating these controls for manual subclassing will be a challenge.

Here's the gotcha. As mentioned, this is not compatible with anything lower than Vista. But you also must get outside of your comfort zone a bit too. Where the older common dialog returned file & folder names, this new version returns objects as IShellItems and IShellItemArrays (when multi-selecting items). That's really not a bad thing. Not only can you have these items give you the file/folder name and 'display' name, you can ask it to give you that name as plain text or as a URL or a few other formats. You can have it give you attributes of the object. Since no TLBs are used, the FileDialog class has methods that will allow you to query the returned object for those things.

What I don't like about this new implementation of the dialog? The folder browsing is too limited in my opinion. The older version allowed several flags to restrict what was displayed. This new version has very little options for folders. Win7 added the ability to restrict browsing to the local domain, but other than that, not much. The lack of options from previous version lends to possibility of needing custom filters (#2 above). Additionally, the previous version allowed files to be displayed with folders when browsing for folders. The new version doesn't have that ability. Matter of learning to adjust I guess, but I feel code for the older version of folder browsing may still be worth keeping around.

There is no sample project included. I think that the FileDialog class is fairly straightforward to use and has lots of comments. A vast majority of the code is needed to simply to offer the wide range of options and provide methods to be called during events. The IFileDialogEvents class is to be implemented only, you will never create this class as a stand-alone object. I'm sure that there will be questions and we can address them here as needed.

These classes don't have all the dummy-proofing I tend to add in. It's a work-in-progress. But it is important you become familiar with it before attempting to use it for any serious projects. And come back occasionally to look for updates.

Edited:
Well that's embarrassing. Thought I'd get the Filter events without having to create FileDialogEvents. Wrong. Quick modification to include FileDialogEvents if either Filter or Control events are wanted.
Attached Files

[VB6] Creating/Using COM Interfaces without TLBs

0
0
This idea was something I just wondered if I could do. So, when I figured it out, wasn't too difficult to create a mostly generic template that could be used & re-used as needed. And that's what we have here.

With VB we may have to jump through hoops trying to declare and use a Windows COM interface that doesn't inherit from IDispatch. Typically, we would add a type library (TLB) to our project that contains the interfaces we plan on using. That works pretty well.

With this code/idea, you can create these interfaces with pure VB code and without using any TLBs. If you're not the type that enjoys low-level coding, run away now, don't look back, just keep running ;).

This is pretty low-level stuff. The code creates a memory only VTable and thunks for the interface methods. But in order to do this, YOU have to research the interface. You need to know the IID (GUID) and the order of the methods in the interface (not the order posted on MSDN). Then you have to create each of the interface's methods as VB functions. Honestly, it's not that bad after you've done a few of them. I can generally whip up a working sample in about 5-10 minutes after I've found the IID and VTable order. MSDN is extremely useful in describing each method's parameters & return values.

FYI: IID is only needed should whatever you are passing this interface to asks it to identify itself. You want it to be able to say it is what it is pretending to be. In reality, and depending on what the interface is being used for, it's not often asked. But why risk it. The IID is the easiest thing to find. The VTable order is often more challenging.

Any how. As I said, low level. Because there are no TLBs to access these interfaces, we have to call them by their pointer and VTable function address offsets. Again, pretty easy really with the code provided and knowing the VTable order. But it does take some getting used to. For example, many times you may create or receive pointers to interfaces which you are required to add or remove a reference to. VB does this for us automatically. VB does not help here. You have to know when/if you must up the ref count or release the reference. Code is available to do that for you once you know those answers.

This is in no way an attempt to code without TLBs. However, it is kinda neat to know that you can create a class-only object that can be moved from project to project without requiring a TLB. I think that is about the only true advantage. The disadvantage, obviously, it's harder to do this manually.

That attached zip file has a sample project and the IFauxInterface template. That template has lots of comments, in fact, more comments than code. The sample project's IBindStatusCallback class was created from that template. The form in the project creates a pointer only interface and uses low-level access to manipulate it. Enjoy

Regarding the thunks. No malicious code in there. The thunks basically do this:
- store the stack
- set up the stack for the callback: add the class' object pointer and the parameters
- call the VB class private function
- restore/adjust/pop the stack as needed & set the VB function return address
- done. 48 bytes used per thunk, 4 bytes per VTable entry, 8 bytes per Interface pointer

Edited: If you don't see the progress bar dialog before the download finishes. The download was too quick or used cached data. The progress dialog is created in the form. The IBindStatusCallback class is what pushes that progress bar to be updated from the events it sends. What is giving us those progress events is the URLmon.dll via the interface we created from the template.
Attached Files

[VB6] Common Dialog Replacement - IFileDialog Vista+ (No TLBs)

0
0
Updated/Revamped a bit: 17 Jan 2015

Attached is a working example of using the IFileDialog interface introduced in Vista. It is the replacement for the common dialog used for opening/saving files and browsing for folders. This code is not compatible with XP and lower. You should ensure your project has needed logic to verify system is Vista or better. This version does not currently support enhancements provided by Win7 and above. I do plan on adding them in the near future. This class-only solution is based off of my project: Creating COM Interfaces without TLBs

The classes are plug & play. No TLB references are required. The main class is the IFileDialog class and can be used-as is. The optional IFileDialogEvents class is provided so that you can receive callbacks while the IFileDialog is displayed. Before Vista, this would have required subclassing the dialog, but Vista provided a way to do this through simple callback events. This optional events class is not required to display the dialog. But that class is required if you want callbacks, wish to filter what is displayed in the dialog, or want to add your own controls to the dialog. That class must be implemented if used. This means adding a line of code in your declarations section: Implements IFileDialogEvents. Lets talk about the three types of events. You can opt to have any of them or none of them.

Each event sent from the class includes a reference to the Dialog, a user-defined key for the instance of the Dialog, and appropriate event-driven parameters. Some events expect an answer back from you. Though all events will be added when you Implement the class, you only have to code for the ones you asked for & want to respond to.

1. Standard Callback Events. The IFileDialog can forward events to whatever is displaying the dialog. The most common events are: DialogOnInit, OnFolderChanging, OnSelectionChange, OnFieOk. The OnFileOk event occurs just before the user is about to close the dialog and offers you a chance to prevent closing the dialog and/or cache any custom control properties set by the user while the dialog was displayed. The OnFolderChanging event offers you a way to prevent the user from navigating to what they clicked on. There are a few other events that are forwarded and can be reviewed on MSDN. Note: DialogOnInit is a custom event the class sends. It is intended for you to finalize any custom controls you may have added, i.e., setting an option button, selecting a combobox item, etc, before the dialog is displayed. Any actions you take will not be forwarded back through events until DialogOnInit returns. Should you need to actually subclass the dialog, you can do it at this time since the hWnd of the dialog is known.

2. Filter Events. We all know we can assign filters to a file dialog like *.exe, *.txt, *.dll, etc. The dialog offers a more advanced filter where you can individually choose whether what is returned during navigation is actually displayed. When the filter is active, Windows sends you, one by one, every item that would be displayed. You can opt to say yes or no. Note: This was deprecated in Win7 & may not be added back.

3. Custom Control Events. You have the option to add controls to the dialog. These include combo/check/text boxes, option/command buttons, labels, & menu-like controls. There is little point in adding controls if you have no way of knowing if the user is clicking on them. So the dialog can send you some events. However, these events are lacking in my opinion. They do offer basic stuff, like something was clicked, something was selected, but little else. Any added textbox controls, for example, have no events passed. There are no got/lost focus events for the controls. Locating these controls for manual subclassing will be a challenge.

Here's the gotcha. As mentioned, this is not compatible with anything lower than Vista. But you also must get outside of your comfort zone a bit too. Where the older common dialog returned file & folder names, this new version returns objects as IShellItems and IShellItemArrays (when multi-selecting items). That's really not a bad thing. Not only can you have these items give you the file/folder name and 'display' name, you can ask it to give you that name as plain text or as a URL or a few other formats. You can have it give you file attributes of the object. Since no TLBs are used, the IFileDialog class has methods that will allow you to query the returned object for those things.

What I don't like about this new implementation of the dialog? The folder browsing is too limited in my opinion. The older version allowed several flags to restrict what was displayed. This new version has very little options for folders. Win7 added the ability to restrict browsing to the local domain, but other than that, not much. The lack of options from previous version lends to possibility of needing custom filters (#2 above). Additionally, the previous version allowed files to be displayed with folders when browsing for folders. The new version doesn't have that ability. Matter of learning to adjust I guess, but I feel code for the older version of folder browsing may still be worth keeping around.

These classes don't have all the dummy-proofing I tend to add in. It's a work-in-progress. But it is important you become familiar with it before attempting to use it for any serious projects. And come back occasionally to look for updates.

What advantage does this have over TLBs? Only one I can think of: can create a class that can be added to any project without needing to carry over TLBs into the target application.

The sample project does not touch all the properties the IFileDialog class offers. Explore a bit.
Attached Files

[VB6] Using the new IFileDialog interface for customizable Open/Save (TLB, Vista+)

0
0
LaVolpe put out an excellent class module that implements these interfaces, but I've been working on using them through a different approach for some time and wanted to post it as well since, while requiring a TLB, it offers advantages like having IShellItem as a declarable type used all over projects, interfaces, and APIs.

IFileDialog / IFileOpenDialog / IFileSaveDialog

IFileSaveDialog and IFileOpenDialog were introduced in Windows Vista to supersede the GetOpenFileName/GetSaveFileName API, and offer several advantages (although certainly some drawbacks). Windows provides a default implementation, so they're fairly easy to use in VB. Among the advantages is easy addition of custom controls, something that was not previously possible in VB (or at least so hard no one bothered).

The typelib:
This project uses oleexp.tlb, which is my own expansion of olelib.tlb. Originally I just had a modified olelib, but now for compatibility reasons I've split off my additions so that there's minimal changes to olelib to maximize compatibility. See the olechanges.txt file for details. This project MIGHT work with an unmodified olelib.tlb since it doesn't use any of the changed interfaces, but I strongly advise using my upgraded olelib, as the only code-breaking changes are turning a few subs into functions since the return value is important (and only on IShellFolder/2 and IEnumIDList).
Full source code for both the upgraded olelib.tlb and oleexp.tlb is included and can be compiled into a hash-identical copy with VS6.0's MKTYPLIB.
This project uses oleexp.tlb version 1.1, if you have an earlier version from my other projects please upgrade it (fully backwards compatible, no code changes to other projects will be needed).

IShellItem:
For modern Windows, IShellItem is becoming more and more important. This project will familiarize you with using this object. The typelib based approach to this offers the advantage of being able to use IShellItem and related types project-wide, and pass them around easily, both within the project and to APIs and interfaces that use them. The sample project has helper functions that show how to create and manipulate IShellItem and related functions.
--

The attached ZIP includes olelib and oleexp, as well as a sample project illustrating the use of the dialogs. There's examples for a very simple Open, a typical Open dialog, a simple Save dialog, a multi-file-open dialog, and a highly customized open dialog.

In Your Own Project
To use IFileDialog-based Open/Save, your project needs to add a reference to olelib and oleexp. cFileDialogEvents.cls is required only if you want to receive feedback while the dialog is open (including from added custom controls).

-----------------------------------
Here's how simple a basic Open File dialog is with this tlb:

Code:

Dim fodSimple As FileOpenDialog
Dim isiRes As IShellItem
Dim lPtr As Long

Set fodSimple = New FileOpenDialog

With fodSimple
    .SetTitle "Simple File Open"
    .Show Me.hWnd
   
    .GetResult isiRes
    isiRes.GetDisplayName SIGDN_FILESYSPATH, lPtr
    Text1.Text = BStrFromLPWStr(lPtr, True)
End With
Set isiRes = Nothing
Set fodSimple = Nothing

That's all it takes to get the very simplest Open File dialog going, and shows how the class is used.

Events
While previously subclassing was required to receive event notifications while the dialog was displayed, this is now accomplished simply by adding the optional cFileDialogEvents class and calling the .Advise method.
This same class also receives events from any custom controls added.

Customization
I thought that even with this approach, it was going to be hard. But the class is set up to make this a very easy process.
To add a simple label and a button,
Code:


Dim fdc As IFileDialogCustomize
Set fdc = pDlg 'pDlg is the FileOpenDialog object

pDlg.Advise cFDE, 0 'the events class

fdc.AddText 1000, "This is a test label."
fdc.AddPushButton 1001, "New Button"

With that, the events class will have its OnButtonClicked method called when its clicked.


Here's the full sample code that produces the dialog in the above screenshot:

Code:

On Error Resume Next 'A major error is thrown when the user cancels the dialog box
List1.Clear
Dim isiRes As IShellItem

Dim isiDef As IShellItem 'default folder
Dim FOLDERID_Pictures As UUID
Dim pidlDef As Long

Dim lPtr As Long
Dim lOptions As FILEOPENDIALOGOPTIONS

'Set up filter
Dim FileFilter() As COMDLG_FILTERSPEC
ReDim FileFilter(1)

FileFilter(0).pszName = "Image Files"
FileFilter(0).pszSpec = "*.jpg;*.gif;*.bmp"

FileFilter(1).pszName = "All Files"
FileFilter(1).pszSpec = "*.*"

'set up default folder: note that this only shows the very first time
'                      after that, the last directory is default
'                      automatically. override with SetFolder.
Call CLSIDFromString(StrPtr(fidPictures), FOLDERID_Pictures)
Call SHGetKnownFolderIDList(FOLDERID_Pictures, 0, 0, pidlDef)
If pidlDef Then
    Call SHCreateShellItem(0, 0, pidlDef, isiDef)
End If

Set fod = New FileOpenDialog
Set cEvents = New cFileDialogEvents

With fod
    .Advise cEvents, 0
    .SetTitle "Select Thine File Sir"
    .GetOptions lOptions
    lOptions = lOptions Or FOS_FILEMUSTEXIST Or FOS_FORCESHOWHIDDEN 'just an example of options... shows hidden files even if they're normally not shown
    .SetOptions lOptions
    .SetOkButtonLabel "Mine File"
    .SetFileNameLabel "Look! A custom file label!!!"
   
    If (isiDef Is Nothing) = False Then
        .SetFolder isiDef
    End If
    .SetFileTypes 2, VarPtr(FileFilter(0).pszName)
   
    'Now we'll begin adding custom controls
    'First, we set up the interface
    'The control IDs can be any number, and should
    'really be stored as consts
    Set fdc = fod
   
    fdc.AddText 1000, "This is a test label."
   
    fdc.AddPushButton 1001, "New Button"
    fdc.MakeProminent 1001 'Moves to by the open button; only checkboxes, buttons, combos, menus can be made prominent
   
    fdc.AddPushButton 1002, "Some Other Button"
   
    fdc.StartVisualGroup 2000, "VG-1"
    fdc.AddCheckButton 2001, "Checkers!", 1
    fdc.AddSeparator 2002
    'For menus, and radio buttons/combos, first you add the control, then add items with AddControlItem
    fdc.AddMenu 2010, "Checkers?"
    fdc.AddControlItem 2010, 3000, "Pretty good."
    fdc.AddControlItem 2010, 3001, "Pretty bad."
    fdc.AddControlItem 2010, 3002, "Neutral!"
    fdc.AddEditBox 2003, "Other."
    fdc.EndVisualGroup
   
    fdc.StartVisualGroup 4000, "Radio station?"
    fdc.AddRadioButtonList 4001
    fdc.AddControlItem 4001, 4010, "Radio Station Alpha"
    fdc.AddControlItem 4001, 4011, "Radio Station Beta"
    fdc.EndVisualGroup
   
    fdc.AddComboBox 5000
    fdc.AddControlItem 5000, 5010, "Combo Alpha"
    fdc.AddControlItem 5000, 5011, "Combo Beta"
    fdc.AddControlItem 5000, 5012, "Combo Gamma"
    fdc.SetSelectedControlItem 5000, 5011


    .Show Me.hWnd
   
    .GetResult isiRes
    isiRes.GetDisplayName SIGDN_FILESYSPATH, lPtr
    Text1.Text = BStrFromLPWStr(lPtr, True)
End With
   
If pidlDef Then Call CoTaskMemFree(pidlDef)
Set isiRes = Nothing
Set isiDef = Nothing
Set fod = Nothing

Attached Files

[VB6] Modern Shell Interface Type Library - oleexp.tlb

0
0
oleexp.tlb : Modern Shell Interfaces
Current Version: 1.3
Previously the latest version of this sub-project was just included in whatever code I released that depended on it, but now I'm going to make it stand as individual project.

So back in the day, E. Morcillo released the very comprehensive interface library olelib.tlb (Edanmo's OLE interfaces & functions). It contained a massive number of interfaces, enums, structs, etc. But after a point it was no longer updated and thus doesn't have any interfaces from Windows Vista or Windows 7. So I set out to bring these interfaces to VB, and quickly realized that so much would have to be duplicated and would then be conflicting, that the only sensible way to approach this would be to create an expansion library based on olelib.

I've kept it as a separate type library, oleexp.tlb, and made minimal changes to the original olelib.tlb to maximize compatibility, although some changes were needed that may require minor code changes (including oleexp as a reference, turning subs into functions). I'll elaborate on these more below.

New Interfaces
Interfaces added by oleexp (v1.3):
interface IEnumIDList;
interface IShellFolder;
interface IShellFolder2;
interface ITaskbarList3;
interface ITaskbarList4;
interface IShellItem;
interface IShellItem2;
interface IShellItemImageFactory;
interface IThumbnailProvider;
interface IEnumShellItems;
interface IShellItemArray;
interface IShellLibrary;
interface IObjectWithPropertyKey;
interface IPropertyChange;
interface IPropertyChangeArray;
interface IProgressDialog;
interface IOperationsProgressDialog;
interface IFileOperationProgressSink;
interface IFileOperation;
interface IContextMenu3;
interface IPropertyStore;
interface IObjectArray;*
interface IObjectCollection;*
interface IApplicationDestinations;*
interface ICustomDestinationsList;*
interface IModalWindow;
interface IFileDialogEvents;
interface IShellItemFilter;
interface IFileDialog;
interface IFileSaveDialog;
interface IFileOpenDialog;
interface IFileDialogCustomize;
interface IFileDialogControlEvents;
interface IFileDialog2;
interface IPropertyDescriptionList;

All related structures and enums are also included.

* - Under development; may not bee 100% error free

Sample Projects

[VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prompts - Also shows usage of IShellItem; now updated to show the Advise method in action- using a class module to implement an interface to get feedback: Have the shell progress dialog (now more detailed) send the operation progress back to your application.

[VB6] Using the new IFileDialog interface for customizable Open/Save (TLB, Vista+) - Has the benefit of allowing easy access to events from the dialog, as well as making it very easy to add your own controls.

[VB6] Working with Libraries (Win7+) - Uses the IShellLibrary interface to get all folders in a library, add more, get the default save location, get the icon, and even create a new library. Also shows the use of IEnumShellItems and IShellItemArray.

...more to come soon!

Changes to olelib.tlb
(olelib v1.9)
-Had to eliminate coclass pointing to ITaskBarList to reassign it to the new ITaskbarList3/4 interfaces; since the CLSID can't be changed, even a new coclass name would result to limiting the new one to the functions of the first.

-IShellFolder, IShellFolder2, and IEnumIDList are not implemented correctly in olelib (some things should be functions instead of subs), so oleexp contains new definitions and they have been removed from olelib. They remain commented out in the source if for some reason you had code depending on the wrong definitions. Any projects using this would now have to include oleexp.

-Included shell32.dll declares and the IFolderFilter interface have been moved to oleexp.tlb. If you're using these in another project you must now include oleexp.tlb in them.

Included in the ZIP
-oleexp.tlb, v1.3
-olelib.tlb, v1.9
-olelib2.tlb and source - Nothing modified from original release; all original files
-Full source for both oleexp and the updated olelib, can be compiled to identical hashes with VS6 MKTYPLIB.
-The originals of source files modified in olelib
-mk.bat and mkex.bat - shortcuts to compile olelib.tlb and oleexp.tlb, respectively. May need to modify if VS6 MKTYPLIB is not in default directory on your system.

------------------------------
Any and all feedback is welcome. Many thanks to E. Morcillo for the olelib foundation, all the olelib source is all his code.
Attached Files

[VB6] Win7 Taskbar Features with ITaskbarList3 (overlay, progress in taskbar, etc)

0
0
ITaskbarList Demo

Windows 7 introduced the ITaskbarList3 and ITaskbarList4 interfaces that added a number of new features to the taskbar for your program. The most commonly seen is the ability to turn the taskbar into a progress bar, and there's also the ability to add an overlay icon, add buttons below the thumbnail, and change the area the thumbnail covers among others. Like many other shell interfaces, it's available to VB either through type libraries or by directly calling the vtable. I prefer the former approach since many times internal structures and interfaces have far wider uses, so they can be put in the TLB and be accessible everywhere, not just within a class.

This project uses oleexp.tlb, my Modern Shell Interfaces expansion of Edanmo's olelib.tlb. The latest version is included in the ZIP; once you've extracted everything, in the sample project, go to References and update the paths. If you already work with olelib, the included olelib.tlb is a higher version that should replace that one. There's a few minor changes, but nothing major- just a few things moved to oleexp and 3 interfaces had some of their subs turned into functions. See the oleexp thread in the link above for complete details. If you're already using oleexp.tlb, make sure you have at least the version included here (dated 1/18/15).


Using ITaskbarList

Usage is fairly simple; you generally want to use it as a module level variable in your main form;

Code:

Private iTBL As TaskbarList

'...then in form_load:

Set iTBL = New TaskbarList

From there you can begin calling its functions, most of which are very straightforward:
Code:

    iTBL.SetOverlayIcon Me.hWnd, hIcoOvr, "Overlay icon active."
    iTBL.SetProgressState Me.hWnd, TBPF_INDETERMINATE 'marquee
    iTBL.SetThumbnailTooltip Me.hWnd, Text1.Text
    iTBL.SetThumbnailClip Me.hWnd, [rect]

The only thing a little complicated is the buttons.

Code:

Dim pButtons() As THUMBBUTTON
ReDim pButtons(2) 'basic 3-button setup

arIcon(0) = ResIconToHICON("ICO_LEFT", 32, 32)
arIcon(1) = ResIconToHICON("ICO_UP", 32, 32)
arIcon(2) = ResIconToHICON("ICO_RIGHT", 32, 32)

Call SubClass(Me.hWnd, AddressOf F1WndProc)

With pButtons(0)
    .dwMask = THB_FLAGS Or THB_TOOLTIP Or THB_ICON
    .iid = 100
    .dwFlags = THBF_ENABLED
    Call Str2Inta("Stop", pInt)
    For i = 0 To 259
        .szTip(i) = pInt(i) 'this doesn't seem to be working... will address in a future release
    Next i
    .hIcon = arIcon(0)
End With

[fill in the other buttons]

iTBL.ThumbBarAddButtons Me.hWnd, 3, VarPtr(pButtons(0))


Icons
The TaskbarList interface deals with hIcons; in the sample project, they're stored in a resource file and loaded from there, but you could load them from anywhere with any method that will give you a valid hIcon.

Subclassing
The only thing that requires subclassing is receiving notification when a user clicks on a button below the thumbnail. If you're not going to be using that feature, then you won't need to subclass. The sample project does include a very simple subclassing module that receives the WM_COMMAND message that's sent when a button is clicked, and passes the ID of the button (the LoWord of the wParam) back to the main form.
Attached Files

HSV and RGB conversion

0
0
Here's some code I wrote that you can place in a Module and use in any graphics program that needs to convert RGB to HSV or HSV to RGB.

Code:

Public Sub RGBtoHSV(ByVal R As Byte, ByVal G As Byte, ByVal B As Byte, _
                    ByRef H As Byte, ByRef S As Byte, ByRef V As Byte)
Dim MinVal As Byte
Dim MaxVal As Byte
Dim Chroma As Byte
Dim TempH As Single
If R > G Then MaxVal = R Else MaxVal = G
If B > MaxVal Then MaxVal = B
If R < G Then MinVal = R Else MinVal = G
If B < MinVal Then MinVal = B
Chroma = MaxVal - MinVal

V = MaxVal
If MaxVal = 0 Then S = 0 Else S = Chroma / MaxVal * 255

If Chroma = 0 Then
    H = 0
Else
    Select Case MaxVal
        Case R
            TempH = (1& * G - B) / Chroma
            If TempH < 0 Then TempH = TempH + 6
            H = TempH / 6 * 255
        Case G
            H = (((1& * B - R) / Chroma) + 2) / 6 * 255
        Case B
            H = (((1& * R - G) / Chroma) + 4) / 6 * 255
    End Select
End If
End Sub
                   


Public Sub HSVtoRGB(ByVal H As Byte, ByVal S As Byte, ByVal V As Byte, _
                    ByRef R As Byte, ByRef G As Byte, ByRef B As Byte)
Dim MinVal As Byte
Dim MaxVal As Byte
Dim Chroma As Byte
Dim TempH As Single

If V = 0 Then
    R = 0
    G = 0
    B = 0
Else
    If S = 0 Then
        R = V
        G = V
        B = V
    Else
        MaxVal = V
        Chroma = S / 255 * MaxVal
        MinVal = MaxVal - Chroma
        Select Case H
            Case Is >= 170
                TempH = (H - 170) / 43
                If TempH < 1 Then
                    B = MaxVal
                    R = MaxVal * TempH
                Else
                    R = MaxVal
                    B = MaxVal * (2 - TempH)
                End If
                G = 0
            Case Is >= 85
                TempH = (H - 85) / 43
                If TempH < 1 Then
                    G = MaxVal
                    B = MaxVal * TempH
                Else
                    B = MaxVal
                    G = MaxVal * (2 - TempH)
                End If
                R = 0
            Case Else
                TempH = H / 43
                If TempH < 1 Then
                    R = MaxVal
                    G = MaxVal * TempH
                Else
                    G = MaxVal
                    R = MaxVal * (2 - TempH)
                End If
                B = 0
        End Select
        R = R / MaxVal * (MaxVal - MinVal) + MinVal
        G = G / MaxVal * (MaxVal - MinVal) + MinVal
        B = B / MaxVal * (MaxVal - MinVal) + MinVal
    End If
End If
End Sub


VB6 QR-Encoding+Decoding and IME-Window-Positioning

0
0
This Demo depends on vbRichClient5 (version 5.0.21 and higher), as well as the latest vbWidgets.dll.
One can download both new packages from the Download-page at: http://vbrichclient.com/#/en/Downloads.htm
(vbWidgets.dll needs to be extracted from the GitHub-Download-Zip and placed beside vbRichClient5.dll,
there's small "RegisterInPlace-Scripts" for both Dll-Binaries now).

After both dependencies were installed, one can load the below Demo-Project into the VB-IDE:
QRandIMEDemo.zip

According to the Title of this Thread, we try to show the:

Free positioning of an IME-Window:
delegating its IME_Char-Messages into free choosable Widget-controls (the Demo does
that against cwTextBox-Widgets exclusively - but could accomplish that also against cwLabels
or cwImages.

Here is the interesting part (the IME-API-Declarations and Wrapper-Functions are left out),
which is contained in the new cIME-Class (available in the Code-Download from the vbWidgets-GitHub-Repo):

Code:

'RC5-SubClasser-Handler
Private Sub SC_WindowProc(Result As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long)
Const WM_IME_SETCONTEXT = 641, WM_IME_STARTCOMPOSITION = 269, WM_IME_CHAR = 646
On Error GoTo 1

  Select Case Msg
      Case WM_IME_SETCONTEXT
        SwitchOpenStatus wParam
       
      Case WM_IME_STARTCOMPOSITION
        HandleIMEPos
     
      Case WM_IME_CHAR
        Dim WFoc As cWidgetBase, KeyCode As Integer
        Set WFoc = FocusedWidget: KeyCode = CInt("&H" & Hex(wParam And &HFFFF&))
        If Not WFoc Is Nothing Then
          If WFoc.Key = tmrFoc.Tag Then RaiseEvent HandleIMEChar(WFoc, KeyCode, ChrW(KeyCode))
        End If
        Exit Sub 'handled ourselves - so we skip the default message-handler at the end of this function
  End Select
 
1: Result = SC.CallWindowProc(Msg, wParam, lParam)
End Sub
 
Private Sub tmrFoc_Timer()
  HandleIMEPos
End Sub

Private Function FocusedWidget() As cWidgetBase
  If Cairo.WidgetForms.Exists(hWnd) Then Set FocusedWidget = Cairo.WidgetForms(hWnd).WidgetRoot.ActiveWidget
End Function

Private Sub HandleIMEPos()
Dim WFoc As cWidgetBase, AllowIME As Boolean
On Error GoTo 1

  Set WFoc = FocusedWidget
  If WFoc Is Nothing Then
    tmrFoc.Tag = ""
  Else
    RaiseEvent HandleIMEPositioning(WFoc, AllowIME)
    If AllowIME Then tmrFoc.Tag = WFoc.Key
  End If
 
1: SwitchOpenStatus AllowIME
End Sub

As one can see, this Class is (currently) only raising two Events to the outside -
received by a hosting (RC5) cWidgetForm-Class.

The elsewhere mentioned problems with "forcibly ANSIed" IME-WChars do not happen in
this Demo, because of a "full queue of W-capable APIs" (including a W-capable MessageLoop,
which is available in the RC5 per Cairo.WidgetForms.EnterMessageLoop...

The Integration of an RC5-cWidgetForm into an existing VB6-Project is relative easy (no need
to rewrite everything you have) - this Demo shows how one can accomplish that, by showing
the RC5-Form modally - starting from a normal VB-Form-CommandButton:

Here's all the code in the normal VB6-Starter-Form, which accomplishes that:
Code:

Option Explicit

Private VBFormAlreadyUnloaded As Boolean

Private Sub cmdShowRC5IMEForm_Click()
  With New cfQRandIME ' instantiate the RC5-FormHosting-Class
 
    .Form.Show , Me 'this will create and show the RC5-Form with the VB-Form as the underlying Parent
   
    'now we enter the W-capable RC5-message-pump, which will loop "in  place" till the RC5-Form gets closed again
    Cairo.WidgetForms.EnterMessageLoop True, False
 
    'the RC5-Form was closed, so let's read-out the Public Vars of its hosting cf-Class
    If Not VBFormAlreadyUnloaded Then '<- ... read the comment in Form_Unload, on why we need to check this flag
      Set Picture1.Picture = .QR1.QRSrf.Picture
      Set Picture2.Picture = .QR2.QRSrf.Picture
    End If
  End With
End Sub

Private Sub Form_Unload(Cancel As Integer) 'this can happen whilst the RC5-ChildForm is showing, ...
  VBFormAlreadyUnloaded = True  'so we set a Flag, to not implicitely load this VB-ParentForm again, when filling the Result-PicBoxes
End Sub

Private Sub Form_Terminate() 'the usual RC5-cleanup call (when the last VB-Form was going out of scope)
  If Forms.Count = 0 Then New_c.CleanupRichClientDll
End Sub

The above Starter-Form (fMain.frm) will look this way


And pressing the CommandButton, it will produce the modal RC5-WidgetForm:


What one can see above is two (cwTextBox-based) Edit-Widgets - and the left one
is showing the free positioned IME-Window - the IME-Window (when visible), will
jump automatically, as soon as the user switches the Input-Focus to a different Widget.

To test this in a bit more extreme scenario even, I've made the two cwQRSimple-Widgets
(in the lower section of the Form) movable - and in case the IME-Window is shown
below one of them as in this ScreenShot:


... the IME-Window will follow the currently focused QR-Widget around, when it's dragged
with the Mouse...

Here's the complete code of the cfQRandIME.cls (which hosts the RC5-cWidgetForm-instance):
Code:

Option Explicit

Public WithEvents Form As cWidgetForm, WithEvents IME As cIME

Public QREnc As New cQREncode, QRDec As New cQRDecode 'the two (non-visible) QR-CodecClass-Vars
Public TB1 As cwTBoxWrap, TB2 As cwTBoxWrap 'the two TextBox-Wrapper-Classes
Public QR1 As cwQRSimple, QR2 As cwQRSimple 'the two QR-Widgets
 
Private Sub Class_Initialize()
  Set Form = Cairo.WidgetForms.Create(vbFixedDialog, "QR-Widgets and IME-Window-Positioning", , 800, 600)
      Form.IconImageKey = "QRico2"
      Form.WidgetRoot.ImageKey = "bgPatForm"
      Form.WidgetRoot.ImageKeyRenderBehaviour = ImgKeyRenderRepeat
     
  Set IME = New cIME 'create the vbWidgets.cIME-instance
      IME.BindToForm Form '...and bind our cWidgetForm-instance to it (IME will throw two Events at us then)
End Sub

Private Sub Form_Load() 'handle Widget-Creation and -Adding on this Form
  Form.Widgets.Add(New cwSeparatorLabel, "Sep1", 11, 8, Form.ScaleWidth - 22, 42).SetCaptionAndImageKey "EditBox-DemoArea", "Edit", &H11AA66
    Set TB1 = Form.Widgets.Add(New cwTBoxWrap, "TB1", 25, 60, 280, 38)
        TB1.TBox.CueBannerText = "Session-Login..."
        TB1.Widget.ImageKey = "session1"
    Set TB2 = Form.Widgets.Add(New cwTBoxWrap, "TB2", 325, 60, 280, 38)
        TB2.TBox.CueBannerText = "Place some Info here..."
        TB2.Widget.ImageKey = "info1"
     
  Form.Widgets.Add(New cwSeparatorLabel, "Sep2", 11, 155, Form.ScaleWidth - 22, 42).SetCaptionAndImageKey "QRCode-DemoArea", "Preview", &H1030EE
    Set QR1 = Form.Widgets.Add(New cwQRSimple, "QR1", 25, 240, 250, 220)
    Set QR2 = Form.Widgets.Add(New cwQRSimple, "QR2", 325, 280, 250, 220)
End Sub

Private Sub Form_BubblingEvent(Sender As Object, EventName As String, P1 As Variant, P2 As Variant, P3 As Variant, P4 As Variant, P5 As Variant, P6 As Variant, P7 As Variant)
  If EventName = "Change" And TypeOf Sender Is cwTextBox Then 'we handle the Change-Event of the QRWidget-Child-Textboxes here
    If Not (Sender Is QR1.TBox Or Sender Is QR2.TBox) Then Exit Sub
   
    'resolve to the (TextBox-Hosting) cwQRSimple-Widget in question
    Dim QR As cwQRSimple: Set QR = IIf(Sender Is QR1.TBox, QR1, QR2)
   
    'Encode the current Text of our QR-Widget - and place the returned Pixel-Surface in QR.QRSrf
    Set QR.QRSrf = QREnc.QREncode(New_c.Crypt.VBStringToUTF8(QR.Text))
 
    'to verify, we perform a true Decoding of the QR-Text from the Pixels of the just created QR-Widgets QR-Surface
    QRDec.DecodeFromSurface QR.QRSrf
    'and reflect this decoded Unicode-StringResult in the Caption of the QR-Widget (so, ideally QR.Caption should match QR.Text)
    If QRDec.QRDataLen(0) Then QR.Caption = New_c.Crypt.UTF8ToVBString(QRDec.QRData(0)) Else QR.Caption = ""
  End If
 
  'the QR-Widgets (cwQRSimple) are moveable - and in case they have an active IME-Window, we will move that too
  If EventName = "W_Moving" And TypeOf Sender Is cwQRSimple Then IME_HandleIMEPositioning Sender.TBox.Widget, True
End Sub

Private Sub IME_HandleIMEPositioning(FocusedWidget As cWidgetBase, AllowIME As Boolean)
  If TypeOf FocusedWidget.Object Is cwTextBox Then
    AllowIME = True '<- here we allow IME-Windows only for cwTextBox-Widgets (but we could also allow IME on other Widget-Types)
    IME.SetPosition FocusedWidget.AbsLeftPxl + 3, FocusedWidget.AbsTopPxl + FocusedWidget.ScaleHeightPxl + 4
  End If
End Sub

Private Sub IME_HandleIMEChar(FocusedWidget As cWidgetBase, ByVal IMEKeyCode As Integer, IMEWChar As String)
  FocusedWidget.KeyPress IMEKeyCode 'simply delegate the incoming IMEKeyCode into the Widget in question
  'the above is the more generic delegation-method into any Widget (which are all derived from cWidgetBase)
 
  '*alternatively* (for cwTextBoxes, which is the only Widget-Type we allow IME for in this Demo here)
  'we could also use:
'  Dim TB As cwTextBox
'  Set TB = FocusedWidget.Object
'      TB.SelText = IMEWChar
End Sub

Note the two blue marked EventHandlers at the bottom of the above code-section, which
make use of the two cIME-Events, which were mentioned at the top of this posting.


QR-Code Generation and Decoding:


The base QR-Encoding/Decoding-support is now included in vb_cairo_sqlite.dll (from two C-libs which are now statically contained).
And the vbWidgets.dll project contains the two Wrapper-Classes (cQREncode, cQRDecode) for these new exposed APIs.

cQREncode/cQRDecode is used in conjunction with thrown Change-Events of our cwQRSimple-Widgets
(which you saw in the ScreenShot above).

Here's the central Eventhandler which is contained in the RC5-WidgetForm-Hosting Class (cfQrandIME):
Code:

Private Sub Form_BubblingEvent(Sender As Object, EventName As String, P1, P2, P3, P4, P5, P6, P7)
  If EventName = "Change" And TypeOf Sender Is cwTextBox Then 'we handle the Change-Event of the QRWidget-Child-Textboxes here
    If Not (Sender Is QR1.TBox Or Sender Is QR2.TBox) Then Exit Sub
   
    'resolve to the (TextBox-Hosting) cwQRSimple-Widget in question
    Dim QR As cwQRSimple: Set QR = IIf(Sender Is QR1.TBox, QR1, QR2)
   
  'Encode the current Text of our QR-Widget - and place the returned Pixel-Surface in QR.QRSrf
    Set QR.QRSrf = QREnc.QREncode(New_c.Crypt.VBStringToUTF8(QR.Text))
 
    'to verify, we perform a true Decoding of the QR-Text from the Pixels of the just created QR-Widgets QR-Surface
    QRDec.DecodeFromSurface QR.QRSrf
    'and reflect this decoded Unicode-StringResult in the Caption of the QR-Widget (so, ideally QR.Caption should match QR.Text)
    If QRDec.QRDataLen(0) Then QR.Caption = New_c.Crypt.UTF8ToVBString(QRDec.QRData(0)) Else QR.Caption = ""
  End If
 
  'the QR-Widgets (cwQRSimple) are moveable - and in case they have an active IME-Window, we will move that too
  If EventName = "W_Moving" And TypeOf Sender Is cwQRSimple Then IME_HandleIMEPositioning Sender.TBox.Widget, True
End Sub

So that's quite simple as far as QR-codes are concerned (because of the Bubbling-Event-mechanism of the
RC5-WidgetEngine - but also due to the quite powerful Cairo-ImageSurface-Objects, which are used in the
cQREncode/Decode-classes to transport the encoded (or to be decoded) Pixel-Information.

From a cCairoSurface it is possible, to write to PNG-, or JPG-ByteArrays or -Files at any time,
so exporting of the QR-Code-Images is not covered by this Demo - but would require only
a line of Code or two, in concrete adaptions of the above example.

Have fun,

Olaf
Attached Files

[VB6] Virtual 5.0 ListView

0
0
Here is another take on the classic vbVision Virtual ListView from 2001.

It has been substantially reworked to remove the IDE-testing dependency on the old Dbgwproc.dll that most people don't even have installed anymore. This rendition also enables item icons and indenting, minor enough features but easy enough to implement. You could expand this to add column header sort indicators or other features.

This is a UserControl wrapper for the 5.0 ListView that shipped with VB5 and VB6. Comments are left to help you try to modify it for the 6.0 ListView, but as written it works with the 5.0 ListView (COMCTL32.OCX). Since the 5.0 ListView can be upgraded to a Common Controls 6.0 ListView using a simple SxS manifest most people do not use the "Visual Basic 6" MSCOMCTL.OCX anymore anyway though.


Virtual ListView?

The idea here is that unlike in default mode where the ListView stores the item and subitem collections, a virtual ListView relies on your program to maintain the underlying datastore. This buys you a couple of things:

  • No data duplication. The ListView only needs copies of the elements currently being displayed. This can save on RAM, especially for large data sets.
  • Fast scrolling and paging. A virtual-mode ListView can be a performance screamer compared to conventional operation over huge data sets. Assuming of course that you can feed it data quickly!


Huge data sets are the main motivation.

Though it is a really poor design practice, some users will insist they need to be able to scroll over entire massive sets of data. What they want is a "giant" grid view of raw data. Since ListView controls work best in report view when in virtual mode, this is the most likely use case for them.

V50ListView is always in report view.


The Demo

The demo offered here goes beyond that of the 2001 original by showing use with an ADO Recordset returned from a Jet SQL query.

To demonstrate what can be done there are a few silly things shown in it. An item icon is used based on the data in each row, here a happy or sad face is displayed depending on whether a "sale" row was "returned" or not. Every 10th row is indented merely for demo purposes.

This demo project will construct a demo database to use with just one table of 100,000 rows. Because it attempts to make "real looking" data it grinds a bit and can take 30 to 45 seconds to do this step, but the database will be reused on subsequent runs. You can also interrupt database creation and just go on from the point of interruption using the rows written at that point.

You can change a constant at the top of Form1 to create a larger database (500,000 or 1,000,000 rows) but you'll have to wait a little longer.

Name:  sshot1.png
Views: 46
Size:  29.8 KB

Once the ListView is "populated" you can scroll it, use the End, Home, Page UP, Page Down, etc. and see how quickly it can move through the data.

I have also tried a modified version with two V50ListView controls on one form to make sure there are no subclassing collisions, and it seems to work fine:

Name:  sshot2.png
Views: 36
Size:  29.2 KB


Using V50ListView

In the attached archive the V50ListView folder contains the pieces necessary:

  • V50LVSubclasser.bas
  • V50ListView.ctl
  • V50ListView.ctx


Copy those 3 files to your own Project's folder to use them, then add the two modules. There is also a resources subfolder there that holds the V50ListView's ToolBoxBitmap image, but of course that's already embedded in the .ctx file so your new Project doesn't really need it.

In order to detect whether it is running at design-time early enough a small hack is also needed. You can put this into your Project's startup Form:

Code:

Private Sub Form_Initialize()
    V50LVSubclasser.UserMode = True
End Sub

Or if you have a startup Sub Main() you can set this global Boolean there instead.

The ItemDataRequest event and the Items property are key here. Setting Items to the number of rows to be displayed causes V50ListView to start raising the ItemDataRequest event to get data to display.

Hopefully the demo Form1 code is enough to help you see how the ItemDataRequest parameters are used to set text, small icons, and indents.

As it stands you also need to associate the ImageList at runtime if you use small icons. Normally you can do this via the design-time Properties window or the Property Pages of the ListView but I haven't implemented that yet. But most people won't be using many V50ListView controls and often won't need icons anyway. However you can just use something like:

Code:

Private Sub Form_Load()
    Set V50ListView1.SmallIcons = ImageList1
End Sub


Caveats

Any subclassing poses risks during IDE testing. If you wish you could revert to the Dbgwproc.dll technique to make breakpoints a little safer to use.

A good solution might be to move V50ListView into a separate ActiveX Control (OCX) Project and compile it. Then during testing of your main Project use the compiled OCX, and when creating a final production version remove the reference and add the two modules to compile it into the program.


Running the Demo

You might compile it first and run the EXE. This speeds database creation a little bit. ;)

Otherwise it runs fine in the IDE.
Attached Images
  
Attached Files

[VB6] FYI: a better `Property Timer As Single`

0
0
`Timer` global property comes handy for measuring elapsed time or for logging time-stamps. It basically returns number of seconds since midnight with 2 digits precision.

Usually to measure elapsed timer in seconds one can do something like this:
Code:

dblTimer = Timer
...
' Code here
...
Debug.Print Timer - dblTimer

Unfortunately this suffers from `Timer`'s midnight rollover and is not milliseconds precise.

Here is a naive fix for the rollover and a complete fix for the precision too:
Code:

Option Explicit

Private Declare Function GetSystemTimeAsFileTime Lib "kernel32.dll" (lpSystemTimeAsFileTime As Currency) As Long

Private Sub Form_Load()
    Debug.Print Timer, NaiveDateTimer, DateTimer
End Sub

Public Property Get NaiveDateTimer() As Double
    NaiveDateTimer = CLng(Date) * 86400# + CDbl(CStr(Timer))
End Property

Public Property Get DateTimer() As Double
    Dim cDateTime      As Currency
   
    Call GetSystemTimeAsFileTime(cDateTime)
    DateTimer = CDbl(cDateTime - 9435304800000@) / 1000#
End Property

The naive version just multiplies `Date` with number of seconds in a day and adds `Timer` which equals to number of seconds elapsed since `CDate(0)` = #1899-12-30#

The completely fixed `DateTimer` return value has the same semantics but is precise to 5 digits after the floating point i.e. 1/100 of a millisecond precise. Of course it all depends on OS and hardware support but the API call is easy and convenient -- the "hacked" parameter type is the trick here.

Here is how we log current date/time with milliseconds precision in our error reporting code:
Code:

    Debug.Print Format$(Now, "yyyy.mm.dd hh:mm:ss") & Right$(Format$(DateTimer, "0.000"), 4)

    > 2015.01.29 20:17:20.771

Enjoy!

cheers,
</wqw>

[VB6] High Quality Multimodal Printing

0
0
This is a refinement of a .BAS module I answered a question thread with.

Basically the module has some helper functions for printing. These let you print in a non-WYSIWYG manner in a sort of "desktop publishing" layout approach and get decent quality results compared to crude approaches like printing VB Forms. It isn't really a "reporting" technique, though since everything it can print could be taken from databases or files you could use it for some simple kinds of reporting that create more of a "document" than lines of report text.

At this point you can print a number of things with it, each item laid out on a sort of "box" within a page. These things now include:

  • Text (String) data.
  • Images.
  • RichTextBox contents.
  • MSHFlexGrid contents (within limits, if you have too many rows this doesn't work, if it is too wide it doesn't work well).
  • MSChart contents (within limits, you may need to fiddle with more properties for fancy charts).


To get a better idea of what this does you almost have to run the demos. They are easier to test if you have some sort of virtual printer device(s), such as a PDF printer or Microsoft's XPS Document Writer or Office Document Image Writer or something.

They all use the same Form2, which is a simple "printer picker" dialog box.

Demo1 does a little of everything to print a single page. It is more complex than the others, so I recommend you begin by looking at Demo2, the simplest. If you run Demo1 in the IDE you may get a "collating sequence" exception. This is a Jet Text IISAM incompatibility within the VB6 IDE. Just run it a second time. Compiled programs won't have this issue. But Demo1 is a good one to go ahead and print to a physical color printer. The print quality isn't too bad.

Demo2 prints from a RichTextBox loaded with a sample document. All it adds is pagination and page numbering.

Demo3 does the same thing for another sample document. What it adds beyond Demo2 is two-column printing.

Printing an MSChart causes it to "blink out" quite visibly for a bit, and I have no fix yet. However this is probably a small penalty to get better chart printing.


Only tested on Windows Vista and Windows 7.

The attachment has all 3 demo projects and some sample data (which makes it as big as it is).
Attached Files

Simple Delay Sub

0
0
Below is some code that enables you to delay execution for a specified number of milliseconds. It uses DoEvents and Sleep to minimize the CPU load when waiting for the specified time.

This runs in VB5/VB6 and all versions of VBA including 64-bit as found in 64-bit Office 2010 and later. It uses one API call and makes use of a compilation constant "VBA7" to determine if it is being compiled in VBA 64-bit.

Code:

#If VBA7 Then
Public Declare PtrSafe Function timeGetTime Lib "Winmm.dll" () As Long
'Retrieves the number of milliseconds that have elapsed since the system was started, up to 49.7 days
' A bit more accurate than GetTickCount
'http://msdn.microsoft.com/en-us/library/windows/desktop/dd757629%28v=vs.85%29.aspx

Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
' http://msdn.microsoft.com/en-us/library/ms686298(VS.85).aspx

#Else
Public Declare Function timeGetTime Lib "Winmm.dll" () As Long
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If


Public Sub Delay(ByVal DelayMS As Long)
' Delays execution for the specified # of milliseconds.
Dim EndDelay As Long, i As Long, Current As Long
Current = timeGetTime
EndDelay = DelayMS + Current
Do
  Select Case EndDelay - Current ' set how long we put the PC to sleep depends on how long is left
      Case Is < 20:  i = 1 ' sleep in 1 millisecond intervals
      Case Is < 100: i = 10
      Case Is > 110: i = 100
      End Select
  DoEvents
  Call Sleep(i) ' uses less CPU cycles than repeatedly calling SwitchToThread
  Current = timeGetTime
  Loop Until Current > EndDelay
End Sub

Viewing all 1304 articles
Browse latest View live




Latest Images