Hello to all,
i try to put a dialog into a DLL and call this from my application.
I can call up the dialog in the program, but stopped in the DLL with an error.
This is the code for the DLL
'------------------------------------------------------------------------------
' FRMTOASTMESSAGE Auto-Generated FireFly Functions
'------------------------------------------------------------------------------
Function FRMTOASTMESSAGE_Show( _
ByVal hWndParent As Dword, _
ByVal ShowModalFlag As Long, _
Optional ByVal UserData As Long _
) Export As Long
Local IsMDIForm as Long
' // Create an instance of the class
Local pWindow As IWindow
pWindow = Class "CWindow"
If IsNothing(pWindow) Then Function = -1: Exit Function
' // Set the flag if this is an MDI form we are creating
IsMDIForm = %FALSE
' // Save the optional UserData to be checked in the Form Procedure CREATE message
App.ReturnValue = UserData
' // Create the main window
pWindow.CreateWindow( hWndParent, _
"ToastMessage", _
0, 0, 500, 310, _
%WS_POPUP Or %WS_CAPTION Or %WS_SYSMENU Or %WS_CLIPSIBLINGS _
Or %WS_CLIPCHILDREN , _
%WS_EX_STATICEDGE Or %WS_EX_CONTROLPARENT _
Or %WS_EX_TOPMOST Or %WS_EX_LEFT Or %WS_EX_LTRREADING _
Or %WS_EX_RIGHTSCROLLBAR, _
CodePtr( FRMTOASTMESSAGE_FORMPROCEDURE ) )
'<<<<Here, the error occurs.
If IsWindow(pWindow.hWnd) = 0 Then '<<<< At this point the processing is terminated with an error.
Function = -1: Exit Function
End If
Can someone tell me what am I doing wrong?
Thanks for any help.
Rudolf Fürstauer
Im not sure aout this, but, shouldn't the line:
App.ReturnValue = UserData
be:
IF varptr(Userdata) THEN App.ReturnValue = UserData
?
As i said, im not sure if the byval overrides the fact that the value is optional, probably it is, in the new version of the compiler, otherwise it would be like using PEEK at address 0, leading to unstability and the crash you mention?
You can't use it in a DLL as a popup window. Doing it, you don't have access to the data of the main window and everything will went wrong. Use an include file instead, e.g.
TestX.inc
Function FRMTOASTMESSAGE_Show ( _
ByVal hWndParent As Dword _
) As Long
' // Create an instance of the class
Local pWindow As IWindow
pWindow = Class "CWindow"
If IsNothing(pWindow) Then Function = -1: Exit Function
' // Create the main window
pWindow.CreateWindow( hWndParent, _
"ToastMessage", _
0, 0, 500, 310, _
%WS_POPUP Or %WS_CAPTION Or %WS_SYSMENU Or %WS_CLIPSIBLINGS _
Or %WS_CLIPCHILDREN , _
%WS_EX_STATICEDGE Or %WS_EX_CONTROLPARENT _
Or %WS_EX_TOPMOST Or %WS_EX_LEFT Or %WS_EX_LTRREADING _
Or %WS_EX_RIGHTSCROLLBAR, _
CodePtr( FRMTOASTMESSAGE_FORMPROCEDURE ) )
' // Default message pump (you can replace it with your own)
pWindow.DoEvents
END FUNCTION
' ========================================================================================
' Main callback function.
' ========================================================================================
FUNCTION FRMTOASTMESSAGE_FORMPROCEDURE (BYVAL hwnd AS DWORD, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
' // Process window mesages
SELECT CASE uMsg
CASE %WM_CREATE
' // Disable the owner of the modal window
EnableWindow GetWindow(hwnd, %GW_OWNER), %FALSE
CASE %WM_COMMAND
SELECT CASE LO(WORD, wParam)
CASE %IDCANCEL
' // If the Escape key has been pressed...
IF HI(WORD, wParam) = %BN_CLICKED THEN
' // ... close the application by sending a WM_CLOSE message
SendMessage hwnd, %WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
END SELECT
CASE %WM_CLOSE
' // The owner window is enabled in WM_CLOSE rather than WM_DESTROY to
' // prevent the application from losing the focus. In WM_DESTROY the
' // modal window has already been removed from the screen by the system.
' // Because the remaining windows are disabled, the system gives the
' // focus to another application.
EnableWindow GetWindow(hwnd, %GW_OWNER), %TRUE
CASE %WM_DESTROY
' // End the application
PostQuitMessage 0
EXIT FUNCTION
END SELECT
' // Pass unprocessed messages to Windows
FUNCTION = DefWindowProc(hwnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
Main file:
#COMPILE EXE
#DIM ALL
' // Include files for external files
#INCLUDE ONCE "CWindow.inc" ' // CWindow class
#INCLUDE "TestX.inc"
%IDTEST = 101
' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS DWORD, BYVAL hPrevInstance AS DWORD, BYVAL lpszCmdLine AS WSTRINGZ PTR, BYVAL nCmdShow AS LONG) AS LONG
' // Create an instance of the class
LOCAL pWindow AS IWindow
pWindow = CLASS "CWindow"
IF ISNOTHING(pWindow) THEN EXIT FUNCTION
' // Create the main window
' // Note: CW_USEDEFAULT is used as the default value When passing 0's as the width and height
pWindow.CreateWindow(%NULL, "Test", 0, 0, 0, 0, 0, 0, CODEPTR(WindowProc))
' // Set the client size
pWindow.SetClientSize 500, 320
' // Center the window
pWindow.CenterWindow
' // Add a button
pWindow.AddButton(pWindow.hwnd, %IDTEST, "&Test", 350, 250, 75, 23)
' // Default message pump (you can replace it with your own)
pWindow.DoEvents(nCmdShow)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Main callback function.
' ========================================================================================
FUNCTION WindowProc (BYVAL hwnd AS DWORD, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
' // Process window mesages
SELECT CASE uMsg
CASE %WM_COMMAND
SELECT CASE LO(WORD, wParam)
CASE %IDTEST
IF HI(WORD, wParam) = %BN_CLICKED THEN
FRMTOASTMESSAGE_Show(hwnd)
EXIT FUNCTION
END IF
CASE %IDCANCEL
' // If the Escape key has been pressed...
IF HI(WORD, wParam) = %BN_CLICKED THEN
' // ... close the application by sending a WM_CLOSE message
SendMessage hwnd, %WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
END SELECT
CASE %WM_DESTROY
' // End the application
PostQuitMessage 0
EXIT FUNCTION
END SELECT
' // Pass unprocessed messages to Windows
FUNCTION = DefWindowProc(hwnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
Ok. I will make a change to the CWindow class to allow to be used in a DLL.
To work you will need to add
pWindow.ClassName = "MyClassName" ' --> or another name
' Don't use "PBWindowClass" or "PBFrameClass"
After:
' // Create an instance of the class
Local pWindow As IWindow
pWindow = Class "CWindow"
If IsNothing(pWindow) Then Function = -1: Exit Function
The problem was that the DLL becomes isolated code. Therefore, CWindows was creating the same class name for the window in the DLL than the one for the main program, and RegisterClassEx failed.
Thanks for your great support!
Maybe Paul should also set it in FF, so the use of dialogues in DLL's is possible.
Quote
Maybe Paul should also set it in FF, so the use of dialogues in DLL's is possible.
Paul doesn't need to do anything. I already have made the needed changes and it works. You just will need to use pWindow.ClassName = <MyClassName> after pWindow = Class "CWindow" when used in a DLL.
Regarding the complex High DPI problem, the only thing that remains pending is when, in Windows 7, you change the DPI percent but at the same time you check the "Use Windows XP Style DPI Scaling" setting. When this setting is checked, the application must not do scaling, to honor the user preferences. The problem is how to know programatically if this setting is checked or not. In Windows 7 there is a way reading the registry:
HKEY_CURRENT_USER
|__ Software
|__ Microsoft
|__ Windows
|__DWM
|__ value of "UseDpiScaling" (0 or 1)
I have read that this key doesn't exist in Windows Vista. Can anyone confirm it?
Normally, when someone changes the DPI to increase the size of the UI elements, applications must also scale windows, controls and fonts. In this case, you no longer have to thing in pixels, but in dots per inch. That is, 100 is no longer 100 pixels, but 100 dots per inch, that depending of the DPI percent can be 120 pixels, 130, etc. If the application is not High DPI aware and Aero is activated, Windows 7 virtualizes it, i.e. it does the scaling but leaves you to believe that you are working at 96 DPI. The problem is, that depending of the application, you get artifacts, fuzzy fonts and several other nasty troubles. To complicate matters further, if you choose a theme like the Windows 7 Basic Theme, virtualization is disabled and the program must do scaling, but if you check the "Use Windows XP Style DPI Scaling" setting, then the program must honor it and not scale.
The only way for a program to work properly in all these options of Windows 7 is making the application fully High DPI aware, and we must know how to deal with it because a visual designer is not going to write all the code of your aplication for you, and if you use a GDI function like GetWindowRect, that is not DPI aware, it will return the dimensions in pixels, not in dot per inchs, and you must downscale these values. Otherwise, you can get a width of, e.g. 120 pixels when in fact it should be 100 dots per inch, for example. This is why Microsoft wants to deprecate GDI. We need new functions that work with dots per inch instead of pixels.
That is why I have been saying during more than a year and a half that everybody should read carefully and understand all that it is said in this article: http://msdn.microsoft.com/en-us/library/windows/desktop/dd464660%28v=vs.85%29.aspx
If you don't do it right, an application that looks fine in your computer can look terrible in another with different settings.
Even Microsoft had to deprecate Windows Forms, that was not DPI aware, and replace it with WPF.
DDT does it almost right if you use dialog units instead of pixels, but has some rounding problems because it uses longs instead of singles, and the font statements aren't DPI aware (fonts also have to be scaled according the DPI settings when you create them). And when using GDI functions, you also need to do conversions with DIALOG UNITS TO PIXELS and PIXELS TO DIALOG UNITS.
Another article that you should read to understand the different mechanisms that have been used in different versions of Windows to accommodate monitors with a high pixel density.
http://www.kynosarges.de/WindowsDpi.html
Thanks Jose, the classname was indeed the problem. I have updated FF's code generation to create unique class names and assign them to pWindow.
I will have this new code in the next FF update.
Thanks!
Quote from: Jose Roca on February 19, 2012, 03:36:47 AM"UseDpiScaling" I have read that this key doesn't exist in Windows Vista. Can anyone confirm it?
Looks like YES, it is in VISTA:
http://social.msdn.microsoft.com/Forums/en-AU/vcgeneral/thread/5deb0008-23ba-4996-9c96-19afc9c41b33
http://msdn.microsoft.com/en-us/library/windows/desktop/ee417691%28v=vs.85%29.aspx
Jose, in my tests, FF is creating the dialogs and controls just fine, its FF_Control_SetLoc and FF_Control_SetSize the ones that arent doing very well, placing and sizing controls incottectly. Also the Firesplitter is guilty of the same sin.
I was thinking that maybe we could pull the DPI setting from registry and do the conversion as follows:
FF_Control_GetLoc(HControl, X, Y)
FF_Pixel_Edit(X, Y, 10, 15) ' Increase the DPI correspondent to 10 pixels for X and 15 for Y.
or...
FF_Pixel_Set(X, Y, 10, 15) ' Set the DPI correspondent to 10 pixels for X and 15 for Y.
FF_Control_SetLoc(HControl, X, Y) ' Set the correct DPI location for the control.
?
Doing it this way we dont have to worry about modifying older programs, just adding this two new functions.
Oh, but the firesplitter is going to be a little out of our reach.
Quote from: Jim Dunn on February 19, 2012, 02:59:07 PM
Quote from: Jose Roca on February 19, 2012, 03:36:47 AM"UseDpiScaling" I have read that this key doesn't exist in Windows Vista. Can anyone confirm it?
Looks like YES, it is in VISTA:
http://social.msdn.microsoft.com/Forums/en-AU/vcgeneral/thread/5deb0008-23ba-4996-9c96-19afc9c41b33
http://msdn.microsoft.com/en-us/library/windows/desktop/ee417691%28v=vs.85%29.aspx
Maybe I did read old information and it was not in the original version and has been added in a service pack.
In this case we must modify the new AfxGetUseDpiScaling function in Windows.inc.
' ========================================================================================
' Retrieve the value of the UseDpiScaling setting (Windows 7).
' ========================================================================================
FUNCTION AfxGetUseDpiScaling () AS LONG
LOCAL hr AS LONG
LOCAL hKey AS DWORD
LOCAL wszBuff AS WSTRINGZ * 256
LOCAL dwType AS DWORD
LOCAL cbData AS DWORD
IF AfxGetWindowsVersion < 6.01 THEN EXIT FUNCTION ' // Must be Windows 7 or superior
IF RegOpenKeyExW(%HKEY_CURRENT_USER, "Software\Microsoft\Windows\DWM", 0, %KEY_QUERY_VALUE, hKey) = %ERROR_SUCCESS THEN
IF hKey THEN
cbData = SIZEOF(wszBuff)
hr = RegQueryValueExW(hKey, "UseDpiScaling", 0, dwType, wszBuff, cbData)
RegCloseKey hKey
IF hr = %ERROR_SUCCESS THEN
wszBuff = EXTRACT$(wszBuff, CHR$(0))
' // Extracts DWORD from string
FUNCTION = CVDWD(wszBuff)
END IF
END IF
END IF
END FUNCTION
' ========================================================================================
It would only be needed to remove this line:
IF AfxGetWindowsVersion < 6.01 THEN EXIT FUNCTION ' // Must be Windows 7 or superior
The rest of the code must work in all Windows versions. Since I'm doing error checking, if the node or key doesn't exist it simply would return FALSE.
Quote from: Elias Montoya on February 19, 2012, 03:45:19 PM
Jose, in my tests, FF is creating the dialogs and controls just fine, its FF_Control_SetLoc and FF_Control_SetSize the ones that arent doing very well, placing and sizing controls incottectly. Also the Firesplitter is guilty of the same sin.
I was thinking that maybe we could pull the DPI setting from registry and do the conversion as follows:
FF_Control_GetLoc(HControl, X, Y)
FF_Pixel_Edit(X, Y, 10, 15) ' Increase the DPI correspondent to 10 pixels for X and 15 for Y.
or...
FF_Pixel_Set(X, Y, 10, 15) ' Set the DPI correspondent to 10 pixels for X and 15 for Y.
FF_Control_SetLoc(HControl, X, Y) ' Set the correct DPI location for the control.
?
Doing it this way we dont have to worry about modifying older programs, just adding this two new functions.
Oh, but the firesplitter is going to be a little out of our reach.
Jose's cWindow class already does the conversions for us. The class has methods that calculate positioning, client size, window size, etc, and will adjust the values based on the scaling ratios. FF needs to update much of its internals and FireFly Functions to call these methods. Fair amount of work but will be worth it in the long run.
To understand the importance of this setting, look at the attached picture. The program has been compiled using the new headers, that takes into account the UseDpiScaling setting.
The image on the left shows the output in my computer with "Use Windows XP Style DPI Scaling" unchecked, and the second with that option checked; a resolution of 1366x768 and a DPI of 140%. The output will vary with different resolution and DPI.
It was something that I had in the ToDo list because I had not enough information about it. Besides, I only have Windows 7 and a modest computer and monitor.
GDI has became inadequate because it was designed when practically all monitors used 96 DPI and all the applications looked the same in almost all systems. Now, if you use a high resolution monitor, you have to increase the DPI because otherwise the text is unreadable. But this doesn't increase the size of the pixels; otherwise you will lose resolution. For legacy applications, M$ had the idea of virtualization, that is what you get when your program is not DPI aware. It first renders the entire application window to an internal bitmap using 96 DPI sizes, and then scales up that bitmap to the current DPI setting before putting it on the screen. But this is just a workaround with many problems.
System fonts still exist for backward compatibility, but are of no use with DPI aware applications beause don't have enough resolution. The new fonts don't auto scale, therefore you have to scale them when you create them. Images an graphics also don't auto scale, so an icon, for example, looks much smaller in a high resolution monitor. You have to stretch them, with loss of quality, or have the same icon in several sizes and choose the most appropiate one at runtime depending of the resolution and DPI.
In the CWindow class I have added several methods such GetWindowRect, GetClientRect Width, Heigh, ClientWidth, ClientHeight, etc., but obviously I can't wrap the entire GDI. M$ can put 200 or 300 programmers to do the task, but I'm a man alone and with scarce information and little resources. As we say in Spain, "El qe hace lo que puede, no está obligado a más." ("No one is bound to do the impossible.")
Chris Boss is always talking about his EZGUI engine and how he has chosen carefully the API functions that work in all versions of Windows, and blah, blah, blah. He is a good programmer, but he is using methods of the dark ages. I once told him that in his quest for backward compatibility he was neglecting forward compatibility. I took one of his tools and make it to look horrible and almost completely unusable with just selecting in my computer the Windows 7 Basic Theme and 145% DPI. Among several other things, the fonts looked fuzzy and over sized, not fitting in the controls.
Today's applications must support high-quality text rendering, resolution-independent outline fonts, and full Unicode text and layout support. For this you need DirectWrite, a new DirectX API, but as it is only available in Windows 7, Windows Vista with Service Pack 2 (SP2) and Platform Update for Windows Vista, Windows Server 2008 R2 or Windows Server 2008 with Service Pack 2 (SP2) and Platform Update for Windows Server 2008, and also requres knowledge of low-level COM programing, I can only use it for my own tests. Anyway, my headers are ready to use it.