I'm in the process of migrating a VB.NET web application from Visual Studio 2005 (.NET 2.0) to Visual Studio 2008 (.NET 3.5) and while it was mostly straightforward I encountered a problem which took some time to resolve.
The code in question reads:
Dim serviceArray = New SecurityLayer.Model.Service()
serviceArray = new SecurityLayer.SecurityBusinessController.GetServices(userId)
Which compiles in VS2005/.NET2.0 but fails in VS2008/.NET3.5 with the following error:
Value of type '1-dimensional array of SecurityLayer.Model.Service' cannot be converted to 'SecurityLayer.Model.Service'
This indicates that serviceArray is not declared as an array and Reading the MSDN documentation it does not look like the syntax has changed between the versions but it states that curly braces are required whether any values are being passed in or not. Sure enough, adding curly braces to it's declaration resolves the problem (and the compiler moves onto the next instance!).
Dim serviceArray = New SecurityLayer.Model.Service(){}
serviceArray = new SecurityLayer.SecurityBusinessController.GetServices(userId)
After updating all of the instances of this declaration the code now builds and runs as expected.
Option Explicit and Option Strict are the same in both IDEs so it can't be that (or at least that's what I'm assuming).
So my question is, why did this build in VS2005/.NET2.0 and not in VS2008/.NET3.5?
Thanks in advance
VB.NET version 9 acquired type inference. Previously your Dim declaration was untyped, serviceArray was of type Object. Now, the compiler inferred from your previous usage that serviceArray is of type Service. Using the same variable to store different objects is fishy.
Related
Consider the following line of code which used to compile without warnings.
Public SetUpDone = False
After upgrading a project to Visual Studio 2017 from Visual Studio 2005 over a hundred of these BC42020 warnings exist. The MSDN description of the warning simply states that the variable defaults to type object. I don't have a good idea of the seriousness of this type of warning. The debugger indicates that the code executes as I expect. Is it merely a performance type of issue?
Secondly, I thought that Visual Basic supported some form of Type Inference so I'm not clear about why it wouldn't be able to deduce that the type should be Bool.
Another example is the following where the function returns a String
Dim dayTxt = " " & GetTextFromIni("General", "Var50")
I would have thought that type inference would work here and deduce that dayTxt is a String.
This:
Public SetUpDone = False
Is equivalent to this:
Public SetUpDone As Object = False
As suggested, type inference is only for local variables, not fields. With Option Infer On, this inside a method:
Dim SetUpDone = False
would indeed be equivalent to this:
Dim SetUpDone As Boolean = False
There are a couple of issues with the code as you have it. Firstly, it means that every use of that False value requires unboxing which makes your code slower. That's the case for any value types, i.e. structures. Value types are normally stored on the stack but, when boxed, are stored on the heap.
Secondly, it means that any member access will require late binding. That's not an issue for Boolean values because they have no members of interest anyway but if it was, say, a DateTime then the IDE would never provide Intellisense for that type because al it would see would be type Object and the existence of the specified member would have to be confirmed at run time, making the code less efficient again.
Thirdly, it means that the compiler can never confirm that you're passing the correct type as a method argument. For instance, if you have a method with a Boolean parameter, the compiler won't know that you're passing a Boolean if you pass that field because it's type Object. That also means that if you pass some other Object variable that doesn't contain a Boolean, the compiler can't warn you.
As suggested, you should turn Option Strict On in the project properties. That will flag every instance of you're not specifying the appropriate type for something. Fixing those errors will, at the very least, make your code a bit more efficient. It may even draw your attention to situations where exceptions will or could be thrown at run time. Having Option Strict On enforces strict typing so it makes you think more about the types you're using. Even if you're conscientious about that with Option Strict Off, you can still make mistakes that Option Strict On will prevent.
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".
Question
How can I rewrite the below Moq tests so they work again in Roslyn? Obviously I could remove the Lambda, but I was hoping to avoid that.
Also, for interest sake: did the Roslyn compiler fix a bug that allowed these invalid statements to work previously or is this a compiler bug that now breaks these statements?
Details
I'm trying to move my mostly VB.NET solution to Visual Studio 2015, from Visual Studio 2013. All projects in the solution are targeting .NET 4.5. I am currently using Moq 4.0 for these tests. I have several Moq based Lambda unit tests that fail to compile in Visual Studio 2015, but compile and run fine in Visual Studio 2013. These tests have worked in Visual Studio 2010 and Visual Studio 2012 as well.
Most of the tests are very simple and look something like this:
Private _view As Mock(Of Views.ICreateSecurityUserView)
<Test>
Public Sub ValidateSave_CallWithBlankLogin_SetsViewToolError()
_view = New Mock(Of Views.ICreateSecurityUserView)()
_view.SetupGet(Of String)(Function(x) x.Login).Returns("")
_view.SetupGet(Of String)(Function(x) x.LoginName).Returns(loginNameValue)
_subject.ValidateSave()
_view.Verify(Sub(x) x.LoginFieldError = It.Is(Of String)(Function(s) Not String.IsNullOrEmpty(s)), Times.Once)
End Sub
The offending line will be this one: _view.Verify(Sub(x) x.LoginFieldError = It.Is(Of String)(Function(s) Not String.IsNullOrEmpty(s)), Times.Once)
The build error I get (as in the title) is:
Error BC36534 Expression cannot be converted into an expression tree.
I've fiddled with the expression a bit, seeing if the compiler would be happier if it was multi-line:
_view.Verify(Sub(x)
x.LoginFieldError = It.Is(Of String)(Function(s)
Return Not String.IsNullOrEmpty(s)
End Function)
End Sub, Times.Once)
But to no avail, since this just adds these extra errors (the Visual Studio 2013 compiler also doesn't like the multi-line version):
Error BC36675 Statement lambdas cannot be converted to expression trees.
Bad "Solution"
I am able to get this compiling if I change the test line to:
_view.Verify(Sub(x) VerifyFunctionNameError(x), Times.Once)
Which then calls the new, pointless, function:
Private Sub VerifyFunctionNameError(x As Views.ICreateSecurityFunctionView)
x.FunctionNameError = It.Is(Of String)(Function(s) Not String.IsNullOrEmpty(s))
End Sub
Your question is hard to answer, I can't get a decent repro for this without knowing the declarations. Nevertheless, there is certainly a hint why this should not compile from the VB.NET Language Specification. Find a copy of it on your machine in C:\Program Files (x86)\Microsoft Visual Studio 14.0\VB\Specifications\1033\Visual Basic Language Specification.docx
Navigate to chapter 11.1.1 and scroll down a bit to arrive at the Annotation block. I'll quote the most relevant part:
The exact translation between lambda methods and expression trees may not be fixed between versions of the compiler and is beyond the scope of this specification. For Microsoft Visual Basic 11.0, all lambda expressions may be converted to expression trees subject to the following restrictions:
1. Only single-line lambda expressions without ByRef parameters may be converted to expression trees. Of the single-line Sub lambdas, only invocation statements may be converted to expression trees.
First noteworthy detail is that Microsoft does not want to nail down the exact rules and that the specification is dated. Why they didn't want to nail is fairly visible, the restrictions are quite crippling and they would have wanted to leave room for improvement.
It does otherwise state explicitly why neither compiler version can convert your multi-line lambda method, only single-line is supported. Next part of the sentence explains why you can't make it work on VS2015, you are using an assignment statement and not a method invocation.
So this strongly hints that VS2013 was at fault, it should not have accepted your lambda method. It is a given that the very drastic Roslyn parser rewrite would tighten-up the syntax rules and avoid replicating bugs in previous versions. Also plenty of room to worry that the VS2013 code did not actually work correctly. Non-zero odds that it incorrectly parsed the lambda as an expression instead of a statement. In other words, getting a comparison expression instead of an assignment statement. VB.NET does open the door to this kind of ambiguity, the = operator is used both in expressions and assignment statements. This is a guess I cannot verify without a repro.
Best to bounce this off the guys that actually wrote the compiler. You can file a feedback report at connect.microsoft.com. Be sure to include a minimum repro that they can compile or they'll quickly give up. Beware that there are good odds that they will close it as "By design". That's what it looks like.
My above "Bad Solution" in my question, doesn't actually even work anyway. This fails when the test runs because it obviously can't determine anything from the sub.
The actual answer to my question is to use Mock.VerifySet, instead of Mock.Verify. We are actually doing this in most places, so I'm not sure why we used the other method here. A rewritten test that will work in 2013 and 2015 will look like this:
_view.VerifySet(Sub(x) x.LoginFieldError = It.Is(Of String)(Function(s) Not String.IsNullOrEmpty(s)), Times.Once)
This compiles and the tests are passing as well.
I was poking around in the Moq source code today and the reason for the different result is seen below:
Verify
public void Verify(Expression<Action<T>> expression, Times times)
{
Mock.Verify(this, expression, times, null);
}
VerifySet
public void VerifySet(Action<T> setterExpression, Times times)
{
Mock.VerifySet(this, setterExpression, times, null);
}
The Expression<Action<T>> has apparently had its handling changed, while the Action<T> doesn't appear to have had its changed.
This is at least a documentation error, if not a bug.
In VB.NET prior to .NET 4.0 (i.e. VB.NET 7 through 9) an empty Structure declaration fails at compile-time with
error BC30281: Structure 'MySimpleEmpty' must contain at least one instance member variable or Event declaration.
E.g. The following two structures compile successfully in VB10, and not prior:
Structure MySimpleEmpty
End Structure
Public Structure AnotherEmpty
Public Const StillEmpty As Boolean = True
End Structure
I note the documentation for the Error BC30281 stops at VB9, but the documentation for the Structure statement still has the datamemberdeclarations as required even as of VB11 (.NET 4.5 VS2012).
These two Structures compile in VB11 (VS2012) as well. (Thanks John Woo.)
Is there some blog entry or documentation confirming this is an intended change or a bug in VB10 and later?
Microsoft have marked this as a fixed bug, without actually stating what was fixed.
The VB11 (VS2012) documentation now says the datamemberdeclarations are optional in the grammar, but in the Parts table it says "Required. Zero or more..."!
I guess that's a fix... The VB10 (VS2010) documentation hasn't been changed.
I need some help with a LINQ query in VB.Net, please.
I have this simple group statement:
Dim drivers = From d In DriversOwners _
Group d By Key = d.UnitNumber Into Group _
Select Key, DriverGroup = Group
This works, and returns me the data I need to work with. Now I want to iterate through the groups, using a For Each construct. like this:
For Each x In drivers
Next
However, the compiler is barking at me, telling me that the
"'x' is not accessible in this context because it is 'Friend'."
Anyone know what I am doing wrong here?
Thanks in advance.
After digging and digging, I finally found the answer to this problem. Talk about obtuse!
Enabling LINQ in a .NET Framework 3.5 Project
When you move a project to .NET
Framework 3.5, a reference to
System.Core and a project-level import
for System.Linq (in Visual Basic only)
are added automatically. If you want
to use LINQ features, you must also
turn Option Infer on (in Visual Basic
only) [my emphasis].
When I changed the target framework from 2.0 to 3.5, Visual Studio automatically added the System.Core assembly, and automatically imported the System.Linq namespace. Now why in the world did it not also set Option Infer to "On" as well?
http://msdn.microsoft.com/en-us/library/bb398197.aspx
Looks to me like the variable x was declared earlier - as a class field, a method parameter, or a local variable.
Am I right?
I'm not sure if you've solved this or not. I had the exact same problem today and what ended up working for me was to not use a single-letter variable in the For Each loop. My code was the same as yours:
For Each x In a
...
Next
When I changed the code to the following it worked:
For Each retVal in a
...
Next
I also found the same 'Friend' error behavior for any single-letter variable.
I have no idea why it behaves this way, but I thought I'd pass this along in case this question is still out there.