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

VB6 - The case for UTF-8

$
0
0
Some people have been critical of the fact that my clsCNG.cls does not preserve Unicode. So with this post, I have attempted to correct that situation. I am by no means any kind of expert on Unicode, and until recently I have only cursed its existence. The Unicode standards are very loose (much like SMTP), but at least there is a fair amount of information out there if you are willing to dig for it.

ClsCNG.cls is a general purpose class designed to perform encryption services on anything that is passed to it. With one small change to the StrToByte routine, it now detects double-wide characters and passes the entire string instead of the just the low order bytes. But with that flexibility comes a new "gotcha". In the image below, you will see the Russian Unicode string does not produce the correct Hash. That is because it is a mixed string, consisting of both ASCII and Russian Unicode. This is not uncommon in HTML code, and this particular string was intercepted from http://www.humancomp.org/unichtm/unichtm.htm using a packet sniffer. There are a couple of ways around that issue. One way is to remove the NULL characters associated with the ASCII characters. The other way is to encode the string using UTF-8. This is the preferred method and is demonstrated using the "Hash UTF-8" button. I should mention at this point that I am using the TextBox provided by the Microsoft Forms 2.0 Object Library to display the Unicode characters. The regular TextBox only accepts ASCII.

The change to the StrToByte routine allowed the implementation of 2 new routines called "ByteToStrShort" and "HexToStrShort". These routines create a string without the intermediate NULL bytes and shorten the process time.

Using UTF-8 introduces another "gotcha". The Unicode standard, and in particular UTF-8, only works with true ASCII characters less than 128 (&H80). If there is any chance that your application could pass ANSI characters above &H7F, you should provide a detection routine to avoid passing it to "clsCNG.cls". DO NOT use "StrConv", as it will cause problems, especially if you are using a non-Latin System Locale.

That's the easy part. Recognizing an incoming byte string as Double-wide Unicode or UTF-8 is difficult to say the least. There is no standard methodology to deal with it. HTTP and XML will announce their intention to use UTF-8. For the Russian page below, the line:
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
was provided. There is another methodology called BOM which is sometimes employed. It stands for Byte Order Mark, and is used to specify "Big Endian" or "Little Endian" order for encoded strings. Since UTF-8 uses bytes instead of words, Endian has little meaning, and it is often referred to as a "UTF-8 Signature" (EF BB BF). HTML5 requires an application to respect it, and it takes precedence over the notification. Unicode standards do not restrict or require it's use, but if you are building an application where you can control both ends, it would make sense to use it. In either case, your application should be prepared to recognize and remove it before display.

Mozilla (and I assume other browsers as well) will use the information provided to determine the type of encoding used on incoming data, and if that fails or is not provided, it then uses a heuristic approach. So I set out to provide my own routine to detect UTF-8. My first reaction was to question the need to scan the data twice. If you are going to convert the string if UTF-8 is detected, why not just attempt to convert the string and respond to any errors. Unfortunately, MultiByteToWideChar does not return encoding errors; it just does the best that it can. So the scan is necessary to detect if the incoming string is indeed UTF-8. The IsUTF8 routine is my interpretation of a C++ routine that I found on the net. It has not been tested extensively, and it could probably be executed more efficiently. Determining if an incoming string is Unicode or not is a different story, and I have not found a reliable way to do that. I tested the Microsoft "IsTextUnicode" function, but as most of the literature indicated, it is virtually useless.

I discovered another "gotcha" with MultiByteToWideChar. It will return NULL characters at the end of the string, depending on the length. That is not a problem with C++, as NULL characters signify the end of the string. But with VB, that is a problem because it identifies the string length in it's definition. So the FromUTF8 routine was modified to remove any NULL characters.

If you convert the Russian sample, you will notice that the UTF-8 string is shorter than the original (due to ASCII NULL removal), but the Chinese sample converts to a longer string. That is because the Chinese sample converts 2 byte characters to mostly 3 byte characters. Even considering the downsides, UTF-8 appears to be the most logical solution.

J.A. Coutts
Attached Images
 
Attached Files

Here's my non CopyMemory based solution for swapping byte order

$
0
0
Often times I find myself trying to write a file for another program to read, or read a file written by another program, in which that other program uses Big Endian byte order, even though VB6 uses Little Endian byte order. I've used CopyMemory solutions before, which are easy to write as you don't have to think about how to multiply and divide in order to perform bit shifts. I've even written a DLL file in assembly language (see this thread http://www.vbforums.com/showthread.p...89#post5000089 ) which has functions that directly use the SHL and SHR bit shifting opcodes, but then I need to make sure my DLL file is always packaged with my VB6 program or the program won't run on somebody else's computer when they try to run it. So below is my code that uses only mathematical operations to rearange the bytes in Integer and Long data types in order to swap byte order. Any Little Endian number will be converted to Big Endian, and any Big Endian number will be converted to Little Endian.

Code:

Private Function FixInteger(ByVal Value As Integer) As Integer
FixInteger = (Value And &H7FFF) \ &H100
If Value < 0 Then FixInteger = FixInteger Or &H80&
FixInteger = FixInteger Or (Value And &H7F&) * &H100
If Value And &H80 Then FixInteger = FixInteger Or &H8000
End Function

Private Function FixLong(ByVal Value As Long) As Long
FixLong = (Value And &H7FFFFFFF) \ &H1000000
If Value < 0 Then FixLong = FixLong Or &H80&
FixLong = FixLong Or (Value And &HFF00&) * &H100&
FixLong = FixLong Or (Value And &HFF0000) \ &H100&
FixLong = FixLong Or (Value And &H7F&) * &H1000000
If Value And &H80& Then FixLong = FixLong Or &H80000000
End Function

Change Private to Public if you are going to put these function definitions in a Module and plan to call them from elsewhere in your program.


The one type of thing that you CAN'T swap byte order with, using only mathematical techniques, is a floating point (Single or Double) variable. Currency type variables MIGHT be able to be byte swapped with purely mathematical techniques, but it would be quite difficult, due to the multiple of 10000 that you need to take into account. Short of using a dedicated DLL file, or a CopyMemory technique, floating point values will remain Little Endian when working with VB6.

VB6 - DNS Filter Service

$
0
0
Version 2.5 of DNSFilSvc utilizes version 3.2 of WinpkFilter from NT Kernel Resources. This version of WinpkFilter is not compatible with older versions because it supports both IPv4 and IPv6. The driver has been signed with a Microsoft approved certificate, so it now loads on all versions of Windows. Although the driver supports IPv6, DNSFilSvc does not. It uses long words (4 bytes) to store IPv4 addresses, whereas IPv6 requires 16 bytes for each address.

Like Version 2, DNSFilSvc consists of 2 programs; the actual service, and a management program to load and manage the service. Although the service can install itself, the management program is needed to store a couple of parameters. Because the Service runs in Session 0, the Registry values must be placed in the Registry in a location that allows System access. Because the management program runs in Session 1 or more, it has no actual interaction with the service. It deals entirely with the Service Manager (services.msc), and because it accesses restricted parts of the Registry, the management program must be "Run as Administrator".

Why would you need this kind of filter? If you operate a DNS Server, hackers can use that server to launch DoS (Denial of Service) attacks against other networks. Because DNS uses UDP packets on port 53, the advertized IP addresses where responses are sent, is not necessarily where the requests originated from. They can be spoofed. How do we know this? Here is just one example:
21:23:25 Request from 99.239.40.201 for any record for isc.org.
Doing an online port scan on this address:
Domain: CPE6c198ff33353-CM00fc8db88650.cpe.net.cable.rogers.com
IP Address: 99.239.40.201
City: Barrie
Region: ON
Country Name: Canada
Country Code: CA CA

Port Type Status
53 domain Closed
This machine does not even offer DNS service (at least not to the outside world).

DNSFilSvc filters out the excess requests. However, hackers figured out that they could get around the filter by slightly modifying each request. In this example:
00:19:11 Request from 183.56.172.145 for A-record for 6483220-0-3073944721-3608005795.ns.183-56-172-145-ns.dns-spider.myxns.cn.
the first number (6483220) was incremented for each request. So I added a DropList feature, where addresses for abusers could be manually added. The DropList file will be created in the same directory as the service the first time the service is activated. After adding an address to the file, the service must be restarted. The first 3 addresses I added to my own file were for a German provider that refused to address the over 13,000 requests per day that their servers were sending to our server (and those were just the ones that made it past the filter). All 3 servers had been blocked twice by our DNS server for sending over 20 requests per second, and after twice warning them, they are now permanently blocked. Any requests from these 3 servers are simply dropped by the filter.

This filter would not be suitable for a high volume server. For that kind of server, direct use of the Kernel Mode driver would be more appropriate.

Note: DNSFilSvc was designed to be run in Development mode as well as a Service. To compile the service, change the IsService flag to True.

J.A. Coutts
Attached Images
 
Attached Files

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

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

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

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



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

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

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

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

AnyArrayToBytes = Bytes()
End Function



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

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

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


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

StdDataFormat as a data parser

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

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


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

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

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

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

See the screen shot:

Name:  sshot.png
Views: 55
Size:  18.6 KB

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

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

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

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


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

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

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

VB6 Threading-Examples using the vbRichClient5 ThreadHandler

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

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

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

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

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

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

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

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

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

Option Explicit

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

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

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

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

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

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

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

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

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

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

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

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

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

Forgot to attach a ScreenShot of the Output produced by the Form_Click-Event above:


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

Olaf
Attached Files

Visual Basic Advance Timer

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

Here it all now :

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


Name:  TimerCodePreview.jpg
Views: 63
Size:  45.5 KB

Code:
Code:

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

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

Happy Coding…:)


Example is coded in following Post
Attached Images
 

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

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

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

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

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

Licence: Do whatever you want with the source, but a shout in your "about" would be great :bigyello:
Attached Files

[VB6] GetAdaptersInfo Example

$
0
0
See GetAdaptersInfo function for the details.

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

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

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

Code:

2 adapter(s) found.

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

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

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

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


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

VB6 - NewSocket 2.5

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

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

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

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

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

J.A. Coutts
Attached Files

[vb6] AddressOf for Class, Form, UC methods

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

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

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

CODE REMOVED. I could not get a reliable function address of a specific private nor public method other than the final private method in all VB code pages.

I concede and tip my hat to Paul Caton whose method seems to be the most reliable. At this point, I cannot top it, except for one specific case: classes. Unfortunately, my logic will not work reliably in any other code page that is built on other interfaces: form, usercontrol, property page, etc.

If interested in Paul Caton's method, it is a matter of googling for "self-subclasser Paul Caton".

I'll ask the moderators to remove this thread.

[vb6] - Filter Listview User control

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

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

Description:

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

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

simple but very useful.

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

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

The filter popup has a help message with more details.

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

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

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

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


examples:

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

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

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

Name:  filterList.jpg
Views: 49
Size:  38.6 KB
Attached Images
 
Attached Files

vb6 - OCX: Javascript Engine, debugger, IDE

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

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

Name:  screenshot.jpg
Views: 105
Size:  30.6 KB

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

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

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

https://github.com/dzzie/scivb2

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

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

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

http://sandsprite.com/blogs/index.php?uid=7&pid=361
Attached Images
 

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

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

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

Code:

Dim myWMI As Object, myObj As Object, Itm

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

[VB6] ProgramData for common files

$
0
0
The Wild West

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

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

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

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


What To Do?

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

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

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

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


How To Do It?

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

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

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


Demo

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

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

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

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

Name:  FolderStructure.png
Views: 165
Size:  12.3 KB

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

Code:

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

Beyond ProgramData

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

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


Installed Programs

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

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

It's as easy as that!
Attached Images
 
Attached Files

[VB6] MapStitcher.cls - Create imagemaps from images

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

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


Limitations

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

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


Demo

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

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

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


Name:  sshot1.png
Views: 76
Size:  14.0 KB

Oops! One image got cut off.


Name:  sshot2.png
Views: 70
Size:  14.5 KB

Changed dimensions to match the large image


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

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

Name:  sshot3.png
Views: 68
Size:  45.1 KB


Summary

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

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

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

Using a VB6 Collection in C++

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

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

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

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

Vb example:
Code:

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

Private Sub Command1_Click()

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

C++ Dll Code
Code:

#include "msvbvm60.tlh"

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

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

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

#import "msvbvm60.dll" no_namespace

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

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

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

col_dll.zip
Attached Files

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

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

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

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

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

Vb example:
Code:

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

Private Sub Command1_Click()

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

C++ Dll Code
Code:

#include "msvbvm60.tlh"

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

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

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

#import "msvbvm60.dll" no_namespace

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

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

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

ChaCha streaming cipher

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

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

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

Example usage:
Code:

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

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

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

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

ChaCha implementation license
Code:

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

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

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

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

Attached Files

VB6 - Unnamed Encryption Routine

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

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

J.A. Coutts
Attached Images
 
Attached Files
Viewing all 1325 articles
Browse latest View live




Latest Images