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

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.

Related

Sleep Function not working. Declaration?

I have this code:
My.Computer.FileSystem.RenameFile("C:\Users\mario\Desktop\Dominio\1\Pc - S.txt", "Pc - S - A.txt")
Sleep(2000)
My.Computer.FileSystem.RenameFile("C:\Users\mario\Desktop\Dominio\1\Pc - S - A.txt", "Pc - S.txt")
But when i run it, it appears this error message:
Managed Debugging Assistant 'PInvokeStackImbalance' has detected a problem in 'C:\Users\mario\Documents\Visual Studio 2010\Projects\WindowsApplication1\WindowsApplication1\bin\Debug\WindowsApplication1.vshost.exe'.
Additional Information: A call to PInvoke function 'WindowsApplication1!WindowsApplication1.Form1::Sleep' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
What is hapening? I use this to declare it: Private Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMilliseconds As Long)
What is hapening and how do I fix it? I assume is the declaration..
SOLUTION: Just use this: System.Threading.Thread.Sleep(2000)
It does not use a declaration.
Your declaration is wrong, the Sleep() argument is Integer, not Long. Watch out for old VB6 declarations you find in various web pages, almost anything that uses Long is wrong for VB.NET. The VB6 types were based on its 16-bit version, VB4 was the last one.
Just don't do this and use the .NET method instead.
Declare Sub Sleep Lib "kernel32.dll" (ByVal Milliseconds As Integer)
You should pay special attention to type declarations. Longs are bigger than they used to be in VB6, so you need to make sure any code you get off the web is right for your current environment.

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

Migrating to VB.NET: what datatype to use for "hcontext" in an x64 project?

I have a old VB6 project.Now I migrating it in VB.Net on vs2008 and the solution platform now I have to use 64bit.In the old code the variable hContext was declared as Integer.
Dim hContext As Integer
And used as:
Dim rc As Integer
dwScope = SCARD_SCOPE_USER
rc = SCardEstablishContext(dwScope, 0, 0, hContext)
When I debug the code the hContext create problem.
This is due to it define as a Integer(32bit).
Now the problem is "What datatype should I use for hContext"? I have also used different datatype like Long, ULong, IntPtr....
NOTE
When I debug the code the hcontext take 4byte address.but in 64bit I take hContext as IntPtr which is platform dependent,But it show only 1byte address. And I am not able to establish the connection.
I suspect the question is "what is the correct signature for SCardEstablishContext in a 64-bit project?"
The C WinAPI signature is as follows:
LONG WINAPI SCardEstablishContext(
__in DWORD dwScope,
__in LPCVOID pvReserved1,
__in LPCVOID pvReserved2,
__out LPSCARDCONTEXT phContext
);
Pointer types ("LP...") should be IntPtr and LONG/DWORD types should map to Integer -- this will be correct for a WinAPI call in either a 32-bit or a 64-bit build. (In some cases it is nice to specify a managed structure type instead of IntPtr and let the .NET interoperability/pinvoke automatically marshal everything.)
pinvoke.net is sometimes helpful -- see pinvoke.net: SCardEstablishConnection and *note how the VB.NET signature at top is wrong -- but care needs to be taken because definitions are sometimes incorrect and/or incomplete ;-)
The correct pinvoke signature, for an opaque context value, is:
<DllImport("winscard.dll", SetLastError:=True)>
Public Shared Function SCardEstablishContext(
dwScope as Integer,
pvReserved1 as IntPtr,
pvReserved2 as IntPtr,
<out>() phContext as IntPtr) As Integer
End Function
Happy coding.
An integer in VB.Net is defined as 32bits, even when running in a 64bit process.
From the MSDN docs:
Holds signed 32-bit (4-byte) integers that range in value from -2,147,483,648 through 2,147,483,647.
Your SCardEstablishContext() function likely calls to unmanaged code that wants to be 32bit. Therefore I would use Integer.
You may also have to specify an x86 (32bit) soluction (rather than Any CPU or x64/64bit) because of this function reference.

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.