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

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!)

Related

VB GetAsyncKeyState' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature.

I'm working on a small program but when running my program in visual studio I get the following error:
GetAsyncKeyState' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature.
But when I build it to an .exe it runs just fine. But that makes it a bit hard to debug the program.
This is the code snipper that throws the error:
If InGame And Not GetAsyncKeyState(Keys.Tab) Then
If Settings.SkinChangera Then SkinChanger.Skinchanger()
End If
and this is GetAsyncKeyState()
Public Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
I advice you to never ever look for, or use, Declare Lib statements you got from the internet. Most of them out there are for VB6 and are therefore almost always not fully compatible with VB.NET.
Instead, stick to the solutions that use the DllImport attribute. The site pinvoke.net is a great place to look for P/Invoke declarations. If you can't find a VB.NET version of a P/Invoke declaration, take the C# version and run it through a converter such as Telerik.
Having that said, you are receiving the error because the parameter and the return value are not of the correct data types. The GetAsyncKeyState() function's parameter should be Integer and its return value should be Short.
Use the DllImport version of the function instead, with the correct data types, and it should work:
<DllImport("user32.dll")> _
Public Shared Function GetAsyncKeyState(ByVal vKey As System.Windows.Forms.Keys) As Short
End Function
Note: The System.Windows.Forms.Keys enumeration is of type Integer.

Enforce Type Alias in VB.NET

How might one go about aliasing a type in VB.NET or C# such that when the alias is used as an argument type in a function, accidentally using the not aliased type is an error?
i.e.
Imports AccessSpecifier = System.String
Module Accessors
Delegate Function IoOper(ByRef strm As System.IO.Stream) As Action
Public Function accessMethod(ByRef spec As AccessSpecifier) As IoOper
' implementation
' ...
End Function
End Module
Module Main
Public Sub Main()
Dim spec As AccessSpecifier = New AccessSpecifier(CType("READ_WRITE", Char()))
Dim val = Accessors.accessMethod(spec)
System.Console.WriteLine(val.GetType())
Dim shouldFail = Accessors.accessMethod("FAIL_ME")
System.Console.WriteLine(shouldFail.GetType())
End Sub
End Module
Or perhaps is there a better way to go about this?
Overall, I'm wanting the IDE to force me to know what I'm doing if I'm throwing Ints around to mean Flags, or States and Strings around to mean Names, Propertys and Records.
Help?
I've never liked Type aliasing in .NET. It makes for imprecise code and it is not immediately clear what is happening. As in your example, when an individual went looking for the AccessSpecifier.vb file (or class) they would not find it.
Also, Aliasing only works within YOUR project and only within a single code file. So you would have to define that alias in all the various code files where it was to be used.
A better (as in easier to read, easier to maintain, and more clear of intent) option is to create a class and overload the type conversion operators for automatic conversion to/from String. In this manner you retain your ability to use your new class as if it were a String, but you gain your strict type checking.

Call a function in a C DLL from VB: Access Violation

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.

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.