Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

MsgBox format 7

Status
Not open for further replies.

deepsheep

Programmer
Sep 13, 2002
154
CA
I have a msgbox in a VB6 application that is doing what I want. It displays a message and stays on top of everything (by vbSystemModal option) until the 'OK' is pressed.

However, I want the text to be bigger in the msgBox. I want it to be big enough that if someone wanders past the screen, they can read it easily.

I tried changing the font size on the form and it didn't work.

Any ideas?
Thanks!
 
Well, your first problem is caused by the fact that you hadn't declared WM_SETFONT. This causes an error to be raised the moment you enter CBTProc (whether the line with WM_SETFONT is called or not; REMming hides the line, so the error does not occur), and getting an error raised in a hook will almost always end in an IDE crash.

Secondly, you don't want to be looking for #32770 (which is indeed the correct class name for the MsgBox window), because you've already got it. That's the window the CBTHook spots being created, and whose handle is in wParam. Since we're looking to increase the size of the text in the MsgBox we need to look for the child window that contains that text, which is a class called 'Static'

Thirdly, even if we replace '#32770' with 'Static' it still won't work, becasue the way I used the call in the original example sought a window class called 'Edit' with a caption of "". But the 'Static' control has a caption of whatever the MsgBox prompt text is, so looking for "" as the caption fails. You have two options: change "" to the prompt text, or change "" to null, so we can match any 'Static' window that we find in the MSgBox (if you do the latter you'll need to change the declaration of FindWindowEx slightly, and be aware that adding an icon to the MSgBox adds a new 'Static' class window which FindWindowEx will now find before the one with text whose font size we want to change.

And finally, if we increase the size of the font we will also probably need to increase the size of the control.

>I'm not sure whether I'm converting points to pixels properly

You can avoid using the API at all for this ...

Example, assuming you have your hFont declared as a global:
Code:
[blue]Private myFont as IFont 

Set myFont=new StdFont
myFont.size=14 ' We can play around with the font to our heart's content here, all in a VB-friendly way
hFont=myFont.hFont[/blue]

And then we can pass hFont, as you already do, to SendMessage. Naturally you could cut out the middle man (hFont) and just pass myFont.hFont





 
In fact, here's my quick modifictaion to your code (excluding the resizing of the Static control).

Form:
Code:
[blue]Option Explicit

Private Sub Command1_Click()
    myMsgBox "Hello Everyone", vbInformation, 14, True, True, True, "Times New Roman", "Test1"
    myMsgBox "And Again", vbInformation, 8, True, False, False, "Arial Narrow", "Test2"
End Sub
[/blue]
Module
Code:
[blue]Option Explicit

Const FW_NORMAL = 400
Const FW_BOLD = 700
Private Const WM_SETFONT = &H30

' Necessary constants  for hooking
Private Const HCBT_ACTIVATE = 5
Public Const WH_CBT = 5

' Working variables that require global scope in hooking module
Private hHook As Long
Private myFont As IFont

' The API declarations we need
Private 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
Private Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook 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 Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, lpsz2 As Any) As Long
Private Declare Function CreateFont Lib "gdi32" Alias "CreateFontA" (ByVal nHeight As Long, ByVal nWidth As Long, ByVal nEscapement As Long, ByVal nOrientation As Long, ByVal fnWeight As Long, ByVal fdwItalic As Boolean, ByVal fdwUnderline As Boolean, ByVal fdwStrikeOut As Boolean, ByVal fdwCharSet As Long, ByVal fdwOutputPrecision As Long, ByVal fdwClipPrecision As Long, ByVal fdwQuality As Long, ByVal fdwPitchAndFamily As Long, ByVal lpszFace As String) As Long

' Wrapper for the normal MsgBox function
Public Sub myMsgBox(Prompt As String, Buttons As VbMsgBoxStyle, fSize As Integer, fBold As Boolean, fItalic As Boolean, fULine As Boolean, fFaceName As String, Optional Title As String, Optional HelpFile As String, Optional Context As Long)
    Set myFont = New StdFont
    myFont.Size = fSize ' We can play around with the font to our heart's content here, all in a VB-friendly way
    myFont.Bold = fBold
    myFont.Italic = fItalic
    myFont.Underline = fULine
    myFont.Name = fFaceName
    hHook = SetWindowsHookEx(WH_CBT, AddressOf CBTProc, App.hInstance, 0)
    MsgBox Prompt, Buttons, Title, HelpFile, Context 'code hangs here
End Sub

Private Function CBTProc(ByVal lMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Dim hwndMsgBox As Long
    If lMsg = HCBT_ACTIVATE Then
        hwndMsgBox = FindWindowEx(wParam, 0&, "Static", ByVal "")
        hwndMsgBox = FindWindowEx(wParam, hwndMsgBox, "Static", ByVal 0&) ' get the msgbox window, which has the obscure name of "#32770" as opposed to something more normal like "Edit"
        If hwndMsgBox Then SendMessage hwndMsgBox, WM_SETFONT, myFont.hFont, True ' Do your stuff here to modify the window
        UnhookWindowsHookEx hHook ' Immediately unhook
    End If
    CBTProc = 0 ' allow operation to continue
End Function[/blue]
 
<you hadn't declared WM_SETFONT
That's embarrassing. I thought you were telling me I needed to use a different constant! [blush]

<Secondly
Well, that's a little less embarrassing, but still a bit. Static makes sense, given that it would hold static text and Edit holds editable text.

<Private myFont As IFont
I knew it was around and couldn't find it. I'm surprised there are so many examples of the various API versions and so few of this. Should have looked in the object browser...

<if you do the latter
I did the former, and this code works (cPrompt is a module-level variable that holds the value of the Prompt argument passed to the wrapper procedure):
Code:
[COLOR=blue]hwndMsgBox = FindWindowEx(wParam, 0&, "Static", ByVal cPrompt & Chr(0))[/color]
However, there are some things I don't quite get about the latter. I see that "lpsz2 As String" has to change to "lpsz2 As Any" so you can pass it a null. I also see that your first FindWindowEx will grab the icon and the next one will move down the zorder and grab our static window. What I'm not sure I see is what happens when there isn't an icon. It looks like the first FindWindowEx will return the handle to the window we're looking for, and then the next one will return 0 which would then mess up the SendMessage call. Since the code works, this is obviously not the case, though. Perhaps there's an empty dummy window for the icon even when there isn't one specified in the call? Also, is your reason for using this alternative (when the other one requires one less call) so that you can interact with the icon window (say, resize it) if you want to, or do you have another reason?

Thanks for your help on this.

Bob
 
<Perhaps there's an empty dummy window
I changed the code that checks for HCBT_ACTIVATE into a select statement and added this:
Code:
        Case HCBT_CREATEWND
            Debug.Print wParam
In this case I should get the handle of each window as it is created. Sure enough, I got 4 handles when I was using one of the msgbox icons, and 3 when I wasn't. So no, there isn't an empty dummy window.

Hmm....
 
One other question, to get started with "version 2". Can you just store the handles to all the child windows and use SetWindowLong to change their sizes upon activation, or do you have to go digging into the CBT_CREATEWINDA and CREATESTRUCTA structures exposed on window creation?
 
You can't? You should know by now that I'm a naturally lazy individual, who will cut corners wherever possible. :) Ok, ok, I'll try SetWindowLong and see where it takes me. Don't spill the beans just yet.
 
The answer is yes, you can use SetWindowLong to change the size. What size to change it to is proving more interesting. Stay tuned....
 
This is great! I've learned alot.
Just wanted to let you guys know I'm not ignoring you, I just got pulled else where.
I showed the higher ups what I had alredy done and they thought it was good enough to be able to prove concept. We're going to do a little testing and get some feedback before I do anymore work on it. Just in case the users manage to break it in a way I didn't think of and I need to come up with a different method of getting the information to them.
 
This is turning into a coding lesson for me, ds. I'm glad it's useful to you as well. :)

<It looks like the first FindWindowEx will return the handle to the window we're looking for, and then the next one will return 0 which would then mess up the SendMessage call.

I get it now. The icon Static window is higher in the Z order than the text Static window. If the first FindWindow doesn't find a Static window with a 0 handle (i. e. top of the zorder) and no text, it will return 0, otherwise it will return the window's handle. If it returns 0, the next FindWindow will return the first Static window in the Zorder no matter what the text value, and in this case it is this window that we want. If the frist FindWindow returns a handle (non-0), the next FindWindow will return the next Static window in the Zorder after the handle, which also is the one we want.

 
Ok, I have a new question. I'm trying to find out the actual screen size of the prompt string after the font is applied, so I can size the window appropriately. I'm sure there's some kind of function that I can call, but so far I haven't been able to find it. After some looking around, I found GetTextExtentPoint32, and started experimenting with it, but I'm getting the feeling it's a blind alley. Here's a bit of code:
Code:
SendMessage hwndStatic, WM_SETFONT, myFont.hFont, True
dc = GetDC(hwndStatic)
GetTextExtentPoint32 dc, cPrompt, Len(cPrompt), sz
Where hwndStatic is the handle to the static window, dc is a handle to a device context, and sz is a SIZE structure. Evaluating sz, I find that sz.cy doesn't change when I change the font size for the prompt, and sz.cx only changes if I alter the text itself rather than the font size.

So, I started tinkering with the PictureBox's TextWidth method, on the idea that it was based on the same principles and would be easier to work with. I added a label and a button that would check the TextWidth of the label caption. I found that no matter what font size I set the label to, the textwidth method returned the same value. I then changed the font property of the PictureBox itself rather than that of the label, and found that the value TextWidth returns varies with the size of the Picture Box's font, and not with that of the text passed in as an argument.

This I found to be a bit counterintuitive. Intuitively, I would think that a function set up as "TextWidth(string argument)" would return the text width of the string argument. Of course, that's not the case, and I had the feeling that GetExtentPoint32 might work in something like the same way. So, I tried passing wParam (the handle to the msgbox window). Unfortunately, that didn't work. So, I passed the Form's window handle, and sure enough, the results vary with changes to the form's font property. (Why I get 29 debug prints instead of 1 I'm not quite sure yet, but that's a side issue.)

Obviously, I don't want to tie the evaluation of the text size in this window to the Form's font property. I can see reasons not to tie it to the main msgbox window too. In any case, it looks like I've been chasing a red herring. So, have I been, and is there a simple solution staring me in the face that I can't see? (I just want to have the size of a string, in some sort of unit value that varies with the actual size of the characters, rather than a character count. I want to do this so I can size the windows containing the text appropriately.)

TIA

Bob
 
Let me simplify that post. I can't see how to get the actual size (in pixels, inches, whatever) to a string of text, as opposed to its character length. Can anyone help?

TIA

Bob
 
OTTOMH

If you are on a Form handy;

Label1.Autosize = true 'may have to set at design time
Label1.Caption = "Hello"

MyVarTextWidth = Label1.width

can work. Almost sure there is an API call for textlength, textsize, textwidth or something providing you know the Font; cannot find it just now.
 
Sorry to be slow to respond, I've been out sick for a week. Yes, that would work, and it's creative. But I want to avoid using any other controls if I can. I'd like to find that API call that you're mentioning, too!

 
Welcome back Bob and hope you are fully recovered.

Then I remembered and I was pretty sure the API was called TextExtent but I must have been dreaming because searches have turned up little, sorry to have raised your hopes.
 
That'll teach me to trust to memory instead of checking my code library ...

The function I was thinking of is actually

GetTextExtentPoint32

There are a couple of of related calls, but this is the one I favour. I even have a code example I put together for this forum a while back ... thread222-1036091 (and note how it repeats the illustration of using iFont to save effort... :) )
 
That looks like it will do the trick, strongm. I found GetTextExtentPointEx which appears to be the 32 bit version of GetTextExtentEx, so I'll see what I can do with that and post back next week.

Thanks a lot! You too Hugh.

Bob
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top