Artículo original: 3rd party apps from within our forms
https://sandstorm36.blogspot.com/2014/05/3rd-party-apps-from-within-our-forms.html
Autor: Jun Tangunan
Traducido por: Luis María Guayán
Ahora es la 1:30 AM y todavía no puedo volver a dormir, así que hagamos que mi tiempo sea un poco útil. Inspirado por lo que Bernard Bout ha mostrado AQUI, hoy decidí ver de que forma se puede haceruna instancia de Excel. Por favor, lea en enlace anterior antes de proceder a continuar como voy a tratar de separar las cosas para nuestra mejor comprensión.
Las herramientas más básicas del oficio
- un Shape en nuestro formulario
- SetParent
- WinExec
- FindWindow
- SetWindowPos
Bernard nos dió un gran punto de partida porque los ingredientes básicos ya están ahí. Y estoy de acuerdo y aprecio que Bernard no haya mostrado todos los trucos porque si los ha hecho, no trataré de entender algunos de ellos y simplemente usaré el truco a ciegas; y no los comprenderé mejor.
Comencemos la deconstrucción y reconstrucción:
¿WinExec es la única forma?
No. Está ya que es uno de los comandos más simples para abrir una aplicación de terceros. Pero hay alternativas como RUN, ShellExecute(), Scripting y algunas más.
Me he dado cuenta de que el objetivo principal y el primer paso es abrir el archivo o la aplicación de cualquiera de las formas posibles y, dado que mi objetivo aquí es Excel, me gusta la automatización cuando se trata de Excel, entonces es la automatización.
¿Donde está la ventana?
El segundo paso después de abrir la aplicación de terceros es controlar la ventana de esa aplicación tratando de encontrarla. Y eso se puede hacer a través de Winapi con FindWindow() de esta manera:
nHwnd = FindWindow(NULL, "Untitled - Notepad")
Donde nHwnd es el identificador de ventana de la aplicación de terceros que queremos. Lo anterior dice, en términos sencillos, busque una instancia de Bloc de Notas recién abierta y sin guardar entre las ventanas abiertas y obtenga el identificador de su ventana para que podamos trabajar más en ella. Ese título es lo que verá como título cuando abra un Bloc de Notas solo.
Si VFP no puede encontrar el identificador de la ventana, nHwnd devolverá 0.
Intente con un formato de archivo xlsx
Como estaba haciendo la automatización, el primer intento se realiza en un archivo xlsx:
Local loExcel As excel.Application, lcFile loExcel = Createobject('excel.application') lcFile = Getfile('xls,xlsx') If !Empty(m.lcFile) loExcel.Workbooks.Open(m.lcFile) * Get a handle on its Window nHwnd = FindWindow('XLMain',Alltrim(Justfname(m.lcFile))+' - Microsoft Excel') Endif
¡Y falla, no pasó nada! Tarde me doy cuenta de que a pesar de lo que se muestra en la pantalla en la barra de título de Excel 2007, internamente todavía lo está haciendo de la manera anterior como esta:
nHwnd = FindWindow('XLMain','Microsoft Excel - '+Alltrim(Justfname(m.lcFile)))
No hace falta decir que lo anterior funciona. ¡Hasta aquí todo bien!
Intente con un formato de archivo xls
Luego intenté abrir un formato de archivo xls y ¡vuelve a fallar! ¡¡¡Maldito!!! Recordé que cuando abres un archivo xls en Excel 2007, se agregará un título adicional de [Modo de compatibilidad]. Esa es una forma de recordarnos visualmente que actualmente estamos trabajando en un formato de archivo antiguo. Hmmm ... pedazo de alcornoque, lo haré así entonces:
* Attempt to open without that compatibility mode caption nHwnd = FindWindow(Null, "Microsoft Excel - "+Alltrim(Justfname(m.lcFile))) If m.nHwnd = 0 * Failed, so attempt to open with that added compatibility mode caption nHwnd = FindWindow('XLMain', 'Microsoft Excel - +Alltrim(Justfname(m.lcFile))+' [Compatibility Mode]') Endif
Y no se abre correctamente. Quiero decir, se abre pero se abre fuera de mi aplicación por sí solo. ¡¡¡Maldito sea !!!
Perdí mi tiempo tratando de encontrar la combinación correcta en el título interno de la barra de título de Excel aplicando los casos reales de nombre de archivo usando FSO ... todavía no tuve suerte (me di cuenta al final a través de repetidas pruebas, aunque ese caso de caracteres como adecuado, superior e inferior no lo afecte), mezclando y reubicando las palabras en el pie de foto ... de nuevo no tuve suerte ... y estaba a punto de rendirme porque ya he perdido casi 2 horas solo para ese estúpido título interno (hey yo estaba frustrado, ¡LOL!) y estba a punto de archivar todo el proyecto cuando se me ocurrió una idea. ¡¡¡¡Diablos!!!! Estoy haciendo automatización, entonces, ¿qué me impide hacer esto?
nHwnd = FindWindow('XLMain', loExcel.Caption)
Y listo !!!! ¡Una forma muy flexible de asegurarse de encontrar el título correcto de un archivo de Excel abierto sin importar si está en modo de compatibilidad o no, o en cualquier versión de Excel que esté usando! ¡Excelente! Por supuesto, dicho enfoque no se limita a Excel.
Shape de mi corazón
Ahora vamos a la otra parte. Lo que también he notado es que el truco usa un Shape. Y cuando leí y vi el truco por primera vez, mi presunción es que Excel o cualquier aplicación de terceros aparece mágicamente en dicho Shape transformando dicho Shape en esa aplicación de terceros. Pero como ahora estoy tratando de separar las cosas, me doy cuenta de que con o sin ese Shape, podemos abrir esas aplicaciones de terceros dentro de nuestra aplicación.
Entonces, ¿para qué es ese Shape? Dicho Shape invisible/visible (su elección) en el formulario existe solo por una razón. Eso no es para mostrar la aplicación de terceros, sino para servir como una manera fácil de establecer las coordenadas de esas aplicaciones de terceros desde nuestro formulario, ya que es más fácil cambiar el tamaño de un Shape e indicar a dicha aplicación de terceros que "siga" las coordenadas de ese Shape, que hacerlo por código, de forma repetida y a prueba y error.
Cree un Shape, dimensione y colóquelo en el formulario a su gusto, escóndelo si lo desea e indique a la aplicación de terceros que siga sus coordenadas. Muy ingenioso por aquellos que originalmente pensaron en la idea.
¡Excel no se puede hacer clic, no se puede editar y está muy loco!
Para simplificar las cosas, me doy cuenta de que con VFP, aunque "nosotros" podemos ver los objetos, internamente no puede ser visto por VFP. Al igual que cuando agregamos mediante códigos algunos objetos en un Grid, tenemos que hacerlo Visible, de lo contrario, puede verlos pero no puede funcionar correctamente en él. Entonces:
loExcel.Visible = .T.
Entonces, si desea que esto se destaque dentro de nuestro formulario solo con fines de visualización pura, establezca la propiedad Visible en .F.
loExcel.Visible = .F.
Y eso es todo. Todo lo que el usuario puede hacer es desplazarse hacia abajo y hacia arriba para ver su contenido. :)
¿Que hay en el menu?
Pero una vez que haya hecho visible Excel, entonces todo el archivo de Excel estará dentro de nuestro formulario con todo su esplendor como cinta, barra de fórmulas, pestañas de la hoja de trabajo, etc. Bueno, algunos de ustedes pueden quererlo de esa manera, pero yo no. Así que tengo que esconderlos. Simplemente verifique los códigos más adelante para saber cómo hacerlo.
¡Dimensióname!
No es sorprendente que el cambio de tamaño del formulario deje la aplicación de terceros en sus coordenadas originales. Pero esto es fácil, revisa los códigos.
Hacer zoom, guardar, detectar y algo más
Solo revisa los códigos ... Estoy empezando a tener sueño, finalmente...
Resumen:
Hay dos o más cosas que estoy buscando pero que todavía no he podido encontrar una solución y que tampoco me siento cómodo dejando esos asuntos sin resolver. Y lo básico de esos deseos son:
- Ocultar la barra de título de Excel
- No permitir arrastrar y soltar
Incluso jugar con uFlags de SetWindowPos no me dio los resultados esperados. Sin embargo, pude utilizar una buen Flag cuando intentamos abrir un nuevo archivo de Excel para que Excel no destruya lentamente sus objetos frente a nuestros ojos cuando lo cerramos.
Seguiré jugando con esto y si encuentro formas, actualizaré esto. O dado que el plan, como de costumbre, es publicar este foro interno de Foxite, que se encuentra entre los foros de desarrolladores más amigables que he visto, con suerte alguien que haya jugado con esto antes que yo (lo siento, siempre llego tarde) pueda compartir con nosotros cómo solucionar esos problemas; luego editaré esta publicación para incluir sus códigos y al colaborador.
El día siguiente:
¡Encontré el eslabón perdido, LOL! El truco para ocultar la barra de título de Excel es, en lugar de buscar propiedades ocultas de Excel para ocultarlas, manipularlo directamente usando estas dos WinAPI, es decir, GetWindowLong y SetWindowLong. Esos son posibles porque ya tenemos el Handle de la ventana. ¡Compruebe en los códigos a continuación cómo se hace!
Códigos:
loTest = Createobject("Form1") loTest.Show(1) Define Class Form1 As Form AutoCenter= .T. Height = 493 Width = 955 Caption = 'Excel within our Form' _nHwnd = .F. _oExcel = .F. Add Object Shape1 As Shape With ; Top = 36, Left = 6, Height= 445, Width = 936,; BackColor = Rgb(255,255,255), BorderColor = Rgb(0,128,192) Add Object label1 As Label With ; Top = 15, Left = 6, Caption = 'Preview', FontBold = .T.,; FontName = 'Calibri', FontSize = 12, AutoSize = .T. Add Object label2 As Label With ; Top = 12, Left = 836, Caption = 'Zoom', FontBold = .T.,; FontName = 'Calibri', FontSize = 12, AutoSize = .T.,; Anchor = 9 Add Object cmdOpen As CommandButton With ; Top = 8, Left = 312, Caption = '\<Open', Width = 84, Height = 24 Add Object cmdSave As CommandButton With ; Top = 8, Left = 399, Caption = '\<Save', Width = 84, Height = 24 Add Object cmdClose As CommandButton With ; Top = 8, Left = 486, Caption = '\<Close', Width = 84, Height = 24 Add Object chkShowTabs As Checkbox With ; Top = 12, Left = 732, Caption = 'Show \<Tabs', AutoSize = .T.,; Anchor = 9 Add Object SpinZoom As Spinner With ; Top = 10, Left = 882, KeyboardLowValue = 10, SpinnerLowValue = 10,; Value = 100, Anchor = 9, Width = 60 Procedure Load Declare Integer SetParent In user32; INTEGER hWndChild,; INTEGER hWndNewParent Declare Integer FindWindow In user32; STRING lpClassName, String lpWindowName Declare Integer SetWindowPos In user32; INTEGER HWnd,; INTEGER hWndInsertAfter,; INTEGER x,; INTEGER Y,; INTEGER cx,; INTEGER cy,; INTEGER uFlags Declare Integer GetWindowLong In User32; Integer HWnd, Integer nIndex Declare Integer SetWindowLong In user32 ; Integer HWnd,; INTEGER nIndex,; Integer dwNewLong Endproc Procedure Resize Thisform._SetCoord() Endproc Procedure Destroy If Type('thisform._oexcel') = 'O' This._Clear() Endif Endproc Procedure cmdOpen.Click If Type('thisform._oexcel') = 'O' Thisform._Clear() Endif Thisform._linkapp() Endproc Procedure cmdSave.Click If Type('thisform._oexcel') = 'O' Thisform._oExcel.activeworkbook.Save() Messagebox('Changes made are saved!',64,'Save') Else Messagebox('Nothing to save yet!',64,'Opppppssss!') Endif Endproc Procedure cmdClose.Click If Type('thisform._oexcel') = 'O' Thisform._Clear() Endif Endproc Procedure SpinZoom.InteractiveChange Thisform._oExcel.ActiveWindow.Zoom=Thisform.SpinZoom.Value Endproc Procedure chkShowTabs.Click Thisform._oExcel.ActiveWindow.DisplayWorkbookTabs=This.Value Endproc Procedure _Clear With This With .Shape1 * Show shape .Visible = .T. * Hide window via uFlags SetWindowPos(This._nHwnd, 1, .Left, .Top, .Width, .Height,0x0080) Endwith With ._oExcel * Restore those we have hidden .DisplayFormulaBar = .T. .DisplayStatusBar = .T. .ActiveWindow.DisplayWorkbookTabs=.T. .ActiveWindow.DisplayHeadings=.T. .activeworkbook.Close() .Visible = .F. .Quit Endwith ._oExcel = .F. Endwith Endproc Procedure _HideRibbon Local loRibbon loRibbon =This._oExcel.CommandBars.Item("Ribbon") If m.loRibbon.Height > 0 This._oExcel.ExecuteExcel4Macro('Show.Toolbar("Ribbon",False)') Endif Endproc Procedure _linkapp Local loExcel As excel.Application, lcFile loExcel = Createobject('excel.application') lcFile = Getfile('xls,xlsx') If !Empty(m.lcFile) loExcel.Workbooks.Open(m.lcFile) * This is so we can tap into it on other methods/events This._oExcel = loExcel With loExcel .Visible = .T. .DisplayAlerts = .F. .Application.ShowWindowsInTaskbar=.F. .DisplayFormulaBar = .F. .DisplayDocumentActionTaskPane=.F. .DisplayStatusBar = .F. * Ensure scroll bars are shown .ActiveWindow.DisplayVerticalScrollBar=.T. .ActiveWindow.DisplayHorizontalScrollBar=.T. * Hide Workbook Tabs .ActiveWindow.DisplayWorkbookTabs=Thisform.chkShowTabs.Value .ActiveWindow.DisplayHeadings=.F. .ActiveWindow.WindowState = -4137 && xlMaximized .ActiveWindow.Zoom=Thisform.SpinZoom.Value * Get a handle on Window nHwnd = FindWindow('XLMain', .Caption) Endwith * Add this so we can work on other methods This._nHwnd = m.nHwnd * Hide Ribbon Thisform._HideRibbon() * Hide the title bar, disallow drag and drop of the excel window Local lnStyle * Get the current style of the window lnStyle = GetWindowLong(nHwnd, -6) * Set the new style for the window SetWindowLong(nHwnd, -16, Bitxor(lnStyle, 0x00400000)) * force it inside our form SetParent(nHwnd,Thisform.HWnd) * Size it Thisform._SetCoord() * Hide shape Thisform.Shape1.Visible = .F. Endif Endproc Procedure _SetCoord * size it based on Invisible shape With This.Shape1 SetWindowPos(This._nHwnd, 0, .Left, .Top, .Width, .Height, 2) Endwith Endproc Enddefine
Palabras de despedida:
En primer lugar, muchas gracias a Bernard Bout, cuya publicación me sirve de inspiración para este enfoque. Un agradecimiento especial a Yousfi Benameur quien nos compartió también antes de los códigos para ocultar la cinta de Excel en Office 2007.
Siempre que estoy haciendo este estilo de redacción, estoy apuntando a que los lectores que son nuevos en esto intenten comprender el propósito de cada paso / objeto / código. ¿Cuál es el propósito de WinExec aquí, por qué necesitamos conocer el título, cuál es el propósito de la forma en el formulario, etc.?
Otra es que mis lectores provienen de diferentes partes del mundo donde el inglés no es el idioma nativo y supongo que mi enfoque de redacción es una de las razones por las que mi Blog todavía recibe nuevas páginas vistas de vez en cuando. Aunque espero no aburrirte de esta manera. :)
Hora de dormir....