Call a function in a C DLL from VB: Access Violation - vb.net

I'm trying to call a Dll function which looks like this (in the C/C++ Dll):
__declspec(dllexport) void execute(char** ReturnMsg, char* serverAddress, char* commandLine)
The VB 'wrapper' function looks like:
<DllImport("TPClient.dll", EntryPoint:="execute", CallingConvention:=CallingConvention.Cdecl, CharSet:=CharSet.Auto, ExactSpelling:=True)> _
Public Shared Sub tg_execute(<Out()> <MarshalAs(UnmanagedType.LPStr)> ByRef returnString As System.Text.StringBuilder, _
<MarshalAs(UnmanagedType.LPStr)> ByVal serverAddress As String, _
<MarshalAs(UnmanagedType.LPStr)> ByVal commandLine As String)
End Sub
The parameters are:
returnString: a string I need to get back from the function, result of the command sent;
serverAddress: a string, input only (an IP or DNS name); and
commandLine: a string, input only (any command)
To call the function, I make a StringBuilder object with some sufficient capacity to use as the returnString variable:
Dim returnString As New System.Text.StringBuilder(128)
tg_execute(returnString, TextBox_serverName.Text.Trim, TextBox_Command.Text.Trim)
When I run the code, I do get the expected string in the returnString (as I can see in the debugger), however I also get an AccessViolationException. So, I get for example "2.6.30.8-x86" in returnString when I use the command "uname -r" in commandLine. But the code hangs due to the memory error.
Now I'm not too familiar with VB and P/Invoke, and I had to do some trial and error to get the arguments passed to the DLL (which I'm also writing and debugging). This is also how I ended up using the "MarshalAs(UnmanagedType.LPStr)" attributes. However now I don't know why I'm getting these memory errors.
I made some other attempts using IntPtr arguments, but I also couldn't get this working and gave up on that approach, as to my understanding the marshaling should be handled automatically (is that correct?).
Any help is much appreciated.

The return value char** ReturnMsg would suggest that ReturnMsg is a pointer to C string. This would imply that the native code was in charge of allocating the buffer. So StringBuilder is not appropriate here.
There is not actually enough information here to know how to call this function. What is missing is knowledge of which party is responsible for deallocating the string. It could be either party and I'm going to assume that the C code will do so, probably by means of the strings being statically allocated, e.g. constants.
Now, I have no experience with VB p/invoke so I hope you don't mind if I give you a C# version. I expect you can translate easily enough.
[DllImport("TPClient.dll", CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Ansi, EntryPoint="execute", ExactSpelling=true)]
private static void tg_execute(out IntPtr returnString,
string serverAddress, string commandLine)
You call the function like this:
IntPtr returnStringPtr;
tg_execute(out returnStringPtr, serverAddress, commandLine);
string returnString = Marshal.PtrToStringAnsi(returnStringPtr);
Note that your character set was incorrect in the question. You have to use Ansi because the native code uses char. I also think that your MarshalAs attributes are spurious since you are just re-stating the default marshalling for those parameter types.
Now, if the native code expects the caller to deallocate the memory, then the native code would have to export a function to do so. If that's the case then you would call it passing returnStringPtr by value.

Related

Chance of breaking existing code by adding optional parameter to VB.NET function?

Is there a chance that existing code in a large project might bomb if I add a new optional parameter to a function that's used everywhere? I know I can overload the function instead and minimize the risk, but really.. what's the risk if I insist on going with an optional parameter?
Here's an example:
Public Function GetContent(ByVal URL As String, ByVal ID As String, Optional ByRef PageTitle As String = "") As String
Try
Dim web As New HtmlWeb()
Dim doc As HtmlDocument = web.Load(URL)
ID = "//div[#id='" & ID & "']"
Dim ContentNode As HtmlNode = doc.DocumentNode.SelectSingleNode(ID)
' The two lines below are the mere extent of what's new inside this function, besides the new Optional ByRef parameter in its signature
Dim PageTitleNode As HtmlNode = doc.DocumentNode.SelectSingleNode("//title")
If Not PageTitleNode Is Nothing Then PageTitle = PageTitleNode.InnerHtml
Return ContentNode.InnerHtml
Catch ex As Exception
Return "<h4> Bad. Very bad. </h4>"
End Try
End Function
PS: I'd like to comment on my question after the fact, having read others' responses below and having done some additional research myself. Originally, I didn't want to question the validity of the approach of using an optional parameter. That was something VB.NET was allowing me to do and I felt I had every right to use--besides that it was simply very convenient! But my original question had more to do with whether there may be gaps in how optional parameters are implemented, from compilation down to execution--gaps that I should consider as I design my code. I was unaware of the historical significance of the optional parameter approach in relation to the overload approach. I've learned now that it's not that there are gaps or flaws in the optional parameter approach; rather, it was a solution designed for a different and older set of concerns that was simply overridden with the advent of the Common Language Runtime. I'm using VS2013. Sure, everything compiled fine with the optional parameter approach and seemed to run fine but I wanted to confirm I wasn't potentially breaking something else by adding an optional parameter--especially since someone looked at my code and suggested I should overload the function instead. I wanted to prove why I shouldn't keep my optional parameter method. James Thorpe answered that question for me now, I think. But as Tim Schmelter asked, is there a benefit for doing it this way (optional parameters) as opposed to the overload approach? To me now the overload approach seems the best and only way, and that is because I'm using a newer set of technologies that the optional parameter approach--which was implemented for Microsoft's older Component Object Model, or COM--simply wasn't designed to address (see page 83 of the book, "Microsoft Visual C# 2013 Step By Step" by John Sharp). Particularly now, if there are external modules expecting to find the old function signature (i.e., the function parameter layout that existed before I added the new optional parameter), they'll break unless I recompile them too! That's a hindrance for me. But overloading handles this software development problem much better without need for recompilation, something only now supported by the newer Common Languange Runtime, or CLR. I suppose the optional parameter support in VB.NET is more of a historical holdover now from the old COM days--and not the very best solution for my specific requirements. I've also just learned that, "The Common Language Specification, which defines the subset of the CLR that all languages should support, explicitly disallows a reliance on optional parameters. This means they are not a candidate for use in the Base Class Library and will probably never been seen in any of the other libraries shipped as part of the .NET Framework." (from the online article, "Optional Parameters Are Gaining Ground in .NET", by Jonathan Allen). Although the rules are laxer for us regular developers that consume Microsoft technologies, I think there's something to be said for their internal decision not to rely on optional parameters. I just wanted to post and share that with you in case like me you've also come here wondering!
Within a single project? No, it should be fine. However, in the comments you said:
Let's say there were other projects calling it (there is a possibility). Would it break those if I didn't rebuild them?
Optional parameters are actually baked in at compile time, so if you have your original method signature:
Public Function GetContent(ByVal URL As String, ByVal ID As String)
And someone is calling it thusly:
GetContent(someUrl, someId)
It will be compiled into their assembly as-is. With your new optional parameter, anything calling it as above without passing in the parameter would actually get compiled as:
GetContent(someUrl, someId, "")
Note how the default value of the optional parameter has automatically been brought in. If you're rebuilding everything, it's all good. However, in those projects that are referencing this one that aren't rebuilt, they will have the original two-parameter call. Your GetContent method now requires 3 parameters at runtime - you'll get a runtime error as it can't find an overload of the function that still takes 2 parameters.
Here's how you can do it without breaking code...
Public Function GetContent(ByVal URL As String, ByVal ID As String, ByRef PageTitle As String = "") As String
' the rest of your function here
End Function
Public Function GetContent(ByVal URL As String, ByVal ID As String) As String
Return GetContent(URL, ID, "")
End Function
That way you have a version of the function with 2 parameters for the existing code and one with 3.
If you want to encourage programmers to switch to the 3 parameter version, then you can mark the 2 parameter version like this:
<Obsolete("Use the version that takes pageTitle as a 3rd parameter">
Public Function GetContent(ByVal URL As String, ByVal ID As String) As String
Return GetContent(URL, ID, "")
End Function
That will give you a compiler warning if you try to call the 2-parameter version.

Trying to call a C DLL from VB. Can't get one of the parameters working

Trying to set up a USB power strip.
Here's the documentation:
Initializes the Power USB API.
Name: InitPowerUSB
Parameters: model:returns the model number(1:basic, 2:digIO, 3:watchdog, 4:Smart), firmware: returns firmware version in ?.? format in a character string (major revision and minor revision)
Return: >0 if successful. Returns number of PowerUSB devices connected
C++ Example:
if (!m_pwrUSBInit)
{
int model; char firmware[8];
if ((ret=InitPowerUSB(&model, firmware)) > 0)
{
m_pwrUSBInit = 1;
m_numDevices = ret;
}
}
I have been trying to get this working with my VB6 code for around an hour now with no luck. The program either crashes, displays an error like Bad Calling Dll Convention, type mismatch, et cetera.
Here's what I have:
Public Declare Function InitPowerUSB Lib "PwrUSBDll.dll" (ByRef model As Integer, ByVal firmware As String) As Integer
Dim model As Integer
model = 0
Dim firmware As String
firmware = ""
If (InitPowerUSB(model, firmware)) > 0) Then
EndIf
I have tried changing firmware to byte arrays, byref, string, integer, long, etc. It just doesn't seem to want to run.
Does anyone know of a solution to this problem? Thanks
I can't answer the rest of your function signature woes since I don't have any documentation for your PwrUSBDll.dll.
However "Bad DLL calling convention" errors generally mean you have a CDecl entrypoint and VB6 can only call those with some help.
There are a couple of fixes.
The obvious one is to modifiy the source and recompile that DLL using StdCall instead.
Another is to create a type library for that DLL, which helps inform VB6 about the issue and resolves it.
Then you have the option of using VB6's undocumented CDecl decorator:
Public Declare Function InitPowerUSB CDecl Lib "PwrUSBDll.dll" ( _
ByRef model As Integer, _
ByVal firmware As String) As Integer
However the downside is that this will not work when run within the IDE, nor will it work when compiled to p-code. The p-code interpreter doesn't process this keyword.
So you could just bypass it in IDE runs and supply dummy results for testing, or you can create a small wrapper DLL in VB6 that you separately compile to native code.
Caveats:
For this to solve your problem we'd have to assume you are passing correct data types in that argument list. A C++ int is a VB6 Long. You are probably better off passing a VB6 Byte array ByRef for that char[8] unless this is a Unicode DLL entrypoint. The function return value is also most likely Long.

C# DLLImport converted to VB.NET DLLImport...what am I missing?

In C# I have this:
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
public static extern IntPtr GetDesktopWindow();
I tried to convert to VB.NET thus:
<DllImport("user32.dll", EntryPoint:="GetDesktopWindow")>
Function GetDesktopWindow() As IntPtr
End Function
But I am getting an error...
"Imports System.Runtime.InteropServices.DllImportAttribute cannot be applied to instance method."
Can some explain what I need to do to fix this, and even better, tell me WHY?
Thanks!
You forgot to convert the static keyword from the C# declaration to VB.NET. That's what the error message is telling you. Unless you have a static method, you're declaring an instance method, and the DllImportAttribute cannot be applied to an instance method.
The VB.NET equivalent of static is Shared. So your declaration should look like this:
<DllImport("user32.dll", EntryPoint:="GetDesktopWindow")>
Shared Function GetDesktopWindow() As IntPtr
End Function
I feel compelled to point out a couple of other things:
It's unnecessary to specify the EntryPoint when your function declaration has the same name. Not that it hurts anything to do so anyway, but I feel that it keeps down duplication and reduces the chances of error if you omit it.
P/Invoke declarations like this should generally go into a static class with a name like NativeMethods (StyleCop enforces this guideline). In VB.NET, static classes are called modules. So it would look like this:
Module NativeMethods
<DllImport("user32.dll")>
Shared Function GetDesktopWindow() As IntPtr
End Function
End Module
In older versions of VB (pre-VB 10, shipped with VS 2010), you needed line continuation characters in order to break up function declarations onto multiple lines. Those ugly warts make it look like this:
Module NativeMethods
<DllImport("user32.dll")> _
Shared Function GetDesktopWindow() As IntPtr
End Function
End Module
And finally, be very careful about how you use the desktop window returned by the GetDesktopWindow function! Lots of people abuse it, and most of the time when I see people trying to retrieve a handle to it, that's a sign that they're already doing it wrong. (Not saying you are, since I can't see the rest of your code, just something to be aware of!)

VB.NET - Calling Kernel32.DLL's Wow64DisableWow64FsRedirection

Looking at Microsoft's page on Wow64DisableWow64FsRedirection, I see some C code. What if you want to call this function and it's revert from VB.net?
So far I have done this:
<Runtime.InteropServices.DllImport("KERNEL32.DLL", EntryPoint:="Wow64DisableWow64FsRedirection")> _
Public Shared Function DisableWow64Redirection() As Boolean
End Function
And I do likewise for the Revert brother function.
I call it like so:
DisableWow64Redirection()
This seems to work, as the shell command I call after actually finds its exe in system32, but I am not sure about the Revert, does it need a parameter? This Revert page seems to want me to take the output from disable and plug it into the revert call. Does that sound right? How do I change my DLLimport to take in a boolean and actually use it in the Kernal32.DLL function?
Thanks!
You could change your function definition to
<DllImport("kernel32.dll", EntryPoint := "Wow64DisableWow64FsRedirection")> _
Public Shared Function DisableWow64Redirection(ByRef output As IntPtr) As Boolean
and define Revert() like so:
<DllImport("kernel32.dll", EntryPoint := "Wow64RevertWow64FsRedirection")> _
Public Shared Function RevertWow64Redirection(ByRef handle As IntPtr) As Boolean
You'd invoke these like so:
Dim handle As IntPtr
DisableWow64Redirection(handle)
RevertWow64Redirection(handle)
I'm not a VB.NET guy, so maybe some syntax is incorrect - the important thing is that you provide a reference parameter of IntPtr type, which maps to the PVOID native type. You'll want to hang on to it, and pass the same value to Revert().
The declaration is wrong, it takes a (ByRef oldValue As IntPtr) argument. Which you need to pass to the revert function.
However, you cannot safely use this in a .NET program. It also affects the CLR, it won't be able to find .NET framework assemblies anymore. You cannot predict when they get loaded. There are surely better ways to accomplish what you need, start a new question about that.

'out' issue in VB.NET

When in C# we have the out and ref parameter options, in VB there is a only one: ByRef.
Now, little 'problem' when trying to 'eliminate' the compiler warning saying that test was not initialized before passing as argument:
Dim test As MyParsableClass ' = Nothing need imperatively?? '
' some code ... '
MyParsableClass.TryParse("value", test) ' warning on "test" here
the class brief declaration:
Class MyParsableClass
Public Shared Function TryParse(ByVal value As String, _
ByRef myParsableClass As MyParsableClass) As Boolean
myParsableClass = Nothing
If True Then
' parse code OK'
myParsableClass = New MyParsableClass()
Return True
Else
' parse code NOK '
' myParsableClass remains Nothing '
Return False
End If
End Function
End Class
maybe a solution was to declare
...Optional ByRef myParsableClass As MyParsableClass = Nothing)
but I can't set this parameter as optional one. What will happen if I'll miss it?
PS. (edit)
In the real project, my "parsable" class is MyHour with Hour and Minute properties. I wrote already the Parse(value as String) with a FormatException, but I think the code could be more clear, compact and quick when I will not use try catch blocks...
I do not believe it's possible to prevent this warning, without an explicit assignment.
Different languages have different features/facilities - if they didn't, there'd only be one programming language :-) In this case, yes, VB doesn't pretend that there are two types of ref parameters, as C# does - so far as the CLR is concerned, "out" doesn't exist.
And I'm not sure what peSHIr is talking about - TryParse was added to later releases of the BCL for precisely the situation where a parse is as likely to fail as to succeed - so you can take a faulting path without requiring an exception to be thrown.
Edit
To add - the reason you don't get a warning for many of the built in types for which a TryParse exists (e.g. Int32) is because they're Structs/Value types, and hence always have a value. If your class is simple enough, would it be logical for it to be a Structure instead?
Not exactly an answer to your question, but out and ref/ByRef are bad, so why use them in the first place? Many developers think that the TryParse paradigm in the .NET Framework 1.0 was a bad way to go.
Why not go for a MyParsableClass that has a Public Shared Function Parse(ByVal value As String) As MyParsableClass method that raises an appropriate exception when needed?
Or even a Public Shared Function Parse(ByVal value As String) As MyParsableClassParsed where, MyParsableClassParsed is a helper inner class that contains two readonly properties: Success As Boolean and Result As MyParsableClass? You could then always get a result from calling Parse, but you'd get Success==True and Result==[whatever], or simply Success==False and Result==Nothing.
Also, your MyParsableClassParsed helper class could also use an enumerator instead of a boolean and/or a list of error messages to tell the caller how/why the parse operation failed. Or the throw exception might have such an enumerated value and/or error message(s).
Much easier to use and more flexible. And all without ByRef to give you headaches/warnings.