PlanetSquires Forums

Support Forums => General Board => Topic started by: José Roca on April 12, 2011, 01:44:39 PM

Title: CWindow: YouTube
Post by: José Roca on April 12, 2011, 01:44:39 PM
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
' ========================================================================================

Title: Re: CWindow: YouTube
Post by: Rolf Brandt on April 12, 2011, 01:51:23 PM
Convincing!

(And I am not only talking about Frankieboy...)
Title: Re: CWindow: YouTube
Post by: José Roca on April 12, 2011, 02:58:58 PM
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!
Title: Re: CWindow: YouTube
Post by: Sean Roe on April 12, 2011, 05:02:10 PM
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!
Title: Re: CWindow: YouTube
Post by: Paul Squires on April 28, 2011, 10:28:11 PM
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

Title: Re: CWindow: YouTube
Post by: José 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.

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.
Title: Re: CWindow: YouTube
Post by: José Roca on April 28, 2011, 11:22:56 PM
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.
Title: Re: CWindow: YouTube
Post by: Paul Squires on April 29, 2011, 12:02:09 AM
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
' ========================================================================================




Title: Re: CWindow: YouTube
Post by: José Roca on April 29, 2011, 02:04:22 AM
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.
Title: Re: CWindow: YouTube
Post by: José 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, ...)

Title: Re: CWindow: YouTube
Post by: José Roca on April 29, 2011, 02:18:20 AM
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
     
Title: Re: CWindow: YouTube
Post by: Paul Squires on April 29, 2011, 10:19:41 AM
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.   :)
Title: Re: CWindow: YouTube
Post by: José Roca on April 29, 2011, 12:42:09 PM
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.

Title: Re: CWindow: YouTube
Post by: José Roca on April 29, 2011, 12:48:46 PM
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)