Oxygen Basic

Information => Development => Topic started by: Arnold on February 18, 2018, 02:46:06 AM

Title: Compliant 64-bit compiling
Post by: Arnold on February 18, 2018, 02:46:06 AM
Hi Charles,

as Oxygen is able to create 64-bit executable code, it seems that for the data types, there are some rules to follow. I wondered when it makes sense to dispense with the int type and prefer to use sys, therefore I took a look at Windows Dev Center:

https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx
https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/the-new-data-types

and I am quite sure that I did not understand everything. But would it be safe to treat all handles (starting with H...) and all kind of pointers to treat as sys? I am also not sure about long, longlong, long32, long64 etc.

How does Oxygen treat GetWindowLong, SetWindowLong in 64-bit Windows? Is there a need for using GetWindowLongPtr, SetWindowLongPtr?

Roland
Title: Re: Compliant 64-bit compiling
Post by: Charles Pegge on February 18, 2018, 06:11:17 AM
Hi Roland,

I would use Get/SetWindowLongPtr, since they are compatible with 32/64 bit values on 32/64 bit platforms.

When passing 32bit values to functions expecting 64bit values:

int is sign extended to sys
dword is zero extended to sys

But when passing variables byref, the types should match.

Title: Re: Compliant 64-bit compiling
Post by: Arnold on February 19, 2018, 12:41:46 AM
Hi Charles,

it seems that using Get/SetWindowLong and Get/SetWindowLongPtr is a little bit tricky. I learned these functions are used to set and get e.g. certain window styles (GWL_STYLE, GWL_EXSTYLE), for subclassing (GWL_WNDPOC), or as a pointer to data with a given window (GWL_USERDATA). The GWLP_ parameters seem to have the same values.

Set/GetWindowLongPtr can only be used for 64-bit compiling, using these functions for 32-bit will fail. Since some time I have been looking for a simple solution which covers this case. In examples\WinGui\ChildWin.o2bas I tried this:

Code: [Select]
...
! GetWindowLongPtr lib "user32.dll" alias "GetWindowLongPtrA" (sys hWnd, sys nIndex) as sys
'% Win_64

  '-------------------------------------------------------------
  function EnumChildProc(sys hwndChild, lParam) as bool callback
  '=============================================================
  {
    RECT * rcParent;
    sys i, idChild;
    sys v,w,info
 
    // Retrieve the child-window identifier. Use it to set the
    // position of the child window.
    info=GWL_ID
#ifdef Win_64
    idChild = GetWindowLongPtr(hwndChild, info)
#else
    idChild = GetWindowLong(hwndChild, info)
#endif
...

I also tried this way:

Code: [Select]
...
! GetWindowLongPtrA lib "user32.dll" alias "GetWindowLongPtrA" (sys hWnd, sys nIndex) as sys
'% Win_64

function GetWindowLongPtr(sys h, sys i) as sys
#ifdef Win_64
    return GetWindowLongPtrA(h,i)
#else
    return GetWindowLong(h,i)
#endif
end function
 
  '-------------------------------------------------------------
  function EnumChildProc(sys hwndChild, lParam) as bool callback
  '=============================================================
  {
    RECT * rcParent;
    sys i, idChild;
    sys v,w,info
 
    // Retrieve the child-window identifier. Use it to set the
    // position of the child window.
    info=GWL_ID
    idChild = GetWindowLongPtr(hwndChild, info)
...

Both of my solutions are not really satisfying. The first one needs a workaround for every Get/SetWindowLong/Ptr. In the second case I need to define Win_64 before the function GetWindowLongPtr. Defining Win_64 after the function does not work. Perhaps with the new macro system there is a better way? Two other examples which use GetWindowLong / SetWindowlong are \tools\FindEd.o2bas and \examples\WinDynDialogs\FullScreen.o2bas.

I suspect for really 64-bit compliant compiling Get/SetWindowLongPtr should be considered. And perhaps these functions should also be added to minwin.inc
 
Roland
Title: Re: Compliant 64-bit compiling
Post by: Charles Pegge on February 19, 2018, 05:38:20 AM
Hi Roland,

Inserted into the user32.dll section of minwin.inc:

Code: [Select]
  #ifdef mode64bit
  ! GetWindowLongPtr "GetWindowLongPtrA"          '2
  ! SetWindowLongPtr "GetWindowLongPtrA"          '3
  #else
  ! GetWindowLongPtr "GetWindowLongA"             '2
  ! SetWindowLongPtr "SetWindowLongA"             '3
  #endif


my test:
Code: [Select]
extern lib "user32.dll"
...
#ifdef mode64bit
  ! GetWindowLongPtr "GetWindowLongPtrA"
#else
  ! GetWindowLongPtr "GetWindowLongA"
#endif
...
end extern

print @GetWindowLongPtr

Are there any other incompatible functions you know of?
Title: Re: Compliant 64-bit compiling
Post by: Arnold on February 19, 2018, 08:26:05 AM
Hi Charles,

the feature to use mode64bit is brilliant. I made some tests with Oxygen in progress, and running in JIT mode, compiling to 32-bit and 64-bit worked without problems.

The only difference I am aware of is the different use of cdecl for some libraries like msvcrt.dll or iup.dll. If I understood MSDN correctly then in 64-bit mode the cdecl modifier is ignored. I assume I could #undef cdecl in the beginning of a program for 64-bit compiling?

But to be honest at the moment I am quite happy to run 32-bit applications. I have progressed much further with my studies than I had dreamed.

Roland
Title: Re: Compliant 64-bit compiling
Post by: Charles Pegge on February 19, 2018, 09:26:48 AM
It's soft-coded. RTL32 and RTL64 have equates mode32bit and mode64bit.

Perhaps we should also have a W version of minwin for the unicode apis: modeWideChar
Title: Re: Compliant 64-bit compiling
Post by: Arnold on April 04, 2018, 11:14:33 PM
Hi Charles,

exploring the WinApi functions I found that for Win64 there is also a SetClassLongPtr / GetClassLongPtr function which in 32-bit will be used as SetClassLong / GetClassLong. Set/GetClassLong is not declared in minwin.inc, but it is in user.inc.

I have not yet fully understood the connection between user.inc and minwin.inc. In minwin.inc you distinguish between 32/64-bit with Set/GetWindowLong/Ptr but not in user.inc. In user.inc there is also no Set/GetWindowLongPtr function. Is this done internally?

Roland
Title: Re: Compliant 64-bit compiling
Post by: Charles Pegge on April 05, 2018, 05:23:54 AM
Hi Roland,

My solution is to place these macro definitions in WinData.inc
Code: [Select]
  #ifndef mode64bit
    def GetClassLongPtr   GetClassLong
    def SetClassLongPtr   SetClassLong
    def GetWindowLongPtr  GetWindowLong
    def SetWindowLongPtr  SetWindowLong
    def GetClassLongPtrW  GetClassLongW
    def SetClassLongPtrW  SetClassLongW
    def GetWindowLongPtrW GetWindowLongW
    def SetWindowLongPtrW SetWindowLongW
  #endif

Then change this user section in MinWin.inc
Code: [Select]
  #ifdef mode64bit
  ! GetWindowLongPtr "GetWindowLongPtrA"          '2
  ! SetWindowLongPtr "SetWindowLongPtrA"          '3
  #endif

Then add this to User.inc
Code: [Select]
#ifdef mode64bit
  ! GetClassLongPtr   "GetClassLongPtrA"
  ! SetClassLongPtr   "SetClassLongPtrA"
  ! GetWindowLongPtr  "GetWindowLongPtrA"
  ! SetWindowLongPtr  "SetWindowLongPtrA"
  ! GetClassLongPtrW
  ! SetClassLongPtrW
  ! GetWindowLongPtrW
  ! SetWindowLongPtrW
#endif

If this is satisfactory, I'll include these changes  in the next update.

Title: Re: Compliant 64-bit compiling
Post by: Arnold on April 05, 2018, 10:17:36 AM
Hi Charles,

I replaced the modified include files and everything looks very good. Until now I had no problems.

This is a small example using SetClassLongPtr and SetWindowLongPtr (Double-clicks and Opacity):

Code: (o2) [Select]
'SetClassLongPtr / SetWindowLongPtr

$ filename "window_click2.exe"

'uses rtl32
'uses rtl64

uses WinUtil

% GCL_STYLE = -26


MainWindow 310,250,WS_OVERLAPPEDWINDOW


function WndProc(sys hwnd, Msg, wParam, lParam) as sys callback

    select Msg
   
        case WM_CREATE
            'Replaces the window-class style bits   
            SetClassLongPtr(hwnd, GCL_STYLE, GetClassLongPtr(hwnd, GCL_STYLE) | CS_DBLCLKS)

            'Change attributes of window
            SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED)
            int WindowOpacity = (255 * 70) / 100
            SetLayeredWindowAttributes(hwnd, 0, WindowOpacity, LWA_ALPHA)


        case WM_LBUTTONDOWN
            SetWindowText(hwnd, "Left Button Down")
        case WM_LBUTTONUP
            SetWindowText(hwnd, "Left Button Up")
        case WM_LBUTTONDBLCLK
            SetWindowText(hwnd, "Left Button Double Click")

        case WM_RBUTTONDOWN
            SetWindowText(hwnd, "Right Button Down")
        case WM_RBUTTONUP
            SetWindowText(hwnd, "Right Button Up")
        case WM_RBUTTONDBLCLK
            SetWindowText(hwnd, "Right Button Double Click")
   
        case WM_CLOSE
            DestroyWindow(hwnd)
       
        case WM_DESTROY
            PostQuitMessage(0)
       
        case else
            return DefWindowProc(hwnd, Msg, wParam, lParam)
           
    end select
   
    return 0
end function

Title: Re: Compliant 64-bit compiling
Post by: Arnold on April 28, 2018, 03:42:38 AM
Hi Charles,

the contribution of Chris about existing files caused me to play with the function GetFileAttributes. The following code works fine with 32 bit, but fails if compiled to 64-bit executable, no matter what I try. I searched in internet but I did not find sufficient information until now.

Is GetFileAttributes not used any more this way with Win64 or must 0xffffffff treated differently? These negative constants start to worry me.

Roland

Code: [Select]
$ filename "ExistFilePath.exe"

'uses rtl32
'uses rtl64

uses corewin
uses console

% INVALID_FILE_ATTRIBUTES = (dword -1) '0xffffffff

function existFP(string szFilePath) as bool
  'path, filename?
  dword attrib = GetFileAttributes(szFilePath)
printl attrib
  'if attrib != 0xffffffff then return true
  if attrib != INVALID_FILE_ATTRIBUTES then return true
  return false
end function

sub checkFP(string fp)
  if existFP(fp) then
    printl fp " exists"
  else
    printl fp " does not exist" 
  end if
end sub

'check folder
checkFP("c:\temp")
checkFP("C:\temp\Get Start\Tgh")
checkFP("c:\oxygenbasic\examples")
checkFP("c:\program files")

'check file
checkFP("C:\OxygenBasic\Oxygen.dll")
checkFP("C:\OxygenBasic\Oxygen.?ll")
checkFP("C:\OxygenBasic\Oxygen.*")
checkFP("c:\oxygenbasic\projectsA\Controls\CtrlSliders.o2bas")

printl "Enter ... " : waitkey
Title: Re: Compliant 64-bit compiling
Post by: chrisc on April 28, 2018, 05:36:50 AM
Hi Roland

according to https://www.pinvoke.net/default.aspx/Constants.INVALID_FILE_ATTRIBUTES (https://www.pinvoke.net/default.aspx/Constants.INVALID_FILE_ATTRIBUTES)

the  INVALID_FILE_ATTRIBUTES =  -1   and it is an int

so i place these into code

% INVALID_FILE_ATTRIBUTES =  -1
 int attrib = GetFileAttributes(szFilePath)

and it works now when compile to 64bits

Code: [Select]
' ExistFilePath.o2bas
' http://www.oxygenbasic.org/forum/index.php?topic=1544.msg18057;topicseen#msg18057
$ filename "ExistFilePath.exe"

'uses rtl32
uses rtl64

uses corewin
uses console

% INVALID_FILE_ATTRIBUTES =  -1  ' 0xffffffff   ' (dword -1) '0xffffffff

function existFP(string szFilePath) as bool
  'path, filename?
 ' dword attrib = GetFileAttributes(szFilePath)
   int attrib = GetFileAttributes(szFilePath)
printl   "  attrib " attrib
  'if attrib != 0xffffffff then return true
 ' if attrib != INVALID_FILE_ATTRIBUTES then return true
   if attrib <> INVALID_FILE_ATTRIBUTES then
      return true
   end if
  return false
end function

sub checkFP(string fp)
  if existFP(fp) then
    printl fp " exists"
  else
    printl fp " does not exist"
  end if
end sub

'check folder
checkFP("c:\temp")
checkFP("C:\temp\Get Start\Tgh")
checkFP("c:\oxygenbasic\examples")
checkFP("c:\program files")

'check file
checkFP("C:\OxygenBasic\Oxygen.dll")
checkFP("C:\OxygenBasic\Oxygen.?ll")
checkFP("C:\OxygenBasic\Oxygen.*")
checkFP("c:\oxygenbasic\projectsA\Controls\CtrlSliders.o2bas")

printl "Enter ... " : waitkey
Title: Re: Compliant 64-bit compiling
Post by: Arnold on April 29, 2018, 12:54:19 AM
Hi Chris,

thank you for the link. Unfortunately the function does still not work correctly in Win64. You can check this if you use some correct / incorrect paths / filenames. In Win32 I will get the value -1, in Win64 I will get 4294967295. Only the value for c:\program files is correct.

I tried some more combinations. Perhaps Charles will find out what goes wrong. And maybe GetFileAttributes must not be used any more for this purpose in Win64 (checking for existing files/folders). I am not sure.

Roland
Title: Re: Compliant 64-bit compiling
Post by: Charles Pegge on April 29, 2018, 01:24:26 AM
Hi Roland,

GetFileAttributes, according to MSDN, definitely returns a dword like most other flag carrying values. Perhaps you could monitor its hex return value using a sys variable, and see what comes out for different folders/files.
Title: Re: Compliant 64-bit compiling
Post by: Arnold on April 29, 2018, 08:49:23 AM
Hi Charles,

I am getting old. (Hopefully I will get older). It is the second time this month that I do not follow my own advice. I use two different computers for 32 and 64 bit with a different folder for Oxygenbasic. The results must of course be different. Sorry if I caused inconvenience.

And as far as I can see Chris has found the only working solution until now. It seems that in Win64 there is the type Int64 which can be used as int in O2. For me the code now works in 32-bit and 64-bit. As I think it is important for similar cases I add the code below. And perhaps there is another solution nevertheless.

Probably negative constants must be checked a little bit more. They are problematic with notification messages and some special cases like this one.

Roland

Code: [Select]
$ filename "ExistFilePath.exe"

'uses rtl32
'uses rtl64

uses corewin
uses console

% INVALID_FILE_ATTRIBUTES = -1 '(dword -1) '0xffffffff

function existFP(string szFilePath) as bool
  'path, filename?
  int attrib = GetFileAttributes(szFilePath)
  if attrib != INVALID_FILE_ATTRIBUTES then return true
  return false
end function

sub checkFP(string fp)
  if existFP(fp) then
    printl fp " exists"
  else
    printl fp " does not exist" 
  end if
end sub

'check folder
...

'check file
...

printl "Enter ... " : waitkey
Title: Re: Compliant 64-bit compiling
Post by: chrisc on April 29, 2018, 09:17:20 AM
Hello Roland

my pc is a win10 64bits and i don't have a 32bit machine so that explain why the
program when i first compile works as it is all 64bits
Title: Re: Compliant 64-bit compiling
Post by: Charles Pegge on April 29, 2018, 06:31:17 PM
Gentlemen,

I think the best solution in these negative number situations is to specify the type:

if a=dword -1 then

The alternative is to use an int or dword variable.

my test code:
Code: [Select]
$ filename "t.exe"
uses rtl64
uses corewin
uses console
string f
do
  print cr "filename "
  f=ltrim rtrim input
  if not f then exit do
  dword a=GetFileAttributes f
  'dword n=-1'INVALID_FILE_ATTRIBUTES
  'if a=n then
  if a=dword -1 then
    print "invalid file/folder name" cr
  else
    print "attrib code " a cr
  end if
  'invalid=-1 folders=16 files=32
loop
Title: Re: Compliant 64-bit compiling
Post by: Arnold on May 07, 2018, 09:17:27 AM
Hi Charles,

this is a nice little demo which I ported to Oxygenbasic using notify statements. I must use:
dword TCN_SELCHANGE= -551
dword TCN_SELCHANGING= -552

to compile it correctly in Win64. This solution seems to work at any rate. But I am not sure if I am a bit obtuse and overlook all the time the same thing?

Roland

Code: [Select]
'https://msdn.microsoft.com/de-de/library/windows/desktop/hh298367(v=vs.85).aspx

$ filename "win_tabnotify.exe"

'uses rtl32
'uses rtl64

uses WinUtil
'uses console

#autodim off

indexbase 0

% WC_TABCONTROL="SysTabControl32"
% WC_STATIC="Static"
% ICC_TAB_CLASSES = 8
% TCIF_TEXT=1
% TCIF_IMAGE=2
% TCM_INSERTITEM=4871
% TCM_GETCURSEL=4875
dword TCN_SELCHANGE= -551
dword TCN_SELCHANGING= -552
% SWP_SHOWWINDOW=64
% HWND_TOP=0

type TCITEM
  int   mask,dwState,dwStateMask
  char* pszText
  int   cchTextMax,iImage
  sys   lParam
end type
typedef TCITEM TC_ITEM


% DAYS_IN_WEEK 7
string day[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}

' Initialize common controls.
INITCOMMONCONTROLSEXt icex
    icex.dwSize = sizeof(INITCOMMONCONTROLSEX)
    icex.dwICC = ICC_TAB_CLASSES
    InitCommonControlsEx(&icex)

sys hInstance=inst
MainWindow 480,360,WS_OVERLAPPEDWINDOW


function WndProc(sys hwnd, uMsg, wParam, lParam) as sys callback
    static sys hwndTab, hwndStatic

    select uMsg
   
    case WM_CREATE
        ' Get the dimensions of the parent window's client area, and
        ' create a tab control child window of that size.
        RECT rcClient
        TCITEM tie
   
        GetClientRect(hwnd, &rcClient)
        hwndTab = CreateWindowEx(0,WC_TABCONTROL, "",
             WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
             0, 0, rcClient.right, rcClient.bottom,
             hwnd, null, hInstance, null)
        if (hwndTab = null) then
           mbox "Create Tab Control failed"
           return null
        end if
        ' Add tabs for each day of the week.
        tie.mask = TCIF_TEXT | TCIF_IMAGE
        tie.iImage = -1 
 
        int i
        for i = 0 to <DAYS_IN_WEEK
           ' Load the day string
           tie.pszText=day[i]
           if SendMessage(hwndTab,TCM_INSERTITEM,i, @tie) = -1 then
             mbox "InsertItem Tab failed"
             DestroyWindow(hwndTab)
             return null
           end if
        next i

        ' Creates a child window (a static control) to occupy the tab control's display area.
        ' hwndTab - handle of the tab control.
        hwndStatic = CreateWindowEx(0,WC_STATIC, "",
                          WS_CHILD | WS_VISIBLE | WS_BORDER,
                          100, 100, 100, 100,        ' Position and dimensions; example only.
                          hwndTab, null, hInstance,    ' g_hInst is the global instance handle
                          null) 
        SendMessage(hwndStatic, WM_SETTEXT, 0, strptr day[0]) 'Sunday
       
        case WM_SIZE
            ' Handles the WM_SIZE message for the main window by resizing the tab control.
            ' hwndTab - handle of the tab control.
            ' lParam - the lParam parameter of the WM_SIZE message.
'           RECT rc
            ' Resize the tab control to fit the client are of main window.
'           SetWindowPos(hwndTab, HWND_TOP, 0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), SWP_SHOWWINDOW)
            int x=loword(lParam) : int y=hiword(lParam)
            SetWindowPos(hwndTab, HWND_TOP, 0,0, x,y, SWP_SHOWWINDOW)

        case WM_NOTIFY
            ' Handles notifications from the tab control, as follows:
            ' TCN_SELCHANGING - always returns FALSE to allow the user to select a different tab. 
            ' TCN_SELCHANGE - loads a string resource and displays it in a
            '      static control on the selected tab.
            ' hwndTab - handle of the tab control.
            ' lParam - the lParam parameter of the WM_NOTIFY message.
            NMHDR pnmhdr at lParam
            select pnmhdr.code

            case TCN_SELCHANGING
                    ' Return FALSE to allow the selection to change.
                    return FALSE

            case TCN_SELCHANGE               
''                  int iPage = TabCtrl_GetCurSel(hwndTab)
                    int iPage = SendMessage(hwndTab, TCM_GETCURSEL, 0,0)                   
                    SendMessage(hwndStatic, WM_SETTEXT, 0, strptr day[iPage])
 
            end select
            return TRUE

       
        case WM_CLOSE
            DestroyWindow(hwnd)
       
        case WM_DESTROY
            PostQuitMessage(0)
       
        case else
            return DefWindowProc(hwnd, uMsg, wParam, lParam)
           
    end select
   
    return 0
end function
Title: Re: Compliant 64-bit compiling
Post by: Charles Pegge on May 07, 2018, 05:54:12 PM
Thanks, Roland.

I'm trying a tiny adjustment to O2, so that integer cases are evaluated as 32bit max. This fixes the negative equate case problem, and I don't think it will have any practical disadvantage.
Title: Re: Compliant 64-bit compiling
Post by: Arnold on October 08, 2018, 12:49:54 AM
Hi Charles,

running this code snippet in Win32 there is no problem. Running in Win64 the app will crash at some point if I only use the Enter key. If I comment out line 11 or add a statement: if len(ans)=0 then there is no problem in Win64 neither. I only report this result because of the differenc to Win32 behaviour. Maybe with your current version of O2 this is already fixed.

Roland

Code: [Select]
$ filename "TestInput.exe"

uses rtl64

uses console

cls
printl "Do wish to see the answer? (Y/N)"
string ans=input()

ans=ltrim rtrim ans     'Enter in Win64 does not work

if lcase(ans) = "y" then
  printl "Your answer is Yes"
else
  printl "Your answer is not Yes"
end if

printl "Enter ... " : waitkey     
Title: Re: Compliant 64-bit compiling
Post by: Charles Pegge on October 08, 2018, 03:18:17 AM
Hi Roland,

There were no problems running this code using my current o2. Could you try it again with the console.inc below.

Title: Re: Compliant 64-bit compiling
Post by: Arnold on October 08, 2018, 03:39:14 AM
Using the modified console.inc I get the same result if I only use the Enter key. But as it will work with the current o2 build this is no big deal.
Title: Re: Compliant 64-bit compiling
Post by: Arnold on June 23, 2019, 12:14:30 AM
Hi Charles,

in the release of June 2019 there is \examples\WinGUI\Autosizing\Demo7Dlg.o2bas. Changing the TabPages does not work any more. Could these lines be modified?

line 17/18:

% TCN_SELCHANGE= dword  -551
% TCN_SELCHANGING= dword-552

to

% TCN_SELCHANGE= -551
% TCN_SELCHANGING= -552

I used the other form as an auxiliary construct to run the demo with older Oxygenbasic releases in Win 64-bit. With the new release this is not necessary any more and I must admit that the other form looks a bit strange anyway.

Roland
Title: Re: Compliant 64-bit compiling
Post by: Charles Pegge on June 23, 2019, 01:23:50 AM
Fixed. Thanks Roland. That is the only one among the examples. But I've discovered a few more 'dword -' instances to clean up.
Title: Re: Compliant 64-bit compiling
Post by: Arnold on August 15, 2019, 05:21:45 AM
Hi Charles,

using version 0.2.6 I found this problem when I try to load a non existing file:

Code: [Select]
$ filename "LoadFile.exe"
'uses rtl32
'uses rtl64

string s
string fname="aFile.txt"  'does not exist
getfile fname, s

's=ltrim rtrim(s)          'without comment will crash in 64-bit mode
if len(s)=0 then print "Error loading " + fname else print "Loaded File " fname

Line 9 does not work in 64-bit mode and the app crashes. Using a comment will work in 32-bit mode and in 64-bit mode.

Roland
Title: Re: Compliant 64-bit compiling
Post by: Charles Pegge on August 15, 2019, 07:14:27 AM
Thanks Roland,

Null strings (with null ponter) not properly processed.  This will be fixed in RTL64.inc, for both ltrim and rtrim

test code:
Code: [Select]
$filename "o.exe"
uses rtl64
string s
print ltrim(rtrim(s))
print "ok"
Title: Re: Compliant 64-bit compiling
Post by: Arnold on December 11, 2019, 03:26:04 AM
Hi Charles, Mike,

I would like to refer to \projectsB\Console\Printf.o2bas. The demo works in 32-bit and failes in 64-bit mode. I tried this code snippet:

Code: [Select]
$ Filename "Test_iob.exe"
'uses rtl32
'uses rtl64

uses corewin
'uses console

'sys _iob = __p__iob()
'print _iob

print __p__iob()

Must __p__iob() be applied differently in 64-bit mode or is it not available in this mode? The structure FILE is used in stdio.h of tcc also, and printf works with tcc in 64-bit too. But it seems there is another approach used. I simply do not understand this piece of code in stdio.h:

#ifdef _WIN64
  _CRTIMP FILE *__cdecl __iob_func(void);
#else
#ifdef _MSVCRT_
extern FILE _iob[];     /* A pointer to an array of FILE */
#define __iob_func()    (_iob)

Is there a solution to run the different printf functions of msvcrt.dll in 64-bit also? It would be a pity if it is not possible in O2.

Roland
Title: Re: Compliant 64-bit compiling
Post by: Mike Lobanovsky on December 11, 2019, 04:31:01 AM
Roland,

Though both C and Win32 provide access to consoles to read from or write to, their console systems are not interchangeable.

For instance, C provides STDIN, STDOUT and STDERR consoles that are regarded as pure files, and printf()/fprintf()/etc. functions, to manage their text. On the other hand, Win32 provides indirect access to its consoles through their read/write buffers that bear no resemblance to files/streams.

Therefore, changing the mini-/nanoscheme printing technique from C to O2 Win32-based printing seemed to me too much error-prone PITA. A 32-bit msvcrt.dll runtime is supposed to provide several exported functions that return an array of pointers to STDIN/-OUT/-ERR "files" usable for printing with C file handling functions. Unfortunately, some of the exports turned out to be mere stubs, and __p__iob() was the only one function whose return I was able to use in Oxygen as valid pointers to the console "files".

64-bit C STDIN/-OUT/-ERR "file" access seems to be a little different from 32 bits, and it wasn't actual for me at the time I worked on the 32-bit oxy-/nanoscheme code.

If you feel you can spare your time on switching from C-style printing over to Oxygen-style Win32 printing, you are free to modify the oxyscheme code (it's Public Domain anyway) as you like. But I can't guarantee it won't have any unexpected side effects on the oxyscheme code that's yet unproven functioning in the latest Oxygene builds.
Title: Re: Compliant 64-bit compiling
Post by: Arnold on December 11, 2019, 07:52:36 AM
Hi MIke,

probably I can apply sprintf in combination with print and get the same result. Some functions of msvcrt.dll seem to work quite nice in Win64. Unfortunately I have not yet found some more extensive documentation about this dll, many links seem to be broken.

Roland
Title: Re: Compliant 64-bit compiling
Post by: Charles Pegge on December 12, 2019, 02:31:10 AM
Hi Roland,

In 64bit mode, variadic params 1..4 are passed in cpu registers regardless of float types. Therefore a prototype is required to declare the variadic types:

uses corewin
...
! sprintf (char *s,*f,...) at @sprintf


Code: [Select]
'10:12 12/12/2019
'float variadic params are passed in cpu in 64bit mode
$filename "o.exe"
uses rtl64
uses corewin
char c[64] 'string buffer
double v1=123
extern
 'create an overlay of sprintf with variadic prototype
! sprintf (char *s,*f,...) at @sprintf
#show sprintf (c,"%4.2f",v1) '123.00
print c
Title: Re: Compliant 64-bit compiling
Post by: Arnold on December 13, 2019, 02:37:42 AM
Hi Charles,

this solution of declaring sprintf is indeed brilliant. Now something like this will be possible:

Code: [Select]
char strg
int a = 10, b = 38, double c
c = a / b

sprintf(strg, "Value of Pi = %15.10f" + chr(10) + "Division of %d by %d is: %15.10f", pi, a, b, c)
print strg   

I see that I will need 'extern' for compiling to 64-bit. Is there also a need for 'end extern' or is this not necessary?

Roland
Title: Re: Compliant 64-bit compiling
Post by: Charles Pegge on December 13, 2019, 05:10:47 AM
Hi Roland,

Yes, end extern is normally inserted after external declarations, and all functions and their declarations are considered 'internal' by default until the next 'extern' statement.

extern statement direct the compiler to use standard (DLL) calling conventions instead of the more efficient internal calling conventions.

Individual procedures are also made 'extern' by any of the following tags:
external extern callback export
Title: Re: Compliant 64-bit compiling
Post by: Arnold on December 15, 2019, 05:42:42 AM
Hi Charles,

I found this code in /inc/crt/win32/stdio.bi of Freebasic v.1.07.1 and it makes me crazy:

...
#ifdef __FB_64BIT__
   declare function __iob_func() as FILE ptr
   #define stdin (@(__iob_func())[STDIN_FILENO])
   #define stdout (@(__iob_func())[STDOUT_FILENO])
   #define stderr (@(__iob_func())[STDERR_FILENO])
#else
   extern import _iob(0 to 2) alias "_iob" as FILE
   #define stdin (@_iob(STDIN_FILENO))
   #define stdout (@_iob(STDOUT_FILENO))
   #define stderr (@_iob(STDERR_FILENO))
#endif
...

I tried to port this to Oxygen but obviously I do it the wrong way:

Code: [Select]
$ Filename "Test_iob.exe"
'uses rtl32
'uses rtl64

uses corewin
uses console

indexbase 0

% STDIN_FILENO 0
% STDOUT_FILENO 1
% STDERR_FILENO 2

#ifndef mode64bit
//  extern import _iob(0 to 2) alias "_iob" as FILE
sys _iob = __p__iob()
sys stdin  = @_iob(STDIN_FILENO)
sys stdout = @_iob(STDOUT_FILENO)
sys stderr = @_iob(STDERR_FILENO)
#else
// declare function __iob_func() as FILE ptr
//    #define stdin  (@(__iob_func())[STDIN_FILENO])
//    #define stdout (@(__iob_func())[STDOUT_FILENO])
//    #define stderr (@(__iob_func())[STDERR_FILENO])
sys stdin  = call @__iob_func[STDIN_FILENO]
sys stdout = call @__iob_func[STDOUT_FILENO]
sys stderr = call @__iob_func[STDERR_FILENO]
#endif

printl stdin
printl stdout
printl stderr

printl "Enter"
waitkey

For 32-bit this will work but 64-bit crashes. I think I must do it similar like it was done using dispatch_table in o2scm.o2bas or was done in the demo \examples\Basics\FuncPointers1.o2bas. Although I tried some alternatives, somehow I cannot get any further. My logic is wrong and it seems that I am not flexible enough. Can you help?

Roland
Title: Re: Compliant 64-bit compiling
Post by: Arnold on December 15, 2019, 01:14:14 PM
This will run in 32-bit and 64-bit mode, but probably there is a better solution:

Code: [Select]
$ Filename "Test_printf.exe"
'uses rtl32
'uses rtl64

uses corewin
uses console

indexbase 0

extern
! printf (char* a, ...) at @printf

% STDIN_FILENO 0
% STDOUT_FILENO 1
% STDERR_FILENO 2

type FILE
char* _ptr
int _cnt
char* _base
int _flag
int _file
int _charbuf
int _bufsiz
char* _tmpfname
end type

#define _O_TEXT 0x4000 // CRLF in file becomes LF in memory!
#define _IONBF  0x4

#ifndef mode64bit
//  extern import _iob(0 to 2) alias "_iob" as FILE
FILE* _iob = __p__iob()
sys stdin  = @_iob(STDIN_FILENO)
sys stdout = @_iob(STDOUT_FILENO)
sys stderr = @_iob(STDERR_FILENO)
#else
// declare function __iob_func() as FILE ptr
sys stdin  = call @__iob_func()
sys stdout = stdin + sizeof(FILE)
sys stderr = stdout + sizeof(FILE)
#endif

'printl stdin
'printl stdout
'printl stderr + cr

sys hCrt = _open_osfhandle(ConsOut, _O_TEXT)
sys hf   = _fdopen(hCrt, "w")
memcpy  stdout, hf, sizeof(FILE)
setvbuf stdout, NULL, _IONBF, 0

fprintf(stdout, "loading %s" + chr(10), "Testfile.txt")
fprintf(stdout, "Oxygen ")
printf (chr(10) + "OxygenBasic")

printl "Enter"
waitkey

It should be possible to create a __iob_func() table, but I do not know how this can be done.
Title: Re: Compliant 64-bit compiling
Post by: Charles Pegge on December 16, 2019, 05:33:19 AM
Hi Roland,

This works for 64bit:

Code: [Select]
#ifndef mode64bit
//  extern import _iob(0 to 2) alias "_iob" as FILE
FILE* _iob = __p__iob()
sys stdin  = @_iob(STDIN_FILENO)
sys stdout = @_iob(STDOUT_FILENO)
sys stderr = @_iob(STDERR_FILENO)
#else
// declare function __iob_func() as FILE ptr
FILE* _iob = __iob_func()
sys stdin  = @_iob(STDIN_FILENO)
sys stdout = @_iob(STDOUT_FILENO)
sys stderr = @_iob(STDERR_FILENO)
'sys stdin  = __iob_func()
'sys stdout = stdin + sizeof(FILE)
'sys stderr = stdout + sizeof(FILE)
#endif
Title: Re: Compliant 64-bit compiling
Post by: Mike Lobanovsky on December 16, 2019, 08:47:57 AM
... or this: ;)

Code: [Select]
#ifndef mode64bit
FILE* _iob = __p__iob()
#else
FILE* _iob = __iob_func()
#endif

sys stdin  = @_iob(STDIN_FILENO)
sys stdout = @_iob(STDOUT_FILENO)
sys stderr = @_iob(STDERR_FILENO)
Title: Re: Compliant 64-bit compiling
Post by: Arnold on December 17, 2019, 02:05:04 AM
Many thanks. This is far better than my trial and error and will help to understand simiiar constructs. And I now can see that the procedure was already described in /Console/Printf.o2bas. I adapted Printf.o2bas a bit and it runs fine in 32-bit and 64-bit:

Code: (o2) [Select]
$filename "printf.exe"
'uses rtl32
'uses rtl64

'Getting printf to operate in JIT mode
======================================

'Thanks to Mike Lobanovsky
'12:28 07/10/2014

#console
'%NoConsole
%TryAttachConsole

uses corewin
uses console

indexbase 0

' _iob[FOPEN_MAX] is msvcrt's internal file buffer array.
' _iob[] can be exported directly, or via cdecl __p__iob(), or via cdecl __iob_func().
' Reference:
' #define FOPEN_MAX     20
' #define STDIN_FILENO  0
' #define STDOUT_FILENO 1
' #define STDERR_FILENO 2
' #define stdin         &_iob[STDIN_FILENO]
' #define stdout        &_iob[STDOUT_FILENO]
' #define stderr        &_iob[STDERR_FILENO]

% STDIN_FILENO 0
% STDOUT_FILENO 1
% STDERR_FILENO 2


type FILE
char* _ptr
int   _cnt
char* _base
int   _flag
int   _file
int   _charbuf
int   _bufsiz
char* _tmpfname
end type

/*
extern cdecl lib "msvcrt.dll"
! __p__iob        () as sys
! _open_osfhandle (sys a, b) as sys
! _fdopen         (sys a, char* b) as sys
! setvbuf         (sys a, char* b, sys c, d)
! fprintf         '(sys a, char* b, ...)
! printf          '(char* a, ...)
! memcpy          (sys a, b, c)
end extern
*/

#define _O_TEXT 0x4000 // CRLF in file becomes LF in memory!
#define _IONBF  0x4

' same for ConsIn/"r", ConsErr/"w"
sys hCrt = _open_osfhandle(ConsOut, _O_TEXT)
sys hf   = _fdopen(hCrt, "w")

' user-defined buffer (for fprintf() only!)
FILE mystdout at hf
setvbuf @mystdout, NULL, _IONBF, 0

' _iob's internals:
' stdin at _iob as "r",
' stdout at iob+sizeof(FILE) as "w"
' stderr at _iob+2*sizeof(FILE) as "w"
'sys _iob = __p__iob()
#ifndef mode64bit
FILE* _iob = __p__iob()
#else
FILE* _iob = __iob_func()
#endif

sys stdin  = @_iob(STDIN_FILENO)
sys stdout = @_iob(STDOUT_FILENO)
sys stderr = @_iob(STDERR_FILENO)

memcpy stdout, hf, sizeof(FILE)
setvbuf stdout, NULL, _IONBF, 0

% lf = chr(10)

fprintf @mystdout, "fprintf via mystdout: %s and his %s%d" + lf, "Charles", "O", 2
fprintf stdout, "fprintf via stdout  : %s and his %s%d" + lf, "Charles", "O", 2
printf "printf  via stdout  : %s and his %s%d" + lf, "Charles", "O", 2

waitkey

I guess that as long as msvcrt.dll is supported, there will be many useful additional functions available which help in programming.
Title: Re: Compliant 64-bit compiling
Post by: Mike Lobanovsky on December 18, 2019, 04:06:35 AM
I guess msvcrt.dll, the oldest one, will be supported forever as there are millions of apps linked against that runtime. Conversely, later incarnations are bound to specific MS VC builds/versions and live only as long as those versions are in use until superseded.