Monday, February 16, 2015

Chrome inside VFP Form

Update February 18, 2015:

This is getting interesting.  I suddenly thought maybe Chrome allows parameters to be passed and indeed there are, tons of it.   Here are the switches:

http://peter.sh/experiments/chromium-command-line-switches/

As for forcing Chrome to open on a new window instead of the existing one as a tab, it is --new-window

I also updated the sample below to know whether the operation is successful or not so it won't accidentally open any other active window aside from our target.

Later:

As  for my problem to get rid of Chrome's internal titlebar, the trick came out to be very easy.   All we need to do is move the chrome up from within our form that its own internal titlebar cannot be seen anymore, and thus rendering it likewise immovable.

Check the codes below, adjust lnMove value based on how you set up your Chrome on your end.  Since in mine I show my bookmarks, then my adjustment is 100.  Maybe on the link I gave above on the command switches, there are commands to hide those, but I did not check anymore. :)

======

Sparked by last week's thread inside Foxite Forum about placing an IE Browser inside VFP Form, I thought to myself it would be cool to put Google Chrome inside our form for a change.

Also, this will ensure that most probably a webpage can be displayed properly being Chrome is always up to date with changes introduced now and then, as compared to IE that has been left far behind and is giving developers now some headache to fix it via Registry tweaks.


Googling does not give me any result on how this new sudden thought of mine can be done as I never found any that shows how to achieve this without downloading and working on extension designed to provide automation to google.  But I simply wanted chrome to be inside our VFP form using stock VFP commands and function plus utilizing WinAPIs.



I knew for a fact beforehand that this one is not as simple as placing other app inside our form like what I have shown in http://sandstorm36.blogspot.com/2014/05/3rd-party-apps-from-within-our-forms.html

For among its obstacles is that Chrome browser creates several processes when you open it.  This can be seen via Task Manager so using its process ID (PID) is not a good way to go.  Although originally that is where my first few thoughts went.


Next is Chrome might open several tabs depending on your local setting if you set it to remember the previous tabs opened when you close it, via "Continue where you left off".

Further, Google Chrome Browser is an entirely separate beast, so to speak.  After I found the way to bring it inside our VFP form, belatedly I realize that it does not respect Windows Styles so while we can put it inside our VFP form, it is somewhat useless to me unless I can hide its TitleBar to disallow its further movements.

Maybe you will ask me, what is the purpose of placing chrome inside VFP form? Honestly, I do not have a use on my end as I never needed this nor the IE browser inside our form due to very slow internet services here in this country where I am working.  But I have to satiate my curiosity for if I don't, then it will bug me now and then when the thoughts interfere on my day to day job.  I simply have to satisfy my curiosity on how this can be done; so I can move on with new lines of thought. Or else I will be stuck thinking about this now and then.

Anyway, it is fun finding a way to bring it inside VFP form.  Plus I learn new WinAPIs in the process just to make this possible.

I am shelving this now and will work on real things that are important.  I am making this post so if anyone got an interest to even just find out how this can be done and maybe tries to enhance this, then this can maybe serve as your starting point.  Codes are below:


Public oForm
oForm = Createobject('form1')
oForm.Show()

Define Class form1 As Form
      Height = 650
      Width = 900
      Caption = "Chrome Inside VFP Form"
      Desktop = .T.
      AutoCenter = .T.
      controlbox = .t.
      windowstate = 2

      
     Add Object cmdexit As CommandButton With ;
            Top = 620, Left = 10, Caption = 'exit', Width = 84, Height = 24,;
            Anchor = 6
           
     Procedure cmdexit.Click
            _Screen.ActiveForm.release
      Endproc  
      
     Procedure Load
             Declare Integer FindWindow In user32;
                  STRING lpClassName, String lpWindowName
             Declare Integer GetActiveWindow  In user32
             Declare Integer GetWindow In user32 Integer HWnd, Integer wFlag
             Declare Integer GetWindowLong In User32 Integer HWnd, Integer nIndex
             Declare Integer GetWindowTextLength In user32 Integer HWnd
             Declare Integer IsWindow In user32 Integer HWnd
             Declare Integer IsWindowVisible In user32 Integer HWnd
             Declare Integer GetWindowText In user32;
                  INTEGER HWnd, String @lpString, Integer cch
             Declare Integer ShellExecute In shell32.Dll ;
                  INTEGER hndWin, ;
                  STRING cAction, ;
                  STRING cFileName, ;
                  STRING cParams, ;
                  STRING cDir, ;
                  INTEGER nShowWin
             Declare Integer SetWindowLong In user32 Integer HWnd,;
                  INTEGER nIndex, Integer dwNewLong
             Declare Integer SetWindowPos In user32;
                  INTEGER HWnd,;
                  INTEGER hWndInsertAfter,;
                  INTEGER x,;
                  INTEGER Y,;
                  INTEGER cx,;
                  INTEGER cy,;
                  INTEGER uFlags
             Declare Integer SetParent In user32;
                  INTEGER hWndChild,;
                  INTEGER hWndNewParent
            Declare Sleep In kernel32 Integer
            Declare SwitchToThisWindow In user32;
                  INTEGER hWindow, Integer fAltTab

            #Define GW_HWNDFIRST  0
            #Define GW_HWNDLAST   1
            #Define GW_HWNDNEXT   2
      Endproc


      Procedure KeyPress
            Lparameters nKeyCode, nCtrlShift
            If m.nKeyCode = 27
                _screen.ActiveForm.release
            Endif
      Endproc

      Procedure Init
           
            Local lcURL
            lcURL = 'www.google.com --new-window '
            ShellExecute(0,'open','chrome.exe',m.lcURL,'',1)
            Activate Window (Thisform.Name)
            Sleep(1000)
            Thisform.SearchProcess()
      Endproc

      Procedure SearchProcess()
            SwitchToThisWindow(Thisform.HWnd, 0)
            With This
                  Local hWinActive, hWindow, lcWinText, lSuccess, lnMove
                  hWinActive = GetActiveWindow()
                  hWindow = -1
                  lSuccess = .F.
                  Do While hWindow <> GetWindow(hWinActive, GW_HWNDLAST)
                        If hWindow = -1
                              hWindow = GetWindow(hWinActive, GW_HWNDFIRST)
                        Else
                              hWindow = GetWindow(hWindow, GW_HWNDNEXT)
                        Endif

                        If IsWindow(hWindow)<>0 And IsWindowVisible(hWindow)<>0;
                                    And GetWindowTextLength(hWindow) > 0
                              lcWinText = .GetWinText(hWindow)
                              nHwnd = FindWindow(Null, m.lcWinText)
                              If 'Google Chrome' $ m.lcWinText
                                    lSuccess = .T.
                                    Exit
                              Endif
                        Endif
                  Enddo
                  If m.lSuccess
                        lnStyle = GetWindowLong(m.nHwnd, -6)
                        SetWindowLong(m.nHwnd, -16, Bitxor(lnStyle, 0x00400000))

* The trick to hide the pesky built-in titlebar of chrome is to
* move chrome up that it won't display the titlebar anymore.  Since there is a difference in settings
* on each of our machines such as in my end, my bookmarks are shown, then here is my setting
* Adjust it based on your end settings of Chrome

                        lnMove = 100

                        With This
                              SetParent(m.nHwnd,.HWnd)
                              SetWindowPos(m.nHwnd, 1, 0, -m.lnMove, .Width,;
                                           SYSMETRIC(2) ,0x0040)
                        Endwith
                  Endif
            Endwith

      Endproc

      Function  GetWinText(hWindow)
            Local lnBufsize, lcBuffer
            lnBufsize = 1024
            lcBuffer = Repli(Chr(0), lnBufsize)
            lnBufsize = GetWindowText(hWindow, @lcBuffer, lnBufsize)
            Return  Iif(lnBufsize=0, "", Left(lcBuffer,lnBufsize))
      Endfunc
Enddefine




 ===== 

As a guide on how I achieve this, it involves forcefully opening a URL to Chrome via ShellExecute(), cycling to all active windows (Chrome, VFP, Explorer, etc.) until it finds Chrome, and getting its corresponding window handle so we can put a hold on to it.

As for Chrome, depending on the speed of the connection, you may get the internal caption of the newly opened Tab or if it is slow, you will get "New Tab - Google Chrome".  In which case, to ensure that no matter what we can know if it is chrome is to look for the word Google Chrome as that will always be a part of its internal caption.

Aside from the purpose of my usage of those APIs above, I posted the above as I deem it is also good for interested readers to study each APIs I used there as that may come handy on other needs as well.

Cheers!




4 comments:

  1. Hi pal. Is it posible to manage gmail with VFP?

    ReplyDelete
  2. Hi friend, very good your post and what you've done with Visual FoxPro and Google Chrome.
    I want to tell the inconvenience that I have that I could not resolve this issue. If the form previously think so, and then from an attempt button opening the chrome, I could not make him do it on a window on Fox, I could not dimension it a part of the form and not on the whole form, and but what I happens is that I open the browser (Chrome) and not on the form.
    If I can help a little with this theme you'll thank.
    I am Richard of Argentina.
    Thanks and regards

    ReplyDelete
  3. This response is very late. I apologize, I seldom check my posts after I posted those and I have not realized there are two queries here already. Looking at tge dates though, I will presume that a solution to both has already been found.

    ReplyDelete
  4. Estimado necesito contactarme contigo lo mas pronto posible, Te dejo mi correo caridi23@hotmail.com

    ReplyDelete