I am currently converting a project from VB to C#. I have a Web Reference in the VB project which I have referenced in the C# project (Add Web Reference).
The signatures are the same. The VB code looks like this:
If Not tws.StartSession(tsd) Then
Throw New systemMonitor.internalEx.securityEx("Failed to initiate a TROPOS session")
End If
I have tried to covert that across as this:
// Start our session
if (!this._service.StartSession(this._details))
throw new Exception("The TROPOS session failed to start.");
The problem I have, is that it won't compile and comes up with the error:
argument 1 must be passed with the 'ref' keyword
so I changed it to this:
// Start our session
if (!this._service.StartSession(ref this._details))
throw new Exception("The TROPOS session failed to start.");
which compiles and runs (although nothing seems to happen, but that is another issue).
My question is simple.
In VB do you not have to set the ByRef keyword?
In VB.NET ByRef or ByVal is (optionally) specified in the method being called (with the default being ByVal if neither is specified) and you don't specify it when calling the method.
In C# if the method specifies ref for the parameter then you must also specify 'ref' when calling the method.
You do not need to specify ByRef in VB.NET
The short answer is no, you don't have to.
Here's why: Once again VB.NET does things for you, if it is an object that is passed to a function, it will automatically be passed by reference. So under the covers VB.NET adds it for you. VB.NET will pass simple data types (Strings, Integers, etc...) ByVal automatically unless you specify ByRef.
Personally I like to write my code explicitly using ByRef when I mean to pass something by reference. I also use following compiler options:
Option Explicit On
Option Strict On
Which limits the amount of things VB does automatically for me. I don't think however it has an affect on passign objects by reference.
Related
Does the C# compiler require a function delegate to pass a function as an argument to another function?
In Visual Basic .Net I can pass a function whose return value is of the same type as a method argument to the method itself without anything fancy. This doesn't appear possible in C# unless the method was written to accept a function delegate??
working vb:
Function DoSomething(r As DataRow())
'...
End Function
Function CallSomething()
Dim dt As DataTable
DoSomething(dt.Select(""))
End Function
C# would require the calling code to assign a variable and pass that, or that the referenced function's signature included a delegate (useful to know). Subtle difference but I was expecting that a simple method call made in one language could be just as easily made in the other since they both compile to the same IL.
here's the C# equivalent I was trying to do:
string DoSomething(ref DataRow[] r)
{ ///do something }
void CallSomething()
{
DataTable dt;
DoSomething(ref dt.Select(""));
}
What I meant by "fancy" was the use of delegates, I've used delegates for more complex scenarios, like assigning event handlers to array elements, but I was surprised they would be required for this.
Thanks for the replies. The VB code is functioning as expected, ByRef is implicit in VB, you can't pass an object managed on the heap byval. Nonetheless, here's the simplest form I can provide in VB, if you use a simple forms project w/ a single listbox and drop this code in, should work fine:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim dt As New DataTable("A really great table")
dt.Columns.Add("Column1", GetType(String))
dt.Rows.Add("Row1") : dt.Rows.Add("Row2")
PutRowsInListbox(dt.Select(""))
End Sub
Private Function PutRowsInListbox(ByRef r() As DataRow)
For i As Integer = 0 To r.Length - 1
Me.ListBox1.Items.Add(r(i).Item("Column1"))
Next
End Function
End Class
Your Vb.Net code is not equivalent to your c# code. If you want them to be equivalent, you need to remove the ref keyword on the c# code (or add ByRef on the VB.Net code).
Both visual basic and c# allows this style of programming. Please note, however, that you are not passing a function (or method) to another method here, not in Vb nor in c# - you are simply populating the DataRow array with the results of the data table's Select method.
Since you are not actually passing a method as an argument, a more readable code would be to get the results of the Select method into a variable and pass that variable into the DoSomething method - but that's more a personal preference thing.
The c# code sample you have in the question will not compile.
Here's an equivalent and compilable c# version of the Vb.Net code you have now:
void DoSomething(DataRow[] r)
{
//do something
}
void CallSomething()
{
DataTable dt = null;
DoSomething(dt.Select(""));
}
Of course, that would throw a NullReferenceException unless you actually initialize the dt with a value instead of null.
BTW, ByRef is not implicit. passing reference types does not mean passing them by Reference. That's a common misconception.
When you pass a reference type to a method, what actually is going on is that you are passing the reference itself by value. You can test it easily yourself if you try to reassign the reference inside the method - you'll still see the old value outside the method if you didn't specify ByRef.
Is it possible to create a new PayPal.Payments.DataObjects.TransactionResponse?
I'm currently working on upgrading (our old ERP system) to TLS 1.2, and I need to override a function that returns a PayPal.Payments.DataObjects.TransactionResponse, but PayPal.Payments.Communication.PayflowNETAPI.SubmitTransaction returns a string. Trying to simply create a new PayPal.Payments.DataObjects.TransactionResponse hasn't worked - I'm told in the VB code that:
'PayPal.Payments.DataObjects.TransactionResponse.Private Sub New()' is not accessible in this context because it is 'private'.
Trying in the C# code yields a less descriptive error:
'TransactionResponse' does not contain a constructor that takes 0 arguments
(replacing the 0 with any number of arguments that you put in -- I tried up to 8 or 9)
I am open to solutions in either VisualBasic or C#. Although the function in question is in VB, we opted to send our transactions to an internal processing server, which will return the string (written in C#), so I can do this from either side.
Basically, I just need to take the response (currently in string format), probably parse it (although a straight string conversion would be fine too), and put the info into a PayPal.Payments.DataObjects.TransactionResponse.
You have source code here.
You can check which parameter send to the constructor.
From a quick read of the Paypal SDK code mentioned by Ygalbel, it looks like there is no new() function. Rather, you would declare your var of type PayPal.Payments.DataObjects.TransactionResponse and then use the set and get accessors to set/get data in the class.
I have a ComVisible COM class written in C#. I want to call it from another C# bit of code using COM and pass the default value for the parameter. I can call plenty of other methods without default arguments.
This is the best I can come up with. The first two lines work for all my other methods.
Type mytype = Type.GetTypeFromProgID("MyType");
dynamic myinstance = Activator.CreateInstance(mytype);
object missingValue = System.Reflection.Missing.Value;
myinstance.generatecsvdocument("mystring", ref missingValue);
My method looks like this:
public void generatecsvdocument(string mystring, string rowseperator = "\n")
When I run it I get the error:
The best overloaded method match for 'generatecsvdocument(string,
string)' has some invalid arguments
object missingValue = System.Reflection.Missing.Value;
That cannot work here. It is only valid for a COM method that takes a VARIANT as an argument. Looks like object or dynamic in C#. A very different kind of default argument mechanism than C# supports, it is the callee that determines the default value. In C# it is the caller that determines it, the C# compiler uses metadata to know that default.
Missing.Value turns in a variant of type vtError with the value DISP_E_PARAMNOTFOUND at runtime. Signalling the COM method to use the default value. Not actually that commonly used, usually only implemented in COM servers that support scripting languages. Office Automation is the most common example, probably what inspired you to try this.
But no, your argument is string, not a variant. There is no way to discover the default either when you use late binding, implicit is that you don't know anything about the default value stored in metadata. Otherwise the reason that the vtError mechanism exists, scripting languages have the same problem. The only real way to get ahead is to rewrite the method and test for a null argument, substituting "\n" if that's the case.
Basically, when I call DeleteUrlCacheEntry (which is part of the Wininet.dll API) I either get returned the number 1 (which means, deletion successful) or the number 0 (which meant, deletion didn't work).
My question is, how can I find out why a deletion did not work? (that is, when a 0 is returned). I heard there is a GetLastError() function in C++, however I am using VB6 and apparently the GetLastError equivalent in VB6 is the Err.LastDllError property.
After a DeleteUrlCacheEntry attempted deletion fails (returns 0) I call/query the Err.LastDllError and it always returns 0 - no matter what. It returns 0 even if DeleteUrlCacheEntry returns 0 (deletion didn't work) and even when it returns 1 (deletion did work). I also make the call/query to Err.LastDllError as soon as possible (as in, right after the DeleteUrlCacheEntry call).
I am really confused, because I don't even get a runtime error or any typical exception (or any exception for that matter). I do not have On Error Resume Next to ignore exception anywhere in my application, so every means of an error being reported to me is available but I cannot for the life of me figure out why a particular DeleteUrlCacheEntry() attempt will fail and return 0 (there doesn't seem to be a way for me to find out).
So, my question is, how do I get extended error information from the DeleteUrlCacheEntry() function (residing in the wininet.dll API)?
If you would like more information on the reason why I am looking for extended error information from the DeleteUrlCacheEntry() function, I have another question which details this (together with actual examples of cache items that work and don't work when a deletion is attempted): https://stackoverflow.com/questions/12096546/deleteurlcacheentry-function-of-wininet-api-not-deleting-some-internet-explo
Also, just to add, I am using VB6 - but principally it should be the same across most languages as it is an API call. Here is my declaration in VB6:
Public Declare Function DeleteUrlCacheEntry Lib "WININET" Alias
"DeleteUrlCacheEntryA" (ByVal lpszUrlName As String) As Long
Also, I am calling it like this:
Dim lReturnValue As Long
Dim cacheFileString As String
' GetStrFromPtrA turns Pointer (Long) to String. cacheFileString is the actual text/string of the lpszSourceUrlName turned from Pointer (Long) to String (human readable cache file name/string). Also, cacheFileString is retreived from FindFirst/NextEntry functions in a loop, so can't be incorrectly formatted or anything, as it also works when deleting most other items.
cacheFileString = GetStrFromPtrA(icei.lpszSourceUrlName)
lReturnValue = DeleteUrlCacheEntry(cacheFileString)
The lReturnValue ends up being 0 if deletion didn't work, and 1 when deletion does work, and that is it. Also, Err.LastDllError always returns 0.
Thank you for your anticipated assistance.
Personally I would set a break point on the call to DeleteUrlCacheEntry(), and step through it observing what the values in the call stack are.
Other than that, there is not much that can be said without a code snippet.
If Err.LastDllError is returning 0 than either GetLastError() really is returning 0, which is unlikely, or VB is not calling GetLastError(), which is more likely. Such as if the VB declaration of DeleteUrlCacheEntry() is wrong, like it is not setting the DllImport.SetLastError property to true under .NET PInvoke.
This might not be directly relevant, but it seems to me that you are doing an unecessary string copy here. Why not directly use the string pointer you already have:
Public Declare Function DeleteUrlCacheEntry Lib "WININET" Alias "DeleteUrlCacheEntryA" (ByVal lpszUrlName As Long) As Long
Dim lReturnValue As Long
lReturnValue = DeleteUrlCacheEntry(icei.lpszSourceUrlName)
(This is assuming that icei.lpszSourceUrlName is a pointer to an ANSI string.)
Also, you do realise that Err.LastDllError is overwritten everytime a command called via a VB declare or type library declare with usesgetlasterror = true? So, for instance, if I declared APIFunc1() and APIFunc2(), and then called them like this;
nRet = APIFunc1(APIFunc2)
... then LastDllError would reflect only APIFunc1().
It's a bit dated ... but I am interested by the subject, so I thought I would share my limited knowledge.
For me, when I called DeleteUrlCacheEntry with a wrong URL, I get an error and Err.LastDllError is set to 2 !
It seems that my library of extension methods that was written in VB.NET has problems.
I have 2 overloaded extension methods Crop().
When i reference the lib from a VB.NET project i see them. If reference it from a C# project i can't see them.
What the hell is going on?
It works like this:
In C#, when you call a method with an out or ref parameter, the compiler requires you to repeat the out or ref keyword on the calling argument. This is because it does matter to the language whether the variable is already initialised or not.
In VB.NET, when you call a method with a ByRef parameter, the compiler does not require you to repeat ByRef on the calling argument, it just sorts it out for you. This is because it does not matter to the language whether the variable is already initialised or not.
Therefore, VB.NET can easily resolve an extension method using ByRef, because there is only one possible way in which it can be called. However, C# does not know what to do because there is no syntax for telling it whether to call the method using out or ref.
All you can do is:
Re-write your extension methods to use a ByVal parameter wherever possible.
If that is not possible, then you are stuck with calling them from C# like normal methods.
Without seeing the code, my guess is that you're missing a using statement in your C# .cs file.
//other usings...
//Extension using statement...
using MyAssembly.Extensions;
class Program {
static void Main() {
//some code
String myString = "blah";
//call the extension method now
String newString = myString.MyExtensionMethod();
}
}
But this is just a guess without seeing your code.
Hope this helps!!
My methods were using byref arguments. I changed it to byval and it worked.
It's very weird apparently. In VB projects its ok but in C# not. Apparently C# does not support extension methods with byref or it is a bug.