Another CWindow template, this time embedding a YouTube video in an instance of the WebBrowser control. I can imagine Paul adding a popup window to allow to introduce the HTML code. This would allow to easily embed images, maps, videos, sounds, help pages, and all that a web browser can do, with ease in your application. Much more that the QHTM control did and for free!
#COMPILE EXE
#DIM ALL
%UNICODE = 1
' // Include files for external files
%USEWEBBROWSER = 1 ' // Use the WebBrowser control
#INCLUDE ONCE "CWindow.inc" ' // CWindow class
#INCLUDE ONCE "mshtml.inc" ' // MSHTML
' // Identifier
%IDC_WEBBROWSER = 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
pWindow.CreateWindow(%NULL, "CWindow.AddWebBrowser Demo: YouTube", 0, 0, 478, 428, -1, -1, CODEPTR(WindowProc))
' // Center the window
pWindow.CenterWindow
' // Add a WebBrowser control and display a YouTube video
LOCAL hCtl AS DWORD
LOCAL s AS WSTRING
' // Build the web page. Remember to always start it with "MSHTML:".
s = "MSHTML:<?xml version=""1.0""?>"
s += "<!DOCTYPE HTML PUBLIC ""-//W3C//DTD XHTML 1.0 Transitional//EN"" ""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"">" & $CRLF
s += "<html>" & $CRLF
s += "<head>" & $CRLF
s += "<title>YouTube video</title>" & $CRLF
s += "" & $CRLF
s += "</head>" & $CRLF
s += "<body scroll=""auto"" style=""MARGIN: 10px 10px 10px 10px"">"
s += "<object width=""428"" height=""356"">" & _
"<param name=""movie"" value=""http://www.youtube.com/v/t6Lp4w8wyy0&hl=es&fs=1""></param>" & _
"<param name=""wmode"" value=""transparent"">" & _
"</param><embed src=""http://www.youtube.com/v/t6Lp4w8wyy0&hl=es&fs=1""" & _
" type=""application/x-shockwave-flash"" wmode=""transparent"" width=""428"" height=""356"">" & _
"</embed></object>"
s += "" & $CRLF
s += "</body>" & $CRLF
s += "" & $CRLF
s += "</html>" & $CRLF
' // Create the control
hCtl = pWindow.AddWebBrowserControl(pWindow.hwnd, %IDC_WEBBROWSER, s, 5, 5, 582, 356, -1, -1)
' // 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
LOCAL rc AS RECT
SELECT CASE uMsg
CASE %WM_SYSCOMMAND
' // Capture this message and send a WM_CLOSE message
' // Note: Needed with some OCXs, that otherwise remain in memory
IF (wParam AND &HFFF0) = %SC_CLOSE THEN
SendMessage hwnd, %WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
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_SIZE
IF wParam <> %SIZE_MINIMIZED THEN
' // Get the client area of the main window
GetClientRect hwnd, rc
' // Resize the control
MoveWindow GetDlgItem(hwnd, %IDC_WEBBROWSER), 5, 5, rc.nRight - rc.nLeft - 10, rc.nBottom - rc.nTop - 10, %TRUE
END IF
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
' ========================================================================================
Convincing!
(And I am not only talking about Frankieboy...)
And we can also use interactive DHTML pages, and installing IE 9 in Windows 7 we can use HTML5... The power of HTML inside our applications!
Quote from: Jose Roca on April 12, 2011, 01:44:39 PM
I can imagine Paul adding a popup window to allow to introduce the HTML code. This would allow to easily embed images, maps, videos, sounds, help pages, and all that a web browser can do, with ease in your application. Much more that the QHTM control did and for free!
Can we bump this over to be a feature request? I am very interested in what Jose has been doing with this, especially his AJAX Map found here: http://www.jose.it-berater.org/smfforum/index.php?topic=4083.0 (http://www.jose.it-berater.org/smfforum/index.php?topic=4083.0)
Thanks to Paul and Jose!
P.S. I should have looked again on the boards here because he also posted it here: http://www.planetsquires.com/protect/forum/index.php?topic=2840.0 (http://www.planetsquires.com/protect/forum/index.php?topic=2840.0) Thanks Jose!
Hi Jose,
Should the following code return the hWnd? It doesn't seem to?
' ========================================================================================
' Main callback function.
' ========================================================================================
Function WindowProc (ByVal hwnd As Dword, ByVal uMsg As Dword, ByVal wParam As Dword, ByVal lParam As Long) As Long
Local rc As Rect
Select Case uMsg
Case %WM_CREATE
Local pWindow As IWindow
pWindow = Class "CWindow"
pWindow = CWindow_GetObjectFromCreateStruct(lParam)
If IsNothing(pWindow) Then
? "invalid object"
Else
? "pWindow.hWnd=" & Str$(pWindow.hWnd)
End If
Only if the window to which WindowProc belongs has been created with CWindow too, in which case it will be superflous as it will be the same value as the hwnd parameter of WindowProc.
This code, without pWindow = Class "CWindow", that will create a new instance of the class...
Local pWindow As IWindow
pWindow = CWindow_GetObjectFromCreateStruct(lParam)
...is only needed if you want to add controls in WM_CREATE.
Why is it needed? Because when you create a window, CreateWindowEx sends the WM_CREATE message to its callback procedure before returning, and the class doesn't know the value of the window handle until CreateWindowEx returns.
To solve this problem, in the CreateWindow method, the class stores a pointer of itself in the window class with SetClassLong m_hwnd, 0, OBJPTR(ME), and the helper function CWindow_GetObjectFromCreateStruct retrieves it:
' ========================================================================================
' Retrieves a pointer to the CWindow class and returns a reference counted object variable.
' ========================================================================================
FUNCTION CWindow_GetObjectFromCreateStruct (BYVAL lParam AS DWORD) AS IUnknown
LOCAL pCreateStruct AS CREATESTRUCT PTR ' // Initialization parameters
LOCAL pCreateParams AS CWindow_CREATEPARAMS PTR ' // Creation parameters
LOCAL pWindowPtr AS DWORD ' // CWindow class pointer
LOCAL pWindow AS IUnknown ' // CWindow class object variable
LOCAL pv AS DWORD PTR ' // Pointer variable
IF lParam = 0 THEN EXIT FUNCTION
pCreateStruct = lParam
IF @pCreateStruct.lpCreateParams THEN
pCreateParams = @pCreateStruct.lpCreateParams
pWindowPtr = @pCreateParams.pWindow
IF pWindowPtr = 0 THEN EXIT FUNCTION
pv = VARPTR(pWindow)
@pv = pWindowPtr
IF ISOBJECT(pWindow) THEN
pWindow.AddRef
FUNCTION = pWindow
END IF
END IF
END FUNCTION
' ========================================================================================
Of course, if the main window has not been created by CWindow, that information does not exist.
BTW the methods that I have added to manage the life of a collection of external objects are working fine. This allows, for example, to create image lists, fonts and brushes with the new CAfxImageList, CAfxFont and CAfxBrush classes without having to use global or static variables and without having to care of destroying them when the program ends.
I have just finished the class CAfxOpenDialog, a wrapper on top of GetOpenFileNameW.
Usage example:
LOCAL pofd AS IAfxOpenFileDialog
pofd = CLASS "CAfxOpenFileDialog"
IF ISNOTHING(pofd) THEN EXIT SUB
pofd.DefaultFolder = CURDIR$
pofd.FileName = "*.BAS;*.INC"
pofd.DefaultExtension = "BAS"
pofd.Filter = CHR$("PB Code Files (*.BAS)", 0, "*.BAS", 0) & _
CHR$("PB Include Files (*.INC)", 0, "*.INC", 0) & _
CHR$("PB Template Files (*.PBTPL)", 0, "*.PBTPL", 0) & _
CHR$("All Files (*.*)", 0, "*.*", 0)
pofd.Options = %OFN_EXPLORER OR %OFN_FILEMUSTEXIST OR %OFN_ALLOWMULTISELECT
IF pofd.Show THEN
LOCAL pFiles AS IPowerCollection
LOCAL vFile AS VARIANT
pFiles = pofd.Files
? "Selected path: " & pofd.SelectedPath
FOR EACH vFile IN pFiles
? VARIANT$$(vFile)
NEXT
END IF
Please note that I'm not going to wrap everything with classes as the Microsoft Foundation Classes did. Only when the use of a wrapper class gives advantages.
Quote from: Jose Roca on April 28, 2011, 11:13:58 PM
Only if the window to which WindowProc belongs has been created with CWindow too, in which case it will be superflous as it will be the same value as the hwnd parameter of WindowProc.
Exactly. I am using cWindow to create the main window.
Quote
This code, without pWindow = Class "CWindow", that will create a new instance of the class...
Local pWindow As IWindow
pWindow = CWindow_GetObjectFromCreateStruct(lParam)
...is only needed if you want to add controls in WM_CREATE.
That is exactly what I am wanting to do... add controls in WM_CREATE. I will remove the line:
pWindow = Class "CWindow"
Quote
Why is it needed? Because when you create a window, CreateWindowEx sends the WM_CREATE message to its callback procedure before returning, and the class doesn't know the value of the window handle until CreateWindowEx returns.
Yes, I figured that part out. You need to use CWindow_GetObjectFromCreateStruct rather than CWindow_GetObjectFromWindowHandle because the hWnd is not known by the class at that point.
However, when I run this code it still does not return the hWnd?
#COMPILE EXE
#Dim All
%UNICODE = 1
' // Include files for external files
%USEWEBBROWSER = 1 ' // Use the WebBrowser control
#Include Once "CWindow.inc" ' // CWindow class
#Include Once "mshtml.inc" ' // MSHTML
' // Identifier
%IDC_WEBBROWSER = 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
pWindow.CreateWindow(%Null, "CWindow.AddWebBrowser Demo: YouTube", 0, 0, 478, 428, -1, -1, CodePtr(WindowProc))
' // Center the window
pWindow.CenterWindow
' // Add a WebBrowser control and display a YouTube video
Local hCtl As Dword
Local s As wString
' // Build the web page. Remember to always start it with "MSHTML:".
s = "MSHTML:<?xml version=""1.0""?>"
s += "<!DOCTYPE HTML PUBLIC ""-//W3C//DTD XHTML 1.0 Transitional//EN"" ""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"">" & $CrLf
s += "<html>" & $CrLf
s += "<head>" & $CrLf
s += "<title>YouTube video</title>" & $CrLf
s += "" & $CrLf
s += "</head>" & $CrLf
s += "<body scroll=""auto"" style=""MARGIN: 10px 10px 10px 10px"">"
s += "<object width=""428"" height=""356"">" & _
"<param name=""movie"" value=""http://www.youtube.com/v/t6Lp4w8wyy0&hl=es&fs=1""></param>" & _
"<param name=""wmode"" value=""transparent"">" & _
"</param><embed src=""http://www.youtube.com/v/t6Lp4w8wyy0&hl=es&fs=1""" & _
" type=""application/x-shockwave-flash"" wmode=""transparent"" width=""428"" height=""356"">" & _
"</embed></object>"
s += "" & $CrLf
s += "</body>" & $CrLf
s += "" & $CrLf
s += "</html>" & $CrLf
' // Create the control
hCtl = pWindow.AddWebBrowserControl(pWindow.hwnd, %IDC_WEBBROWSER, s, 5, 5, 582, 356, -1, -1)
' // 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
Local rc As Rect
Select Case uMsg
Case %WM_CREATE
Local pWindow As IWindow
pWindow = CWindow_GetObjectFromCreateStruct(lParam)
If IsNothing(pWindow) Then
? "invalid object"
Else
? "pWindow.hWnd=" & Str$(pWindow.hWnd)
End If
Case %WM_SYSCOMMAND
' // Capture this message and send a WM_CLOSE message
' // Note: Needed with some OCXs, that otherwise remain in memory
If (wParam And &HFFF0) = %SC_CLOSE Then
SendMessage hwnd, %WM_CLOSE, 0, 0
Exit Function
End If
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_SIZE
If wParam <> %SIZE_MINIMIZED Then
' // Get the client area of the main window
GetClientRect hwnd, rc
' // Resize the control
MoveWindow GetDlgItem(hwnd, %IDC_WEBBROWSER), 5, 5, rc.nRight - rc.nLeft - 10, rc.nBottom - rc.nTop - 10, %TRUE
End If
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
' ========================================================================================
This is what does the CreateWindow method:
IF bNoScale THEN
m_hwnd = CreateWindowEx(dwExStyle, BYVAL dwClass, BYCOPY strTitle, dwStyle, _
IIF&(x = %CW_USEDEFAULT, %CW_USEDEFAULT, x), _
IIF&(y = %CW_USEDEFAULT, %CW_USEDEFAULT, y), _
IIF&(nWidth = %CW_USEDEFAULT, %CW_USEDEFAULT, nWidth), _
IIF&(nHeight = %CW_USEDEFAULT, %CW_USEDEFAULT, nHeight), _
hParent, %NULL, m_hInstance, tCreateParams)
ELSE
m_hwnd = CreateWindowEx(dwExStyle, BYVAL dwClass, BYCOPY strTitle, dwStyle, _
IIF&(x = %CW_USEDEFAULT, %CW_USEDEFAULT, x * m_rx), _
IIF&(y = %CW_USEDEFAULT, %CW_USEDEFAULT, y * m_ry), _
IIF&(nWidth = %CW_USEDEFAULT, %CW_USEDEFAULT, nWidth * m_rx), _
IIF&(nHeight = %CW_USEDEFAULT, %CW_USEDEFAULT, nHeight * m_ry), _
hParent, %NULL, m_hInstance, tCreateParams)
END IF
IF m_hwnd = %NULL THEN EXIT METHOD
The window handle is assigned to m_hwnd when the call to CreateWindowEx returns, and it does not return until the WM_CREATE message has been processed. Therefore, the class can't retrieve this value in WM_CREATE. But it is not needed, because you already have it: it's given to you in the hwnd parameter of the window procedure.
If you want to add controls in WM_CREATE (generally there is not any advantage in adding them in this place instead of WinMain), then...
Case %WM_CREATE
Local pWindow As IWindow
pWindow = CWindow_GetObjectFromCreateStruct(lParam)
pWindow.AddButton(hwnd, ...)
And if you want/need to call some other methods of the CWindow class that need to know the value of hwnd, in WM_CREATE, then...
Case %WM_CREATE
Local pWindow As IWindow
pWindow = CWindow_GetObjectFromCreateStruct(lParam)
pWindow.hwnd = hwnd
Quote from: Jose Roca on April 29, 2011, 02:12:17 AM
If you want to add controls in WM_CREATE (generally there is not any advantage in adding them in this place instead of WinMain), then...
Case %WM_CREATE
Local pWindow As IWindow
pWindow = CWindow_GetObjectFromCreateStruct(lParam)
pWindow.AddButton(hwnd, ...)
Sometimes I can be completely stupid! :)
Yes, the pWindow pointer is valid, and that is what I really want (not the hWnd). I was testing pWindow.hWnd and because that was zero then I was thinking that I was doing something wrong. Of course, the pWindow.hWnd had not been set at that point! I am such a bonehead. I only need the pWindow object - that is really what I was after all along. I can get the hWnd from the incoming parameter of the code procedure. Sheesh! I need more sleep. Sorry about that. :)
It takes a bit to get used to COM, but I have used CWindow with a great variety of applications and all the important things have been solved. The new CSED editor has been written entirely using CWindow and it's a complex application: MDI, Unicode and High DPI.
I have taken a COM approach, not an OOP one. None of the classes inherit from another one. Therefore, they are self contained and can be used both with CWindow or wihout it. The last touch that I have added, and that requires the upcoming PB 10.01, is to allow CWindow to manage a collection of objects. This way, you can optionally use my new classes to create image lists, fonts and brushes and avoid the need to use globals and free resources at the end of the application by registering the object with CWindow.
With an OOP approach, used by the Microsoft Foundation Classes, you need to have classes for everything, since each one inherits from another, and you end creating a bloated monster. With my COM approach, you just write reusable and self contained classes when they can be useful, and its use is optional (you can use them or you can use API calls).
For example, we can create a control with the CWindow AddXXX methods, but manage it with SendMessage or the available wrapper functions for controls, or you can write a class for a given control, but you don't need to have classes for everything. This makes the system extensible.
I have given special attention to the easy integration of the WebBrowser control, since it is a super OLE container that will allow us to do all kinds of cool things that HTML and javascript can do, but inside our application, not in an external internet browser.
And if you have Internet Explorer 9 installed, you can use HTML5 in your scripts.
I have made a test and it works:
' // Build the html page
LOCAL s AS WSTRING
s = "<!doctype html>" & $CRLF
s += "<head>" & $CRLF
s += " <title>Audio Element Sample</title>" & $CRLF
s += " <meta http-equiv='X-UA-Compatible' content='IE=9' />" & $CRLF
s += "</head>" & $CRLF
s += "<body>" & $CRLF
s += " <h1>Audio Element Sample</h1>" & $CRLF
s += " <!-- Display the audio player with control buttons. -->" & $CRLF
s += " <!-- Remember to change tha path ir url of the audio file. -->" & $CRLF
s += " <audio src='C:\Users\Pepe\Tests\Kalimba.mp3' controls autoplay loop>" & $CRLF
s += " HTML5 audio not supported" & $CRLF
s += " </audio>" & $CRLF
s += "</body>" & $CRLF
s += "</html>" & $CRLF
' // Save the script as a temporary file
LOCAL bstrTempFileName AS WSTRING
bstrTempFileName = AfxSaveTempFile(s, "", "TMP", "html", 1)
' // Create the WebBrowser control with the embedded map
pWindow.AddWebBrowserControl(pWindow.hwnd, %IDC_WEBBROWSER, bstrTempFileName, NOTHING, 0, 0, pWindow.ClientWidth, pWindow.ClientHeight)