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!
Related
One common problem we have in our codebase is that people forget to check if a list is empty before using it in an in clause.
For example (in Scala with Anorm):
def exists(element: String, list: List[String]): Boolean =
SQL("select {element} in {list} as result")
.on('element -> element, 'list -> list)
.as(SqlParser.bool("result").single)
This code works perfectly well as long as list has at least one element.
If it has 0 elements, you get a syntax error, which is weird if you're used to other programming languages that would allow this empty list case.
So, my question is: what's the best way to prevent this error from happening?
Initially, we did this:
def exists(element: String, list: List[String]): Boolean =
if (list.nonEmpty) {
SQL("select {element} in {list} as result")
.on('element -> element, 'list -> list)
.as(SqlParser.bool("result").single)
} else {
false
}
This works perfectly well, and has the added advantage that it doesn't hit the database at all.
Unfortunately, we don't remember to do this every time, and it seems that 1-2 times a month we're fixing an issue related to this.
An alternate solution we came up with was to use a NonEmptyList class instead of a standard List. This class must have at least one element. This works excellent, but again, people have not been diligent with always using this class.
So I'm wondering if there's an approach I'm missing that prevent this type of error better?
It looks like you've already found a way to resolve this problem - you have an exists() function which handles an empty list cleanly. The problem is that people are writing their own exists() functions which don't do that.
You need to make sure that your function is accessible as a utility function, so that you can reuse it whenever you need to, rather than having to rewrite the function.
Your problem is an encapsulation problem: the Anorm API is like an open flame and people can burn themselves. If you rely just on people to take precautions, someone will get burnt.
The solution is to restrict the access to the Anorm API to a limited module/package/area of your code:
Anorm API will be private and accessible only from very few places, where it is going to be easy to perform the necessary controls. This part of the code will expose an API
Every other part of the code will need to go through that API, effectively using Anorm in the "safe" way
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".
Is there any difference under the hood between line 4 and line 5?
Why can't VB.net handle Line 3?
What is the proper way to call the function?
Dim aFunc As New Tuple(Of Func(Of String))(Function() "Hello World")
Dim s As String
s = aFunc.Item1() 'does not compile
s = (aFunc.Item1)()
s = aFunc.Item1.Invoke()
This looks like a compiler bug to me, the parentheses should make it unambiguously a method call. Hard to state this for a fact however, parens are heavily overloaded in vb.net to mean many things. Clearly it is the tuple that makes the compiler fumble, it works fine without it. This came up in this week's StackExchange podcast with Eric Lippert btw, you might want to listen to it to get the laundry list of things it can mean.
You could post this to connect.microsoft.com to get the opinion of the language designers. The behavior is certainly unintuitive enough to call it a bug. The workarounds you found are good. Both generate the exact same code and add no overhead, something you can see by running ildasm.exe on your assembly.
aFunc.Item1 is a Function, so you can't assign it to a String. You appear to want:
Dim aFunc As New Tuple(Of Func(Of String))(Function() "Hello World")
Dim s As String
Dim f As Func(Of String) = aFunc.Item1
s = f.Invoke()
EDIT:
s = aFunc.Item1() accesses the property Item1. To invoke the function which that property refers to, you can use s = aFunc.Item1()(), which is equivalent to your line 4. At a guess, property access is stronger than function invocation (if those are the correct terms).
I maintain a program which can be automated via COM. Generally customers use VBS to do their scripting, but we have a couple of customers who use Matlab's ActiveX support and are having trouble calling COM object methods with a NULL parameter.
They've asked how they do this in Matlab - and I've been scouring Mathworks' COM/ActiveX documentation for a day or so now and can't figure it out.
Their example code might look something like this:
function do_something()
OurAppInstance = actxserver('Foo.Application');
OurAppInstance.Method('Hello', NULL)
end
where NULL is where in another language, we'd write NULL or nil or Nothing, or, of course, pass in an object. The problem is this is optional (and these are implemented as optional parameters in most, but not all, cases) - these methods expect to get NULL quite often.
They tell me they've tried [] (which from my reading seemed the most likely) as well as '', Nothing, 'Nothing', None, Null, and 0. I have no idea how many of those are even valid Matlab keywords - certainly none work in this case.
Can anyone help? What's Matlab's syntax for a null pointer / object for use as a COM method parameter?
Update: Thanks for all the replies so far! Unfortunately, none of the answers seem to work, not even libpointer. The error is the same in all cases:
Error: Type mismatch, argument 2
This parameter in the COM type library is described in RIDL as:
HRESULT _stdcall OurMethod([in] BSTR strParamOne, [in, optional] OurCoClass* oParamTwo, [out, retval] VARIANT_BOOL* bResult);
The coclass in question implements a single interface descending from IDispatch.
I'm answering my own question here, after talking to Matlab tech support: There is no equivalent of Nothing, and Matlab does not support this.
In detail: Matlab does support optional arguments, but does not support passing in variant NULL pointers (actually, to follow exactly how VB's Nothing works, a VT_EMPTY variant, I think) whether as an optional argument or not. There is documentation about some null / pointerish types, a lot of which is mentioned in my question or in various answers, but these don't seem to be useable with their COM support.
I was given a workaround by Matlab support using a COM DLL they created and Excel to create a dummy nothing object that could be passed around in scripts. I haven't managed to get this workaround / hack working, and even if I had unfortunately I probably could not redistribute it. However, if you encounter the same problem this description might give you a starting point at least!
Edit
It is possible this Old New Thing blog post may be related. (I no longer work with access to the problematic source code, or access to Matlab, to refresh my memory or to test.)
Briefly, for IUnknown (or derived) parameters, you need a [unique] attribute for them to legally be NULL. The above declaration required Matlab create or pass in a VT_EMPTY variant, which it couldn't do. Perhaps adding [unique] may have prompted the Matlab engine to pass in a NULL pointer (or variant containing a NULL pointer), instead - assuming it was able to do that, which is guesswork.
This is all speculation since this code and the intricacies of it are several years behind me at this point. However, I hope it helps any future reader.
From the mathworks documentation, you can use the libpointer function:
p = libpointer;
and then p will be a NULL pointer. See that page for more details.
See also: more information about libpointer.
Peter's answer should work, but something you might want to try is NaN, which is what Matlab ususally uses as a NULL value.
In addition to using [] and libpointer (as suggested by Peter), you can also try {}.
The correct answer for something in VB that is expecting a Nothing argument, is to somehow get a COM/ActiveX Variant which has a variant type of VT_EMPTY. (see MSDN docs which reference marshaling behavior for Visual Basic Nothing)
MATLAB may do this with the empty array ([]), but I'm not sure.... so it may not be possible purely in MATLAB. Although someone could easily write a tiny COM library whose purpose is to create a Variant with VT_EMPTY.
But if the argument has the [optional] atttribute, and you want to leave that optional argument blank, you should not do this. See the COM/ActiveX docs on Variants which say under VT_EMPTY:
VT_EMPTY: No value was specified. If an optional argument to an Automation method is left blank, do not pass a VARIANT of type VT_EMPTY. Instead, pass a VARIANT of type VT_ERROR with a value of DISP_E_PARAMNOTFOUND.
Matlab should (but probably does not) provide methods to create these objects (a "nothing" and an "optional blank") so you can interface correctly with COM objects.
I have a following code which works fine
MsgBox(AddSomething(Of String)("Hello", "World"))
Public Function AddSomething(Of T)(ByVal FirstValue As T, ByVal SecondValue As T) As String
Return FirstValue.ToString + SecondValue.ToString
End Function
Now we are redesigning the application to work with parameters of different types which will be provided through XML
<SomeValues>
<Add Param1="Somedata" Param2="SomeData" MyType="String"/>
<Add Param1="Somedata" Param2="SomeData" MyType="MyBusinessObject"/>
</SomeValues>
If I try to provide the following it gives error as Of accepts only type
''''Get DetailsFromXml --- MyType,Param1,Param2
MsgBox(AddSomething(Of Type.GetType(MyType))(Param1,Param2))
How to solve this issue.
Edit
The above example is given to make the question simple. Actual issue is as follows
I am using SCSF of P&P.
Following is per view code which has to be written for each view
Private Sub tsStudentTableMenuClick()
Dim _StudentTableListView As StudentListView
_StudentTableListView = ShowViewInWorkspace(Of StudentListView)("StudentTable List", WorkspaceNames.RightWorkspace)
_StudentTableListView.Show()
End Sub
Now I want to show the views dynamically.
Public Sub ShowModalView(ByVal ViewName As String)
Dim _MasterListView As >>>EmployeeListView<<<<
_MasterListView = ShowViewInWorkspace(Of >>>EmployeeListView<<<)("Employee List", WorkspaceNames.RightWorkspace)
_MasterListView.Show()
End Sub
So the part shown using the arrows above has to be somehow dynamically provided.
The point of generics is to provide extra information at compile-time. You've only got that information at execution-time.
As you're using VB, you may be able to get away with turning Option Strict off to achieve late binding. I don't know whether you can turn it off for just a small piece of code - that would be the ideal, really.
Otherwise, and if you really can't get the information at compile-time, you'll need to call it with reflection - fetch the generic "blueprint" of the method, call MethodInfo.MakeGenericMethod and then invoke it.
I assume that the real method is somewhat more complicated? After all, you can call ToString() on anything...
(It's possible that with .NET 4.0 you'll have more options. You could certainly use dynamic in C# 4.0, and I believe that VB10 will provide the same sort of functionality.)
In .Net generics, you must be able to resolve to a specific type at compile time, so that it can generate appropriate code. Any time you're using reflection, you're resolving the type at run time.
In this case, you're always just calling the .ToString() method. If that's really all your code does, you could just change the parameter type to Object rather than use a generic method. If it's a little more complicated, you could also try requiring your parameters to implement some common interface that you will define.
If all you are doing is ToString, then making the parameters object instead would solve the problem in the simplest way. Otherwise you are going to have to bind the type at run-time, which in C# looks like:
System.Reflection.MethodInfo mi = GetType().GetMethod("AddSomething");
mi = mi.MakeGenericMethod(Type.GetType(MyType));
object result = mi.Invoke(this, new object[] { Param1, Param2 });
Because it involves reflection it won't be fast though... but I assume that's not a problem in this context.