I know it's unusual, but I have to port some code from VB.NET to VB6, and I need to convert 2 APIs to VB6.
To be honest, I have never written an API declaration in VB6, I have only copied it from websites and other code.
Could somebody tell me how I could convert the following code from VB.NET to VB6?
<DllImport("XInput1_4", EntryPoint:="XInputGetState", setlasterror:=True)> _
Private Shared Function XInputGetState_1_4(ByVal dwUserIndex As UInteger, ByRef pState As XINPUT_STATE) As UInteger
End Function
<DllImport("XInput9_1_0", EntryPoint:="XInputGetState", setlasterror:=True)> _
Private Shared Function XInputGetState_9_1_0(ByVal dwUserIndex As UInteger, ByRef pState As XINPUT_STATE) As UInteger
End Function
Is the following VB6 code correct?
Private Declare Function XInputGetState_1_4 Lib "XInput1_4.dll" (ByVal dwUserIndex As Long, ByRef pState As XINPUT_STATE) As Long
Private Declare Function XInputGetState_9_1_0 Lib "XInput9_1_0.dll" (ByVal dwUserIndex As Long, ByRef pState As XINPUT_STATE) As Long
Related
From VB .Net, I'm trying to send a string to a textbox of another application but I cannot make it work. I'm able to get the handle and even set the focus to the textbox but my SendMessage function doesn't seem to be correct as I get the error message "SendMessage' 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."
Here is my code:
Module Module1
Private Const WM_SETTEXT As Int32 = &HC
Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As IntPtr) As Long
Private Declare Auto Function FindWindow Lib "user32" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Private Declare Auto Function FindWindowEx Lib "user32" (ByVal hwndParent As IntPtr, ByVal hwndChildAfter As IntPtr, ByVal lpszClass As String, ByVal lpszWindow As String) As IntPtr
Private Declare Auto Function SendMessage Lib "user32" (ByVal hwnd As IntPtr, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As String) As IntPtr
Sub Main()
Dim AppHwnd As IntPtr = FindWindow(vbNullString, "Test Application"
Dim WinHwnd1 As IntPtr = FindWindowEx(AppHwnd, 0&, "SWT_Window0", vbNullString)
Dim WinHwnd2 As IntPtr = FindWindowEx(WinHwnd1, 0&, "SWT_Window0", vbNullString)
Dim WinHwnd3 As IntPtr = FindWindowEx(WinHwnd2, 0&, "SWT_Window0", vbNullString)
Dim TextBoxHwnd1 As IntPtr = FindWindowEx(WinHwnd3, 0&, "Edit", vbNullString)
Dim TextBoxHwnd2 As IntPtr = FindWindowEx(WinHwnd3, TextBoxHwnd1, "Edit", vbNullString)
MsgBox(TextBoxHwnd2)
SetForegroundWindow(TextBoxHwnd2)
SendMessage(TextBoxHwnd2, WM_SETTEXT, 0&, "text")
End Sub
End Module
The line "MsgBox(TextBoxHwnd2)" returns the handle number I found using Window Detective so I'm assuming the code is correct up to this point. Also, I tested "SetForegroundWindow(TextBoxHwnd1)" and the cursor is on the first textbox whereas "SetForegroundWindow(TextBoxHwnd1) sets the cursor on the second textbox.
Your declaration looks like it might have been migrated from legacy VB code, where Long was a 32-bit integer and Integer was only 16-bit (dating to legacy VB's origination in 16-bit Windows). The length of the data types changed in .NET which was created after the shift to universal 32-bit Windows, so Integer is 32-bit and Long is 64-bit.
Moreover, the Windows API itself needed to be updated for 64-bit Windows. SendMessage is one of the functions that has some parameters that are longer in 64-bit because they are expected to hold pointers.
I checked on the native header file declaration of SendMessage and found that:
The parameter Msg is declared as UINT which would correspond to Integer in VB. This needs to be changed regardless of whether you have a 32-bit or 64-bit build.
The parameter wParam is declared as UINT_PTR so it should in fact be Long for a 64-bit build, but Integer for a 32-bit build; if it's possible to declare it as IntPtr and then pass 0 to it, that would probably be best, because it should automatically adjust the length depending on whether you're building for 32-bit or 64-bit.
I'm attempting to send automated keystrokes to an application that does not support copy+paste via a small VB form. The form loads data from a text file and uses SendKeys to fire it over once I click a button.
Everything appears to work except for the ShowWindow portion. I'm currently testing using Notepad and, with one exception, I can't seem to get ShowWindow to kick focus to Notepad. Obviously I'm worried it will do the same to the application I'll eventually be running this against (I don't currently have access to it). The only ShowWindow parameter that makes Notepad active is SW_SHOWMAXIMIZED. SW_SHOW and SW_SHOWNORMAL don't appear to do anything while SW_RESTORE will restore Notepad if minimized but my VB form remains the active window.
I'm not a programmer but I had made the mistake of telling my boss I dabbled in Pascal Turbo in high school (over a decade ago) so I'm the one stuck with trying to make this work. My current code cobbled together from S.O. and other sources:
(I'm running Windows 7 and using MVSE2013)
Imports System.Runtime.InteropServices
Public Class Form1
Private Declare Function FindWindow _
Lib "user32" _
Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As IntPtr
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow As ShowWindowCommands) As Boolean
End Function
Enum ShowWindowCommands As Integer
SW_SHOWNORMAL = 1
SW_SHOWMAXIMIZED = 3
SW_RESTORE = 9
End Enum
Private Sub Form1_Load
[form]
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim lHwnd As IntPtr = FindWindow("Notepad", vbNullString)
If lHwnd <> IntPtr.Zero Then
ShowWindow(lHwnd, ShowWindowCommands.SW_SHOWNORMAL)
SendKeys.Send(TextBox1.Text)
Else
[blah blah error handling]
End If
End Sub
I'd try another technique like SetForegroundWindow but I read it doesn't play nice with Windows 7.
Found what I hope will be a passable workaround from PInvoke. I ended up swapping this block:
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow _
As ShowWindowCommands) As Boolean
End Function
For this:
Public Declare Function BringWindowToTop Lib "user32" (ByVal hwnd As IntPtr) As Boolean
And then this line:
ShowWindow(lHwnd, ShowWindowCommands.SW_SHOWNORMAL)
For this:
BringWindowToTop(lHwnd)
I realize there are functional differences between the two but the change works in my specific instance so I'm happy.
I have compiled an old vb6 app into a dll. Eventually we will convert to .NET but for now I am trying to load the forms from the DLL. So far I have been able to load and see the vb6 form in .NET but I cannot activate the controls. So the setparent in the code below is working but the sendmessage and following code is not. By the way in debug mode this all works fine. It's only when I used the compiled dll that I have a problem.
My declare's are:
Private Shared Function SetParent(ByVal hWndChild As IntPtr, _
ByVal hWndNewParent As IntPtr) As Int32
End Function
Public Declare Auto Function SendMessage Lib "user32.dll" ( _
ByVal hWnd As IntPtr, _
ByVal wMsg As Int32, _
ByVal wParam As Int32, _
ByVal s As String _
) As Int32
Private Shared Function SetForegroundWindow(ByVal hwnd As IntPtr) As IntPtr
End Function
Private Shared Function LockWindowUpdate(ByVal hwndLock As IntPtr) As Boolean
End Function
The code I am using is as follows:
SetParent(mintFormHandle, Me.Handle.ToInt32)
SendMessage(mintFormHandle, WM_ACTIVATE, 1, IntPtr.Zero)
SetForegroundWindow(mintFormHandle)
LockWindowUpdate(0)
Me.Refresh()
Please no comments about old vb6 code. Yes in a perfect world a conversion would have been done years ago.
I am trying to add an 'About' button to the System menu of my app, but the code that I found is throwing an error -
Unable to find an entry point named 'AppendMenu' in DLL 'user32'.
I wonder if someone could please take a look at the code and advise on how what I would need to do to fix it? Thanks.
Private Declare Function GetSystemMenu Lib "user32" (ByVal hWnd As IntPtr, ByVal bRevert As Boolean) As IntPtr
Private Declare Function AppendMenu Lib "user32" (ByVal hMenu As IntPtr, ByVal uFlags As Int32, ByVal uIDNewItem As IntPtr, ByVal lpNewItem As String) As Boolean
Private Const MF_STRING As Integer = &H0
Private Const MF_SEPARATOR As Integer = &H800
Private Sub AddSysMenuItems()
'Get the System Menus Handle.
Dim hSysMenu As IntPtr = GetSystemMenu(Me.Handle, False)
'Add a standard Separator Item.
AppendMenu(hSysMenu, MF_SEPARATOR, 1000, Nothing)
'Add an About Menu Item.
AppendMenu(hSysMenu, MF_STRING, 1001, "About")
End Sub
Well, the message is accurate, there is no entry point named "AppendMenu" in user32.dll. It actually has two versions of it. One is named AppendMenuA, the A means Ansi. The legacy version that uses 8-bit encoded strings, commonly used in old C programs. And AppendMenuW, the W means Wide. It takes a Unicode string like all winapi functions do on modern Windows versions.
Your old-style Declare statement is using the legacy function. You should use the Alias keyword to give the proper entrypoint name:
Private Declare Function AppendMenu Lib "user32.dll" Alias "AppendMenuA" (ByVal hMenu As IntPtr, ByVal uFlags As Int32, ByVal uIDNewItem As IntPtr, ByVal lpNewItem As String) As Boolean
Or just plain call it AppendMenuA. Using the legacy function isn't very pretty, although it won't have a problem converting "About" to Unicode. But do favor the modern way to declare pinvoke functions, it has many advantages beyond automatically mapping to the A or W version:
Imports System.Runtime.InteropServices
Imports System.ComponentModel
...
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function AppendMenu(ByVal hMenu As IntPtr, ByVal uFlags As Int32, ByVal uIDNewItem As IntPtr, ByVal lpNewItem As String) As Boolean
End Function
...
If Not AppendMenu(hSysMenu, MF_STRING, IntPtr.Zero, "About") Then
Throw New Win32Exception()
End If
How should I DLLImport things in VB.NET? An example would be:
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function GetWindowText(ByVal hwnd As IntPtr, ByVal lpString As StringBuilder, ByVal cch As Integer) As Integer
End Function
If I put it inside a Class or somewhere else, I get "DLLimport is not defined" I am using Visual Studio 2008 Professional
You have to add Imports System.Runtime.InteropServices to the top of your source file.
Alternatively, you can fully qualify attribute name:
<System.Runtime.InteropService.DllImport("user32.dll", _
SetLastError:=True, CharSet:=CharSet.Auto)> _
Imports System.Runtime.InteropServices
I know this has already been answered, but here is an example for the people who are trying to use SQL Server Types in a vb project:
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Namespace SqlServerTypes
Public Class Utilities
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Public Shared Function LoadLibrary(ByVal libname As String) As IntPtr
End Function
Public Shared Sub LoadNativeAssemblies(ByVal rootApplicationPath As String)
Dim nativeBinaryPath = If(IntPtr.Size > 4, Path.Combine(rootApplicationPath, "SqlServerTypes\x64\"), Path.Combine(rootApplicationPath, "SqlServerTypes\x86\"))
LoadNativeAssembly(nativeBinaryPath, "msvcr120.dll")
LoadNativeAssembly(nativeBinaryPath, "SqlServerSpatial140.dll")
End Sub
Private Shared Sub LoadNativeAssembly(ByVal nativeBinaryPath As String, ByVal assemblyName As String)
Dim path = System.IO.Path.Combine(nativeBinaryPath, assemblyName)
Dim ptr = LoadLibrary(path)
If ptr = IntPtr.Zero Then
Throw New Exception(String.Format("Error loading {0} (ErrorCode: {1})", assemblyName, Marshal.GetLastWin32Error()))
End If
End Sub
End Class
End Namespace
I saw in getwindowtext (user32) on pinvoke.net that you can place a MarshalAs statement to state that the StringBuffer is equivalent to LPSTR.
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Ansi)> _
Public Function GetWindowText(hwnd As IntPtr, <MarshalAs(UnManagedType.LPStr)>lpString As System.Text.StringBuilder, cch As Integer) As Integer
End Function
You can also try this
Private Declare Function GetWindowText Lib "user32.dll" (ByVal hwnd As IntPtr, ByVal lpString As StringBuilder, ByVal cch As Integer) As Integer
I always use Declare Function instead of DllImport...
Its more simply, its shorter and does the same