TL:DR; How can I compile a VB6 module file into a standard DLL which I can use across multiple VB6 applications?
I am tasked with the support of multiple legacy applications written in VB6.
All of these applications make use of piece of hardware constructed by my employer. Before I came on to work for my employer, he had outsourced the work of developing a DLL for the project to a company that is no longer capable of supporting it since the individual working for THEM recently quit and no one else is capable of figuring it out.
My employer has recently upgraded our hardware, so even worse - the DLL that Company furnished us with is no longer useful either.
Further exacerbated by the fact that the company who released to us the NEW hardware did not release to us a DLL file which is capable of running in VB6.
It now falls to me to create a DLL file ( NOT a device driver ) which is capable of facilitating communications between the new ( and hopefully the old ) devices and VB6 applications.
My knowledge of VB6 is... limited, at best. I am mostly familiar with .Net and have had much success in creating DLLs in .Net, but when it comes to VB6, I know enough to get by. I'm entering into uncharted territory here.
I'm well acquainted with the HID.dll and the SetupAPI.dll P/Invokes and structs necessary to make this work, and I was even fortunate enough to stumble upon this, which had a working bit of VB6 code which facilitates read/writing to/from HIDs connected to the system. I tested this and ( with a bit of fidgeting ) it worked for our device out of the box. But that doesn't help me because I can't compile the module into a DLL file ( let alone figuring out events in VB6 and a truck load of other things, but I'm getting ahead of myself ).
I've read and tried a few different methods and while they proved promising, they didn't work.
Google has also inundated me with a lot of red herrings and been in general not very helpful.
If necessary, I would even write it in C/C++ ( though I'd rather not if there is some other way ).
So is what I am trying to do possible? Can someone direct me to some step-by-step for this sort of thing?
EDIT 1 :
To expound a bit, when I say that "they didn't work", what I mean is that in the case of the first link, the program still failed to find the function ( with an error message like "Function entry point not found" ) and in the second case I consistently and repeatedly received a memory write error when trying to call the function ( not fun ).
Here's a link to a way to do a standard DLL, that looks more straightforward than the links you've posted. I can say that if Mike Strong ("strongm") posts code, it works, too. You might want to have a look at it.
However, it's probably better to use COM if you're able: it's easier to set up (obviously) and it also has some standard capabilities for keeping track of the object's interface, that are built into VB6. For example, when you use the TypeOf keyword, VB6 actually makes an internal call to the object's QueryInterface method, which is guaranteed to exist as one of the rules of COM (and, if you use the keyword on a reference to a standard DLL object you'll get an error).
VB6 does "static" classes by setting the class's Instancing property to GlobalMultiUse. Warning: the "static" keyword has an entirely different meaning in VB6: static local variables' values persist between method calls.
1. After your trip to 1998 to get your copy of VB6, start a new ActiveX DLL project:
2. Edit Project Properties for the name of the beast.
3. Add a Class for the interface you are creating. I cleverly named the class VB6Class because the project/DLL is named VB6DLL.
4. Write code. I added some test methods to perform complex calculations:
Option Explicit
Public Function GetAString(ByVal index As Integer) As String
Dim ret As String
Select Case index
Case 0
ret = "Alpha"
Case 1
ret = "Beta"
Case Else
ret = "Omega"
End Select
GetAString = ret
End Function
Public Function DoubleMyInt(ByVal value As Integer) As Integer
DoubleMyInt = (2 * value)
End Function
Public Function DoubleMyLong(ByVal value As Long) As Long
DoubleMyLong = (2 * value)
End Function
5. Make DLL from File menu. You may need to be running As Admin so it can register the DLL.
6. In the project which uses it, add a reference to the DLL.
Test code:
Private Sub Command1_Click()
Dim vb6 As New VB6DLL.VB6Class
Dim var0 As String
Dim var1 As Integer
Dim var2 As Long
var0 = vb6.GetAString(0)
var1 = vb6.DoubleMyInt(2)
var2 = vb6.DoubleMyLong(1234)
Debug.Print "GetAString == " & var0
Debug.Print "DoubleMyInt == " & var1
Debug.Print "DoubleMyLng == " & var2
End Sub
Result:
GetAString == Alpha
DoubleMyInt == 4
DoubleMyLng == 2468
Not sure what to do about the "truck load of other things".
Related
How to transfer this code from C# to VB.net?
This is from C#
if (local > 0)
{ //Local patches set offsets to data located elsewhere in this section
IntPtr start = data + section->_localPatchesOffset;
LocalPatch* patch = (LocalPatch*)start;
while ((int)patch - (int)start < local && patch->_dataOffset >= 0)
{ //Make the pointer offset relative to itself so it's self-contained
int ptrOffset = patch->_pointerOffset;
int* ptr = (int*)(data + ptrOffset);
*ptr = patch->_dataOffset - ptrOffset;
patch++;
}
}
Or I have this sample from C:
What exactly does it mean (byte*) in C
For example I have some byte array (like MemoryStream loaded from hole file....)
Dim arr As Byte() = New Byte() {1, 2, 33, 4, 55, 6, 7, 8, 9, 10, 10, 114, .....}
For example 33 is (at fromoffset 3) and 114 is (at tooffset 12)
This is not I think... Arr(fromoffset) = Arr(tooffset)
This is from C:
*(byte**)(SectionStart + LF->fromOffset) = SectionStart + LF->toOffset;
As jmc points out, you won't be able to convert that code to vb then compile it. I think your only option will be to make a c# DLL project, put that code into it (with any extra code it needs to function) as a method and then compile the c#
After you've done this you can reference the DLL, or you can even add the C# project to your solution continuing your VB item, and reference the project. The whole lot will compile and even the debugger will happily step from your VB.NET code into the c# and back again; these are not two separate languages when compiled - you could think of each language as merely being a whole lot of syntactic sugar for IL; but as VB simply doesn't have the sugar for unsafe stuff you'll have to do it in C#
The (byte*) is a pointer to a function with a parameter that is a pointer to a pointer of type byte.
Regarding unsafe pointers, the following is an excerpt from my Enhancing Visual Basic Book:
A Note on Recovering VarPtr, ObjPtr, StrPtr, VarPtrArray, and VarPtrStringArray.
If upgraded VB6 code required the “undocumented” VB6 functions VarPtr, ObjPtr, StrPtr, VarPtrArray, or VarPtrStringArray, use this one VB.NET function to duplicate all of them:
Imports System.Runtime.InteropServices
'--------------------------------------------------------------------------------------
' This Module Provides the memory addresses of Numeric Variables, Objects, and Strings.
'--------------------------------------------------------------------------------------
Module modVarPtr
'VB.NET version of VB6 VarPtr, ObjPtr, StrPtr, etc. (ALL of them are now supported by this one function).
'----------------------------------------------------------------------------------------------------------------------
'NOTES: Strings are not BSTR, so this returns the text address, not a BSTR pointer to the address pointing to it. This
' provides C# 'Unsafe Pointers' for VB.NET! Use the returned address right away before the Garbage Collector
' tries to change it! If we must hold it a short time, do not execute the GC.Free() instruction until done.
' We can break this code out in-line, using GC.Free() once we are done, but do not go on Break in the meantime.
' Garbage Collection will be held up until we GC.Free(), until then preventing the release of unused objects.
'----------------------------------------------------------------------------------------------------------------------
Public Function VarPtr(ByVal o As Object) As IntPtr 'Object 'catch all' (Return can also be 'As Integer').
Dim GC As GCHandle = GCHandle.Alloc(o, GCHandleType.Pinned) 'Get a trackable handle and pin (lock) the o address.
VarPtr = GC.AddrOfPinnedObject 'Get an IntPtr to the pinned o (var's data address).
GC.Free() 'free the allocated space used and unlock o address.
End Function 'Be aware IntPtr is 32-bit only on x86 compiles, otherwise it is 64-bit on 64-bit systems.
End Module
I am amaze so many VB6 users demanded a full VB OOPL environment with absolutely no sacrifice of OOPL functionality or data safety in the then-proposed VB.NET in 1999, with fist-shaking threats to abandon VB if their demands were not met. Yet, when they got exactly that, they had the gall to whine about the loss of the unsafe VarPtr, which did not jive with OOPL or data safety, but threatened user-demanded specifications. What was the result? They again threatened to abandon VB. There is a funny anecdote to describe such people, but I doubt they would appreciate it. Me? I love making fun of myself. I am a geek, and my jokes about geeks are merciless!
Finally, notice VB.NET Varptr again allows us to directly copy structure object data using the CopyMemory Pinvoke. However, even though we can grab class object memory addresses as well, we cannot use CopyMemory to copy data from or to it due to violating protected memory. Although some creative developers have managed to achieve this using intermediate memory areas, we can actually bypass employing intermediate buffers and use the .NET methods they recommend, the StructToPtr and PtrToStructure marshalling methods, to actually copy data directly to and from our local memory data and class objects. Even so, because these Unsafe Pointers are an allowed extension of OOPL rules, Microsoft should consider adding unsafe pointers to VB.NET.
VERY LONG story short, I'm trying to develop a MS Excel AddIn to allow an uneducated user (open to interpretation) to create Excel-based scripts that Visual Basic (yeah not C#) can essentially parse into the individual pieces and send to the browser via Selenium commands. So far, I've had quite a bit of success. Granted there is a little bit clunkiness, but this is round 1, and I've only been working with Selenium for a week.
Up to this point I have been able to use the CallByName function to call various Selenium methods where each _argument is passed from a higher level handler originating from values in spreadsheet cells.
Dim eleActionElement as Remote.RemoteWebElement = Nothing
eleActionElement = driver.FindeElement(By.Id("PresentObjectID"))
CallByName(eleActionElement, _strAction, CallType.Method, _strActionArg)
Initially I had problems with Bys:
Dim myBy As By = CallByName(By, _strBy, CallType.Method, _strByArg)
Intelisense warns that "By" is a class type and cannot be used as an expression. Fortunately, there are individual methods for each by type, so I have been able to retrieve elements effectively through:
Public Function DriverFindElementBy(_strBy As String, _strByArg As String) As Remote.RemoteWebElement
Dim strElementBy As String = "FindElementBy" & _strBy
Dim eleWebElement As Remote.RemoteWebElement = Nothing
eleWebElement = CallByName(ThisAddIn.driver, strElementBy, CallType.Method, _strByArg)
Return eleWebElement
End Function
Unfortunately, I haven't found a way to get ExpectedConditions to work. My best guess (of many) is:
Public Function WaitOnCondition(_strCondition As String, _strBy As String, _strByArg As String)
Dim myExpectedConditionObj As ExpectedConditions
CallByName(myExpectedConditionObj, _strCondition, CallType.Method)
waitDefault.Until(CallByName(myExpectedConditionObj, _strCondition, CallType.Method))
If strTestResult.Length = 0 Then
Return "Success"
Else
Return strTestResult.ToString
End If
End Function
Intelisense warns this could result in a NullReference and it does. I can see how the object is not instantiated yet, but the same approach worked with the RemoteWebElement. If studied the Selenium docs, and both are class types; so I'm left thinking the problem has to do with various methods of the ExpectedConditions and By classes take and return different numbers and types of arguments. It may be because both classes are delegates. It may be some simple error I'm making - although, I've written and rewritten these functions a dozen times with the recurring thought, "geez, this should work." One of those times you'd think I'd have blindly gotten it right.
I'm certainly not a .Net expert and advanced techniques can be baffling at times, but I do study the solutions people provide on SO and often have to go and expand my skills; so please know that nay help will be appreciate and will not go in vain.
One tiny request, if you give a C# (or Java or Python) explanation, just let me know if you KNOW / DON'T KNOW or AREN'T SURE if it will work in VB. My biggest challenge in advanced topics is that narrow segment of stuff that doesn't crossover between C# and VB.
THANKS in advance!
So I'm writing a standalone JScript file to be executed by Windows Script Host (this file is not going to be used as a web application).
My goal is to load a dll file. Just like using LoadLibrary function in a C++ application.
I tried researching the subject but I didn't come up with anything useful. I'm so lost I don't have any piece of code to share. I understand using ActiveXObject may come to my rescue. if so, any idea how to use it?
Update:
If we all agree that loading is impossible, I'll settle for validity check. Meaning, don't try to load but check if it is loaded and functional.
You can export a specific function for this purpose.
Then, from your JScript, execute rundll32.exe and check that the function ran as expected.
You might also give Gilles Laurent's DynaWrap
ocx a chance.
This kind of dll needs to be registered on the target system like regsvr32 /s DynaWrap.dll.
It is restricted to 32-bit DLLs, and this might be inconvenient for you to use, but it works on a 64bit Windows. You can't access function exported by ordinal number and you can't directly handle 64bit or greater values/pointers.
Here's a sample to call MessageBoxA from JScript:
var oDynaWrap = new ActiveXObject( "DynamicWrapper" )
// to call MessageBoxA(), first register the API function
oDynaWrap.Register( "USER32.DLL", "MessageBoxA", "I=HsSu", "f=s", "R=l" )
// now call the function
oDynaWrap.MessageBoxA( null, "MessageBoxA()", "A messagebox from JScript...", 3 )
And here from VBScript:
Option Explicit
Dim oDynaWrap
Set oDynaWrap = CreateObject( "DynamicWrapper" )
' to call MessageBoxA(), first register the API function
UserWrap.Register "USER32.DLL", "MessageBoxA", "I=HsSu", "f=s", "R=l"
' now call the function
UserWrap.MessageBoxA Null, "MessageBoxA()", "A messagebox from VBScript...", 3
To use a function you need to "register" the exported function of your DLL.
To do this you need to call the register method with a first parameter containing a string object to the complete path of the DLL, a second parameter for the exported name of the function to use, and following three paremeters describing the functions declartion in a somehow obscure syntax.
i= describes the number and data type of the functions parameters.
f= describes the type of call: _stdcall or _cdecl. Default to _stdcall.
r= describes the return values data type.
The supported data types are:
Code Variant Description
a VT_DISPATCH IDispatch*
b VT_BOOL BOOL
c VT_I4 unsigned char
d VT_R8 8 byte real
f VT_R4 4 byte real
h VT_I4 HANDLE
k VT_UNKNOWN IUnknown*
l VT_I4 LONG
p VT_PTR pointer
r VT_LPSTR string by reference
s VT_LPSTR string
t VT_I2 SHORT
u VT_UINT UINT
w VT_LPWSTR wide string
Thus the Register method call used in the examples describes MessageBoxA like this:
_stdcall LONG MessageBoxA( HANDLE, LPSTR, LPSTR, UINT );
For a explanation of MessageBoxA look at the docs on MSDN.
Please read the DynaWrap docs for more sophisticated examples... But you might need Google translate, 'cos they are written in french ;-)
To be able to use a dll as ActiveXObject, it needs to be registered as COM object. There are some restrictions on this but if you have a code for this dll, it is certainly doable.
When you register your dll as COM object, it is assigned a name. You use this name to create an object. This example from MSDN uses excel since it is already registered if you installed office.
var ExcelApp = new ActiveXObject("Excel.Application");
var ExcelSheet = new ActiveXObject("Excel.Sheet");
// Make Excel visible through the Application object.
ExcelSheet.Application.Visible = true;
// Place some text in the first cell of the sheet.
ExcelSheet.ActiveSheet.Cells(1,1).Value = "This is column A, row 1";
// Save the sheet.
ExcelSheet.SaveAs("C:\\TEST.XLS");
// Close Excel with the Quit method on the Application object.
ExcelSheet.Application.Quit();
Apart from restriction of registering dll, using dll is no different from using it as c++ or c# dll. Note that, C# (or other .NET dlls) should be ComVisible to be used from javascript this way.
EDIT: The only other way of using C/C++ dll from javascript is swig interfaces. I have not used it, therefore I can only point you in that direction.
SWIG is a software development tool that connects programs written in
C and C++ with a variety of high-level programming languages. SWIG is
used with different types of target languages including common
scripting languages such as Javascript, Perl, PHP, Python, Tcl and
Ruby.
Passing commands as variables. I am creating a POP3 client and was crunching through some code when I though of something interesting.
Is it possible to pass strings of vb code to an object so that the object will execute it. I am relatively familiar with vb.net's source code being converted to Intermediate language and then being thrown into a JIT virtual machine, but I was hoping there was a simple way to implement this idea.
I want to be able to use strings
Dim Command as string
Command = "If a + b > 0 then c = a + b" '<----syntactical sugar!
System.Compiler.Something.execute(command)
If anyone has any direction, or any correction to any of the above. I appreciate your time.
Rah!
As a start you may want to check out the open source FileHelpers project.
The Runtime.ClassBuilder class in that project creates a .Net class from text, which can then be used as a normal class.
I'm a C++ programming who was tapped to write a small application in Visual Basic. The application hosts an IronPython runtime and I am attempting to define some function in python and then call them from VB. I have written a simple test function in python
def test():
print "Test was Called"
Then I use the iron python to create a ScriptSource from the python file. I am able to look up the "test" variable through object operations but I can not figure out how to call the object that. For example (in VB):
pyScope = engine.CreateScope()
pySource = engine.CreateSourceFromFile("C:\Some\File\path\test.py")
pySource.Execute(pyScope)
' Now I expect the function test() to be defined in pyScope
Dim tmp as Object
pyScope.TryGetVariable("test", tmp)
At this point in my code tmp is defined as an object of type PythonFunction. I can't figure out how to call this function.
tmp()
Is not valid VB syntax. I've gotten this far, now how do I perform this seemingly simple task?
Edit: By calling
pyEngine.Operations.Invoke(tmp)
I am able to call the function and I see the expected output at stdout. I am still under the impression that there is some function-pointer-like type that I can cast objects of type PythonFunction to which will let me invoke temp directly without calling to the Python engine.
Not sure this will work, but try casting it to an Action type:
DirectCast(tmp, Action)()
Based on the comment, try this:
engine.ObjectOperations.Invoke(tmp, Nothing)
VB in .NET 4 should have the same dynamic support as C#. According to http://msdn.microsoft.com/en-us/library/ee461504.aspx#Y5108 (near the bottom), you should be able to do:
Dim tmp As Object = scope.GetVariable("test")
... which is what you're already doing, so make sure you're targeting .NET 4.
If that doesn't work you should be able to cast it with the generic version of GetVariable:
Dim tmp As Action = scope.GetVariable(Of Action)("test")
Finally, you already discovered Invoke on ObjectOperations.
(You may need to tweak the syntax, since I don't know VB.)