VB6 SetWindowLong Causing Refresh Issue in Windows 7 64-bit - winapi

I am still supporting and old vb6 application that utilizes GetWindowLong and SetWindowLong to remove the ControlBox at runtime depending on a setting. This works great on all 32-bit systems but when it runs on a 64bit system the main window no longer refreshes properly. The problem seems to be input controls like TextBox, ListBox, or CommandButton. After being covered up by certain windows they don't display until they receive the focus and even then their borders don't show up properly.
I've read the MSDN documentation http://msdn.microsoft.com/en-us/library/ms633591%28v=vs.85%29.aspx that says these functions have been superseded by ...WindowLongPtr functions to be compatible with both 32-bit and 64-bit systems. From everything I've been able to read that is really talking about compiling both 32-bit and 64-bit version instead of running on the different platforms. I've tried changing my declare from
Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
To
Public Declare Function GetWindowLongPtr Lib "user32" Alias "GetWindowLongPtrA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Public Declare Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongPtrA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
But I get the error "Can't find DLL entry point GetWindowLongPtrA in user32". So I tried leaving the Alias as "...WindowLongA" and that runs and as I would expect doesn't make any difference in the refresh problem.
Has anybody else seen this or have any suggestions.
Here is a sample of how the code is used.
Private Sub Form_Activate()
...
Call SetControlBox(Me.hWnd, DisableFullScreen)
End Sub
Public Sub SetControlBox(ByVal hWnd As Long, ByVal Value As Boolean)
' Set WS_SYSMENU On or Off as requested.
Call FlipBit(hWnd, WS_SYSMENU, Value)
End Sub
Public Function FlipBit(ByVal hWnd As Long, ByVal Bit As Long, ByVal Value As Boolean) As Boolean
Dim nStyle As Long
' Retrieve current style bits.
nStyle = GetWindowLongPtr(hWnd, GWL_STYLE)
' Attempt to set requested bit On or Off,
' and redraw
If Value Then
nStyle = nStyle Or Bit
Else
nStyle = nStyle And Not Bit
End If
Call SetWindowLongPtr(hWnd, GWL_STYLE, nStyle)
Call Redraw(hWnd)
' Return success code.
FlipBit = (nStyle = GetWindowLongPtr(hWnd, GWL_STYLE))
End Function
Public Sub Redraw(ByVal hWnd As Long)
' Redraw window with new style.
Const swpFlags As Long = SWP_FRAMECHANGED Or SWP_NOMOVE Or SWP_NOZORDER Or SWP_NOSIZE
SetWindowPos hWnd, 0, 0, 0, 0, 0, swpFlags
End Sub
Thanks
dbl

Try adding SWP_NOACTIVATE (&H10) bit to your swpFlags constant.
Btw, this redraws non-client area only. A name like RedrawNonclient would make it apparent.

Related

Reading a Hebrew string into VB6 from a DLL res file

I created a String Resource .RES file in Visual Studio 2019 with various string tables for multiple languages, then I compiled the .RES into a VB6 DLL (no code, the VB6 project is just a compiled VB6 DLL). Here is the no-code VB6 project that creates the DLL:
I then read the strings from this DLL into a VB6 program, and output to a Unicode-aware label control.
The strings read/output fine from English and Arabic, but for Hebrew, it just shows the same character.
Option Explicit
Private Declare Function LoadString Lib "user32" Alias "LoadStringA" (ByVal hInstance As Long, ByVal wID As Long, ByVal lpBuffer As String, ByVal nBufferMax As Long) As Long
Private Declare Function LoadStringW Lib "user32" (ByVal hInstance As Long, ByVal wID As Long, ByVal lpBuffer As String, ByVal nBufferMax As Long) As Long ' Works Arabic
Private Declare Function SetThreadUILanguage Lib "kernel32" (ByVal dwLCID As Long) As Long
Private Declare Function SetThreadLocale Lib "kernel32" (ByVal dwLCID As Long) As Long
Private Sub Form_Load()
Dim hInst As Long, lResult As Long
Dim resstring As String
Dim icc As Long
Const STRLENGTH As Long = 1000
Const HEBREW As Long = 1037
Const ARABIC As Long = 3073
Const ENGLISH As Long = 1033
icc = ENGLISH ' convenience, set it once here
SetThreadUILanguage icc
SetThreadLocale icc
hInst = LoadLibrary("c:\temp\resstr.dll")
If hInst Then
resstring = String(STRLENGTH, Chr(0))
If icc = ENGLISH Then
lResult = LoadString(hInst, 101, resstring, STRLENGTH)
Label1.Caption = Left$(resstring, lResult)
Else
lResult = LoadStringW(hInst, 101, resstring, STRLENGTH)
Label1.Caption = StrConv(Left(resstring, lResult * 2), vbFromUnicode, icc)
End If
lResult = FreeLibrary(hInst)
End If
End Sub
As you can see, the Arabic output is fine (and so is the English, just not screen captured). BUT...the Hebrew prints out the same character?!
You cannot Declare an argument As String to the *W family of functions.
VB6 will automatically convert a String to the current system codepage for non-Unicode programs when calling into a Declared function, and convert back to Unicode when the call returns. This mechanism is designed to interact with the *A family of functions that deal with ANSI.
When calling a *W function in that way, not only the Unicode data will be destroyed even before you get a chance to execute your StrConv(vbFromUnicode) (which you should almost never do, and here it will only destroy the data even further), but you also have a buffer overflow where you promise to the function that you have provided 1000 characters of space, whereas you only provide 1000 bytes, which is half as much.
In order to call a *W function, you must declare the string buffer As Long and pass StrPtr() of the string variable.
You also don't need to fall back to LoadStringA, as it is nothing more than a wrapper around LoadStringW.
Your declaration for SetThreadUILanguage is also wrong (LANGID is an Integer, as opposed to LCID which is a Long).
Option Explicit
Private Declare Function LoadStringW Lib "user32" (ByVal hInstance As Long, ByVal wID As Long, ByVal lpBuffer As Long, ByVal nBufferMax As Long) As Long
Private Declare Function SetThreadUILanguage Lib "kernel32" (ByVal LangId As Integer) As Integer
Private Declare Function SetThreadLocale Lib "kernel32" (ByVal dwLCID As Long) As Long
Private Sub Form_Load()
Dim hInst As Long, lResult As Long
Dim resstring As String
Dim icc As Long
Const STRLENGTH As Long = 1000
Const HEBREW As Long = 1037
Const ARABIC As Long = 3073
Const ENGLISH As Long = 1033
icc = ENGLISH ' convenience, set it once here
SetThreadUILanguage icc
SetThreadLocale icc
hInst = LoadLibrary("c:\temp\resstr.dll")
If hInst Then
resstring = String(STRLENGTH, vbNullChar)
lResult = LoadStringW(hInst, 101, StrPtr(resstring), STRLENGTH)
Label1.Caption = Left$(resstring, lResult)
lResult = FreeLibrary(hInst)
End If
End Sub
FIX: In Control Panel/Region/Administrative tab, I had to change the "Current language for non-Unicode programs" to 'Hebrew' or 'Arabic' to get it to display correctly. #GSerg also added helpful tips of properly calling a W function.

Marquee progress bar and PM_SETMARQUEE being ignored in VB6

I've just tried setting up a scrolling marquee progress bar in one of my VB6 applications, and while it seems to switch into marquee mode, the automatic timer (set via PBM_SETMARQUEE) either fails or is ignored.
I have an appropriate visual style manifest.
I have called InitCommonControls in the form's Initialize event.
I am using the v5 SP2 common controls.
The project is compiled.
I have set the PBS_MARQUEE style via SetWindowLong and confirmed it has applied.
The PBM_SETMARQUEE message returns 1.
The results:
If I minimise and restore the window it progresses one step.
If I set .Value it progresses one step.
If I send PBM_STEPIT it progresses one step.
If I send PBM_SETMARQUEE it sits there doing nothing.
If I don't pass the initial PBM_SETMARQUEE enable message, then the minimise/restore doesn't cause it to progress suggesting the timer is the problem.
This is the same issue discussed in this thread from 2006
Does anyone have any deeper insight as to why the VB wrapper is dropping the PBM_SETMARQUEE message or the associated timer events and why I need to do it "manually"?
Option Explicit
Private Const GWL_STYLE = (-16)
Private Const PBS_MARQUEE = &H8
Private Const WM_USER = &H400
Private Const PBM_STEPIT = WM_USER + 5
Private Const PBM_SETMARQUEE = WM_USER + 10
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Sub Form_Load()
'Set the marquee style
Dim Style As Long
Style = GetWindowLong(ProgressBar1.hWnd, GWL_STYLE)
Style = Style Or PBS_MARQUEE
SetWindowLong ProgressBar1.hWnd, GWL_STYLE, Style
'Set automatic marquee mode
SendMessage ProgressBar1.hWnd, PBM_SETMARQUEE, 1, ByVal 0&
End Sub
This is needed to make it progress.
Private Sub Timer1_Timer()
SendMessage ProgressBar1.hWnd, PBM_STEPIT, 0, ByVal 0&
End Sub

Performing a KeyDown function without focus in Visual Basic

It's quite simple really. I want for an application to keep monitoring KeyDown events even without focus.
Private Sub Form1_KeyDown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
Select Case e.KeyData
Case Keys.MediaStop
PictureBox2_Click(sender, e)
Case Keys.MediaPlayPause
PauseToolStripMenuItem_Click(sender, e)
Case Keys.MediaNextTrack
SkipTrackToolStripMenuItem_Click(sender, e)
Case Keys.MediaPreviousTrack
PreviousTrackToolStripMenuItem_Click(sender, e)
End Select
End Sub
The above code is for a music player. Functions are called when the media keys are pressed ('Fn'+ 'Home', 'Fn' + 'Pg Up'...etc)
In a previous comment, somebody suggested looking into WH_KEYBOARD_LL for a solution but I didn't really understand much of it, if I'm honest.
UPDATE:
The link suggested isn't great as the 'As Any' keyword is not supported in 'Declare' functions.
This is how far I've got with it...
Public Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, ByVal ncode As Long, ByVal wParam As Long, lParam As **Any**) As Long
Public Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long
Public Declare Function SetWindowsHook Lib "user32" Alias "SetWindowsHookA" (ByVal nFilterType As Long, ByVal pfnFilterProc As Long) As Long
Public Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As **Any**, Source As **Any**, ByVal Length As Long)
**Global** Const WH_KEYBOARD_LL = 13
Public Const HC_ACTION = 0
Structure HookStruct
Dim vkCode As Long
Dim scancode As Long
Dim flags As Long
Dim time As Long
Dim dwExtraInfo As Long
End Structure
The errors that Visual Studio is underlining are all written in bold
On hover of 'Any' it gives me the message "'As Any' is not supported in 'Declare' statements"
On hover of 'Global' it gives "Syntax error"
Implementing Keyboard hook using Windows API is the solution. Please check : http://www.planetsourcecode.com/vb/scripts/ShowCode.asp?txtCodeId=13506&lngWId=1
If you are using VB6 and want to Hide the Navigation Bar, simply do this.
aw$ = "^(h)"
SendKeys aw$
Apply this after pdf file loads.

VB4 AddressOf equivalent (Adding items to the system menu)

Is there an equivalent to AddressOf in Visual Basic 4?
I am attempting to get some Visual Basic 5/6 code that adds menu items to my form's system menu working in VB4. Below is the code snippet that will not compile:
'Add Custom Menu To System Menu
Call AppendMenu(GetSystemMenu(Me.hWnd, 0&), MF_SEPARATOR, 0&, vbNullString)
Call AppendMenu(GetSystemMenu(Me.hWnd, 0&), MF_STRING, IDM_SYSTEMTRAY, "Minimize To &System Tray")
Call AppendMenu(GetSystemMenu(Me.hWnd, 0&), MF_STRING, IDM_ABOUT, "&About...")
procOld = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf WindowProc)
Here is the API declarations from the module:
' Win32 API
Public Const MF_SEPARATOR = &H800&
Public Const MF_STRING = &H0&
Public Const GWL_WNDPROC = (-4)
Public Declare Function GetSystemMenu Lib "user32" (ByVal hWnd As Long, ByVal bRevert As Long) As Long
Public Declare Function AppendMenu Lib "user32" Alias "AppendMenuA" (ByVal hMenu As Long, ByVal wFlags As Long, ByVal wIDNewItem As Long, ByVal lpNewItem As Any) As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Const WM_SYSCOMMAND = &H112
' User Created
Public procOld As Long
Public Const IDM_ABOUT As Long = 1010
Public Const IDM_SYSTEMTRAY As Long = 1011
And finally, the code for WindowProc:
Public Function WindowProc(ByVal hWnd As Long, ByVal iMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Select Case iMsg
Case WM_SYSCOMMAND
Select Case wParam
Case IDM_ABOUT
MsgBox "About goes here..."
Exit Function
Case IDM_SYSTEMTRAY
MsgBox "Send to system tray code goes here..."
Exit Function
End Select
End Select
' Pass all messages on to VB and then return the value to windows
WindowProc = CallWindowProc(procOld, hWnd, iMsg, wParam, lParam)
End Function
Any other pointers or alternate methods of adding items to my form's system menu would be appreciated too!
From: Xtreme VB Talk: AddressOf work around for vb4
[Banjo]
I don't think that you can without resorting to somesort of outside
DLL written in C.
[OnErr0r]
Matt Curland wrote just such a dll. Allows you to get the address of a class function, even works with vb5/6.
You can download callback.zip from the above page after registering. As a fallback, you can get the file here: callback.zip
EDIT: The direct download link has been removed, because it appears that this source code is part of a book by Matt Curland and copyrighted.

sendmessage not working for jetaudio in vb6

i am trying to implement the Jetaudio API in vb6...
i have taken the values of the constants from the API SDK..
Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function RegisterWindowMessage _
Lib "user32" Alias "RegisterWindowMessageA" _
(ByVal lpString As String) As Long
Public Const WM_APP As Long = &H8000
Public Const MyMSG As Long = WM_APP + 740
Public Function GetJetAudioSong()
Dim v As Long
Dim JAhwnd As Long
Dim lngMyMsg As Long
lngMyMsg = RegisterWindowMessage(MyMSG)
JAhwnd = FindWindow("COWON Jet-Audio Remocon Class", "Jet-Audio Remote Control")
v = SendMessage(JAhwnd, lngMyMsg, 0, 995)
MsgBox v
End Function
Now, FindWindow() is working cause JAhwnd is set with a value...
its just the sendmessage() that doesn't seem to be working...
the code is suppose to msgbox the version number for the running Jet Audio instance.
i've been at it for days now and i have no way of making sure weather this error is a VB thing or not... i am taking Jet Audio's SDK's word that the values of the const are correct...
the value of v is always 0 where it should be 6 on my system.
what am i doing wrong?
Don't call RegisterWindowMessage, MyMSG is message number that you should send to the Jet-Audio window.
Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Const WM_APP As Long = &H8000
Public Const MyMSG As Long = WM_APP + 740
Public Function GetJetAudioSong()
Dim v As Long
Dim JAhwnd As Long
Dim lngMyMsg As Long
JAhwnd = FindWindow("COWON Jet-Audio Remocon Class", "Jet-Audio Remote Control")
v = SendMessage(JAhwnd, MyMSG, 0, 995)
MsgBox v
End Function
What Windows Version?
SendMessage and SendKeys no longer works with VB6 code starting at Windows Vista and above.
Do a Google search for it.
I know this is 2 years too late. Please use this as a future reference for anyone reading this in the future.
The fix for your issue is this:
'[ Use 'ByVal' for your lParam to make sure you are passing the actual value not the Reference
v = SendMessage(JAhwnd, lngMyMsg, 0, ByVal 995)
'[ Or you could perform PostMessage(..) and not use ByVal
v = PostMessage(JAhwnd, lngMyMsg, 0, 995)
Also, i HIGHLY recommend against anyone using SendKeys. API is the correct method to ensure you are sending message to the correct hWnd. I would suggest using SendKeys only if in desperation; it can happen.

Resources