PlanetSquires Forums

Support Forums => WinFBX - Windows Framework for FreeBASIC => Topic started by: Paul Squires on August 02, 2018, 09:19:19 PM

Title: José, if you're bored...
Post by: Paul Squires on August 02, 2018, 09:19:19 PM
...and looking for some new stuff to add to WinFBX...

How about maybe adding WinAPI unicode ready wrappers to replace all of the FB file handling routines?

See, it's easy for me to blindly suggest things without having to code it myself!  LOL   :D  :D  :D

Title: Re: José, if you're bored...
Post by: José Roca on August 03, 2018, 12:52:14 PM
I already provide:

CTextSTream

The CTextStream class allows to read and write sequential text files (sometimes referred to as a text stream).
Works with ASCII and Unicode.
Works with Windows CRLF files and with Linux LF files.

CFindFile

Performs local file searches. CFindFile includes member functions that begin a search, locate a file, and return the title, name, or path of the file. Contrarily to DIR, it works with unicode.

CFileSys

The CFileSys class wraps the Microsoft File System Object and provides methods to work with files and folders, giving your application the ability to create, copy, alter, move, and delete files and folders, or to determine if and where particular files or folders exist. It also enables you to get information about files and folders, such as their names and the date they were created or last modified.

------------

To work with files in binary mode, what I could do is to write classes to work with the IStream interface. CFileStream will work with files and CMemStream with memory files.

The available methods would be:

Open
Close
Read
Write
Seek
SetSize
CopyTo
Commit
Revert
LockRegion
UnlockRegion
Stat
Clone

Additional Read and Write methods that accept different data types can be added.

What do you think?
Title: Re: José, if you're bored...
Post by: Paul Squires on August 03, 2018, 01:03:37 PM
I love the idea :)  CFileStream should be enough because I doubt many people would have a overly great need for memory files (although for the sake of completeness it would be great to have them) :)

Having an easy to use replacement for FB's built-in file handling routines would certainly lesson those "gotcha" moments when a new/existing user realizes that the FB commands automatically convert your Unicode data to ansi. That has burned me since I started writing the editor and without you pointing it out, I probably would have struggled for a long time trying to fin the bug in my code.
Title: Re: José, if you're bored...
Post by: Paul Squires on August 03, 2018, 01:09:14 PM
I am more than happy to test the routines because I will replace every FB file handling routine in WinFBE to use the new ones.
Title: Re: José, if you're bored...
Post by: José Roca on August 03, 2018, 11:46:50 PM
Well, here you have something to read and test: first version of CFileStream.

List of possible error codes:

' Storage HRESULT's (error codes)
'#define STG_E_INVALIDFUNCTION _HRESULT_TYPEDEF_(&h80030001)
'#define STG_E_FILENOTFOUND _HRESULT_TYPEDEF_(&h80030002)
'#define STG_E_PATHNOTFOUND _HRESULT_TYPEDEF_(&h80030003)
'#define STG_E_TOOMANYOPENFILES _HRESULT_TYPEDEF_(&h80030004)
'#define STG_E_ACCESSDENIED _HRESULT_TYPEDEF_(&h80030005)
'#define STG_E_INVALIDHANDLE _HRESULT_TYPEDEF_(&h80030006)
'#define STG_E_INSUFFICIENTMEMORY _HRESULT_TYPEDEF_(&h80030008)
'#define STG_E_INVALIDPOINTER _HRESULT_TYPEDEF_(&h80030009)
'#define STG_E_NOMOREFILES _HRESULT_TYPEDEF_(&h80030012)
'#define STG_E_DISKISWRITEPROTECTED _HRESULT_TYPEDEF_(&h80030013)
'#define STG_E_SEEKERROR _HRESULT_TYPEDEF_(&h80030019)
'#define STG_E_WRITEFAULT _HRESULT_TYPEDEF_(&h8003001D)
'#define STG_E_READFAULT _HRESULT_TYPEDEF_(&h8003001E)
'#define STG_E_SHAREVIOLATION _HRESULT_TYPEDEF_(&h80030020)
'#define STG_E_LOCKVIOLATION _HRESULT_TYPEDEF_(&h80030021)
'#define STG_E_FILEALREADYEXISTS _HRESULT_TYPEDEF_(&h80030050)
'#define STG_E_INVALIDPARAMETER _HRESULT_TYPEDEF_(&h80030057)
'#define STG_E_MEDIUMFULL _HRESULT_TYPEDEF_(&h80030070)
'#define STG_E_PROPSETMISMATCHED _HRESULT_TYPEDEF_(&h800300F0)
'#define STG_E_ABNORMALAPIEXIT _HRESULT_TYPEDEF_(&h800300FA)
'#define STG_E_INVALIDHEADER _HRESULT_TYPEDEF_(&h800300FB)
'#define STG_E_INVALIDNAME _HRESULT_TYPEDEF_(&h800300FC)
'#define STG_E_UNKNOWN _HRESULT_TYPEDEF_(&h800300FD)
'#define STG_E_UNIMPLEMENTEDFUNCTION _HRESULT_TYPEDEF_(&h800300FE)
'#define STG_E_INVALIDFLAG _HRESULT_TYPEDEF_(&h800300FF)
'#define STG_E_INUSE _HRESULT_TYPEDEF_(&h80030100)
'#define STG_E_NOTCURRENT _HRESULT_TYPEDEF_(&h80030101)
'#define STG_E_REVERTED _HRESULT_TYPEDEF_(&h80030102)
'#define STG_E_CANTSAVE _HRESULT_TYPEDEF_(&h80030103)
'#define STG_E_OLDFORMAT _HRESULT_TYPEDEF_(&h80030104)
'#define STG_E_OLDDLL _HRESULT_TYPEDEF_(&h80030105)
'#define STG_E_SHAREREQUIRED _HRESULT_TYPEDEF_(&h80030106)
'#define STG_E_NOTFILEBASEDSTORAGE _HRESULT_TYPEDEF_(&h80030107)
'#define STG_E_EXTANTMARSHALLINGS _HRESULT_TYPEDEF_(&h80030108)
'#define STG_E_DOCFILECORRUPT _HRESULT_TYPEDEF_(&h80030109)
'#define STG_E_BADBASEADDRESS _HRESULT_TYPEDEF_(&h80030110)
'#define STG_E_DOCFILETOOLARGE _HRESULT_TYPEDEF_(&h80030111)
'#define STG_E_NOTSIMPLEFORMAT _HRESULT_TYPEDEF_(&h80030112)
'#define STG_E_INCOMPLETE _HRESULT_TYPEDEF_(&h80030201)
'#define STG_E_TERMINATED _HRESULT_TYPEDEF_(&h80030202)
'#define STG_S_CONVERTED _HRESULT_TYPEDEF_(&h00030200)
'#define STG_S_BLOCK _HRESULT_TYPEDEF_(&h00030201)
'#define STG_S_RETRYNOW _HRESULT_TYPEDEF_(&h00030202)
'#define STG_S_MONITORING _HRESULT_TYPEDEF_(&h00030203)
'#define STG_S_MULTIPLEOPENS _HRESULT_TYPEDEF_(&h00030204)
'#define STG_S_CONSOLIDATIONFAILED _HRESULT_TYPEDEF_(&h00030205)
'#define STG_S_CANNOTCONSOLIDATE _HRESULT_TYPEDEF_(&h00030206)
'#define STG_E_STATUS_COPY_PROTECTION_FAILURE _HRESULT_TYPEDEF_(&h80030305)
'#define STG_E_CSS_AUTHENTICATION_FAILURE _HRESULT_TYPEDEF_(&h80030306)
'#define STG_E_CSS_KEY_NOT_PRESENT _HRESULT_TYPEDEF_(&h80030307)
'#define STG_E_CSS_KEY_NOT_ESTABLISHED _HRESULT_TYPEDEF_(&h80030308)
'#define STG_E_CSS_SCRAMBLED_SECTOR _HRESULT_TYPEDEF_(&h80030309)
'#define STG_E_CSS_REGION_MISMATCH _HRESULT_TYPEDEF_(&h8003030A)
'#define STG_E_RESETS_EXHAUSTED _HRESULT_TYPEDEF_(&h8003030B)
Title: Re: José, if you're bored...
Post by: José Roca on August 04, 2018, 12:52:40 AM
Next, I will add a constructor and a LET operator that accepts an IStream pointer, a CAST operator to return the IStream pointer and methods to attach or detach the IStream object from the class. They will be useful to easily manipulate streams returned by some COM methods. If a method returns an stream object, we can attach it to an instance of CFileStream, use the available methods with it and release it when we call Close or the class is destroyed. We don't need to call Close unless we want to reuse an instance of the class to attach another stream object to it.
Title: Re: José, if you're bored...
Post by: José Roca on August 04, 2018, 01:34:49 AM
BTW I'm thinking in using GitHub docs for the documentation because it has become very big and HelpNDoc has become very slow. How do you generate the .pdf files?

Title: Re: José, if you're bored...
Post by: Paul Squires on August 04, 2018, 08:41:00 AM
I will use the code and give you my opinions today! Awesome, thanks.
PDF files were created using Microsoft Word documents printed to a PDF driver. Simple, but the resulting PDFs are a little bigger than they should be.
Title: Re: José, if you're bored...
Post by: José Roca on August 04, 2018, 01:30:05 PM
I have added the additional constuctor that accepts an stream object, the LET and CAST operators, and the Attach/Detach functions. You aren't going to need them, but I have a good use for COM programming.
Title: Re: José, if you're bored...
Post by: José Roca on August 04, 2018, 01:37:36 PM
A memory stream class will reuse most of the code for the file stream, but I'm still doubting about using CreateStreamOnHGlobal or SHCreateMemStream to create it. SHCreateMemStream is more efficient, but it is no thread safe and also does not support cloning in Windows versions below 8.
Title: Re: José, if you're bored...
Post by: Paul Squires on August 04, 2018, 11:51:33 PM
I'm reading and understanding your new class. I am not able to provide my thoughts yet... I will give an opinion as soon as I can.

Once I understand the class, I want to see how the traditional FB commands correlate to the class. Basically, if an FB user wanted to easily transition to the class, what roadblocks and questions would they raise.

For example,

f = freefile
Open "TextA1.txt" For Input As #1
Do Until Eof(#f)
   Line Input #f, st
Loop
Close #f

Also, it will be nice if the class can somewhat automatically detect (or be an option) if the file ansi, utf8, utf16. I don't know, yet, if this could even be applied to the class as I haven't fully studied your approach to the class.

Looks awesome so far!
Title: Re: José, if you're bored...
Post by: Paul Squires on August 05, 2018, 12:11:15 AM
If using CTextStream and passing CWSTR to the functions that take CBSTR, is there any issues with the conversion?
Title: Re: José, if you're bored...
Post by: Paul Squires on August 05, 2018, 12:16:17 AM
Quote from: Paul Squires on August 04, 2018, 11:51:33 PM

For example,

f = freefile
Open "TextA1.txt" For Input As #1
Do Until Eof(#f)
   Line Input #f, st
Loop
Close #f


Lol, silly me... I must have been tired when I wrote that post. The new class is for binary files... the CTextStream class is for sequential text files.
:)
Title: Re: José, if you're bored...
Post by: José Roca on August 05, 2018, 12:50:42 AM
Quote from: Paul Squires on August 05, 2018, 12:11:15 AM
If using CTextStream and passing CWSTR to the functions that take CBSTR, is there any issues with the conversion?


No, you can pass any kind of string data type or literals, ansi or unicode. Using CBSTR is a bit faster because it does not need to be converted.
Title: Re: José, if you're bored...
Post by: José Roca on August 05, 2018, 01:04:51 AM
Quote from: Paul Squires on August 05, 2018, 12:16:17 AM
Quote from: Paul Squires on August 04, 2018, 11:51:33 PM

For example,

f = freefile
Open "TextA1.txt" For Input As #1
Do Until Eof(#f)
   Line Input #f, st
Loop
Close #f


Lol, silly me... I must have been tired when I wrote that post. The new class is for binary files... the CTextStream class is for sequential text files.
:)

Anyway, it would be:


DIM pTxtStm AS CTextStream
pTxtStm.OpenForInputA(TextA1.txt")

DIM cwsText AS CWSTR   ' or CBSTR
DO UNTIL pTxtStm.EOS
   cwsText = pTxtStm.ReadLine
   PRINT cwsText
LOOP
pTxtStm.Close


EOS means End of Stream.


If the file is ansi, you can also use the FB's STRING data type:


DIM s AS STRING
DO UNTIL pTxtStm.EOS
   s = pTxtStm.ReadLine
   PRINT s
LOOP


If it is Unicode, use OpenForInputW. The other available modes are OpenForOutputA/W and OpenForAppendA/W.

It also works with Linux files.
Title: Re: José, if you're bored...
Post by: José Roca on August 05, 2018, 01:18:50 AM
CTextStream can't know in advance if the file is ansi or unicode. However, if the file is unicode with a BOM and you open it as unicode using the OpenUnicode method, it will skip the BOM automatically, i.e. the first line will be returned without the BOM marker.

Maybe we can write a function that checks if the file has an UTF-16 BOM and, if not, call the IsTextUnicode API function, although this function isn't foolproof.

Title: Re: José, if you're bored...
Post by: José Roca on August 05, 2018, 02:13:55 PM
What about this function?


' ========================================================================================
' Determines if a file is likely to contain a form of unicode text.
' ========================================================================================
PRIVATE FUNCTION AfxIsFileUnicode (BYREF wszFileName AS WSTRING) AS BOOLEAN
   DIM hFile AS HANDLE = CreateFileW(@wszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, _
                         OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL)
   IF hFile = INVALID_HANDLE_VALUE THEN RETURN FALSE
   DIM dwFileSize AS DWORD, dwHighSize AS DWORD
   dwFileSize = GetFileSize(hFile, @dwHighSize)
   DIM dwBytesToRead AS DWORD = 1024
   IF dwFileSize < dwBytesToRead THEN dwBytesToRead = dwFileSize
   DIM pBuffer AS UBYTE PTR = CAllocate(1, dwBytesToRead)
   DIM bRes AS BOOLEAN, dwBytesRead AS DWORD
   bRes = ReadFile(hFile, pBuffer, dwBytesToRead, @dwBytesRead, NULL)
   IF bRes THEN bRes = IsTextUnicode(pBuffer, dwBytesRead, NULL)
   IF pBuffer THEN DeAllocate(pBuffer)
   IF hFile THEN CloseHandle(hFile)
   RETURN bRes
END FUNCTION
' ========================================================================================

Title: Re: José, if you're bored...
Post by: José Roca on August 05, 2018, 04:19:46 PM
I'm going to add a file called AfxFile.inc that will give access to all the file related classes and functions:


' ########################################################################################
' Microsoft Windows
' File: AfxFile.inc
' Contents: Includes all the WinFBX procedures and functions that deal with files and folders.
' Compiler: Free Basic 32 & 64 bit
' Copyright (c) 2018 José Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################

#pragma once

#include once "Afx/AfxWin.inc"
#include once "Afx/CStream.inc"
#include once "Afx/CTextStream.inc"
#include once "Afx/CFindFile.inc"
#include once "Afx/CFileSys.inc"
#include once "Afx/CFileTime.inc"

Title: Re: José, if you're bored...
Post by: Paul Squires on August 05, 2018, 04:28:56 PM
Excellent idea to have an AfxFile.inc file to encapsulate all of the file routines. I will use it a lot.
The AfxIsFileUnicode  is a great addition as well.

If you ever get bored again, just ask, I have many more ideas!  LOL  :)
Title: Re: José, if you're bored...
Post by: José Roca on August 05, 2018, 05:15:18 PM
The attached file contains the updated AfxWin.inc and CStream.inc files, and the new AfxFile.inc. If somebody doesn't need to work with unicode, he can use the FB native functions instead.

> If you ever get bored again, just ask, I have many more ideas!  LOL

August is a good month because I don't have many things to do. I stopped adding code because of the lack of testers. I don't want to write code that nobody will use.

The WinFBX repository has been updated with these changes.
Title: Re: José, if you're bored...
Post by: Paul Squires on August 06, 2018, 10:35:03 AM
Thanks Jose, I will ensure that the next WinFBE Suite upload includes the latest WinFBX code.
I will be replacing all internal WinFBE file handling code with this new AfxFile code.
Title: Re: José, if you're bored...
Post by: Paul Squires on August 06, 2018, 11:41:08 AM
A question I get a lot from ex-PBers is a replacement for XPRINT commands. Early in my FB learning I started to build on a previous XPRINT type of code (FBWinPrint) - I called mine WinXPrint. It is horrible looking code by today's standards.
https://www.freebasic.net/forum/viewtopic.php?f=3&t=25321&p=227827
If you're feeling bored and looking for a nice challenge - XPRINT code would certainly fill a gap in the FB library universe.

Title: Re: José, if you're bored...
Post by: Paul Squires on August 06, 2018, 11:48:35 AM
....and an all encompassing tutorial on how to build and use DLL's in Windows using FreeBASIC. Especially figuring out a good way to handle import libraries *.a which is a real pain compared to PowerBASIC. I tried to learn it many months ago and built a rudimentary utility "MakeLIB" that attempted to automate the process. That needs to be revisited.  http://www.planetsquires.com/protect/forum/index.php?topic=3675.0
I know that you use dynamic loading of the DLL functions but it would be cool if it was easy like PB.  :)
Title: Re: José, if you're bored...
Post by: José Roca on August 06, 2018, 01:44:05 PM
Quote from: Paul Squires on August 06, 2018, 11:41:08 AM
A question I get a lot from ex-PBers is a replacement for XPRINT commands. Early in my FB learning I started to build on a previous XPRINT type of code (FBWinPrint) - I called mine WinXPrint. It is horrible looking code by today's standards.
https://www.freebasic.net/forum/viewtopic.php?f=3&t=25321&p=227827
If you're feeling bored and looking for a nice challenge - XPRINT code would certainly fill a gap in the FB library universe.

I don't even have a printer... :)

Maybe this code could be a starting point: http://www.planetsquires.com/protect/forum/index.php?topic=4059.msg30559#msg30559
Title: Re: José, if you're bored...
Post by: Johan Klassen on August 06, 2018, 01:44:17 PM
actually you can use a dll without an import lib, you can #inclib "mydll" where "mydll" is mydll.dll
the dll probably needs to reside where the program using it resides, beware that if you have an import lib of the dll then perhaps FB may link agains the import lib, have not tested this case, only made sure that there was no import lib, but the dll only.
Title: Re: José, if you're bored...
Post by: José Roca on August 06, 2018, 04:25:46 PM
Quote from: Paul Squires on August 06, 2018, 11:48:35 AM
I know that you use dynamic loading of the DLL functions but it would be cool if it was easy like PB.  :)

The only thing that comes to my mind is to prepare the .bi file as:


TYPE PFNSQLITE3LIBVERSIONNUMBERPROC AS FUNCTION CDECL () AS LONG
DIM SHARED sqlite3_libversion_number AS PFNSQLITE3LIBVERSIONNUMBERPROC
' ... more procedures

FUNCTION InitSqLite3Lib (BYREF wszDllPath AS WSTRING = "sqlite3.dll") AS HMODULE
   DIM hLib AS HMODULE
   hLib = LoadLibraryW(wszDllPath)
   IF hLib = NULL THEN RETURN NULL
   sqlite3_libversion_number = CAST(PFNSQLITE3LIBVERSIONNUMBERPROC, GetProcAddress(hLib, "sqlite3_libversion_number"))
   ' ... more procedures
   RETURN hLib
END FUNCTION


Then, to use it:


InitSqLite3Lib("sqlite3_32.dll")
print sqlite3_libversion_number()


If it has no parameters, the use of () is mandatory; otherwise, it will print the value of the sqlite3_libversion_number pointer instead of calling the sqlite3_libversion_number function.

Another possibility is to build the .dll as a low-level COM server and use a class instead of functions.

Of course, the best solution would be to modify the compiler to accept a LIB or IMPORT clause in the declare.
Title: Re: José, if you're bored...
Post by: José Roca on August 07, 2018, 11:15:04 AM
Some ideas for a printing class:

1) Methods to attach a printer: AttachPrinter and ChoosePrinter.

2) Properties to set printer values, such paper size, orientation, etc.

3) Pages can be constructed using a memory bitmap and drawn using GDI and/or GDI+ and passed to a method of the class that will send them to the printer.

Criticism?
Title: Re: José, if you're bored...
Post by: Paul Squires on August 08, 2018, 02:11:40 PM
Quote from: José Roca on August 07, 2018, 11:15:04 AM
Some ideas for a printing class:

1) Methods to attach a printer: AttachPrinter and ChoosePrinter.

2) Properties to set printer values, such paper size, orientation, etc.

3) Pages can be constructed using a memory bitmap and drawn using GDI and/or GDI+ and passed to a method of the class that will send them to the printer.

Criticism?


Sorry for my lateness. Yes, that is the kind of approach to a printing class that was in my mind also. Some basic properties to set elements of what is being printed, and then constructing the document in memory and output to the printer via api methods. Also being able to handle different fonts, colors, and graphics. It would be a great addition. Not sure that a Print Preview would be an absolute necessity but it would be cool also.
Title: Re: José, if you're bored...
Post by: Paul Squires on August 08, 2018, 02:13:23 PM
I just finished reviewing WinFBE code. There are 10 source code files that contain 22 references to FB related file handling routines. I will post a new WinFBE package tonight and then start with replacing the FB file handling routines with Jose's classes.
Title: Re: José, if you're bored...
Post by: José Roca on August 08, 2018, 02:17:00 PM
A first try.

The idea is to create an instance of the CPrint class and attach a printer to it with AttachPrinter or ChoosePrinter.


'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "Afx/CPrint.inc"
USING Afx

DIM pPrint AS CPrint
pPrint.AttachPrinter("Bullzip PDF Printer")


Then we can use the CMemBmp class to create a memory bitmap (with the dimensions returned by the GetHorizontalResolution and GetVerticalResolution methods of the CPrint class), draw its contents using GDI or GDI+ and send it to the PrintBitmap method.

For documents with more than one page, we could build an array of memory bitpamps and send it to a future new method of the CPrint class that will print them.

Title: Re: José, if you're bored...
Post by: José Roca on August 08, 2018, 02:31:50 PM
GDI+ would be one of the best options to draw the memory bitmap because you can use any kind of fonts and color, draw images and text, and also make it DPI aware (in this case PPI aware), getting the same results no matter the resolution of the printer. Of course, the users will have to learn to use GDI+, but with it you can go much more far than with the few XPRINT GDI methods to draw lines of boxes.

Title: Re: José, if you're bored...
Post by: José Roca on August 08, 2018, 02:49:09 PM
> Not sure that a Print Preview would be an absolute necessity but it would be cool also.

Because I'm a lazy Mediterranean enduring a very hot summer, I try to use more my mind that my fingers. Therefore, I'm thinking of Base64 encode these memory bitmaps, add them to a web page built on the fly and display them in the Preview of Internet Explorer (it can be called separately using COM without having to launch IE).
Title: Re: José, if you're bored...
Post by: José Roca on August 08, 2018, 03:25:05 PM
A pity that no one else participates. Later, when they will try to use the framework, they will be more lost than a prawn in the desert. There are so many wrappers that the only way is to practice with them little by little and step by step. Testing is also essential to bring to light possible bugs and make suggestions.
Title: Re: José, if you're bored...
Post by: SeaVipe on August 08, 2018, 04:14:36 PM
Hi José,
I've been working with CTime64 and I must say that it was a bit of a learning curve at first but once I had the basics figured out it all started to fall into place.
Initially WinFBE crashed repeatedly but that was just me not understanding what I was doing (not that I do now). I'm using snippets of code from a large FireFly project that utilizes Format() to display dates and time in a specific format like "08-Aug-2018" and "01:23:45". As suggested, I reviewed the Microsoft documentation on-line; the following (from the example) not only works but is much more elegant than my earlier code:
DIM ct AS CTime64

ct = ct.GetCurrentTime()

ct.Format("%Y-%m-%d")

I need print functions so that is my next challenge.
Thanks for all your hard work, José, this is really good stuff!
Title: Re: José, if you're bored...
Post by: Paul Squires on August 08, 2018, 04:22:50 PM
Quote from: José Roca on August 08, 2018, 02:49:09 PM
Because I'm a lazy Mediterranean enduring a very hot summer, I try to use more my mind that my fingers.

LOL, that made me smile :)
Title: Re: José, if you're bored...
Post by: Paul Squires on August 08, 2018, 04:26:20 PM
Quote from: José Roca on August 08, 2018, 02:17:00 PM
A first try.

The idea is to create an instance of the CPrint class and attach a printer to it with AttachPrinter or ChoosePrinter.


'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "Afx/CPrint.inc"
USING Afx

DIM pPrint AS CPrint
pPrint.AttachPrinter("Bullzip PDF Printer")


Then we can use the CMemBmp class to create a memory bitmap (with the dimensions returned by the GetHorizontalResolution and GetVerticalResolution methods of the CPrint class), draw its contents using GDI or GDI+ and send it to the PrintBitmap method.

For documents with more than one page, we could build an array of memory bitpamps and send it to a future new method of the CPrint class that will print them.

Holy smokes! Awesome! I have saved the class and started looking through it. I have access to a few different types of printers so I will test them to see how the class reacts. Great job  :)
Title: Re: José, if you're bored...
Post by: José Roca on August 08, 2018, 07:40:30 PM
Quote from: SeaVipe on August 08, 2018, 04:14:36 PM
Hi José,
I've been working with CTime64 and I must say that it was a bit of a learning curve at first but once I had the basics figured out it all started to fall into place.
Initially WinFBE crashed repeatedly but that was just me not understanding what I was doing (not that I do now). I'm using snippets of code from a large FireFly project that utilizes Format() to display dates and time in a specific format like "08-Aug-2018" and "01:23:45". As suggested, I reviewed the Microsoft documentation on-line; the following (from the example) not only works but is much more elegant than my earlier code:
DIM ct AS CTime64

ct = ct.GetCurrentTime()

ct.Format("%Y-%m-%d")

I need print functions so that is my next challenge.
Thanks for all your hard work, José, this is really good stuff!


This is what everybody interested in using my framework should do. You don't need to learn everything, but you should become familiar in what interests you for future use and try it.
Title: Re: José, if you're bored...
Post by: Paul Squires on August 08, 2018, 08:48:42 PM
I think that if people would just browse your library and then compare it to their own code they'll see that they have been missing out on a vast array of incredibly useful functionality. 

I bet that if a user took your library and did this one thing (this is what I did)....... convert their existing program to be fully unicode and high dpi aware..... then they'd instantly see the benefit of your library and the universality it opens up across the winapi and languages across the world. I will never write anything anymore that is not fully unicode by default nor high dpi aware. These are two core things that Jose himself and his code has taught me. The CWSTR dynamic unicode string class alone is worth its weight in gold.

Title: Re: José, if you're bored...
Post by: SeaVipe on August 08, 2018, 09:20:17 PM
Indeed,A few examples would be helpful. I spent hours trying to figure out how to get a handle to a bitmap just so I could try CPrint (which is going to be very useful). Looking forward to Print Preview.
I got stumped here:
    Dim as ???? h
    Dim as boolean b = pPrint.PrintBitmap ( _
        wsz, _
        h, _
        FALSE, _
        InterpolationModeHighQualityBicubic )

The above returns False as h is not defined.
Title: Re: José, if you're bored...
Post by: José Roca on August 08, 2018, 10:31:00 PM
CPrint is still preliminary work. I have made an small test using a PDF printer driver and the PrintBitmap method is not working well yet. It would have been a miracle if I had got everything right at the first try.


'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "Afx/CPrint.inc"
#INCLUDE ONCE "Afx/CMemBmp.inc"
USING Afx

DIM pPrint AS CPrint
pPrint.AttachPrinter("Bullzip PDF Printer")
DIM pMemBmp AS CMemBmp = CMemBmp("MyImage.jpg")
pPrint.PrintBitmap("Test", pMemBmp.GethBmp)

PRINT
PRINT "Press any key..."
SLEEP

Title: Re: José, if you're bored...
Post by: José Roca on August 08, 2018, 10:39:16 PM
This works, so the problem must be in CMemBmp when loading an image.


'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "Afx/CPrint.inc"
#INCLUDE ONCE "Afx/CMemBmp.inc"
USING Afx

DIM pPrint AS CPrint
pPrint.AttachPrinter("Bullzip PDF Printer")
DIM pMemBmp AS CMemBmp = CMemBmp(pPrint.GetHorizontalResolution, pPrint.GetVerticalResolution)
Rectangle pMemBmp.GetMemDC, 10, 10, 150, 150
LineTo pMemBmp.GetMemDC, 30, 180
pPrint.PrintBitmap("Test", pMemBmp.GethBmp)

PRINT
PRINT "Press any key..."
SLEEP

Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 12:13:54 AM
Not sure which is the problem with CMemBmp when loading an image from a file, but if you just want to load a file and print it, this works:


'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "Afx/CPrint.inc"
#INCLUDE ONCE "Afx/AfxGdiplus.inc"
USING Afx

AfxSetProcessDPIAware
DIM pPrint AS CPrint
pPrint.AttachPrinter("Bullzip PDF Printer")
DIM wszFileName AS WSTRING * MAX_PATH = ExePath & "\VEGA_PAZ_01.jpg"
pPrint.PrintBitmap("Test", AfxGdipBitmapFromFile(wszFileName))

PRINT
PRINT "Press any key..."
SLEEP

Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 02:27:41 AM
New file. I had forgot to delete the graphic object in the PrintBitmap method.
Title: Re: José, if you're bored...
Post by: Joerg B. on August 09, 2018, 08:36:00 AM
Hello José
Thank you for your tirelessness in taking up and implementing new ideas. What you do for the FreeBasic community is indescribable for me. Your WinFBX offers possibilities in programming for everyone, which are normally only available to professionals with corresponding experience. Maybe the feedback doesn't come in the crowd. Maybe it's because your level needs to be understood first. Well, I can't.
Thank you again.
Title: Re: José, if you're bored...
Post by: Paul Squires on August 09, 2018, 08:39:14 AM
I'm working on doing the conversion of the FB file handling routines to your stream classes. Not sure if I'll get to the print class today but I'll try.

Using the WinFBX help file, I'm thinking that you might need to explain what the HRESULT code is. I remember it from your COM programming but I'm not sure if others would readily identify it. Maybe put a hyperlink in the help file to a general page that describes HRESULT and lists some common codes like are specified at https://docs.microsoft.com/en-us/windows/desktop/seccrypto/common-hresult-values

I realize that your GetErrorInfo will return an empty string if no error but it is cleaner to do an inline error result check when opening a file. Like this:

   dim pStream AS CTextStream
   if pStream.OpenUnicode(wszFileName, IOMode_ForReading) <> S_OK then exit function

Maybe even if you indicate that the Return Value of Open/OpenUnicode is a non-zero HRESULT value. At least then users can do this:

   if pStream.OpenUnicode(wszFileName, IOMode_ForReading) <> 0 then exit function

Just a suggestion... as I think of stuff when I'm working through the conversion, I'll post them for you to consider.

Title: Re: José, if you're bored...
Post by: Paul Squires on August 09, 2018, 08:41:36 AM
Quote from: Joerg Buckel on August 09, 2018, 08:36:00 AM
Hello José
Thank you for your tirelessness in taking up and implementing new ideas. What you do for the FreeBasic community is indescribable for me. Your WinFBX offers possibilities in programming for everyone, which are normally only available to professionals with corresponding experience. Maybe the feedback doesn't come in the crowd. Maybe it's because your level needs to be understood first. Well, I can't.
Thank you again.

My sentiments exactly Joerg!

If José had not created all of this wonderful code then I'd most likely be a C++ or C# coder by now instead of sticking with BASIC that I love more.

Title: Re: José, if you're bored...
Post by: Paul Squires on August 09, 2018, 08:55:59 AM
CTextStream.Create

' - bOverwrite: Boolean value that indicates whether you can overwrite an existing file.
'   The value is true if the file can be overwritten, false if it can't be overwritten.
'   If omitted, existing files are not overwritten.

However, in the parameter list, bOverwrite defaults to TRUE (and not FALSE like implied by the source code comments and the Help file text).

PRIVATE FUNCTION CTextStream.Create (BYREF cbsFileName AS CBSTR, BYVAL bOverwrite AS BOOLEAN = TRUE, BYVAL bUnicode AS BOOLEAN = FALSE) AS HRESULT

Unless I'm reading this wrong, it looks like the source and help description is reverse of what it should be. For the record, I vote to keep the function declaration default as TRUE and just amend the help text.


Title: Re: José, if you're bored...
Post by: Paul Squires on August 09, 2018, 09:46:38 AM
CTextStream.Open

bUnicode: One of three Tristate values used to indicate the format of the opened file. If omitted, the file is opened as ASCII

That whole "Tristate" comment seems strange? The parameter is a true/false Boolean indicating if Unicode or not.

Title: Re: José, if you're bored...
Post by: Chris Maher on August 09, 2018, 09:48:00 AM
Hello José

A request for a small addition to CPrint.inc please.

I have added the lpszOutput Member of the DOCINFOW Structure to a test version of CPrint.inc and used it to create 'silent' output files from my 'Microsoft Print to PDF' printer on Windows 10.

It seems to work very well and would be very useful for all kinds of reports/bills linked to email or archive systems etc.

'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "Afx/CPrint.inc"
#INCLUDE ONCE "Afx/AfxGdiplus.inc"
USING Afx

AfxSetProcessDPIAware
DIM pPrint AS CPrint
pPrint.AttachPrinter("Microsoft Print to PDF")

DIM wszFileName AS WSTRING * MAX_PATH = ExePath & "\TestGraphic.jpg"
DIM wszOutput AS WSTRING * MAX_PATH = ExePath & "\TestFileName.pdf"

pPrint.PrintBitmap("Test Queue Name", wszOutput, AfxGdipBitmapFromFile(wszFileName))

PRINT
PRINT "Press any key..."
SLEEP


I also agree with both Joerg and Paul. These are excellent tools.

..and Paul's effort bringing all this together is truly amazing.

Many thanks to you both.

Regards,

Chris.
Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 12:26:17 PM
Using a memory bitmap doesn't seem a good idea after all because the results will depend of the resolution of the monitor and if your application is DPI aware.

The printing has to be done between StartDoc/EndDoc, StartPage/EndPage:


   StartDoc(hdcPrint, &docInfo);
   StartPage(hdcPrint);
      Graphics* graphics = new Graphics(hdcPrint);
      Pen* pen = new Pen(Color(255, 0, 0, 0));
      graphics->DrawLine(pen, 50, 50, 350, 550);
      graphics->DrawRectangle(pen, 50, 50, 300, 500);
      graphics->DrawEllipse(pen, 50, 50, 300, 500);
      delete pen;
      delete graphics;
   EndPage(hdcPrint);
   EndDoc(hdcPrint);


All graphics commands between StartDoc and EndDoc are routed to a temporary metafile. After the call to EndDoc, the printer driver converts the data in the metafile into the format required by the specific printer being used.

See: https://docs.microsoft.com/en-us/windows/desktop/gdiplus/-gdiplus-sending-gdi-output-to-a-printer-use

Also worth reading: Optimizing Printing by Providing a Printer Handle
https://docs.microsoft.com/en-us/windows/desktop/gdiplus/-gdiplus-optimizing-printing-by-providing-a-printer-handle-use

I don't have expertise in printing, so I'm learning.
Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 12:43:56 PM
Quote from: Paul Squires on August 09, 2018, 09:46:38 AM
CTextStream.Open

bUnicode: One of three Tristate values used to indicate the format of the opened file. If omitted, the file is opened as ASCII

That whole "Tristate" comment seems strange? The parameter is a true/false Boolean indicating if Unicode or not.

For the documentation, I do a lot of copy and paste and this leads to mistakes. The scarce documentation talks about a third available value, TriState_UseDefault (aka TriState_Mixed), but it is not very explanatory. It only says: "Opens the file using the system default."

I will change the documentation to remove the reference to TriState and only say that TRUE opens the file as unicode and FALSE as ASCII.

In fact, in the code source file I have it right:
bUnicode: TRUE or FALSE. Indicates the format of the opened file. If omitted, the file is opened as ASCII.

I will change it to:
bUnicode: Boolean value indicating the format of the opened file. True to open the file as unicode, False to open it as ASCII. If omitted, the file is opened as ASCII.
Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 01:39:06 PM
Quote from: Paul Squires on August 09, 2018, 08:55:59 AM
CTextStream.Create

' - bOverwrite: Boolean value that indicates whether you can overwrite an existing file.
'   The value is true if the file can be overwritten, false if it can't be overwritten.
'   If omitted, existing files are not overwritten.

However, in the parameter list, bOverwrite defaults to TRUE (and not FALSE like implied by the source code comments and the Help file text).

PRIVATE FUNCTION CTextStream.Create (BYREF cbsFileName AS CBSTR, BYVAL bOverwrite AS BOOLEAN = TRUE, BYVAL bUnicode AS BOOLEAN = FALSE) AS HRESULT

Unless I'm reading this wrong, it looks like the source and help description is reverse of what it should be. For the record, I vote to keep the function declaration default as TRUE and just amend the help text.

Yet another copy and paste problem. In the original interface, the default is False, but apparently I thought that it was better if it was True. I will remove the "not" from "If omitted, existing files are not overwritten."

Not only the code has to be tried, but the documentation has also to be reviewed.
Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 02:17:51 PM
Quote
A request for a small addition to CPrint.inc please.

I have added the lpszOutput Member of the DOCINFOW Structure to a test version of CPrint.inc and used it to create 'silent' output files from my 'Microsoft Print to PDF' printer on Windows 10.

It seems to work very well and would be very useful for all kinds of reports/bills linked to email or archive systems etc.

Interesting suggestion. This is why I want participation.

I have added a new method called PrintBitmapToFile. Works like the existing PrintBitmap method but instead of the document name you will pass the output file name.

BTW I wasn't aware of the existence of the "Microsoft Print to PDF" driver. I did not exist in my previous Windows 7 system. I will use it instead of Bullzip.
Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 02:52:44 PM
Quote from: Paul Squires on August 09, 2018, 08:39:14 AM
I'm working on doing the conversion of the FB file handling routines to your stream classes. Not sure if I'll get to the print class today but I'll try.

Using the WinFBX help file, I'm thinking that you might need to explain what the HRESULT code is. I remember it from your COM programming but I'm not sure if others would readily identify it. Maybe put a hyperlink in the help file to a general page that describes HRESULT and lists some common codes like are specified at https://docs.microsoft.com/en-us/windows/desktop/seccrypto/common-hresult-values

I realize that your GetErrorInfo will return an empty string if no error but it is cleaner to do an inline error result check when opening a file. Like this:

   dim pStream AS CTextStream
   if pStream.OpenUnicode(wszFileName, IOMode_ForReading) <> S_OK then exit function

Maybe even if you indicate that the Return Value of Open/OpenUnicode is a non-zero HRESULT value. At least then users can do this:

   if pStream.OpenUnicode(wszFileName, IOMode_ForReading) <> 0 then exit function

Just a suggestion... as I think of stuff when I'm working through the conversion, I'll post them for you to consider.

The problem with hyperlinks is that they die very often. Microsoft is moving all the documentation to Microsoft Docs and it is a mess until they finish it, since the documentation for many of the parameters is still lacking and replaced with TBD.

Besides, the help file is intended as a reference, not as a tutorial. We have also to explain what is a bitmap, a handle, a stream...?

Error Handling in COM:
https://docs.microsoft.com/en-us/windows/desktop/com/error-handling-in-com

Structure of COM Error Codes
https://docs.microsoft.com/en-us/windows/desktop/com/structure-of-com-error-codes

I doubt that a novice to COM will understand anything posted in these links.

Maybe I can change "An HRESULT code" to "S_OK (0) on success, or an HRESULT error code on failure."

The GetErrorInfo function is only intended to provide a little help because, apart from some standard HRESULT codes, they aren't unique, i.e. the same HRESULT can mean different things depending of the COM server that returns it. In the AfxCOM.inc file, I provide the AfxGetOleErrorInfo function, that returns a description (sometimes localized) of the error, but unfortunately it will only work if the COM server supports the IErrorInfo interface, and most don't.

That said, any suggestion to improve the documentation is welcome. Currently it has more than four thousand entries and it must contain many mistakes and many things should be better explained.
Title: Re: José, if you're bored...
Post by: Paul Squires on August 09, 2018, 03:23:31 PM
QuoteMaybe I can change "An HRESULT code" to "S_OK (0) on success, or an HRESULT error code on failure."

I agree with you regarding links and explanations, and I think the short sentence above is sufficient. :)
Title: Re: José, if you're bored...
Post by: Paul Squires on August 09, 2018, 03:31:29 PM
Hi José,

Can you run the following code? I get a GPF when the code finishes. A problem with the DESTRUCTOR maybe?

Quote
#include once "\afx\AfxFile.inc"

dim pStream as CFileStream

if pStream.Open("_binary.txt", STGM_CREATE or STGM_WRITE) = S_OK then
   'dim as string pBuffer = string(65, 10)
   'pStream.Write strptr(pBuffer), len(pBuffer)
   'pStream.Close
end if

I had no problems at all with the CTextStream class. I was able to convert every instance of FB file handling commands over to CTextStream without any trouble at all. I have two areas in WinFBE that deal with binary files so I tried to change that code to use CFileStream. I noticed that it correctly created the file with correct contents but it would GPF the editor. I then wrote a simple test file (the code I posted above) and that code also GPF's. Maybe I am not correctly understand the CFileStream syntax.

Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 03:58:16 PM
Something as stupid as forgetting to set m_pStream to NULL.


' ========================================================================================
' Releases the stream object.
' ========================================================================================
PRIVATE SUB CFileStream.Close
   ' // Release the IStrean interface
   IF m_pStream THEN m_pStream->lpvtbl->Release(m_pStream)
   m_pStream = NULL
   m_Result = 0
END SUB
' ========================================================================================
Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 05:54:03 PM
Well, I have succesfully written a working test.


'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "Afx/CPrint.inc"
#INCLUDE ONCE "Afx/CGdiplus/CGdiplus.inc"
USING Afx

DIM pPrint AS CPrint
pPrint.AttachPrinter("Microsoft Print to PDF")
DIM hdcPrint AS HDC = pPrint.GetDC
DIM docInfo AS DOCINFOW
StartDocW(hdcPrint, @docInfo)
StartPage(hdcPrint)
SCOPE
   DIM graphics AS CGpGraphics = hdcPrint
   DIM pen AS CGpPen = GDIP_ARGB(255, 0, 0, 0)
   graphics.DrawLine(@pen, 50, 50, 350, 550)
   graphics.DrawRectangle(@pen, 50, 50, 300, 500)
   graphics.DrawEllipse(@pen, 50, 50, 300, 500)
END SCOPE
EndPage(hdcPrint)
EndDoc(hdcPrint)

PRINT
PRINT "Press any key..."
SLEEP


It's time to learn how to use my GDI+ classes :) Fortunately, there are several hundred examples available.

Title: Re: José, if you're bored...
Post by: Paul Squires on August 09, 2018, 06:04:47 PM
That fix didn't seem to stop the GPF.... I will check more tonight but the sample code I posted seems to GPF even after the m_pStream = NULL fix.
Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 06:22:45 PM
It works in my computer. Try adding using Afx after #include once "\afx\AfxFile.inc", although in my computer it works even without adding it.


'#CONSOLE ON
#define UNICODE
#include once "\afx\AfxFile.inc"
using Afx

dim pStream as CFileStream

if pStream.Open("_binary.txt", STGM_CREATE or STGM_WRITE) = S_OK then
   dim as string pBuffer = string(65, 10)
   pStream.Write strptr(pBuffer), len(pBuffer)
   pStream.Close
end if

PRINT
PRINT "Press any key..."
SLEEP

Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 06:35:29 PM
Seems that the problem happens when compiling with 64 bit. I will check it. It works with 32 bit.
Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 07:14:25 PM
It fails with 64 bit if you use the -O switch. Without it, it works fine.
Title: Re: José, if you're bored...
Post by: Paul Squires on August 09, 2018, 08:08:24 PM
Quote from: José Roca on August 09, 2018, 07:14:25 PM
It fails with 64 bit if you use the -O switch. Without it, it works fine.

That is interesting. It was failing for me in 32-bit because I am using additional compiler switches " -gen gcc -Wc -O2 "
Strange that the -O switch causes it to fail in both 32 and 64 bit.
Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 08:17:47 PM
These gcc optimizations seem dangerous. Next time, don't forget to post which switches are you using or we will end with you saying "It GPF's" and I saying "It works". If at least there was a way to disable optimizations in parts of the code...


Title: Re: José, if you're bored...
Post by: Paul Squires on August 09, 2018, 08:29:44 PM
I have removed the optimization switch from all of my compiles. I fear though that others may fall into this trap if they use CFileStream with -O as I've seen the -O switch being talked about a lot and somewhat recommended on the forums (well, at least the lower number -O optimizations).
Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 08:56:44 PM
As I never have used the O switch to test my code, I don't know if other parts will fail. It has been a happy coincidence that I had the -O 2 switch added in the 64 bit build configurations; otherwise, I will be still scratching my head. I remember that I did it to try the difference on size.
Title: Re: José, if you're bored...
Post by: Johan Klassen on August 09, 2018, 08:58:14 PM
Quote from: José Roca on August 09, 2018, 08:17:47 PM
... If at least there was a way to disable optimizations in parts of the code...
I agree, gcc already has pragmas to turn optimization on/off, so it seems feasible to add to fbc, actually I made a feature request https://www.freebasic.net/forum/viewtopic.php?p=225167
but sadly it got no attention.
Title: Re: José, if you're bored...
Post by: Johan Klassen on August 09, 2018, 09:05:56 PM
hello José Roca
please see this post https://www.freebasic.net/forum/viewtopic.php?p=249654#p249654
Quote from: deltarho[1859]In the opening post replace
dim as ulongint i,j
with
dim as ulongint i
dim shared as ulongint j

and the loop is no longer optimized out.

Compiled with -gen gcc -Wc -O3
is it possible to perhaps use key shared variables to trick gcc in not over optimizing?
Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 09:16:32 PM
I'm not using loops. Just calls to the methods of the IStream interface. I have no idea why the O switch is messing it.
Title: Re: José, if you're bored...
Post by: Johan Klassen on August 09, 2018, 10:28:47 PM
I can't get your example http://www.planetsquires.com/protect/forum/index.php?topic=4191.msg32153#msg32153 to crash, am using WinFBE 1.7.3, build options -s gui -gen gcc -Wc -O2, tried both 32 and 64-bit
Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 11:26:07 PM
Hi Paul,

If I rem the line DyLibFree(pLib) in this function, located at the top of CStream.inc, it works with 64 bit and the -O switch. Why? I have no idea.


' ========================================================================================
' Opens or creates a file and retrieves a stream to read or write to that file.
' ========================================================================================
PRIVATE FUNCTION AfxSHCreateStreamOnFileEx (BYVAL pwszFile AS WSTRING PTR, BYVAL grfMode AS DWORD, _
BYVAL dwAttributes AS DWORD, BYVAL fCreate AS WINBOOL, BYVAL ppStream AS IStream PTR PTR) AS HRESULT
   DIM AS ANY PTR pLib = DyLibLoad("shlwapi.dll")
   IF pLib = NULL THEN EXIT FUNCTION
   DIM pShCreateStreamOnFileEx AS FUNCTION (BYVAL pwszFile AS WSTRING PTR, BYVAL grfMode AS DWORD, BYVAL dwAttributes AS DWORD, _
       BYVAL fCreate AS WINBOOL, BYVAL pstmTemplate AS IStream PTR, BYVAL ppStream AS IStream PTR PTR) AS HRESULT
   pShCreateStreamOnFileEx = DyLibSymbol(pLib, "SHCreateStreamOnFileEx")
   IF pShCreateStreamOnFileEx = NULL THEN EXIT FUNCTION
   DIM hr AS HRESULT = pShCreateStreamOnFileEx(pwszFile, grfMode, dwAttributes, fCreate, NULL, ppStream)
'   DyLibFree(pLib)
   RETURN hr
END FUNCTION
' ========================================================================================

Title: Re: José, if you're bored...
Post by: José Roca on August 09, 2018, 11:58:43 PM
I have slightly modified the function changing two EXIT FUNCTION to RETURN E_POINTER. If I use EXIT FUNCTION it will return 0, that can be interpreted as success.


' ========================================================================================
' Opens or creates a file and retrieves a stream to read or write to that file.
' ========================================================================================
PRIVATE FUNCTION AfxSHCreateStreamOnFileEx (BYVAL pwszFile AS WSTRING PTR, BYVAL grfMode AS DWORD, _
BYVAL dwAttributes AS DWORD, BYVAL fCreate AS WINBOOL, BYVAL ppStream AS IStream PTR PTR) AS HRESULT
   DIM AS ANY PTR pLib = DyLibLoad("shlwapi.dll")
   IF pLib = NULL THEN RETURN E_POINTER
   DIM pShCreateStreamOnFileEx AS FUNCTION (BYVAL pwszFile AS WSTRING PTR, BYVAL grfMode AS DWORD, BYVAL dwAttributes AS DWORD, _
       BYVAL fCreate AS WINBOOL, BYVAL pstmTemplate AS IStream PTR, BYVAL ppStream AS IStream PTR PTR) AS HRESULT
   pShCreateStreamOnFileEx = DyLibSymbol(pLib, "SHCreateStreamOnFileEx")
   IF pShCreateStreamOnFileEx = NULL THEN RETURN E_POINTER
   DIM hr AS HRESULT = pShCreateStreamOnFileEx(pwszFile, grfMode, dwAttributes, fCreate, NULL, ppStream)
   ' For some reason, if we free the library, calling the methods that use the returned
   ' IStream pointer will GPF if compiled with the -O switch.
'   DyLibFree(pLib)
   RETURN hr
END FUNCTION
' ========================================================================================

Title: Re: José, if you're bored...
Post by: José Roca on August 10, 2018, 02:07:14 AM
Modified again to check if the library is already loaded to avoid multiple loadings.


' ========================================================================================
' Opens or creates a file and retrieves a stream to read or write to that file.
' ========================================================================================
PRIVATE FUNCTION AfxSHCreateStreamOnFileEx (BYVAL pwszFile AS WSTRING PTR, BYVAL grfMode AS DWORD, _
BYVAL dwAttributes AS DWORD, BYVAL fCreate AS WINBOOL, BYVAL ppStream AS IStream PTR PTR) AS HRESULT
   ' // See if the library is already loaded in the address space
   DIM AS ANY PTR pLib = GetModuleHandleW("shlwapi.dll")
   ' // If it is not loaded, load it
   IF pLib = NULL THEN pLib = DyLibLoad("shlwapi.dll")
   IF pLib = NULL THEN RETURN E_POINTER
   DIM pShCreateStreamOnFileEx AS FUNCTION (BYVAL pwszFile AS WSTRING PTR, BYVAL grfMode AS DWORD, BYVAL dwAttributes AS DWORD, _
       BYVAL fCreate AS WINBOOL, BYVAL pstmTemplate AS IStream PTR, BYVAL ppStream AS IStream PTR PTR) AS HRESULT
   pShCreateStreamOnFileEx = DyLibSymbol(pLib, "SHCreateStreamOnFileEx")
   IF pShCreateStreamOnFileEx = NULL THEN RETURN E_POINTER
   DIM hr AS HRESULT = pShCreateStreamOnFileEx(pwszFile, grfMode, dwAttributes, fCreate, NULL, ppStream)
   ' For some reason, if we free the library, calling the methods that use the returned
   ' IStream pointer will GPF if compiled with the -O switch.
'   DyLibFree(pLib)
   RETURN hr
END FUNCTION
' ========================================================================================

Title: Re: José, if you're bored...
Post by: Chris Maher on August 10, 2018, 04:37:42 AM
Hi José,

CPrint.PrintBitmapToFile is working fine for the 'Microsoft Print to PDF' printer, but it needs the lpszDocName member available too to be able to view, control and debug the print queue.

Thanks.
Title: Re: José, if you're bored...
Post by: José Roca on August 10, 2018, 06:57:21 AM
Added a parameter for the document name.
Title: Re: José, if you're bored...
Post by: Chris Maher on August 10, 2018, 08:58:50 AM
Thank you. That amendment to PrintBitmapToFile works perfectly.

QuoteSee: https://docs.microsoft.com/en-us/windows/desktop/gdiplus/-gdiplus-sending-gdi-output-to-a-printer-use

Also worth reading: Optimizing Printing by Providing a Printer Handle
https://docs.microsoft.com/en-us/windows/desktop/gdiplus/-gdiplus-optimizing-printing-by-providing-a-printer-handle-use

I have been looking at the links to GDI+ that you posted. I have never used it before but it all seems familiar as most of my work in this area was via PB's GRAPHIC and XPRINT commands.

My knowledge of C and FB is a bit limited as yet to be able to convert the examples they give across to WinFBE/WinFBX but I'll keep trying and look at your templates and examples. Your various individual include files (coupled with the WinFBX Help file) are also a key learning resource. I am hoping to work through them one at a time as there is a lot to learn and I try to understand the code that I call.

Chris.
Title: Re: José, if you're bored...
Post by: José Roca on August 11, 2018, 01:34:00 AM
I think that the use of GDI+ to do the drawing is one of the best options (certainly much better that the old GDI). I have been doing some tests and if using the default UnitWorld as the unit of measure, we can use the same coordinates no matter of the printer resolution. We can use other units of measure, such pixels, points, millimeters or inches, but then we will have to do scaling because the number of the units change with the resolution, whereas the world units remain the same. Therefore, I have added the GetHorizontalUnits and GetVerticalUnits methods to retrieve the number of units available for printing. I also have added PixelsToPointsX/Y and PointsToPixelsX/Y that perhaps we may need to work with fonts.

So, we can use the CPrint class to attach/choose a printer, with properties to get/set the printer values and also a method, PageSetup, to call the Page Setup Dialog if needed, and we can use GDI+ to do all the drawing. Although we can use the GDI+ Flat API, the GDI+ classes of WinFBX are more convenient.
Title: Re: José, if you're bored...
Post by: Chris Maher on August 11, 2018, 04:58:37 AM
That all looks good and shows direct examples of how you have translated and encapsulated the Microsoft GDI+ printer C code within FB. It really highlights the value of your classes as robust building blocks that remove the clutter and complexity without losing speed and control.

I agree the world units are really important to be device independent. I am looking forward to playing with it.
Title: Re: José, if you're bored...
Post by: José Roca on August 12, 2018, 06:09:32 AM
I have made some small changes to the CPrint class and I have made a try at using GitHub and markdown for the documentation. Tell me what you think.

See: https://github.com/JoseRoca/WinFBX/blob/master/docs/Printing/CPrint%20Class.md

The advantage is that the documentation can be reviewed as soon as changes are made. without having to wait to an updated help file. If you find mistakes or have suggestions to improve it, please do it.
Title: Re: José, if you're bored...
Post by: Chris Maher on August 12, 2018, 06:16:09 PM
I like this GitHub and Markdown idea because it provides the ability to understand the class as a whole, as you are developing it, without initially having to decipher the code. Perhaps the uptake and response will be easier for those of us that are less used to the complexities that you are dealing with within each class.

For me it is about keying in a few lines of code and seeing what happens. This really helps with that. Code examples are good, but initially it's hard to absorb someone else's keystrokes and style.

Once it is understood then Paul's WinFBE will really make it fly!
Title: Re: José, if you're bored...
Post by: José Roca on August 12, 2018, 11:10:10 PM
I have documented the other new class, CFileStream. As writing the documentation gives us the opportunity to revise the code, I have made some small changes and removed the Commit and Revert methods that are of no use unless we work with streams inside an storage file. New CStream.zip file attached.

I plan to document the available file functions and add some that are missing, such AfxCopyFile, AfxDeleteFile, AfxReplaceFile, AfxMoveFile, AfxCreateFolder or AfxDeleteFolder.

CFileStream documentation: https://github.com/JoseRoca/WinFBX/blob/master/docs/CFileStream%20Class.md
Title: Re: José, if you're bored...
Post by: José Roca on August 13, 2018, 04:31:04 AM
CTextStream Class documentation:
https://github.com/JoseRoca/WinFBX/blob/master/docs/CTextStream%20Class.md
Title: Re: José, if you're bored...
Post by: José Roca on August 13, 2018, 09:54:16 AM
This is a replacement for the FB's FileDateTime function, that uses the obsolete Visual Basic's DATE format.


' ========================================================================================
' * Unicode replacement for Free Basic's FileDateTime.
' Returns the file's last modified date and time as Date Serial.
' wszFileName : Filename to retrieve date and time for.
' Return Value : Returns a Date Serial.
' Example:
' #include "windows.bi"
' #include "vbcompat.bi"
' #include "win/ole2.bi"
' DIM wszFileName AS WSTRING * MAX_PATH = ExePath & "\test.bas"
' DIM dt AS DOUBLE = AfxFileDateTime(wszFileName)
' PRINT Format(dt, "yyyy-mm-dd hh:mm AM/PM")
' ========================================================================================
PRIVATE FUNCTION AfxFileDateTime (BYREF wszFileName AS WSTRING) AS DOUBLE
   DIM fd AS WIN32_FIND_DATAW
   DIM hFind AS HANDLE = FindFirstFileW(wszFileName, @fd)
   IF hFind = INVALID_HANDLE_VALUE THEN RETURN 0
   FindClose hFind
   DIM ft AS FILETIME
   FileTimeToLocalFileTime(@fd.ftLastWriteTime, @ft)
   DIM st AS SYSTEMTIME
   FileTimeToSystemTime(@ft, @st)
   DIM dt AS DOUBLE
   SystemTimeToVariantTime @st, @dt
   RETURN dt
END FUNCTION
' ========================================================================================


FileLen replaced with
#define AfxFileLen AfxGetFileSize
Title: Re: José, if you're bored...
Post by: José Roca on August 13, 2018, 10:29:45 AM
AfxChDir: Replacement for Free Basic's ChDir.


' ========================================================================================
' Changes the current directory for the current process.
' lpPathName : The path to the new current directory. This parameter may specify a relative
'              path or a full path. In either case, the full path of the specified directory
'              is calculated and stored as the current directory.
' Return value:
'   If the function succeeds, the return value is TRUE.
'   If the function fails, the return value is FALSE.
'   To get extended error information, call GetLastError.
' ========================================================================================
PRIVATE FUNCTION AfxSetCurDir (BYVAL pwszPathName AS LPCWSTR) AS BOOLEAN
   RETURN SetCurrentDirectory(pwszPathName)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Unicode replacement for Free Basic's ChDir.
' Return value: Returns 0 on success, or -1 on failure.
' ========================================================================================
PRIVATE FUNCTION AfxChDir (BYVAL pwszPathName AS LPCWSTR) AS BOOLEAN
   RETURN NOT SetCurrentDirectory(pwszPathName)
END FUNCTION
' ========================================================================================

Title: Re: José, if you're bored...
Post by: José Roca on August 13, 2018, 11:12:22 AM
Unicode replacements for Environ and SetEnviron.


' ========================================================================================
' Retrieves the contents of the specified variable from the environment block of the
' calling process.
' - pwszName : The name of the environment variable.
' Return value: The contents of the specified environment variable.
' Example: DIM cws AS CWSTR = AfxEnviron("path")
' ========================================================================================
PRIVATE FUNCTION AfxEnviron (BYVAL pwszName AS LPCWSTR) AS CWSTR
   DIM wszBuffer AS WSTRING * 32767
   DIM cb AS DWORD = GetEnvironmentVariable(pwszName, @wszBuffer, 32767)
   RETURN LEFT(wszBuffer, cb)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Sets the contents of the specified environment variable for the current process.
' - pwszName : The name of the environment variable.
'              The name of the environment variable.
'              The operating system creates the environment variable if it does not exist
'              and pwszValue is not NULL.
' - pszValue : The contents of the environment variable.
'              The maximum size of a user-defined environment variable is 32,767 characters.
' Return value:
'   If the function succeeds, the return value is TRUE.
'   If the function fails, the return value is FALSE.
'   To get extended error information, call GetLastError.
' Example: AfxSetEnviron("path", "c:")
' ========================================================================================
PRIVATE FUNCTION AfxSetEnviron OVERLOAD (BYVAL pwszName AS LPCWSTR, BYVAL pwszValue AS LPCWSTR) AS BOOLEAN
   RETURN SetEnvironmentVariable(pwszName, pwszValue)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Sets the contents of the specified environment variable for the current process.
' - varexpr = Name and setting of an environment variable in the following (or equivalent)
'   form: varname=varstring. (varname being the name of the environment variable, and
'   varstring being its text value to set).
' Returns 0 on success, or -1 on failure.
' Example: AfxSetEnviron "path=c:"
' ========================================================================================
PRIVATE FUNCTION AfxSetEnviron OVERLOAD (BYREF varexp AS WSTRING) AS BOOLEAN
   DIM cwsName AS CWSTR = AfxStrExtract(1, varexp, "=")
   IF LEN(cwsName) = 0 THEN RETURN TRUE
   cwsName = TRIM(**cwsName)
   DIM cwsValue AS CWSTR
   DIM p AS LONG = INSTR(varexp, "=")
   IF p = 0 THEN RETURN TRUE
   cwsValue = MID(varexp, p + 1)
   cwsValue = TRIM(**cwsValue)
   IF LEN(cwsValue) = 0 THEN RETURN TRUE
   RETURN NOT SetEnvironmentVariable(cwsName, cwsValue)
END FUNCTION
' ========================================================================================


For Kill, #define AfxKill AfxDeleteFile.
Title: Re: José, if you're bored...
Post by: José Roca on August 13, 2018, 11:33:48 AM
Replacement for Free Basic's MkDir.


' ========================================================================================
' Creates a new directory.
' - lpPathName : The path of the directory to be created.
'   To extend the limit to 32,767 wide characters, prepend "\?" to the path.
' Return value:
'   If the function succeeds, the return value is TRUE.
'   If the function fails, the return value is FALSE.
'   To get extended error information, call GetLastError.
'   Possible errors include the following.
'   - ERROR_ALREADY_EXISTS
'     The specified directory already exists.
'   - ERROR_PATH_NOT_FOUND
'     One or more intermediate directories do not exist; this function will only create
'     the final directory in the path.
' ========================================================================================
PRIVATE FUNCTION AfxCreateDirectory (BYVAL lpPathName AS LPCWSTR) AS BOOLEAN
   RETURN CreateDirectory(lpPathName, NULL)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Returns 0 on success, or -1 on failure.
' ========================================================================================
PRIVATE FUNCTION AfxMkDir (BYVAL lpPathName AS LPCWSTR) AS BOOLEAN
   RETURN NOT CreateDirectory(lpPathName, NULL)
END FUNCTION
' ========================================================================================

Title: Re: José, if you're bored...
Post by: José Roca on August 13, 2018, 12:05:57 PM
FreeBasic's replacement for RmDir.


' ========================================================================================
' Deletes an existing empty directory.
' - lpPathName : The path of the directory to be removed. This path must specify an empty
'   directory, and the calling process must have delete access to the directory.
' To extend the MAX_PATH limit to 32,767 wide characters prepend "\?" to the path.
' If the function succeeds, the return value is TRUE.
' If the function fails, the return value is FALSE.
' To get extended error information, call GetLastError.
' The RemoveDirectory function marks a directory for deletion on close. Therefore, the
' directory is not removed until the last handle to the directory is closed.
' To recursively delete the files in a directory, use the SHFileOperation function.
' RemoveDirectory removes a directory junction, even if the contents of the target are not
' empty; the function removes directory junctions regardless of the state of the target object.
' ========================================================================================
PRIVATE FUNCTION AfxRemoveDir (BYVAL lpPathName AS LPCWSTR) AS BOOLEAN
   RETURN RemoveDirectoryW(lpPathName)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Unicode replacement for Free Basic's RmDir.
' Returns 0 on success and -1 on failure.
' ========================================================================================
PRIVATE FUNCTION AfxRmDir (BYVAL lpPathName AS LPCWSTR) AS BOOLEAN
   RETURN NOT RemoveDirectoryW(lpPathName)
END FUNCTION
' ========================================================================================

Title: Re: José, if you're bored...
Post by: José Roca on August 13, 2018, 12:24:53 PM
Replacement for Free Basic's function FileCopy.


' ========================================================================================
' Copies an existing file to a new file.
' - lpExistingFileName : The name of an existing file.
'   To extend the limit of MAX_PATH characters to 32,767 wide characters prepend "\?" to the path.
'   If lpExistingFileName does not exist, CopyFile fails, and GetLastError returns ERROR_FILE_NOT_FOUND.
' - lpNewFileName : The name of the new file.
'   To extend the limit of MAX_PATH characters to 32,767 wide characters prepend "\?" to the path.
' bFailIfExists
' If this parameter is TRUE and the new file specified by lpNewFileName already exists, the
' function fails. If this parameter is FALSE and the new file already exists, the function
' overwrites the existing file and succeeds.
' If the function succeeds, the return value is nonzero.
' If the function fails, the return value is FALSE. To get extended error information, call GetLastError.
' ========================================================================================
PRIVATE FUNCTION AfxCopyFile (BYVAL lpExistingFileName AS LPCWSTR, BYVAL lpNewFileName AS LPCWSTR, BYVAL bFailIfExists AS BOOLEAN = FALSE) AS BOOLEAN
   RETURN CopyFile(lpExistingFileName, lpNewFileName, bFailIfExists)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Unicode replacement for Free Basic's FileCopy.
' Returns 0 on success, or 1 if an error occurred.
' ========================================================================================
PRIVATE FUNCTION AfxFilecopy (BYVAL lpExistingFileName AS LPCWSTR, BYVAL lpNewFileName AS LPCWSTR) AS LONG
   DIM nRet AS LONG = IIF(CopyFile(lpExistingFileName, lpNewFileName, FALSE) = 0, 1, 0)
   RETURN nRet
END FUNCTION
' ========================================================================================

Title: Re: José, if you're bored...
Post by: José Roca on August 13, 2018, 12:40:01 PM
Unicode replacement for Free Basic's Name function.


' ========================================================================================
' Moves an existing file or a directory, including its children.
' - lpExistingFileName : The name of an existing file.
'   To extend the limit of MAX_PATH characters to 32,767 wide characters prepend "\?" to the path.
'   If lpExistingFileName does not exist, CopyFile fails, and GetLastError returns ERROR_FILE_NOT_FOUND.
' - lpNewFileName : The name of the new file.
'   To extend the limit of MAX_PATH characters to 32,767 wide characters prepend "\?" to the path.
' Return value:
'   If the function succeeds, the return value is nonzero.
'   If the function fails, the return value is FALSE. To get extended error information, call GetLastError.
' ========================================================================================
PRIVATE FUNCTION AfxMoveFile (BYVAL lpExistingFileName AS LPCWSTR, BYVAL lpNewFileName AS LPCWSTR) AS BOOLEAN
   RETURN MoveFile(lpExistingFileName, lpNewFileName)
END FUNCTION
' ========================================================================================
' ========================================================================================
PRIVATE FUNCTION AfxRenameFile (BYVAL lpExistingFileName AS LPCWSTR, BYVAL lpNewFileName AS LPCWSTR) AS BOOLEAN
   RETURN MoveFile(lpExistingFileName, lpNewFileName)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Unicode replacement for Free Basic's Name function.
' Returns 0 on success, or non-zero on failure.
' ========================================================================================
PRIVATE FUNCTION AfxName (BYVAL lpExistingFileName AS LPCWSTR, BYVAL lpNewFileName AS LPCWSTR) AS LONG
   RETURN NOT AfxMoveFile(lpExistingFileName, lpNewFileName)
END FUNCTION
' ========================================================================================

Title: Re: José, if you're bored...
Post by: José Roca on August 13, 2018, 12:44:22 PM
If I'm not wrong, we already have unicode replacements for all the FB file functions.

Curiously, the FB versions return inverse values that the API ones for success and failure.
Title: Re: José, if you're bored...
Post by: José Roca on August 13, 2018, 03:36:06 PM
Changed the functions AfxWindowsVersion and AfxWindowsBuild to work with Windows 10. AfxWindowsVersion will return 1000 as the version number, meaning 10.0.


' ========================================================================================
' Returns the Windows version
' Platform 1:
'   400 Windows 95
'   410 Windows 98
'   490 Windows ME
' Platform 2:
'   400 Windows NT
'   500 Windows 2000
'   501 Windows XP
'   502 Windows Server 2003
'   600 Windows Vista and Windows Server 2008
'   601 Windows 7
'   602 Windows 8
'   603 Windows 8.1
'  1000 Windows 10
' Note: As Windows 95 and Windows NT return the same version number, we also need to call
' GetWindowsPlatform to differentiate them.
' ========================================================================================
'PRIVATE FUNCTION AfxWindowsVersion () AS LONG
'   DIM dwVersion AS DWORD
'   DIM AS LONG nMajorVer, nMinorVer
'   dwVersion = GetVersion
'   nMajorVer = LOBYTE(LOWORD(dwVersion))
'   nMinorVer = HIBYTE(LOWORD(dwVersion))
'   FUNCTION = (nMajorVer + nMinorVer / 100) * 100
'END FUNCTION
' ========================================================================================
' ========================================================================================
' GetVersion returns version 6.02 (Windows 8) in Windows 10 if the application has not a
' manifest.
' ========================================================================================
PRIVATE FUNCTION AfxWindowsVersion () AS LONG
   DIM hLib AS HMODULE = LoadLibraryW("NtDll.dll")
   IF hLib = NULL THEN EXIT FUNCTION
   DIM pfnRtlGetVersion AS FUNCTION (BYVAL lpVersionInformation AS OSVERSIONINFOEXW PTR) AS LONG
   pfnRtlGetVersion = CAST(ANY PTR, GetProcAddress(hLib, "RtlGetVersion"))
   IF pfnRtlGetVersion = NULL THEN EXIT FUNCTION
   DIM osverinfo AS OSVERSIONINFOEXW
   IF pfnRtlGetVersion(@osverinfo) = 0 THEN   ' STATUS_SUCCESS
      FUNCTION = (osverinfo.dwMajorVersion + osverinfo.dwMinorVersion / 100) * 100
   END IF
   FreeLibrary hLib
END FUNCTION
' ========================================================================================



' ========================================================================================
' Returns the Windows build
' ========================================================================================
'PRIVATE FUNCTION AfxWindowsBuild () AS LONG
'   DIM dwVersion AS DWORD
'   dwVersion = GetVersion
'   IF dwVersion < &H80000000 THEN FUNCTION = HIWORD(dwVersion)
'END FUNCTION
' ========================================================================================
' ========================================================================================
PRIVATE FUNCTION AfxWindowsBuild () AS LONG
   DIM hLib AS HMODULE = LoadLibraryW("NtDll.dll")
   IF hLib = NULL THEN EXIT FUNCTION
   DIM pfnRtlGetVersion AS FUNCTION (BYVAL lpVersionInformation AS OSVERSIONINFOEXW PTR) AS LONG
   pfnRtlGetVersion = CAST(ANY PTR, GetProcAddress(hLib, "RtlGetVersion"))
   IF pfnRtlGetVersion = NULL THEN EXIT FUNCTION
   DIM osverinfo AS OSVERSIONINFOEXW
   IF pfnRtlGetVersion(@osverinfo) = 0 THEN   ' STATUS_SUCCESS
      FUNCTION = osverinfo.dwBuildNumber
   END IF
   FreeLibrary hLib
END FUNCTION
' ========================================================================================

Title: Re: José, if you're bored...
Post by: Chris Maher on August 15, 2018, 05:11:30 AM
Hi José,

Unless I am reading it wrong the following Methods in CPrint are being blocked by this initial m_hDC check:
IF m_hDC THEN RETURN 0

GetHorizontalUnits
GetVerticalUnits
PixelsToPointsX
PixelsToPointsY
PointsToPixelsX
PointsToPixelsY

Also a minor typo on the GitHub documentation "GetDC - Returns the name of the attached printer."
Title: Re: José, if you're bored...
Post by: José Roca on August 15, 2018, 08:08:33 AM
Yes, it should be IF m_hDC = NULL THEN RETURN 0. I noticed that there was not check and added one (unfortunately wrong) and copy pasted it to the other functions. These are the kind of silly mistakes that I'm trying to avoid by having the code being checked by others.

I also have modified the GtHub documentation, which I plan to arrange by groups:
https://github.com/JoseRoca/WinFBX/tree/master/docs

Thanks very much. It is the first time that somebody, except Paul, has collaborated. GitHub it's a great way to keep the code and documentation up to date. I have added to the File Management group the documentation for CFindFile, CFileStream and CTextStream, and I'm preparing the one for CFileSys. I also have added to the Strings group documentation for the String Procedures. Later, I will add documentation for CWSTR and CBSTR.


Title: Re: José, if you're bored...
Post by: José Roca on August 15, 2018, 08:37:09 AM
The available Free Basic classes, implemented as types, are fast and lightweight, yet powerful because you can have several constructors and overloading of methods and operators. The available classes in WinFBX are a great way to learn how to use them. True classes will support interfaces. Now, is like we had a class with only one interface, but are a great way to encapsulate code and make it reusable. I'm very happy with them. They allow to make complex things simpler to use.
Title: Re: José, if you're bored...
Post by: José Roca on August 15, 2018, 11:26:39 AM
I'm making changes to CFileSys and CTextStream to allow to work with the standard StdIn, StdErr and StdOut using the methods of the CTextStream class.

First working test:


'#CONSOLE ON
#include "Afx/AfxFile.inc"
using Afx

' // Create an instance of the CFileSys class
DIM pFileSys AS CFileSys
' // Create an instance of the CTextStream class initialized
' // with a pointer to the standard StdOut stream
DIM pTxtStm AS CTextStream = pFileSys.GetStdOutStream
' // Write a string and an end of line to the stream
pTxtStm.WriteLine "This is a test."

PRINT "Press any key to end..."
SLEEP


The default is ansi, but we can also work with unicode passing TRUE to GetStdOutStream.


DIM pTxtStm AS CTextStream = pFileSys.GetStdOutStream(TRUE)


Alternative syntax:


'#CONSOLE ON
#include "Afx/AfxFile.inc"
using Afx

' // Create an instance of the CTextStream class initialized
' // with a pointer to the standard StdOut stream
DIM pTxtStm AS CTextStream = CFileSys().GetStdOutStream(TRUE)
' // Write a string and an end of line to the stream
pTxtStm.WriteLine "This is a test."

PRINT "Press any key to end..."
SLEEP


Isn't it cool?
Title: Re: José, if you're bored...
Post by: Paul Squires on August 15, 2018, 11:31:32 AM
Quote from: José Roca on August 15, 2018, 11:26:39 AM
I'm making changes to CFileSys and CTextStream to allow to work with the standard StdIn, StdErr and StdOut using the methods of the CTextStream class.

First working test:


'#CONSOLE ON
#include "Afx/AfxFile.inc"
using Afx

' // Create an instance of the CFileSys class
DIM pFileSys AS CFileSys
' // Create an instance of the CTextStream class initialized
' // with a pointer to the standard StdOut stream
DIM pTxtStm AS CTextStream = pFileSys.GetStdOutStream
' // Write a string and an end of line to the stream
pTxtStm.WriteLine "This is a test."

PRINT "Press any key to end..."
SLEEP


The default is ansi, but we can also work with unicode passing TRUE to GetStdOutStream.


DIM pTxtStm AS CTextStream = pFileSys.GetStdOutStream(TRUE)



Well, that's interesting!
Title: Re: José, if you're bored...
Post by: Chris Maher on August 15, 2018, 11:37:13 AM
Hi José,

1.) I am pretty sure that using your WinFBX Classes is a key skill that needs to be learned to produce fast, solid and reliable FB Windows code based upon a great framework.

It certainly isn't the simple text output that I used when I was first learning BASIC, but the power of the windows and the additional tools it gives me are fascinating (and 64 bit). I am finding that I am slowly understanding more and more of CPrint just by playing with the code and reading your GitHub documentation (I really like the new format and further short examples in FB echoing the Microsoft Docs could be really valuable).

2.) I also have a small request. I am not sure if you left PaperLength and PaperWidth out of CPrint for a reason but they are vital for thermal label printers using a windows driver where the page size is almost never standard.

' ========================================================================================
' Returns the printer paper length in units of 1/10 of a millimeter.
' ========================================================================================
PRIVATE PROPERTY CPrint.PaperLength () AS LONG
   RETURN this.GetDocumentProperties(DM_PAPERLENGTH)
END PROPERTY
' ========================================================================================
' ========================================================================================
' Sets the printer paper length in units of 1/10 of a millimeter. This value overrides the
' length of the paper specified by PaperSize, and is used if the paper is of a custom size,
' or if the device is a dot matrix printer, which can print a page of arbitrary length.
' ========================================================================================
PRIVATE PROPERTY CPrint.PaperLength (BYVAL nSize AS LONG)
   this.SetPrinterInfo(DM_PAPERLENGTH, nSize)
END PROPERTY
' ========================================================================================

' ========================================================================================
' Returns the printer paper width in units of 1/10 of a millimeter.
' ========================================================================================
PRIVATE PROPERTY CPrint.PaperWidth () AS LONG
   RETURN this.GetDocumentProperties(DM_PAPERWIDTH)
END PROPERTY
' ========================================================================================
' ========================================================================================
' Sets the printer paper width in units of 1/10 of a millimeter. This value overrides the
' width of the paper specified by PaperSize. This MUST be used if PaperLength is used.
' ========================================================================================
PRIVATE PROPERTY CPrint.PaperWidth (BYVAL nSize AS LONG)
   this.SetPrinterInfo(DM_PAPERWIDTH, nSize)
END PROPERTY
' ========================================================================================


These are my versions based upon your work - please feel free to use and tidy these up as you see fit.

3.) It would also be useful for all printer types to Get and Set the non-printing margin widths at the Top, Bottom, Left and Right of a page. This could/would be affected by orientation and very often they are non-symmetrical due to mechanical limitations. Currently I don't know where to start with this.

Chris.
Title: Re: José, if you're bored...
Post by: José Roca on August 15, 2018, 11:47:15 AM
> I am not sure if you left PaperLength and PaperWidth out of CPrint for a reason

Just an oversight. This is why I want some collaboration to help with testing and suggestions. Much more with CPrint, since I don't even have a printer.

> It would also be useful for all printer types to Get and Set the non-printing margin widths at the Top, Bottom, Left and Right of a page.

Currently, you can set them with calling the SetPageSetup method, that activates the Set Page Setup dialog. I have yet to find if there is a way to do it programatically.

Title: Re: José, if you're bored...
Post by: José Roca on August 15, 2018, 12:54:38 PM
I have added the suggested properties and also the SetPageSize function, that allows to set both the length and width with one call.

The GitHub repository and documentation has been updated.
Title: Re: José, if you're bored...
Post by: José Roca on August 15, 2018, 01:05:49 PM
> I am finding that I am slowly understanding more and more of CPrint just by playing with the code

This is the way to do it. With practice, it will become second nature. I started using Free Basic not very long ago and now I feel like I have been using it all my life. As it uses many C conventions, probably the most important skill is to be proficient with the use of pointers.
Title: Re: José, if you're bored...
Post by: José Roca on August 15, 2018, 01:41:53 PM
Modified CFileSys and CTextStream to allow to use the methods of the CTextStream class with the standard StdErr, StdIn and StdOut streams. It works with ansi and unicode.

- CFileSys.inc: Added the GetStdErrStream, GetStdInStream and GetStdOutStream methods.
- CTextStream.inc: Added a new constructor and the LET and CAST operators. The new constructor and the LET operator allow to initialize the class by passing a pointer to an existing TextStream interface.

Example:


'#CONSOLE ON
#include "Afx/AfxFile.inc"
using Afx

' // Create an instance of the CTextStream class initialized
' // with a pointer to the standard StdOut stream
DIM pTxtStm AS CTextStream = CFileSys().GetStdOutStream(TRUE)
' // Write a string and an end of line to the stream
pTxtStm.WriteLine "This is a test."

PRINT "Press any key to end..."
SLEEP


The GitHub repository and documentation have been updated.
Title: Re: José, if you're bored...
Post by: José Roca on August 16, 2018, 02:09:21 AM
Hi Chris,

Regarding the margins, I have found a way to get them:


DIM totalWidth AS LONG = GetDeviceCaps(m_hdc, PHYSICALWIDTH)
DIM printWidth AS LONG = GetDeviceCaps(m_hdc, HORZRES)
DIM leftMargin AS LONG = GetDeviceCaps(m_hdc, PHYSICALOFFSETX)
DIM rightMargin AS LONG = totalWidth - printWidth - leftMargin

DIM totalHeight AS LONG = GetDeviceCaps(m_hdc, PHYSICALHEIGHT)
DIM printHeight AS LONG = GetDeviceCaps(m_hdc, VERTRES)
DIM topMargin AS LONG = GetDeviceCaps(m_hdc, PHYSICALOFFSETY)
DIM bottomMargin AS LONG = totalHeight - printHeight - topMargin


I will add properties to the CPrint class.

But setting the margins is a different question. Even PowerBasic provides a XPRINT GET MARGIN statement, but not a XPRINT SET MARGIN.
Title: Re: José, if you're bored...
Post by: Chris Maher on August 16, 2018, 04:13:12 AM
Hi José,

Brilliant. I am sure I looked at GetDeviceCaps but just didn't see them!

As for setting the margins, I suppose that makes sense if the margins are mechanical limitations they could not be made smaller.

I think what I was looking for can be done in code as virtual margins (or an offset) that may be wider than these minimums.

Thanks for spending the time to find them. Also I keep forgetting what a massive resource the PB code base is in pointing to some of the answers.
Title: Re: José, if you're bored...
Post by: Chris Maher on August 16, 2018, 06:58:54 AM
Hi José,

I guess I am doing it wrong but I can't seem to set any properties in CPrint. Here's a sample of me trying to set Paper Size and Orientation but it is the same for everything I have tried.

'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "Afx/CPrint.inc"
#INCLUDE ONCE "Afx/CGdiplus/CGdiplus.inc"
USING Afx

DIM pPrint AS CPrint
pPrint.AttachPrinter("Microsoft Print to PDF")

DIM HRX as long = pPrint.GetHorizontalResolution()
DIM HRY as long = pPrint.GetVerticalResolution ()
Print "Print Area pix:", HRX, HRY
DIM PPIX as long = pPrint.GetPPIX()
DIM PPIY as long = pPrint.GetPPIY()
Print "Resolution dpi:", PPIX, PPIY
DIM PWmm as long = (HRX/PPIX)*25.4
DIM PHmm as long = (HRY/PPIY)*25.4
Print "Print Area mm:", PWmm,PHmm
Dim WX as long = pPrint.GetHorizontalUnits()
Dim WY as long = pPrint.GetVerticalUnits()
Print "World Units:",, WX,WY
Dim PaperW as long = pPrint.PaperWidth
Dim PaperL as long = pPrint.PaperLength
Print "Paper Size 0.1 mm:", PaperW,PaperL
Dim PO as long = pPrint.Orientation
Print "Orientation:",, PO

DIM wszOutput AS WSTRING * MAX_PATH = ExePath & "\GdiplusPrint.pdf"
DIM hdcPrint AS HDC = pPrint.GetDC
DIM docInfo AS DOCINFOW
docInfo.cbSize = SIZEOF(DOCINFOW)
docInfo.lpszDocName = CAST(LPCWSTR, @"GdiplusPrint")
docInfo.lpszOutput = @wszOutput

dim PaperSet as boolean = pPrint.SetPaperSize (2800, 1900)
Print "Paper Size set:", PaperSet
pPrint.Orientation = DMORIENT_LANDSCAPE
Print "Orientation:",, pPrint.Orientation

StartDocW(hdcPrint, @docInfo)
StartPage(hdcPrint)
SCOPE
   DIM graphics AS CGpGraphics = hdcPrint
   DIM pen AS CGpPen = GDIP_ARGB(255, 255, 0, 0)
   graphics.DrawLine(@pen, 0, 0, WX, WY)
   graphics.DrawRectangle(@pen, 0, 0, WX-2, WY-2)
   graphics.DrawEllipse(@pen, 0, 0, WX-1, WY-1)
END SCOPE
EndPage(hdcPrint)
EndDoc(hdcPrint)

PRINT "Press any key to end..."
SLEEP


Any help would be greatly appreciated..
Title: Re: José, if you're bored...
Post by: José Roca on August 16, 2018, 04:26:47 PM
Try with a real printer. With Microsoft PDF printer OpenPrinterW fails with an access denied error.
Title: Re: José, if you're bored...
Post by: José Roca on August 16, 2018, 05:06:21 PM
I have added some new methods:


DECLARE SUB GetMarginPixels (BYREF nLeft AS LONG, BYREF nTop AS LONG, BYREF nRight AS LONG, BYREF nBottom AS LONG)
DECLARE SUB GetMarginUnits (BYREF nLeft AS LONG, BYREF nTop AS LONG, BYREF nRight AS LONG, BYREF nBottom AS LONG)
DECLARE FUNCTION PixelsToUnitsX (BYVAL pix AS LONG) AS LONG
DECLARE FUNCTION PixelsToUnitsY (BYVAL pix AS LONG) AS LONG
DECLARE FUNCTION UnitsToPixelsX (BYVAL units AS LONG) AS LONG
DECLARE FUNCTION UnitsToPixelsY (BYVAL units AS LONG) AS LONG

Title: Re: José, if you're bored...
Post by: Chris Maher on August 16, 2018, 06:19:39 PM
No success yet changing properties with an HP laser printer but I will keep trying.

The document name seen in the printer queue is a little strange but the print was ok (un-modified A4) see attached.
Title: Re: José, if you're bored...
Post by: José Roca on August 16, 2018, 07:03:06 PM
> The document name seen in the printer queue is a little strange

Use

DIM wszDocName AS WSTRING * 260 = "GdiplusPrint"
docInfo.lpszDocName = VARPTR(wszDocName)
Title: Re: José, if you're bored...
Post by: José Roca on August 16, 2018, 07:45:40 PM
> No success yet changing properties with an HP laser printer but I will keep trying.

Maybe a problem of access rights? It works with the driver of my broken printer. To be able to change values we have to open the printer with read-write rights. Try to play changing values in pd.DesiredAccess in the SetprinterInfo method. See possible values in https://msdn.microsoft.com/en-us/library/windows/desktop/dd162751(v=vs.85).aspx

Using PRINTER_ACCESS_USE or passing a null pointer (that is what I'm doing in the GetDocumentProperties method, that does not need write rights) will always succeed, but then you can't change any value.


   ' // Start by opening the printer
   DIM hPrinter AS HANDLE
   DIM pd AS PRINTER_DEFAULTSW
   pd.DesiredAccess = PRINTER_ALL_ACCESS
   IF OpenPrinterW(m_wszPrinterName, @hPrinter, @pd) = FALSE THEN RETURN FALSE


Lets see if we find somebody else to try.
Title: Re: José, if you're bored...
Post by: José Roca on August 16, 2018, 08:10:53 PM
One way to retrieve the paper size and margins set in the Page Setup Dialog is


DIM psd AS PAGESETUPDLGW
psd.lStructSize = SIZEOF(PAGESETUPDLGW)
psd.Flags = PSD_RETURNDEFAULT
IF PageSetupDlgW(@psd) THEN
   print psd.ptPaperSize.x, psd.ptPaperSize.y
   print psd.rtMinMargin.Left, psd.rtMinMargin.Top, psd.rtMinMargin.Right, psd.rtMinMargin.Bottom
   print psd.rtMargin.Left, psd.rtMargin.Top, psd.rtMargin.Right, psd.rtMargin.Bottom
   IF psd.hDevMode THEN GlobalFree(psd.hDevMode)
   IF psd.hDevNames THEN GlobalFree(psd.hDevNames)
END IF


Probably I could provide my own DEVMODE and DEVNAMES structures, retrieve the handle of the OK button of the PageSetup dialog and send a click message to it, but anyway, these values are only meant as a reference to the programmer to know the wishes of the user and adjust the printing according them, so I don't see any purpose in setting them programatically.

BTW I have modified the PageSetup method to free the hDevMode and hDevNames handles.


' ========================================================================================
' Displays a Page Setup dialog box that enables the user to specify the attributes of a
' printed page. These attributes include the paper size and source, the page orientation
' (portrait or landscape), and the width of the page margins.
' ========================================================================================
PRIVATE FUNCTION CPrint.PageSetup (BYVAL hwndOwner AS HWND = NULL) AS BOOLEAN
   DIM psd AS PAGESETUPDLGW
   psd.lStructSize = SIZEOF(PAGESETUPDLGW)
   psd.hwndOwner = hwndOwner
   IF PageSetupDlgW(@psd) THEN
      IF psd.hDevMode THEN GlobalFree(psd.hDevMode)
      IF psd.hDevNames THEN GlobalFree(psd.hDevNames)
      RETURN TRUE
   END IF
   RETURN FALSE
END FUNCTION
' ========================================================================================

Title: Re: José, if you're bored...
Post by: José Roca on August 16, 2018, 09:02:32 PM
I have added the following methods:


DECLARE FUNCTION GetDefaultPrinter () AS CWSTR
DECLARE FUNCTION GetDefaultPrinterDriver () AS CWSTR
DECLARE FUNCTION GetDefaultPrinterPort () AS CWSTR
DECLARE FUNCTION ChoosePrinter (BYVAL hwndOwner AS HWND = NULL) AS BOOLEAN
DECLARE FUNCTION EnumPrinterNames () AS CWSTR


and modified the AttachPrinter method to make the wszPrinterName optional. If you don pass a printer name or you pass "", the default printer will be attached.
Title: Re: José, if you're bored...
Post by: Chris Maher on August 17, 2018, 06:14:56 AM
The document name in the printer queue is now correct.

I have installed an Epson XP-245 inkjet printer and as you can see from the attached, the properties appear to be changed, but this was not reflected in the printed output.

I did not change any rights before trying this printer. I will now see if I can change them and if that makes a difference - I am using Win10 Pro but I will also check Win7 Pro.

>I don't see any purpose in setting them programatically.
Not for Home or Office but where the 'User' is an 'Operator', for Graphics, In-Store, Mail Merge or Industrial use, then the printer may be set up in a production queue with the Operator being asked to load and confirm substrate before the next job is run. All settings including XY offsets with step and repeat would be programmatic. Perhaps this area may be a little off-topic for now..

All the new methods make this class very comprehensive. I like the default printer option as there may be 150 printer workstations in a warehouse packing area, that all do the same job, but may be of different types and connections, and this makes it simple to code and operate.
Title: Re: José, if you're bored...
Post by: José Roca on August 17, 2018, 12:48:29 PM
> [...] but this was not reflected in the printed output.

The output will be what you have drawn. The printer isn't going to change the contents. Your drawing routines must read the setting values and adjust the drawing according them, that is, depending on the paper size, you will have more or less room to draw, etc. CPrint only simplifies the task of attaching a printer and get or set the setting values for reference. It doesn't provide drawing routines. These must be written separately.
Title: Re: José, if you're bored...
Post by: Chris Maher on August 17, 2018, 05:58:17 PM
Yes I understand that. My design uses the page print area metrics with an additional line to mark the top left of the design - see attached.

I have had the same results on both Win10 and Win7. What I see with the Microsoft PDF Printer (inkjet jamming), only with the permissions pd.DesiredAccess = PRINTER_ACCESS_USE, is that it updates the user default for orientation and that appears on the next run but not this one.

I was assuming that if I made changes before StartDocW(hdcPrint, @docInfo) that they would have been updated by the DocumentProperties function merging the new settings prior to the page graphic object being created.
I was thinking that the settings would be local to the application as detailed here:
https://msdn.microsoft.com/en-gb/e89a2f6f-2bac-4369-b526-f8e15028698b (https://msdn.microsoft.com/en-gb/e89a2f6f-2bac-4369-b526-f8e15028698b)
but my ability to understand the full code defeated me.

I guess we need someone else to give it a try.
Title: Re: José, if you're bored...
Post by: José Roca on August 17, 2018, 06:31:49 PM
Quote
I have had the same results on both Win10 and Win7. What I see with the Microsoft PDF Printer (inkjet jamming), only with the permissions pd.DesiredAccess = PRINTER_ACCESS_USE, is that it updates the user default for orientation and that appears on the next run but not this one.

In fact, the call to SetPrinterW fails with error 5 (Access Denied), although it ends changing the settings. This is a strange an unrealiable behavior.
Title: Re: José, if you're bored...
Post by: José Roca on August 18, 2018, 02:27:15 AM
BTW I have added documentation for the regular expressions class.

https://github.com/JoseRoca/WinFBX/blob/master/docs/String%20Management/CRegExp%20Class.md

I have tried hard to make it a bit understandable by adding many examples and an introduction, but it is a difficult subject.
Title: Re: José, if you're bored...
Post by: José Roca on August 18, 2018, 04:17:29 AM
Quote
I was assuming that if I made changes before StartDocW(hdcPrint, @docInfo) that they would have been updated by the DocumentProperties function merging the new settings prior to the page graphic object being created.
I was thinking that the settings would be local to the application as detailed here:
https://msdn.microsoft.com/en-gb/e89a2f6f-2bac-4369-b526-f8e15028698b
but my ability to understand the full code defeated me.

I have made some modifications, but I can't see how the technique detailed in your link can work. If I make modifications to the DocumentProperties but I don't call SetPrinterW, when I will call DocumentProperties to retrieve a setting it will return it unchanged. I think that the title is misleading.

Title: Re: José, if you're bored...
Post by: José Roca on August 23, 2018, 12:52:02 AM
Somebody as asked in the Free Basic forum if there was an API function to know if the Windows operating system was 32 or 64 bit. It doesn't exist, but as WMI is a great technology to retrieve all kind of hardware information, I have used it to write the following function:


' ========================================================================================
' Returns the address width of the operating system.
' On a 32-bit operating system, the value is 32 and on a 64-bit operating system it is 64.
' ========================================================================================
PRIVATE FUNCTION AfxGetWindowsBitness (BYREF wszServerName AS WSTRING = ".") AS LONG
   DIM pServices AS CWmiServices = $"winmgmts:{impersonationLevel=impersonate}!\\" & wszServerName & $"\root\cimv2"
   IF pServices.ServicesPtr = NULL THEN RETURN 0
   pServices.ExecQuery("SELECT AddressWidth FROM Win32_Processor")
   pServices.GetNamedProperties
   RETURN pServices.PropValue("AddressWidth").ValLong
END FUNCTION
' ========================================================================================


The beauty of this technique is that just changing the WMI class name and the name of the property, we can easily retrieve hardware information for which an API function does not exist.

It uses the CWmiServices class located in CWmiDisp.incm that I have documented today in my GutHub repository:
https://github.com/JoseRoca/WinFBX/blob/master/docs/COM/CWmiDisp%20Class.md
Title: Re: José, if you're bored...
Post by: Marc Pons on August 23, 2018, 04:05:40 AM
Quote from: José Roca on August 23, 2018, 12:52:02 AM
Somebody as asked in the Free Basic forum if there was an API function to know if the Windows operating system was 32 or 64 bit.

It is also possible to know via IsWow64Process from kernel32.dll

#include "windows.bi"

Function OsBits() as Long
    #IfDef __FB_64BIT__
        Return 64                                'if compiled under 64, its normal to be 64
    #Else
        Dim handle as HANDLE
        Dim ret1   as Long       
        ' Assume initially that this is not a Wow64 process     
        ' and check  if IsWow64Process function exists
        handle = GetProcAddress(GetModuleHandle( "kernel32") , "IsWow64Process")       
        If handle > 0 Then                       ' IsWow64Process function exists
            ' Now use the function to determine if we are running under Wow64
            IsWow64Process(GetCurrentProcess() , @ret1)
        End If
        If ret1 Then Return 64
        Return 32
    #EndIf
End Function
Title: Re: José, if you're bored...
Post by: José Roca on August 23, 2018, 02:00:55 PM
Yes, I also found this hack in the web, but as I was documenting my WMI class I wanted to use it for the task. As I said, the beauty of WMI is that you can use similar code for other tasks. For example, changing "AddressWidth" with "NumberOfCores" in my posted function will return the number of cores for the current instance of the processor.
Title: Re: José, if you're bored...
Post by: José Roca on August 23, 2018, 02:46:45 PM
This code posted by Pierre Bellisle seems to be a better solution that calling IsWow64Process:


#include "Windows.bi"
Dim SysInfo AS SYSTEM_INFO
GetNativeSystemInfo(@SysInfo) 'Available since Windows XP.
MessageBox(HWND_DESKTOP, "Windows is " & _
           IIf(SysInfo.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64, "64", "32") & _
           " bit", "System info", MB_OK OR MB_TOPMOST)
Title: Re: José, if you're bored...
Post by: Marc Pons on August 27, 2018, 01:05:08 PM
José and Pierre thank's

The Pierre's solution is much more elegant, but i think it can be little bit completed to insure all 64 bits processor possibilities

#include "Windows.bi"
Dim SysInfo AS SYSTEM_INFO
GetNativeSystemInfo(@SysInfo) 'Available since Windows XP.
MessageBox(HWND_DESKTOP, "Windows is " & _
           IIf(SysInfo.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64 _
   or  SysInfo.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64 , _
   "64" , "32") & " bits", "System info", MB_OK OR MB_TOPMOST)

'Constants for GetSystemInfo and GetNativeSystemInfo API functions (SYSTEM_INFO structure)
'PROCESSOR_ARCHITECTURE_AMD64  = 9 'x64 (AMD or Intel)
'PROCESSOR_ARCHITECTURE_IA64  = 6 'Intel Itanium Processor Family (IPF)
'PROCESSOR_ARCHITECTURE_INTEL  = 0 'x86
'PROCESSOR_ARCHITECTURE_UNKNOWN  = &HFFFF& 'Unknown architecture
Title: Re: José, if you're bored...
Post by: José Roca on August 27, 2018, 01:18:42 PM
Windows no longer supports Itanium. The last version that did it was Windows Server 2008.
Title: Re: José, if you're bored...
Post by: Joerg B. on August 29, 2018, 06:46:27 AM
Hi José
I have downloaded and installed the current 32-bit and 64-bit compiler versions of FreeBasic.
Now I get error messages when compiling.
I put the screenshots on you.

However, the source code is still compiled into an executable file.
Title: Re: José, if you're bored...
Post by: José Roca on August 29, 2018, 07:31:20 AM
There is not any error in the code. Apparently they have broken the compiler.

I have posted a message in the FB forum:
https://www.freebasic.net/forum/viewtopic.php?f=3&t=26976
Title: Re: José, if you're bored...
Post by: Joerg B. on August 29, 2018, 07:53:42 AM
Hi José
Thanks for taking care of this.
Title: Re: José, if you're bored...
Post by: Pierre Bellisle on August 29, 2018, 01:23:43 PM
> ...insure all 64 bits processor possibilities.

Hi Marc,
To add at José's comment, FB does not provide any compiler for the Itanium IA-64 instruction set.
So this FB code won't work on those platform.

It is either,
PROCESSOR_ARCHITECTURE_INTEL - Intel 80386 - i386 - 386 - x86 wich have a 32-bit instruction set.
or
PROCESSOR_ARCHITECTURE_AMD64 - AMD64 - x86-64 - x64 - Intel 64 wich is the 64-bit version of the x86 instruction set.
Title: Re: José, if you're bored...
Post by: Marc Pons on August 29, 2018, 01:59:56 PM
Pierre and José : Thank's for your informations.
Title: Re: José, if you're bored...
Post by: José Roca on August 30, 2018, 02:20:55 AM
I have modified AfxWin.inc and CWindow.inc to comply with the stricter casting rules of the latest compilers (don't know yet if I have to do changes to other files) and avoid the warnings.
Title: Re: José, if you're bored...
Post by: Joerg B. on August 30, 2018, 05:48:03 AM
Hey José
It looks like other files are affected by the change.
I replaced AfxWin.inc and CWindows.inc and tested them with the old and the new compiler.
The results can be seen in the attached screenshots.
Title: Re: José, if you're bored...
Post by: Joerg B. on August 30, 2018, 06:01:22 AM
Hey José
Your work worked fine before. Then the problem is actually the changes to the new compiler.

If you adjust your files according to the errors, the error messages will come back at the latest when the errors are fixed.

I'm back to the "old" version of AfxWin and CWindows and the compiler from WinFBE Suite now.
Title: Re: José, if you're bored...
Post by: José Roca on August 30, 2018, 06:26:10 AM
> I'm back to the "old" version of AfxWin and CWindows and the compiler from WinFBE Suite now.

Are you going to use the old compiler and the old include files for ever? The new behavior is not because of a bug in the new compilers, but because of stricter type checking, and this isn't going to change.

The errors that you're getting now are all related with WinFormsX and the fix is easy:

Paul must change


Cast(WNDPROC, @wfxApplication.SubclassProc)


to


Cast(SUBCLASSPROC, @wfxApplication.SubclassProc)

Title: Re: José, if you're bored...
Post by: José Roca on August 30, 2018, 06:42:09 AM
Better to fix the problems sooner than later. Paul and I use the latest builds of the compilers to avoid bad surprises later, when they will be released. This time you have gone ahead us by installing them before I even had noticed its existence.
Title: Re: José, if you're bored...
Post by: Joerg B. on August 30, 2018, 07:34:41 AM
Hola Josè.

No. If possible, I always prefer to work with the latest versions.
This is the only way I can benefit from additions and/or bug fixes if required.
In this case I went back because I'm not sure if the error messages will have an effect on my program......
As soon as I see the green light here, I will install the latest versions again.

I read the posts in the FB Forum earlier.

When I see or read what discussion your post in the FB forum caused, then the experimental installation was probably not wrong.   :)
Title: Re: José, if you're bored...
Post by: José Roca on August 30, 2018, 08:02:07 AM
Don't be afraid. It is not a show stopper, but an small change almost cosmetic.
Title: Re: José, if you're bored...
Post by: Paul Squires on August 30, 2018, 08:52:17 AM
I'm updating all of the WinFBE and WinFormsX code regarding those new CAST warnings.

Cast(WNDPROC, @wfxApplication.SubclassProc)
To:
Cast(SUBCLASSPROC, @wfxApplication.SubclassProc)

I have already combined both 32 and 64 bit compilers into one folder. Once we get our source code playing nice with the new compiler then I will look at upgrading the GCC files (cc1) to the latest version. I know that David Roberts has been playing with this in his builds so I may have to post some questions to see if there are any gotchas I need to be aware of.

Title: Re: José, if you're bored...
Post by: Paul Squires on August 30, 2018, 02:26:41 PM
I have made all of the necessary changes to both WinFBE and the WinFormsX code (that WinFBE's visual designer uses). Everything now compiles perfectly using the latest nightly build FB compiler and Jose's WinFBX includes. I may wait until tomorrow before posting an updated WinFBE_Suite just in case more CAST warning areas are discovered.
Title: Re: José, if you're bored...
Post by: José Roca on August 30, 2018, 02:43:38 PM
I did choose WNDPROC instead of SUBCLASSPROC because at the time I wrote CWindow I used SetWindowLongPtrW for subclassing. Later, when I implemented the new subclassing method with SetWindowSubclass i thought that just casting it to SUBCLASSPROC was enough, and it worked until now.
Title: Re: José, if you're bored...
Post by: Paul Squires on August 31, 2018, 01:42:00 PM
I have also updated GCC to 8.1 for both 32 and 64 bit. Seems to be working fine.
Title: Re: José, if you're bored...
Post by: Joerg B. on September 02, 2018, 11:47:18 AM
Hello Together

The discussion and the contributions in the FB Forum regarding the stricter type checks, become (in my opinion) increasingly emotional.
Rational arguments are (again in my opinion) much more effective for a solution.
So I am only insecure in which direction the ship is heading.

Or in other words...

The developers are at the crossroads and do not yet know in which direction they should turn.
Title: Re: José, if you're bored...
Post by: Paul Squires on September 02, 2018, 12:59:41 PM
I am confident that CoderJeff, fxm, and dkl will make the best decision possible given all of the factors related warnings and the new CONST code that they have been adding over the last month or so. The rest of the conversation is, like you say, just emotional reactions. I wouldn't worry about this issue affecting development.
Title: Re: José, if you're bored...
Post by: José Roca on September 02, 2018, 01:09:06 PM
As I write reusable code, that must work with no matter which options the user chooses to compile, I need all the warnings. The problem are the beginners, that have a tendency to leave at the first difficulty.
Title: Re: José, if you're bored...
Post by: Joerg B. on September 03, 2018, 07:18:30 AM
Hola José
Now there is an updated dos and win64 version based on your post for download.  ;D
I download it and see what messages are coming now.
Title: Re: José, if you're bored...
Post by: José Roca on September 03, 2018, 09:34:39 AM
According the CoderJeff's post:

- added '-w none' option to suppress warnings. 'none' is based on warning level and is a better way of saying '-w <level>' where level has sufficiently high value to suppress warnings...etc
- warnings generated by default now are same again as fbc 1.05/pre-August 1.06
- warnings generated with '-w pedantic' now are same again as fbc 1.05/pre-August 1.06
- '-w all' will show the additional function pointer warnings
- '-w funcptr' implies '-w all' and will show function pointer warnings, inside CAST()/CPTR() also
- '-w constness' implies '-w funcptr' and will show const discarded warnings

-w none is what I have suggested.

This will make life easier to beginners, that don't know how to solve these warnings.

Be aware that if somebody intends to write reusable code it should use -w all and resolve all the conflicts to avoid warnings when used by somebody that won't use -w none.

Title: Re: José, if you're bored...
Post by: José Roca on September 04, 2018, 06:02:07 PM
Added CMemStream and CMemTextStream classes to CStream.inc.

CMemStream is suited to work with byte arrays and CMemTextStream is tailored to work with unicode strings (can be used as a fast string builder).

I wanted to have them implemented because streams are used very often in COM programming.

I also have ported most of the documentation to the .md format:
https://github.com/JoseRoca/WinFBX/tree/master/docs

And a new .chm file with many corrections is available at:
https://github.com/JoseRoca/WinFBX/tree/master/Help

Title: Re: José, if you're bored...
Post by: José Roca on September 04, 2018, 09:10:25 PM
Tomorrow I will explore the ADO Stream interface. With some additional wrappers I think it can offer a good alternative to all the previous stream classes in which I have been working.

1. It can work with ansi, unicode and utf-8 text files, terminated with CRLF, CR or LF.

2. It can work with binary files.

3. It can work in memory.

Since the ADO stream is not compatible with the standard IStream interface, I still will need the previous classes for some tasks, but to work with files and with the memory, the ADO streams seems to cover all.
Title: Re: José, if you're bored...
Post by: Paul Squires on September 04, 2018, 10:47:44 PM
Interesting. I look forward to seeing what the ADO stream is all about and if it will be easier to use than the IStream code I'm using from you now.
Title: Re: José, if you're bored...
Post by: José Roca on September 05, 2018, 07:33:22 AM
Maybe there is not need to change anything or very little, unless you have suggestions. Maybe an overloaded Open method with a parameter for the opening mode (Read, Write, etc.). The main advantage is than one class fits all and that the AfxGetOleErrorInfo function (sort of GetLastResult for COM) will return a localized description of the error (much better than just getting an error code). I wonder why I haven't remembered until now that ADO has a Stream interface that can work with files (maybe because I always have associated the use of ADO with databases). It can also be used to open and save files from the web. LoadFromFile not only works with unicode paths, but also accepts any valid path and name in UNC format.

This opens an stream in memory. If we don't specify the Type_, Charset and LineSeparator properties, the defaults are a Windows unicode text file.


'#CONSOLE ON
#define UNICODE
#include "Afx/CADODB/CADODB.inc"
USING Afx

' // Open a stream in memory
DIM pStream AS CAdoStream
pStream.Type_ = adTypeText   ' We can also use adTypeBinary. Default is adTypeText.
pStream.Charset = "unicode"   ' We can also use "ascii", "utf-8" and other encondings. Default is unicode.
pStream.LineSeparator = adCRLF   ' We can also use LF (for Linux files) or CR (for Mac files). Default is adCRLF.
pStream.Open

' // Write some text to it (adWriteLine writes a line and a line separator). The default is adWriteChar, that writes
' // the line without a line separator.
pStream.WriteText "This is a test string", adWriteLine
pStream.WriteText "This is another test string", adWriteLine

' // Set the position at the beginning of the file
pStream.Position = 0
' // Read the lines sequentially. If we don't specify adReadLine or we specify adReadAll (the deault), it will read all the lines
DIM cbsText AS CBSTR = pStream.ReadText(adReadLine)
print cbsText
cbsText = pStream.ReadText(adReadLine)
print cbsText

' // Save the contents to a file
pStream.SaveToFile "TestStream.txt", adSaveCreateOverWrite
pStream.Close


This loads a file in binary mode


' // Open a stream
DIM pStream AS CAdoStream
pStream.Charset = "ascii"
pStream.Type_ = adTypeBinary
pStream.Open
pStream.LoadFromFile(AfxGetExePath & "\TextA1.txt")
IF pStream.GetLastResult THEN print AfxGetOleErrorInfo
DIM cv AS CVAR = pStream.Read   ' // You can specify the number of bytes to read
print cv.ToBuffer


ToBuffer returns the contents of the file as a FreeBasic string used as a byte array buffer. An overloaded ToBuffer method allows you to pass a pointer to any kind of buffer and the length of the buffer.

The Write method writes binary data to the stream and WriteText writes strings.

The Write method uses a variant. You can use the CVAR data type for it. It has methods to allow to use almost any data type, even arrays.

The Mode property indicates the permissions:

adModeRead : Indicates read-only permissions.
adModeReadWrite : Indicates read/write permissions.
adModeShareDenyNone : Allows others to open a connection with any permissions. Neither read nor write access can be denied to others.
adModeShareDenyRead : Prevents others from opening a connection with read permissions
adModeShareDenyWrite : Prevents others from opening a connection with write permissions.
adModeShareExclusive : Prevents others from opening a connection.
adModeUnknown : Default. Indicates that the permissions have not yet been set or cannot be determined.
adModeWrite : Indicates write-only permissions.

Other properties include EOS and SetEOS, to get and set the end of the stream; Size, to get the size of the stream in number of bytes; Position, a value that specifies the offset, in number of bytes, of the current position from the beginning of the stream (a value of 0 represents the first byte in the stream).

For text streams, SkipLine, skips one entire line when reading a text stream.

Looks like we have a very good alternative to work with files.
Title: Re: José, if you're bored...
Post by: José Roca on September 05, 2018, 04:04:36 PM
Apparently it only works in memory, not directly with the disk file. It remains as a good alternative to work with memory streams.
Title: Re: José, if you're bored...
Post by: José Roca on September 05, 2018, 06:38:48 PM
I have added documentation about CAdoStream (excluding information about URLs and Record objects to avoid confusions) as an alternative to the CMemStream class, in my GitHub repository:

https://github.com/JoseRoca/WinFBX/blob/master/docs/File%20Management/CMemStream%20Class.md
Title: Re: José, if you're bored...
Post by: Marc Pons on September 07, 2018, 05:30:35 AM
Hi José,

I've followed with interest your developments, as you probably remember i was very interested on your CWSTR

I've seen your extention to DWSTRING, and was curious to compare with my DWSTR proposal (intended to be for Win and linux)
in fact no big difference, at the end you followed my way on not having the codepage on the class , you simplified also  CWSTR that way,
the way you use **  when i prefer only * is still a difference, but not big point

I have done some speed comparaison, and the results were comparable
only  big difference on one point : utf8 string

in fact the difference is because I use the UTFToWChar when you use 2 times MultiByteToWideChar to make the conversion

see that test under that shows the difference

#include ONCE "AFX/DWSTRING.bi"


'convert utf8 string to wstring 
PRIVATE FUNCTION pwstr_from_str(BYREF ansiStr AS STRING, BYVAL nCodePage AS LONG = 0)AS WSTRING PTR
   DIM as uinteger i1 = len(ansiStr)
   if i1 = 0 THEN return 0

   DIM ps2 as wstring ptr = allocate((i1 + 2) * 2 )
   if ps2 THEN
      IF nCodePage = CP_UTF8 THEN
         UTFToWChar(1, strptr(ansiStr), ps2, @i1)  ' **** much faster than 2 times MultiByteToWideChar (20 to 25%)
      ELSE
         MultiByteToWideChar(nCodePage, MB_PRECOMPOSED, STRPTR(ansiStr), i1, ps2 , i1 + 2)
      END IF
      RETURN ps2
   END IF
   return 0
END FUNCTION




dim as double z1,z2
dim i as long
Dim as string utf80, utf81

Print "Building utf8 string with euro symbols separated by spaces, with the 3 last 'characters' as euro"
print : print "please wait... (some seconds)" : print
'  just to create a long utf8 string
for i = 1 to 50000000
   utf80 &= !"\&he2\&h82\&hac"       ' euro symbol
   utf80 &= " "
next
utf80 &= !"\&he2\&h82\&hac"       ' euro symbol
utf80 &= !"\&he2\&h82\&hac"       ' euro symbol
utf80 &= !"\&he2\&h82\&hac"       ' euro symbol

'to have 2 separate strings, just in case
utf81 = utf80


' normal way
z1 = clock()
dim as DWSTRING dw0 =  Dwstring(utf80, CP_UTF8)
z2 = clock()
print "normal time    = " , str(z2-z1) & "ms", "len = " & len(dw0)
messagebox(0, right(**dw0,100), "normal", 0)


' optimized way
z1 = clock()
Dim AS WSTRING PTR pw1temp = pwstr_from_str(utf81, CP_UTF8)
dim as DWSTRING dw1 =  pw1temp
if pw1temp then
   deallocate pw1temp
   pw1temp = 0
end if
z2 = clock()
print "optimized time = " , str(z2-z1) & "ms", "len = " & len(dw1)
messagebox(0, right(**dw1,100), "optimized", 0)


messagebox(0, "end","close",0)


I put a very long utf8 string to show better the difference, on short strings is not visible
I think as Paul is using CWSTR on his nice WinFBE ide , for utf8 files faster would be better. 
Title: Re: José, if you're bored...
Post by: José Roca on September 07, 2018, 06:48:53 AM
I removed the code page because it was no longer needed when Paul added to the editor the option to save the file as UTF-16. If you work directly with UTF-16 then no code page is needed. Both the code page and UTF-8 were workarounds to allow to use unicode with ansi files.

With Windows, the use of UTF-8 is not needed at all. I just have support for it for the sake of completeness.

I use MultiByteToWideChar to make the conversion because a Chinese user reported problems if I did otherwise. Unfortunately, although I asked him for some testing code, he remains hermetic.

DWSTRING was an experiment to see if it was some interest in the Free Basic forum to work with Unicode. As I did suspect, there is none. Maybe just including a file is too easy and you may need to write it in C, provide libraries for both 32 and 64-bit and make its use as difficult as possible.

It also seems that there is no interest to work with COM, other that automating Office with disphelper.

I think that I already have provided all you need to work with unicode. People that don't need it can use ansi strings and the native ansi procedures for work with files if they want.
Title: Re: José, if you're bored...
Post by: José Roca on September 07, 2018, 07:06:41 AM
>  I think as Paul is using CWSTR on his nice WinFBE ide , for utf8 files faster would be better.

To deal with UTF-8 in the editor, Paul is using his own procedures. They're only needed when loading or saving the file because the Scintilla control works with UTF-8.
Title: Re: José, if you're bored...
Post by: Marc Pons on September 07, 2018, 11:04:18 AM
>I removed the code page because it was no longer needed ...

in fact, as i was saying you previously, codepage on the class is never safe because, you could concatenate elements from different codepage together to create CWSTR/CWTRING
so  after what codepage was the one ?

but who cares?

Have good day José, be sure i'm still interrested on your code...

some time, i'll will rework on the COM features because current pdfcreator version totally changed their COM interfaces so if i want to update my exe i need to do it.
my current version was done with powerbasic, the future will be with freebasic
Title: Re: José, if you're bored...
Post by: José Roca on September 10, 2018, 07:55:22 PM
I've finished the porting of the WinFBX documentation to the mark down format:

https://github.com/JoseRoca/WinFBX/tree/master/docs

I have excluded the documentation about the Windows common controls because I guess that everybody will use Paul's WinFormsX.

Some of the include files have been slightly modified. In particular, there was a bug in the Update method of the CADORecordset class.
Title: Re: José, if you're bored...
Post by: Paul Squires on September 10, 2018, 09:49:37 PM
Wow, that was a lot of work! At least now it will be easier for you to maintain in the future. I plan to use github markdown as well for all of my documentation.
Title: Re: José, if you're bored...
Post by: José Roca on September 13, 2018, 03:53:39 AM
CMaskedit: First version of a Masked Edit Control. A Masked Edit Control is very useful for data entry.

Documentation: https://github.com/JoseRoca/WinFBX/blob/master/docs/Windows%20GUI/CMaskedEdit%20Class.md

BTW the code demosntrates how to create a custom control subclasing another control, in this case the standard Edit control.
Title: Re: José, if you're bored...
Post by: Chris Maher on September 13, 2018, 07:54:19 AM
Hi José

Another excellent well-rounded control with lots of easy flexibility and applications. Thank you..

I think there are a couple of small typos in the Hex Mask sample within the SetValidChars section of the GitHub docs:
https://github.com/JoseRoca/WinFBX/blob/master/docs/Windows%20GUI/CMaskedEdit%20Class.md#setvalidchars (https://github.com/JoseRoca/WinFBX/blob/master/docs/Windows%20GUI/CMaskedEdit%20Class.md#setvalidchars)

m_wndMaskEdit.EnableMask(" AAAA"), _   ' // Mask string
("0x____"), _   ' // Template string
('_')   ' // The default character that replaces the backspace character
m_wndMaskEdit.SetValidChars("1234567890ABCDEFabcdef")   ' // Valid string characters
m_wndMaskEdit.SetWindowText("0x01AF")


Should be:

m_wndMaskEdit.EnableMask("  AAAA"), _   ' // Mask string
("0x____"), _   ' // Template string
("_")   ' // The default character that replaces the backspace character
m_wndMaskEdit.SetValidChars("1234567890ABCDEFabcdef")   ' // Valid string characters
m_wndMaskEdit.SetWindowText("0x01AF")

Title: Re: José, if you're bored...
Post by: José Roca on September 13, 2018, 10:35:30 AM
You're right. I did copy it from the MSDN C++ documentation and modified it manually. This time I have tested it before the copy and paste operation. The good thing about the new documentation format is that I can update it as soon as a mistake is reported.

I have made several modifications. One of them is the use of the SetPos method . It will try to position the cursor in the first input place if you use a mask or at the begining of the edit control if you don't use it, or in the position that you indicate.

Therefore, the new testing code will be:


pMskEd.EnableMask("  AAAA"), _   ' // Mask string
("0x____"), _   ' // Template string
("_")   ' // The default character that replaces the backspace character
pMskEd.SetValidChars("1234567890ABCDEFabcdef")   ' // Valid string characters
pMskEd.SetWindowText("0x01AF")
pMskEd.SetPos


A masked edit control has always been in my ToDo list because of its usefulness. I also wanted to experiment on writing a custom control subclassing an existing one. Currently, Cut and Paste are disabled. Looks like implementing it would be very hard.
Title: Re: José, if you're bored...
Post by: José Roca on September 13, 2018, 11:09:41 AM
By using "_" as a placeholder, we are free to use any other characters in the mask, e.g.


pMskEd.EnableMask("       cc       ddddd-dddd", "State: __, Zip: _____-____", " ")
SetWindowText pMskEd.m_hCtl, "State: NY, Zip: 12345-6789"


So we can do many things with just an small number of mask characters, instead of a long list impossible to remember.

By default, the GetWindowText method will return the inputed data without the mask, i.e. "NY123456789". If we call EnableGetMaskedCharsOnly(FALSE), then it will return it with the mask, i.e. "State: NY, Zip: 12345-6789". To clear the input fields, just call the SetWindowText method with an empty string.

Also, because it is a child of CWindow, it is DPI and Unicode aware, and because it is a subclassed Edit control, you can use the context menu available when you right click the mouse in the control. It also honors other features of the Edit control such the ES_UPPERCASE and ES_LOWERCASE styles, right to left reading and IME.


Title: Re: José, if you're bored...
Post by: José Roca on September 13, 2018, 06:28:37 PM
Well, the Paste operation has been very easy to implement after I have figured how to do it.

In the WM_CHAR message, I detect if the user has pressed Ctrl+V and send a WM_PASTE message:


      CASE WM_CHAR
         ' // Handle Ctr+V - Decimal 22, Hex 16 - by sending a WM_PASTE message
         IF wParam = 22 THEN
            SendMessageW hwnd, WM_PASTE, 0, 0
            EXIT FUNCTION
         END IF


Then, when processing the WM_MESSAGE, I retrieve the text from the clipboard and send the characters, one by one, to the OnCharPrintchar method, that processes keystrokes and accepts or rejects them according the mask:


      CASE WM_PASTE
         DIM pthis AS CMaskedEdit PTR = CAST(CMaskedEdit PTR, dwRefData)
         IF pthis = NULL THEN EXIT FUNCTION
         DIM cwsText AS CWSTR = AfxGetClipboardText
         IF LEN(cwsText) THEN
            FOR i AS LONG = 1 TO LEN(cwsText)
               pthis->OnCharPrintchar(ASC(MID(**cwsText, i, 1))), 0, 0
            NEXT
         END IF
         EXIT FUNCTION


Works with Russian, Chinese, etc., too.

Now, let's go for Cut and Copy.
Title: Re: José, if you're bored...
Post by: José Roca on September 13, 2018, 06:54:20 PM
Copy has been even easier.


      CASE WM_CHAR
         ' // Handle Ctrl+C - Decimal 3, Hex 3 - by sending a WM_COPY message
         IF wParam = 3 THEN
            SendMessageW hwnd, WM_COPY, 0, 0
            EXIT FUNCTION
         END IF

Title: Re: José, if you're bored...
Post by: José Roca on September 13, 2018, 09:04:57 PM
I have implemented Cut, Copy, Paste and Undo. The control seems finished unless somebody finds a bug or has suggestions.
Title: Re: José, if you're bored...
Post by: José Roca on September 14, 2018, 07:57:05 AM
Added an overloaded GetWindowText with a boolean parameter to get the text with or without mask.

Mosified the code to get Undo working also after a Cut or Paste operation.
Title: Re: José, if you're bored...
Post by: José Roca on September 14, 2018, 10:33:03 AM
If Paul decides to add this control to the Visual Designer, besides the standard settings for an Edit control, he will need to add the following fields in the Properties:

* A field for the Mask
* A field for the Input Mask
* A field for the Valid Characters (optional; the default is to allow all the characters)
* A Boolean for enabling to set masked characters only (default: TRUE)
* A Boolean for enabling to get masked characters only (default: FALSE)
Title: Re: José, if you're bored...
Post by: José Roca on September 14, 2018, 05:29:45 PM
Hi guys, if you're bored... you can test a new data type, CInt96.

CInt96 is a wrapper class for the DECIMAL data type. Holds signed 128-bit (16-byte) values representing 96-bit (12-byte) integer numbers. The largest possible value is +/-79,228,162,514,264,337,593,543,950,335.

Documentation: https://github.com/JoseRoca/WinFBX/blob/master/docs/Numeric%20datatypes/CInt96%20Class.md
Title: Re: José, if you're bored...
Post by: Johan Klassen on September 14, 2018, 08:04:41 PM
hello José Roca
my small test show that it works ok, tried both 32 and 64 bit, thanks José
Title: Re: José, if you're bored...
Post by: José Roca on September 14, 2018, 09:00:40 PM
Thanks for testing. There are many big numbers libraries, but this class if fast, and 29 digits should be enough for most tasks.
Title: Re: José, if you're bored...
Post by: José Roca on September 16, 2018, 11:21:46 AM
New class: CMemMapFile (memory-mapped files).

Documentation:
https://github.com/JoseRoca/WinFBX/blob/master/docs/File%20Management/CMemMapFile%20Class.md

Wikipedia article:
https://en.wikipedia.org/wiki/Memory-mapped_file

Usage example:

Example
The following code maps the contents of the ansi file "textA.txt", retrieves access to the data and converts it to lower case.


#INCLUDE ONCE "windows.bi"
#INCLUDE ONCE "Afx/CMemMapFile.inc"

DIM pMemMap AS CMemMapFile
IF pMemMap.MapFile(AfxGetExePath & "/testA.txt") THEN
   DIM pData AS ANY PTR = pMemMap.AccessData(100)
   IF pData THEN
      CharLowerBuffA(pData, pMemMap.GetFileSize)
      pMemMap.UnaccessData
   END IF
   pMemMap.Unmap
END IF


Unicode:


#INCLUDE ONCE "windows.bi"
#INCLUDE ONCE "Afx/CMemMapFile.inc"

DIM pMemMap AS CMemMapFile
IF pMemMap.MapFile(AfxGetExePath & "/testW.txt") THEN
   DIM pData AS ANY PTR = pMemMap.AccessData(100)
   IF pData THEN
      CharLowerBuffW(pData, pMemMap.GetFileSize)
      pMemMap.UnaccessData
   END IF
   pMemMap.Unmap
END IF

Title: Re: José, if you're bored...
Post by: José Roca on September 16, 2018, 04:00:17 PM
CMaskEdit: Fixed a problem with Undo not working after deleting characters.
Title: Re: José, if you're bored...
Post by: José Roca on September 17, 2018, 03:28:42 AM
CMaskedEdit

- Removed the DisableMask method. It was causing many problems and it is not needed: if you want an standard edit control, use it instead of a MskedEdit control with the mask disabled.

- Changed SetWindowText to SetText, GetWindowTextLength to GetTextLenght, and GetWindowText to GetText.

- Added SetMaskedText.

- The cursor now is positioned automatically at the beginning of a group, without having to call SetPos.

Documentation updated:
https://github.com/JoseRoca/WinFBX/blob/master/docs/Windows%20GUI/CMaskedEdit%20Class.md
Title: Re: José, if you're bored...
Post by: Paul Squires on October 14, 2018, 12:11:26 PM
Hi José,

Just starting to use the new masked edit control and integrate it into the WinFBE visual designer. I am integrating the control's properties and methods. Here are some of my initial thoughts (your thoughts on these would be appreciated)....

EnableGetMaskedCharsOnly - is this redundant? The GetText method allows retrieving text with or without the mask via the optional true/false bGetMaskedCharsOnly parameter. Why include this property?

SetMaskedText - why not just use SetText and add an additional true/false parameter that specifies that the pointed to text has (or doesn't have have) a mask in it? This would be consistent with the logic used by GetText.

GetTextLength - doesn't seem to be documented in your GitHub docs repository.

Thanks for your hard work on this control. It be a great addition to the visual designer!

Paul
Title: Re: José, if you're bored...
Post by: José Roca on October 14, 2018, 05:56:31 PM
Welcome back! I was waiting for your input before doing cleanup. Meanwhile I have been experimenting with Oxygen Basic because this forum is dead if you're on vacation.

I first tried to mimic the original control, then I changed several things and now I have implemented your suggestions and updated the documentation.
Title: Re: José, if you're bored...
Post by: Paul Squires on October 14, 2018, 06:02:38 PM
:-) I have been back a couple of weeks now but busy getting up to speed with my regular day job. Finally have time now to get back at the editor. I have noticed your activity with Oxygen Basic.

One thing I have been toying with is documentation. I love the markdown approach to documentation but the only downside is viewing it when you're offline (exporting to pdf doesn't always look good). I have been thinking of using a markdown parser to convert the markdown to html and then combine it with the github markdown css. Finally, display it all in an embedded web browser control within WinFBE. In theory, it all seems to be do-able and would make for a nice help system rather than CHM files.
Title: Re: José, if you're bored...
Post by: José Roca on October 14, 2018, 06:11:50 PM
I'm using markdown because I can made changes to the documentation instantaneously, instead of having to prepare new .chm files. Besides, HelpNDoc is awfully slow with big help files like the WinFBX documentation and Help+Manual is expensive.

Title: Re: José, if you're bored...
Post by: Paul Squires on October 14, 2018, 06:21:20 PM
I agree 100%. I was using markdown a bit for my winformsx documentation before I switched to pdf. It was so incredibly easy to write and looks really nice. I think we can leverage the markdown format to get something working nicely inside WinFBE that should easily display your existing markdown files as well as be easily expandable for the long term. I have always hated the whole process of creating CHM files.
Title: Re: José, if you're bored...
Post by: José Roca on October 14, 2018, 07:35:10 PM
Added an small change to the new overloaded SetText function (I was doing nothing with the bSetMaskedCharsOnly parameter).


' ========================================================================================
' Sets masked text to the control.
' ========================================================================================
PRIVATE FUNCTION CMaskedEdit.SetText (BYREF cwsText AS CWSTR, BYVAL bSetMaskedCharsOnly AS BOOLEAN) AS BOOLEAN
   DIM cws AS CWSTR = cwsText
   IF bSetMaskedCharsOnly THEN
      FOR i AS LONG = 1 TO LEN(m_strInputTemplate)
         IF MID(**cws, i, 1) = MID(**m_strInputTemplate, i, 1) THEN MID(**cws, i, 1) = "_"
      NEXT
      cws = AfxStrRemoveAny(cws, "_")
   END IF
   FUNCTION = this.SetText(cws)
END FUNCTION
' ========================================================================================

Title: Re: José, if you're bored...
Post by: Paul Squires on October 14, 2018, 07:44:20 PM
Quote from: Paul Squires on October 14, 2018, 06:02:38 PM
...I have been thinking of using a markdown parser to convert the markdown to html and then combine it with the github markdown css. Finally, display it all in an embedded web browser control within WinFBE. In theory, it all seems to be do-able and would make for a nice help system rather than CHM files.
We already have code in your Templates folder to create and embedded web browser and display either a html string or file. It might now just be a case of adding a client side javascript code file to take the string/file and render the markdown as html in the embedded browser. Something like showdownjs or markedjs might do the trick.
Title: Re: José, if you're bored...
Post by: Paul Squires on October 17, 2018, 05:07:33 PM
Okay, here is what my approach will be to the help/documentation for WinFBE and WinFBX, etc.

Instead of CHM files, I am building a form that will show a treeview on the left, a tab control on top, and an embedded web browser control on the right. The tab control will have tabs for "WinFBE", "WinFBX", etc...  Depending on what tab is selected, the treeview will display the folder structure of the help files. When a file is selected then that help topic will be rendered in the embedded web browser.

To generate the HTML files to display in the web browser, I will run all the documentation markdown files (*.md) through a batch converter to create standalone stylized HTML files in the exact same format as GitHub markdown. This is accomplished via this process:

Use Pandoc to batch convert the markdown files to HTML
https://pandoc.org/

The command line looks like (ran via a batch file):
pandoc -f markdown -t html5 -o "%2" "%1" -c ".\github.css" --self-contained

%1 is the input markdown file
%2 is the html file

The "github.css" is the specific stylesheet that gives the final HTML file its look and feel of a github markdown file. I am using the css from:
https://github.com/otsaloma/markdown-css

Using this approach it will be easier to create help documentation for WinFBE and the visual designer, as well as easily work with Jose's new markdown files that he is using to document the WinFBX library.