2022-10-13

FoxyPreviewer3 and its files

FoxyPreviewer3 needs to create several files in order to run, so here I will explain what happens behind the scenes, so that people can get the most benefit from it.

Initilization
At initialization, FP search for 2 files - if not found it will restore from the embedded app
 - LibHaru.dll - the PDF engine created by Takeshi Kano, that is used to generate PDFfiles using ".nPDFType=1". This file will be stored at the same folder that FoxyPreviewer.App resides.
 - FoxyPreviewer_Settings.dbf - stores the user customized settings. This table can be edited as you like. Whenever a user changes an option in the settings screen it gets updated, and it will be loaded at initialization. This file will be recreated if you delete it.

Folders
Also at initialization 3 folders will be created:
 - \FP_IMAGES - Here all the images used for creating the toolbar buttons will reside. FP3 will restore them from the APP, and whenever you or your users change the button colors or behaviors, the new images created will be stored there. If you open that folder, you'll see all buttons that you used previously. The images are stored there physically so that in the next run they wont need to be recreated. All buttons, the original, the new "font based" buttons need some GDI+ processing, and some users could not like the delay needed to recreate them at each run. If you remove any image, or even the whole folder FP3 will recreate it on the next run.

FoxyPreviewer3 and sending e-mails

FoxyPreviewer3 - Getting support

When asking for support for FoxyPreviewer, to help me reproducing your environment, please immediately after your issue execute the following line of code:

_Screen.oFoxyPreviewer.GetFoxyScript()





This will create a script that will help me to mimic partially your environment, so that I can reproduce what is happening at your side.

Please revise that file, erase any data that you feel is sensitive and send it for support.


2022-10-06

FoxyPreviewer3 and localization strings

FoxyPreviewer's default language is English.

Right now there is suport to 22 languages: English, Portuguese, Spanish, French, German, Greek, Turkish, Czech, Persian, Arabic, Italian, Indonesian, Polish, Swahili, Russian , Simplified Chinese , Traditional Chinese , Dutch, Bulgarian, Hungarian, Kazakh and Serbian.

Special tweaks were applied to allow double-byte languages, such as Chinese and Japanese.


To change the default language there are 2 options:

1 - Setting the _Screen.oFoxyPreviewer object, by passing  pass the English language name or the local language name - for instance, to change the language to French or spanish:

_Screen.oFoxyPreviewer.cLanguage = "SPANISH"  && or
_Screen.oFoxyPreviewer.cLanguage = "ESPANIOL"

_Screen.oFoxyPreviewer.cLanguage = "FRENCH"   && or
_Screen.oFoxyPreviewer.cLanguage = "FRANÇAIS"

2 - Users can change by themselves to their desired language in the Settings form:



Some of the languages are not updated. FP3 brings new facilities and dialogs that still need to be updated.. 

If you're interested, please download the Localizations table from the link below and send it back to vfpimaging at hotmail dot com

I'll be happy to update the localizations table in the future updates.

2022-09-25

FoxyPreviewer3 and embedded images in your EXE

For security reasons a file embedded in your EXE can't be accessed by an external library, such as FoxyPreviewer3. That means that FP3 can't deal with the images in your reports when you run the reports from your EXE. We still save some options to deal with that:

OPTION #1 - Store the image files outside your EXE, in a drive location, that can be passed with the full address, so that FoxyPreviewer.App can "see" these files and include them in your desired output.

OPTION #2 - To make these images appear automatically, without the need of removing the image files from your project. 

  • Include the program "FOXYGETIMAGE.PRG" in your EXE project. This file is found at the main folder of FoxyPreviewer. This file will provide access to FoxyPreviewer (and any other VFP program) to get the needed images from the executable. Please note that this may bring some security issues to your EXE, because this program will allow FoxyPreviewer and other programs to access the embedded images of your EXE. I recommend you to open this file, and analyze it. You can make any changes you find necessary to it, for example to make it allow only some specific files to be accessed. Right now, FOXYGETIMAGE.PRG allows accessing only image files, with the extensions: "BMP", "GIF", "PNG", "JPG", "JPEG", "TIFF", "TIF", "EMF".
  • Set the new property: _Screen.oFoxyPreviewer.nSearchImgMode = 2 or 3 - That's an additional setting to tell FP3 to turn on or off this setting. That is to avoid an unnecessary process, forcing VFP to search in the disk for a file that is already available. This can slow down a little the report run. Behind the scenes, FP3 will store the image files at the FP_IMAGES\TEMP folder during the report run. These files will be deleted immediately after the report is rendered.

Property:
nSearchImgMode - Numeric, determines how FoxyPreviewer will search for images while rendering; 1=Default, images available at the current Path (images embedded in the EXE are not available); 2=Uses "FoxyGetImage.prg" embedded in the main EXE to get only files not found in disk; 3=Always retrieve images from the EXE using "FoxyGetImage.prg"


Below is the file FOXYGETIMAGE.PRG contents:


* PROCEDURE FoxyGetImage.prg
* To be used with FP3 if you use embedded images in your EXE
* Retrieves image files embedded in the main EXE
* To be included in the main VFP EXE project. This may bring security issues - use at our own risk
LPARAMETERS tcImageFile, tlTempFile
LOCAL lcPictVal, lcReturn
m.lcPictVal = ""
IF INLIST(UPPER(JUSTEXT(m.tcImageFile)), "BMP", "GIF", "PNG", "JPG", "JPEG", "TIFF", "TIF", "EMF")
	TRY
		m.lcPictVal = FILETOSTR(m.tcImageFile)
	CATCH
	ENDTRY
ENDIF

IF m.tlTempFile AND NOT EMPTY(m.lcPictVal) && Store in a TempFile in disk
	LOCAL lcTempFile, lcTempPath
	IF PEMSTATUS(_Screen, "_FoxyTempImagesPath", 5)
		lcFolder = _Screen._FoxyTempImagesPath
	ELSE
		lcFolder = SYS(2023)
	ENDIF 	
	m.lcTempFile = ADDBS(lcFolder) + JUSTFNAME(m.tcImageFile)
	STRTOFILE(m.lcPictVal, m.lcTempFile)
	m.lcReturn = m.lcTempFile
ELSE
	m.lcReturn = m.lcPictVal
ENDIF

RETURN m.lcReturn







2022-09-18

Sending e-mails with Microsoft Powerscript and VFP, inspired by Doug Hennig

After finally releasing FoxyPreviewer3, I started receiving several requests for sending e-mails. FP3 currently supports the use of CDOSYS - the good and old way Windows used to allow us to send e-mails. The usage was really simple, but it's not compatible with TLS - evolved from the good and old SSL protocol, providing more security to the process.

Unfortunately, there is no simple way for Foxers to send messages using TLS, unless if we use some bridges to access other platforms. So, this was the right time to make tests, inspired by Doug Hennig's session on the last VFF - Virtual Fox Fest - the new online VFP conference, when he presented the amazing session Windows PowerShell: Batch Files on SteroidsThe organizers of VFF were very kind offering a recorded version of all sessions for free for the community in YouTube. At the end of this post you'll find the link to his presentation on YouTube. By the way, the next release of VirtualFoxFest will be soon, on October 13, 19 and 25 2022.

So, first of all I just googled about sending e-mails with PowerScript, and found tons of posts all over, in all languages. And here is my first script to send e-mails. This is raw code, no error handling, very few parameter checking. Needs development, but looks promising, and very effective!

  • create a script in Powershell, and test it
  • since we can't send parameters directly, we need to change the script to adapt it to our needs. One easy way is to store the Script in a "TEXT / ENDTEXT", and using TEXTMERGE make the needed replacements
  • Save the script using the ".PS1" file extension
  • Use the WScript.Shell.Run to run the PS1 file script in invisible mode. Powershell allows us to save the outputs in a text file. This file will be used for us to store the returning values we need
  • Delete the temporary PS1 and Output files


Before you go:

HOW TO RUN POWERSHELL SCRIPT FILE ON WINDOWS 10

On Windows 10, to run a script file with the PowerShell console, you have to change the execution policy.

To change the execution policy to run PowerShell scripts on Windows 10, use these steps:

  • Open Start.
  • Search for PowerShell, right-click the top result, and select the Run as administrator option.
  • Type the following command to allow scripts to run and press Enter: Set-ExecutionPolicy RemoteSigned
  • Type A and press Enter (if applicable).




Save the script below as SENDPSEMAIL.PRG


* FILE  : SENDPSEMAIL.PRG
* AUTHOR: VFPIMAGING http://vfpimaging.blogspot.com 2022-09-08
* Send e-mails using PowerShell 

FUNCTION SendPSEmail(tcDest, tcSubject, tcBody, tcAttachment, tcServer, tcUser, tcPwd, tcPort)

	LOCAL lcBodyAsHtml, lcCredentials, lcPSScript, lcSender
	m.lcSender = m.tcUser
	IF "<" $ m.tcUser
		m.tcUser   = STREXTRACT(m.tcUser, "<", ">")
	ENDIF

	m.tcPort = TRANSFORM(m.tcPort)
	m.tcDest = [@('] + STRTRAN(m.tcDest, [,], [';']) + [')] && Adjust for multiple recipients
	IF EMPTY(m.tcAttachment)
		m.tcAttachment = ""
	ELSE
		m.tcAttachment = [Attachment = ] + [@('] + STRTRAN(m.tcAttachment, [,], [', ']) + [')]
	ENDIF

	IF "<html>" $ LOWER(m.tcBody)
		m.lcBodyAsHtml = "BodyAsHtml = $true"
	ELSE
		m.lcBodyAsHtml = ""
	ENDIF

	IF EMPTY(m.tcPwd)
		m.lcCredentials = "$cred = Get-Credential"
	ELSE
		m.lcCredentials = "$pass = ConvertTo-SecureString -String $pass -AsPlainText -Force" + CHR(13) + ;
			"$cred = New-Object System.Management.Automation.PSCredential $user, $pass"
	ENDIF


	* Here starts the Powershell script
	TEXT TO m.lcPSScript NOSHOW TEXTMERGE
$to = <<tcDest>>
$user = '<<tcUser>>'
$pass = '<<tcPwd>>'

<<lcCredentials>>
$mailParam = @{
    To = $to.Split(';')
    From = '<<lcSender>>'
    Subject = '<<tcSubject>>'
    Body = '<<tcBody>>'
    SmtpServer = '<<tcServer>>'
    Port = <<tcPort>> #587 or 465
    Credential = $cred
    UseSsl = $true
    <<tcAttachment>>
	<<lcBodyAsHtml>>
}

try
{
	# Send Email
	Send-MailMessage @mailParam 
}
catch
{
	$_.Exception.Message
	# $_.Exception.Message | out-file C:\Temp\error.log && #=Comment
	Write-Output 'Message send failed.'
}
	ENDTEXT
	* End of script

	lcReturn = RunPowerShell(m.lcPSScript)
	IF EMPTY(ALLTRIM(m.lcReturn))
		m.lcReturn = "Message Sent successfully"
	ENDIF
	RETURN m.lcReturn	
	


*************************************************************
* Run PowerShell script hidden
* by VFPImaging - Adapted from Antonio Lopez FUNCTION PS_ExecScript(tcScript)
FUNCTION RunPowerShell(tcScript)
*************************************************************
	LOCAL loWShell AS "WScript.Shell"
	LOCAL lcCmd, lcEcho, lcFile, lcSetEscape, lcText, loPS
	IF EMPTY(m.tcScript)
		RETURN .F.
	ENDIF

	m.lcFile = ADDBS(SYS(2023)) + SYS(2015) + ".ps1"
	m.lcEcho = FORCEEXT(m.lcFile, "log")

	STRTOFILE(m.tcScript, m.lcFile)
	m.lcCmd = "cmd /c powershell.exe -ExecutionPolicy RemoteSigned " + ;
		"-File " + IIF(" " $ m.lcFile, '"' + m.lcFile + '"', m.lcFile) + ;
		" > " + IIF(" "$ m.lcEcho, '"' + m.lcEcho + '"', m.lcEcho)

	m.lcSetEscape = SET("Escape")
	SET ESCAPE ON
	m.loWShell = CREATEOBJECT("WScript.Shell")

	m.lcCmd = "cmd /c powershell -ExecutionPolicy RemoteSigned " + ;
		"-File " + IIF(" " $ m.lcFile, '"' + m.lcFile + '"', m.lcFile) + ;
		" > " + IIF(" "$ m.lcEcho, '"' + m.lcEcho + '"', m.lcEcho)
	m.loPS = m.loWShell.RUN(m.lcCmd, .F., .T.)

	DOEVENTS
	SET ESCAPE &lcSetEscape.

	TRY
		m.lcText = FILETOSTR(m.lcEcho)
	CATCH
		m.lcText = ""
	ENDTRY
	ERASE (m.lcFile)

	TRY
		ERASE (m.lcEcho)
	CATCH
	ENDTRY

	RETURN m.lcText
ENDFUNC



Adapt the codes below according to your SMTP server and run!


CLEAR 

* Here is our HTML body
TEXT TO lcHTML NOSHOW
<!DOCTYPE html>
<html><body><font size="3" face="Verdana">
<h1>CONGRATULATIONS!</h1>
<br/><br/><br/>
<p>You successfully managed to send an e-mail using PowerShell and VFP</p>
<br/><br/><br/>
<table><tbody><tr>
<td><img height="65" src="http://www.files.foxypreviewer.com/Logo1.png" width="65" /></td>
<td><table><tbody style="font-family: "Calibri">
<tr><td>CESAR<br/><a href="https://www.FoxyPreviewer.com">FoxyPreviewer</a></td></tr>
<tr><td>+55-44.98765.4321<br/>
<img height="16" src="http://www.files.foxypreviewer.com/Mail.bmp" width="16" />  <a href="mailto:vfpimaging@hotmail.com">vfpimaging@hotmail.com</a></td></tr></tbody></table>
</td></tr></tbody></table>
</body></html>

ENDTEXT 


lcReturn = SendPSEmail("yourdestination@hotmail.com", ;
		"PowerShell E-mail " + TRANSFORM(DATETIME()), ;
		lcHTML, ; && Message body (plain text or HTML)
		GETFILE(), ; && attachment (comma separated)
		"smtp.office365.com", ; && SMTP Server
		"VfpImaging <vfpimaging@hotmail.com>", ; && User
		"yourpassword", ; && pwd
		587) && SMTP port 25, 465, 587
? lcReturn

RETURN 



Running the script above, if you omit the password it will ask you for the credentials:




Below is the video session from Doug Hennig

 


Related links:

POP, IMAP, and SMTP settings for Outlook.com

Send-MailMessage

How to pass credentials to the Send-MailMessage command for sending emails

Powershell tutorials

Send-MailMessage: Sending Emails from PowerShell

How to Send an Email Using Windows PowerShell



2022-05-08

Doug Hennig - Windows PowerShell: Batch Files on Steroids

On last May 05 at VFF - Virtual Fox Fest - the new online VFP conference, Doug Hennig presented the amazing session Windows PowerShell: Batch Files on SteroidsThe organizers of VFF were very kind offering a recorded version of all sessions for free for the community in YouTube.



This specific session dropped me completely, opening a whole new world of possibilities. As we can see in the video description: "Windows PowerShell has been included with the operating system since Windows 7 and is available for download for older systems. What is PowerShell? It's Microsoft's task automation scripting framework. PowerShell isn't just a replacement for batch files; it can do a lot more than batch files ever could. This session looks at PowerShell, including why you should start using it and how to create PowerShell scripts."

I just loved to know we can access almost the whole "Net Framework" using some batch files! I just started with some simple tests, and this looks really very promising, and will simplify several tasks.

Thanks very much for sharing!

XFRX vs FoxyPreviewer

I felt very honored to know that the VFP guru Rick Schummer presented a session - Visual FoxPro Reporting: XFRX vs. FoxyPreviewer in the 2022 version of VFF - Virtual Fox Fest. The organizers were very generous offering a recorded version of all sessions for free for the community in YouTube.



Rick very quickly and impartially shown some of the main features of both tools. 

Regarding FoxyPreviewer, I have some few considerations or remarks. Of course it would be impossible or Rick to know all the possibilities and features available. I just want to make clear that I feel very thankful for the review. And I am totally aware that the documentation needs some updates, and some points could be better explained, so here I take the opportunity to try to clarify some points.


  • Exporting report to an image file - There are 3 available options:
    - OBJECT TYPE 16 - This is the object type related to exporting to image files. Works exactly the same as the others.
    - OBJECT TYPE 20 - This OBJECT TYPE was not discussed in the presentation. It will choose automatically the ReportListener needed to generate the desired output, according to the file extension passed in the TO FILE clause. The available types are: PDF, RTF, HTML, MHTML, GIF, TIFF, EMF, JPG or PNG.
    REPORT FORM YourReport TO FILE MyImageReport.JPG OBJECT TYPE 20
    - Using the FoxyListener.OutputPage method &&Like regular VFP9
  • The truncated numeric fields in reports - He was very precise in his explanation, but I still would like to add some few information from a FAQ related to this:
When I run my reports with FoxyPreviewer sometimes asterisks symbols ********************* appear instead of the field. This was originally working, before using FoxyPreviewer

That happens because FoxyPreviewer uses the SET REPORTBEHAVIOR 90 mode, that uses GDI+ to render the texts. Unfortunately there is a slight difference of the size of the strings between these modes. To fix it, just edit your report and enlarge that field!

You can set the property - lExpandFields to make the report engine show the field numeric value ignoring the field size.
Using "lExpandFields", FoxyPreviewer retrieves the value that overflowed and resends it to the report engine with an enlarged field width.

This is a known issue, and Lisa Slater Nicholls wrote a short blog post regarding it:
Why do report layouts in VFP9 need wider field/expression controls than in VFP8 and earlier?

And here's another interesting text from Lisa, that explains the reason for that:
With REPORTBEHAVIOR=90, the new report engine uses GDI+ to render output, and text string rendering requires more space than plain old GDI.
The Report Designer uses GDI - not GDI+ - to render the report layout components, including all the text strings that you see. So if you visually right-align a label report element, the report designer records the leftmost co-ordinates of the element (the text start position) in the layout.
The length of the string under GDI+ rendering will most likely be greater than what you would think, based on what you see in the Designer.

  • SET COMPATIBLE ON - Oh, this is a known issue, and I had it fixed before. I just don't know why the fix was not included in the last version. I hope to post an update very soon.
  • ReportPreview.App and ReportOutput.App - As shown by Rick, these files are no longer needed if FoxyPreviewer was elected to be your single ReportEngine. I just feel important to add that if you are used to turn it off, you still need those files to restore the Reporting engine to the default VFP9.
  • Excel bug / Enhancements - Please send me your files and I will be very happy and honored to aply your updates to the main distributable version!
  • lOpenViewer property - In one of your samples Rick was using the property ".lOpenViewer = .T." - That means the generated output file would be opened automatically after generated. But the next REPORT FORM command contained the "TO FILE" and "PREVIEW" clauses. The "PREVIEW" clause means exactly the same - to open the file automatically!
    That's a very interesting usage of FoxyPreviewer. If you want your report to be shown in the default PDF viewer instead of a VFP FORM, just use the command:
    REPORT FORM YourReport OBJECT TYPE 20 TO FILE TempFile.pdf PREVIEW
  • The "PrintingPreferences" - Rick told us he feels some kind of frustration with the fact that the report is closed after you change your report settings in that dialog. But that's exactly what the "PRINTER PROMPT" clause does! From that dialog you have the opportunity to change the printer, copies and other settings and call the printer directly.
  • Rick was asked about embedding the toolbar in the Preview window. As he said, not in v2.99, but docking the toolbar is possible.
  • David Acuña asked about determining the SMTP server values previously. Yes, that's possible. Just set the "cSMTPServer", "nSMTPPort" and other related properties previously, just after FoxyPreviewer was initialized.
  • Maybe I misunderstood the real meaning of Rick's statement when he said that "The configurations are Global, not Per User". In fact, when you start FoxyPreviewer.App it will load the settings from the file "FoxyPreviewerSettings.dbf". This file is automatically generated after the 1st run of Foxy in that folder. Each of your users can have their own Settings Dbf file! You can save a copy of FoxyPreviewerSettings.DBF when your user logs off. Next time, after logging in and before calling FoxyPreviewer.App replace this file with the desired one. So, you can save a settins file for each of your users! When FoxyPreviewer does not find that DBF in its folder it will create a new one.

    Yes, everyone using that APP on that same machine will use those settings, as he mentioned.

    Here are some new thoughts regarding this, using a not very well documented possibility... There is an option to start FoxyPreviewer in a separate folder:

    Immediately after the user logs in your App, and the user is stored as "m.lcUser", people can try the following:

    lcFolder = ADDBS(JUSTPATH(SYS(16)))
    lcUserFolder = lcFolder + "Users\" + m.lcUser && Make sure the folder exists
    DO FOXYPREVIEWER.APP WITH (lcUserFolder)

    This way, every user will easily have his own Settings table. What do you think?


  • Distributing FoxyPreviewer - People just need ONE SINGLE FILE - FoxyPreviewer.App. And there is no need to install it. Just a simple"DO FOXYPREVIEWER.APP" at the beginning of your EXE code and it's done! The recommended is to store it at the same folder of your EXE, and make sure that folder will allow Foxy to create a subfolder and save some custom configuration files.

2022-04-13

Sharing files with WhatsApp and Telegram from your applications

I've seen tons of discussions in the web about techniques to send files to WhatsApp Web or Telegram Desktop. Lots of ideas, people usually send the file to the clipboard, locate the WhatsApp window, set the focus to it, and paste the file using SendKeys. Yes, that works! But it fails very often too!

So, I tried using the OLEStartDrag / OLECompleteDrag methods, in order to provide a more friendly and reliable way to send files to these Apps. This resulted very promising, and the files were dropped to WhatsApp, but it seems that WhatsApp kept holding a reference to the parent Window from where the file was dragged, making it impossible to close the "Sender" window! Sample in Foxite forum

Finally, since we know that from the "Windows File Explorer" files can be dragged and dropped with no issues, my next obvious try was to add a WebBrowser control, navigate to any folder, and voilá! Files can be dropped normally to WhatsApp from the VFP window, bringing more productivity to our Apps!

Below is the final result, that was introduced to FoxyPreviewer 3:

Sharing files with WhatsApp and Telegram

In summary, the trick is:

  • Create a new temporary folder
  • Store only the files you want to allow to share
  • From the WebBrowser control, navigate to that folder
Super simple, isn't it?