Using vbAccelarator Win32 Hook crashes application along with VB IDE - vb6

I'm working on VB6 project where I need to have keyboard short-cuts for Buttons on Toolbar Control. To accomplish this, I have used Win32 Hooks library from vbAccelerator. Here's my IWindowsHook_HookProc function, that I use to retrieve Key strokes & perform action based on pressed short-cut (Ctrl + N for New, Ctrl + O for Open and Ctrl + S for Save) , but the I don't know what's wrong with the code that crashes my application along with VB6 IDE. The function is currently incomplete as I just tried to identify Ctrl + N key combination to test this feature. Please help me out.... :-|
Private Function IWindowsHook_HookProc(ByVal eType As EHTHookTypeConstants, ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long, bConsume As Boolean) As Long
If KeyboardlParam(lParam).KeyDown Then
Select Case True
Case Me.ActiveControl = Me
If wParam = vbKeyControl + vbKeyN Then
frmNewReport.show
bConsume = True
End If
End Select
End If

Using a hook in the IDE can lead to lots of crashes, heck using a hook without fully understanding what you are doing is going to lead to a lot of crashes period...
Mark is correct about the Timer with the show form, as the Hook function should return as fast possible (< 50 ms) or your will end up with deadlock (and a crashed app) very quickly. Never set a breakpoint inside the Hook procedure, or you will kill your IDE (maybe crash, maybe hung, maybe some wierd state were you can never leave a breakpoint and you can't stop debugging). If you have a ton of long running functions you want to run based on a keypress, then set up a stack of actions to perform in the timer. Using a hook library is very powerful, but with great power comes great crashes...

I have no experience with that hooks library, but my guess is that you should do very little in the HookProc procedure itself. You are being called directly from the Windows API, rather than via the VB6 runtime. I'm not surprised that showing forms crashes everything out as you describe. Was there any advice on the vbAccelerator site about what sort of code to put in HookProc? vbAccelerator is an excellent site by the way.
I suggest you just set a flag variable somewhere to indicate that frmNewReport should be shown. You should have a Timer running with a short tick interval, say 100 milliseconds, which checks the flag variable: if the flag is set, clear the flag and show the form.

I've found a solution to my own question, its still crash-prone if not handled carefully, but now my application actually responds to key combinations that I wanted, Ctrl + N, Ctrl + O, etc.
Following is my rectified code that works fine as far as I know. Do suggest if you find any bug in it that lead to crash my application.
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
Private Property Get CtrlPressed() As Boolean
CtrlPressed = (GetAsyncKeyState(vbKeyControl) <> 0)
End Property
Private Function IWindowsHook_HookProc(ByVal eType As EHTHookTypeConstants, ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long, bConsume As Boolean) As Long
If wParam = vbKeyN Then
If CtrlPressed Then
LoadFormNewReport 'Method that opens Child Form 'New Report'
End If
bConsume = True
ElseIf wParam = vbKeyS Then
If CtrlPressed Then
SaveNewReport 'Method that saves new Report
End If
bConsume = True
ElseIf wParam = vbKeyF5 Then
If Not CtrlPressed Then
frmSettings.Show 'This form needs to be displayed Modally but if tried so then crashes application along with VB IDE, other short-cuts work fine.
bConsume = True
End If
End If
End Function

Related

What is happening in this code

I was looking for some vb6 that would allow me to populate a listbox with a directory of files when I found the following elegant piece of code.
List1.hwnd, &H18D, &H20, "directory*.*"
Most of the other examples I found were 4 or more lines of code. Can someone help me understand what is happening here? What is the
List1.hwnd, &H18D, &H20
Part doing?
There was a general declaration I left out.
Private Declare Function SendMessageStr Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As String) As Long
Your edit still makes not enough sense - unless the code was
SendMessageStr List1.hwnd, &H18D, &H20, "directory*.*"
and you thought the space means it's a stand-alone bit (it isn't).
That code is
SendMessage (List1.hwnd, LB_DIR, DDL_ARCHIVE, "directory*.*") - it sends a documented Windows message to a ListBox control named List1 to fill it with files that match the mask "directory*.*", including the archived ones.

Buttonless Temperature Converter in Visual Basic?

Public Class Form1
Private Sub FahrenheitTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles FahrenheitTextBox.TextChanged
CelsiusTextBox.Text = 5 / 9 * (Val(FahrenheitTextBox.Text) - 32)
End Sub
Private Sub CelsiusTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CelsiusTextBox.TextChanged
FahrenheitTextBox.Text = (9 / 5) * Val(CelsiusTextBox.Text) + 32
End Sub
End Class
So, that's my code. Our assignment was to create a temperature converter that worked without buttons- so, when I typed in the Fahrenheit box, it would automatically convert to Celsius in the Celsius box. The way I determined doing this was by using an if statement- my professor said I was not allowed to do that because we have not learned it yet. My problem right now is that the two TextChanged events are competing with each other- so when I type in one, it calculates the other, and then calculates the other and keeps going and messing up the numbers. I am not allowed to use an 'if statement'. His reply to my question if we could use an if statement was "Adding IF statement would solve the problem together with TextChanged event. However, at this moment, let’s assume that we do not know IF statement (with Boolean values). In addition, it does not have to be complicated if you choose the right event for textbox." So...I don't really know how to proceed without an if statement or an event that would require pressing a key. I emailed him with my problems (stating I did not know how to proceed without the if statement or pressing a button (which would take away the automatic conversion)) He also stated, when I inquired about a KeyPress event, "You are so close to the answer. KeyPress event asks you to press and hold the key before it works. How about other key events? I am sure you will get the solution out soon."
Could anyone please help me?
Have you tried KeyDown event? Here is an example: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.keydown(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-2

Is there an API to get the advanced printer device settings?

I need to get the available supported printer document features for a particular printer through the win32 api.
The options I'm referring to are displayed for a sample printer in the image below. You can get this dialog by right clicking the printer icon and the click preferences, and the clicking the advanced tab in the dialog.
Can anyone tell me the need api i need to call, and in what other?
I am targeting Windows Windows XP+ and using vb6.
The DeviceCapabilities function is read only. What you're looking for is the DocumentProperties function. DeviceCapabilities is actually rather dated and should have been obsoleted long ago since it assumes only a single user and single application is using the printer. You don't want to set printer properties for every print job; you want to set document properties for your current print job. The DocumentProperties function will do that for you, but pay close attention to the instructions in the link above on how to make changes. It's a needlessly complex function.
What I think you are looking for is DeviceCapabilities from winspool.drv. Sample code from Microsoft. Most of the documentation online now days is for .Net, So I typed in the VB6 definition from Dan Appleman's Visual Basic Programmer's Guide to the Win32 API
The declaration for VB6 is:
Declare Function DeviceCapabilities& Lib "winspool.drv" Alias "DeviceCapabilitiesA" (ByVal lpDeviceName as String, ByVal lpPort as String, ByVal iIndex as Long, ByVal lpOutput as String, ByVal lpDeviceMode as Long)
Based on your Comment you would need to use DocumentProperties it can be used to retreive or modify a DevMode Structure. It may or may not have what you are looking for. Your best bet would be to get the above book, Chapter 12 has wealth of information. Also looking at your picture it looks like you are using somesort of a POS print, you should consult the manufacturers documentation about specific api's that the printer might support.
Declare Function DocumentProperties& Lib "winspool.drv" Alias "DocumentPropertiesA" (ByVal hwnd as Long, ByVal hPrinter as Long, ByVal pDeviceName as String, ByVal pDeviceModeOutput as Long, ByVal pDeviceModelInput as Long, ByVal fMode as Long)
DevMode Structure
Public Const CCHDEVICENAME = 32
Public Const CCHFORMNAME = 32
Type DEVMODE
dmDeviceName as String * CCHDEVICENAME
dmSpecVersion as Integer
dmDriverVersion as Integer
dmSize as Integer
dmDriverExtra as Integer
dmFields as Long
dmOrientation as Integer
dmPaperSize as Integer
dmPaperLength as Integer
dmPaperWidth as Integer
dmScale as Integer
dmCopies as Integer
dmDefaultSource as Integer
dmPrintQuality as Integer
dmColor as Integer
dmDuplex as Integer
dmYResolution as Integer
dmTTOption as Integer
dmCollate as Integer
dmFormName as String * CCHFORMNAME
dpBitsPerPixel as Integer
dmBitsPerPel as Long
dmPelWidth as Long
dmPelHeight as Long
dmDisplayFlags as Long
dmDisplayFrequency as Long
dmICMMethod as Long
dmICMIntent as Long
dmMediaType as Long
dmDitherType as Long
dmReserved1 as Long
dmReserved2 as Long
End Type

xsel for windows equivalent API or command line?

Is there an API or command line utility that returns the currently selected text from either active window or even globally, like the linux utility "xsel" ?
I don't mind getting less than 100% success.
i know that each window can have its own text selection, but it's negligible for now.
For now the only solution/workaround i have is to sendkeys "ctrl+c" and read from clipboard, but that's bad solution for 2 obvious reasons.
I know how to do it on MS-Word, but that's 1% of the cases.
thanks
edit
From this discussion i learn that there are too many technologies to select text. so i'll fall back to using clipboard. thanks anyway.
i'm leaving this question open for a while in case somebody have a miracle.
this is a solution i compiled from several sources.
it will work only on simple text controls. for the other types of text there are other solutions, but for simplicity i'll use the clipboard.
(for a complete code, declarations and dependencies, google for "SendMessage hWndCaret")
If hWndCaret <> 0 Then
'first, get all text
nLength = SendMessage(hWndCaret, WM_GETTEXTLENGTH, 0&, ByVal 0&)
If nLength <> 0 Then
buff = Space$(nLength + 1)
res = SendMessage(hWndCaret, WM_GETTEXT, nLength + 1, ByVal buff)
If res <> 0 Then Txt = Left$(buff, res)
End If
' then
If nLength <> 0 Then
buff = Space$(nLength + 1)
res = SendMessage(hWndCaret, EM_GETSEL, VarPtr(StartPos), EndPos)
selection = Mid(Txt, StartPos + 1, EndPos - StartPos)
End If
End If
Check if the program supports accessible interface like IAccessible/IAccClientDocMgr or TextPattern_GetSelection/TextRange_GetText. Many software need to implement accessible to sell to the US government because of the Americans with Disabilities Act. You can call AccessibleObjectFromWindow or AutomationElement::FromHandle on a window.
It looks like nobody documents their accessible object tree, and if there is an API exists, then the API is the preferred way to get information from the program. E.g. you should use Q249232 to get IHTMLDocumnent2 if the application is IE. There are significant changes in IE7 and IE8's assessible tree when inspected in UI spy.
For other programs you may be out of luck. I cannot find the selection in the login email edit when using UISpy. Accessiblility depends on the good will of programmers to implement accessibility in their programs.

How do you make a MsgBox with a "Do Not Ask This Again" or "Don't Ask Me Again" Checkbox in VB6?

I ask this partly because I want to know the best practice way of doing this, and partly because the top google result I got was a forum thread from 2002 in which the question wasn't even answered.
I inherited some VB6 code, and there are some MsgBox calls in said code, many of which display messages that end users would probably find very annoying after a short time (e.g. "Printing Complete," "Record Added," etc.)
I would like to add the standard user interface control of a checkbox on the MsgBox saying "Don't ask me this again", so that when checked and OK is clicked, a setting is saved that lets the program know, you know...to never ask that again. Pretty standard control, the idea is fairly self-explanatory.
What I would like to know is what is the best practice way of doing this in VB6. There is the obvious way of just making a new form for these type of msgboxen and replacing the old MsgBox call with a .Show on that form, but do the VB6 gurus on Stack Overflow have a better way?
Thanks in advance
As far as I know, there is no other way. You need to make your own message box form with the check box. Of course, you'll also need to modify the code to store and retrieve this setting (and acting appropriately based on the setting).
I have done this in my own application many times. One thing to think about.... suppose the user checks the box "don't show me this again". In my opinion, there should be a way to reset the setting. Since the message box form won't show again, I added this to the configuration form (for my app).
One thing you may want to consider is sub-classing the MSGBOX function. You could create a function within your app that has a similar parameter list, but with a couple extra. If the extra parameters are missing, simply call vba.MsgBox (to get the standard behaviour). If you pass in extra parameters, you could call your new form instead.
Well... You are not absolutely correct guys ;)
Starting from Win2000, there's SHMessageBoxCheck function that does the trick.
VB6 declaration:
Private Declare Function SHMessageBoxCheck Lib "shlwapi" Alias "#185" (ByVal hWnd As Long, ByVal lpszText As String, ByVal lpszTitle As String, ByVal dwType As VbMsgBoxStyle, ByVal iDefault As Long, ByVal lpszId As String) As Long
For everything else follow the link :)
If you provide such a functionality, it might require "turning-on" of showing messagebox.
i.e. user should have an option to see the msgbox again using some setting.
Instead of that, you could use statusbar to display notifications OR have a label with the notification messages and turn it off after a few seconds.
This was my down and dirty solution:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
myfile = Workbooks.Application.ActiveWorkbook.Path & "\noprompt.txt"
If Dir(myfile) <> "" Then Exit Sub
exportData = MsgBox("Export data?" & vbCrLf & "Select Cancel (or × top right) to prevent this prompt from displaying again.", vbYesNoCancel, "Close Workbook")
If exportData = vbYes Then
Call ExportValues 'a separate function...
ElseIf exportData = vbCancel Then
'create file noprompt.txt
Open myfile For Output As #1
Write #1, "Delete this file to restore prompt to Export Data when workbook is closed."
Close #1
Exit Sub
ElseIf exportData = vbNo Then
Exit Sub
End If
End Sub
Documentation for my app explains that deleting the file restores the prompt.

Resources