Lazarus

Free Pascal => Beginners => Topic started by: Peter H on January 21, 2021, 01:20:40 pm

Title: [Solved] 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: Peter H on January 21, 2021, 01:20:40 pm
This compiles in 32 Bit, but not in 64 Bit.
Must I declare "MessageRes" as Windows.DWORD_PTR or must I pass "MessageRes" as "@MessageRes"? Both compiles in 64 Bit.
I use FPC331 (Trunk) on Windows 64 Bit.
How must I test "CompilerVersion" to get this reliably going in FPC 3.3.1 64 Bit and 32 Bit?
The code snippet is taken from a Sourceforge project "path manager" that was last updated in 2013.
https://sourceforge.net/projects/pathtool/ (https://sourceforge.net/projects/pathtool/)
There is a 32 bit binary exe included that works without problems, but I want to add functionality (automatic Backup of environment variables before and after)

The $IF, $IFDEF conditions marked with "// -> No" are not active, I indented them for clarity, for these the $ELSE branch is active.

Code: Pascal  [Select][+][-]
  1. procedure EnvInformation.RefreshEnvironment(const Timeout: Cardinal = 5000);
  2. var
  3.   {$IFDEF CONDITIONALEXPRESSIONS}   //->No
  4.           {$IF CompilerVersion >= 23.0} // Delphi XE2
  5.           MessageRes: Windows.DWORD_PTR;
  6.           {$ELSE}
  7.           MessageRes: Windows.DWORD;
  8.           {$IFEND}
  9.   {$ELSE}
  10.   MessageRes: Windows.DWORD;
  11.   {$ENDIF}
  12. begin
  13.   Windows.SendMessageTimeout(
  14.     Windows.HWND_BROADCAST,
  15.     Messages.WM_SETTINGCHANGE,
  16.     0,
  17.     LPARAM(PChar('Environment')),
  18.     Windows.SMTO_ABORTIFHUNG, Timeout,
  19.     {$IFDEF CONDITIONALEXPRESSIONS}  //->No
  20.             {$IF CompilerVersion >= 23.0} // Delphi XE2    //-No
  21.             @MessageRes
  22.             {$ELSE}
  23.             MessageRes
  24.             {$IFEND}
  25.     {$ELSE}
  26.     MessageRes    // -> Compiles in 32 Bit, but not in 64 Bit
  27.     {$ENDIF}
  28.   );
  29. end;
  30.  
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: MarkMLl on January 21, 2021, 01:46:26 pm
I don't like opining on a Windows topic, but what exactly is your error message and have you tried either PtrInt or PtrUint as the type?

MarkMLl
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: Peter H on January 21, 2021, 01:54:27 pm
I tried this:
 MessageRes: Windows.DWORD_PTR;
and - alternatively -  this
 @MessageRes

Both compiles in 64 Bit

Then I destroyed my environment variables  :-[
I am unsure about the reason: Is this a typical 32/64 Bit problem or shouldnt I write to environment variables while in debugger?  ;D
The error Messages in 64 Bit compilation:
Code: Pascal  [Select][+][-]
  1. Compile Project, Target: PathManager.exe: Exit code 1, Errors: 1, Warnings: 1, Hints: 4
  2. regunit.pas(254,62) Warning: Function result variable of a managed type does not seem to be initialized
  3. regunit.pas(299,21) Hint: Function result variable of a managed type does not seem to be initialized
  4. regunit.pas(342,5) Hint: Conversion between ordinals and pointers is not portable
  5. regunit.pas(353,3) Error: Call by var for arg no. 7 has to match exactly: Got "LongWord" expected "QWord"
  6. redef.inc(832,10) Hint: Found declaration: SendMessageTimeout(QWord;LongWord;Int64;Int64;LongWord;LongWord;var QWord):Int64;
  7. ascdef.inc(184,10) Hint: Found declaration: SendMessageTimeout(QWord;LongWord;Int64;Int64;LongWord;LongWord;PDWORD_PTR):Int64;
  8.  
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: MarkMLl on January 21, 2021, 02:01:31 pm
You said you had something that didn't compile in 64 bit. Post the code that didn't compile and the error message, then find the declaration of the function that's failing.

MarkMLl
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: Peter H on January 21, 2021, 02:06:12 pm
The error is where I marked it in the source above at line 26.

This is argument no. 7 as indicated in the error message.
There is no other error, apart from this the unmodified project compiles in 64 bit.

I attached the unmodified Sourceforge project.
If you or somebody tries it, build or compile in 64 bit, but dont run it and save the environment variables unless you are in a throw away virtual machine!
If you want a working Pathmanager, the 32 bit exe from sourceforge is save and comfortable to use so far I found much more features than the windows pathmanager.
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: PascalDragon on January 21, 2021, 02:22:24 pm
SendMessageTimeout is declared by FPC as follows which matches the declaration in MSDN (https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessagetimeouta):

Code: Pascal  [Select][+][-]
  1. function SendMessageTimeout(hWnd:HWND; Msg:UINT; wParam:WPARAM; lParam:LPARAM; fuFlags:UINT;uTimeout:UINT; lpdwResult:PDWORD_PTR):LRESULT; external 'user32' name 'SendMessageTimeoutA';

Thus you need to pass a pointer to MessageRes which should be declared as DWORD_PTR.

For simplicity you can rework the code like this:

Code: Pascal  [Select][+][-]
  1. procedure EnvInformation.RefreshEnvironment(const Timeout: Cardinal = 5000);
  2. {$IFDEF FPC}
  3.   {$DEFINE USE_DWORD_PTR}
  4. {$ELSE}
  5.   {$IFDEF CONDITIONALEXPRESSIONS}   //->No
  6.     {$IF CompilerVersion >= 23.0} // Delphi XE2
  7.        {$DEFINE USE_DWORD_PTR}
  8.     {$IFEND}
  9.   {$ENDIF}
  10. {$ENDIF}
  11. var
  12.   {$IF USE_DWORD_PTR}
  13.   MessageRes: Windows.DWORD_PTR;
  14.   {$ELSE}
  15.   MessageRes: Windows.DWORD;
  16.   {$ENDIF}
  17. begin
  18.   Windows.SendMessageTimeout(
  19.     Windows.HWND_BROADCAST,
  20.     Messages.WM_SETTINGCHANGE,
  21.     0,
  22.     LPARAM(PChar('Environment')),
  23.     Windows.SMTO_ABORTIFHUNG, Timeout,
  24.     {$IFDEF USE_DWORD_PTR}
  25.     @MessageRes
  26.     {$ELSE}
  27.     MessageRes
  28.     {$ENDIF}
  29.   );
  30. {$IFDEF USE_DWORD_PTR}
  31. {$UNDEFINE USE_DWORD_PTR}
  32. {$ENDIF}
  33. end;
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: marcov on January 21, 2021, 02:38:06 pm
Without the pointer will also work, because there is an overload in redef. But in general we recommend using the pointer values.

COMPILER_VERSION is a Delphi define, it has no meaning in Free Pascal. Testing for Free Pascal is done with

Code: Pascal  [Select][+][-]
  1. {$ifdef FPC}
  2. // this is FPC
  3. {$else}
  4.   // legacy Object Pascal compilers (Delphi)
  5. {$endif}

In general, there are not as much FPC versions in circulation so the number of version tests will be much less.
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: Peter H on January 21, 2021, 02:43:51 pm
Thus you need to pass a pointer to MessageRes which should be declared as DWORD_PTR.

Thank you!
Is this supposed to work in a 32 bit compilation too?

I will try it, however first I must find a secure way to test it, currently I have no virtual machine, I must set up one.
Last time I destroyed my path variables then I tried to go to a recovery point, but windows said "There is none".  :'(
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: MarkMLl on January 21, 2021, 02:48:29 pm
I agree that  Got "LongWord" expected "QWord"  isn't exactly helpful, but happily defer to the others for the correct fix.

MarkMLl
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: Peter H on January 21, 2021, 02:54:50 pm
I agree that  Got "LongWord" expected "QWord"  isn't exactly helpful, but happily defer to the others for the correct fix.

I can find and fix the problem by trial and eror and studying the sources and docs on my own. The problem in this case is, simply trying is risky without parachute, so I preferred to ask more experienced people.
Thank you all so far.

BTW. I have studyed this project a little bit and the purpose of this routine is to notify other windows or processes about the changed environment, so probably this is not the reason why I destroyed my User and System path variables.
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: ASerge on January 21, 2021, 05:52:35 pm
This compiles in 32 Bit, but not in 64 Bit.
Must I declare "MessageRes" as Windows.DWORD_PTR or must I pass "MessageRes" as "@MessageRes"? Both compiles in 64 Bit.
...
You're making this too complicated for nothing. Throw out all IFDEFS altogether.
Such code compiles correctly in both FPC and Delphi in both 32-bit and 64-bit mode:
Code: Pascal  [Select][+][-]
  1. procedure RefreshEnvironment(const Timeout: Cardinal = 5000);
  2. var
  3.   MessageRes: DWORD_PTR;
  4. begin
  5.   SendMessageTimeout(
  6.     HWND_BROADCAST,
  7.     WM_SETTINGCHANGE,
  8.     0,
  9.     LPARAM(PChar('Environment')),
  10.     SMTO_ABORTIFHUNG,
  11.     Timeout,
  12.     @MessageRes
  13.   );
  14. end;
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: Peter H on January 21, 2021, 06:07:30 pm
This makes me think.

If c is of type char then "@c" will compile as well as "@MessageRes", because "@" generates an equivalent to a void * .

I already had versions that compiled.
The question is, if it will work.
However the content of "MessageRes" is not used, so if the size of the variable is correct or larger than required it should work.

Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: ASerge on January 21, 2021, 06:19:06 pm
However the content of "MessageRes" is not used, so if the size of the variable is correct or larger than required it should work.
No "or large" ones. The size of the variable which address is passed to the function is what the function expects. In 32-bit mode, it is 32-bit, in 64-bit - 64-bit.
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: Peter H on January 21, 2021, 06:29:05 pm
No. the result of the call will be stored at the location where Arg7 aka MessageRes points to.
The size of the result is determined by windows, probably it is Quadword in Win64,
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessagetimeouta (https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessagetimeouta)

lpdwResult
Type: PDWORD_PTR
The result of the message processing. The value of this parameter depends on the message that is specified.


Therefore Arg7 must be a valid pointer that points to large enough memory
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: ASerge on January 21, 2021, 06:38:00 pm
No...
I don't understand you. You said the same thing I did, why "no"?
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: Peter H on January 21, 2021, 06:40:19 pm
Ok. Lets agree. Its confusing sometimes. :D
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: MarkMLl on January 21, 2021, 06:47:45 pm
So Arg7 is a pointer to a block of memory, not a pointer-to-a-pointer where the size of the referent would depend on whether it's a 32- or 64-bit system.

That means that the function could be defined with either a pointer (typed or untyped) expected at arg 7, or with a var parameter referring to that block of memory: they have very similar behaviour except that a nil parameter can be passed in the former case which has no equivalent in the latter. And I /did/ ask about the declaration rather earlier in the thread.

MarkMLl
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: Peter H on January 21, 2021, 07:13:00 pm
The Problem is: the meaning of Arg7 can change. This depends from the windows API. The meaning and semantic type of the answer depends from the type of message sent.
So the syntactic types can not tell us the full truth.

This is the prototype that Codetools point me to:
Code: Pascal  [Select][+][-]
  1. function SendMessageTimeout(
  2. hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM; fuFlags, uTimeout: UINT;
  3. var lpdwResult: DWORD_PTR  ): LRESULT;external 'user32' name 'SendMessageTimeoutA';
  4.  

var lpdwResult: DWORD_PTR, this is argument 7
But "@MessageRes" is a constant pointer.
So, why can I give a constant pointer as argument for a var parameter?
There are more prototypes for this function and some would match.
Is Codetools pointing to the wrong polymorphic declaration? (I have seen this before with overloaded functions)
Unfortunately I cannot run this code and see it in debugger, because this would write a new path to the registry or destroy it.

Edit: I tried it anyway and Windows Defender became suspicious and closed lazarus, a program in debugger and manipulating registry hits a breakpoint, defender did not like it  :(
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: ASerge on January 21, 2021, 07:43:21 pm
The Problem is: the meaning of Arg7 can change. This depends from the windows API. The answer depends from the message sent.
So the types can not tell us the full truth.
Yes, the meaning can change, but the size of the variable, which was the question at the beginning, remains constant and equal to SizeOf (DWORD_PTR).

Quote
This is the prototype that Codetools point me to:
Is Codetools pointing to the wrong polymorphic declaration?
The windows.pp unit include the files "ascfun.inc" and "redef.inc". The first describes the last parameter as DWORD_PTR, the second as var DWORD_PTR. Technically, this is the same, since var passes a pointer to a variable.
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: MarkMLl on January 21, 2021, 07:47:44 pm
var lpdwResult: DWORD_PTR, this is argument 7
But "@MessageRes" is a constant pointer.
So, why can I give a constant pointer as argument for a var parameter?
There are more prototypes for this fuction and some would match.
Is Codetools pointing to the wrong polymorphic declaration? (I have seen this before with overloaded functions)

I wouldn't like to say what's going on here, and I'm not spending the next few hours refamiliarising myself with the windows API. Sorry :-)

In situations like that that argument would normally contain a pointer (so it can be set null) rather than being passed by address (i.e. a var parameter), and in cases where it might instead contain a scalar (i.e. not a pointer, not a valid address) this would be either a PtrInt or a PtrUInt i.e. something which specifically has the same size as a pointer on the current platform (hence my original suggestion).

That definition that you've given us var lpdwResult: DWORD_PTR might appear misleading due to machine translation, since the combination of Pascal and "Hungarian notation" appears to suggest a pointer-to-a-pointer (var lp) rather than a pointer-to-an-unstructured block (var dwResult).

So it seems to me that that argument could be declared in Pascal as

* var something

* a pointer (typed or untyped)

* a scalar PtrInt or PtrUint

and while the code calling the function would have to make slight adjustment by applying @ and/or a typecast, from the POV of the function being called the parameter would be the same (a sequence of bits the same size as a pointer, which could be translated to the address of a block of memory).

But again I'll happily defer to Somebody with more recent "hands on" of the header translation process etc.

MarkMLl
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: Peter H on January 21, 2021, 08:06:18 pm
I would say "lpdwResult: DWORD_PTR" means semantically a pointer pointing to a pointer sized memory variable.
If it is really a pointer or reinterpreted as an integer or bitpattern, I do not know it and do not want to know, this could be different from message to message.

Anyway I have given up on this, Windows Defender closed Lazarus and removed it from the taskbar, maybe deleted it (dont know yet) when the program hit a breakpoint, I do not want to deal further with this.
Thanks to all! O:-)
Title: Re: [Solved] 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: Peter H on January 22, 2021, 01:15:06 am
I got it working.
The project is from 2013, so I deleted and rebuilt the whole project.
The blue dots were missing, this was solved by setting Debug Symbols explicitely to "Dwarf with sets".
Then it stopped at the breakpoint and in the disassembly I could see, after building the parameter list it calls "SendMessageTimeout" (which is in a windows DLL) directly.

The problem with confusing compiler error messages is: The compiler does not necessarily compile this prototype declaration that is found by searching the declaration of "SendMessageTimeout" in the editor (which uses Codetools so far I know).

It must be added, M$ Windows declares Param No. 7 as "PDWORD_PTR"
"DWORD_PTR" is according to https://docs.microsoft.com/de-de/windows/win32/winprog/windows-data-types?redirectedfrom=MSDN (https://docs.microsoft.com/de-de/windows/win32/winprog/windows-data-types?redirectedfrom=MSDN) a parameter that is DWORD in 32 Bit WIndows and is expanded to 64 Bit in 64 Bit Windows, so it is always Pointer size, and "PDWORD_PTR" is a pointer to this pointer sized type.
Therefore "@MessageRes" should be always correct in 64 and 32 BĀ“bit, if it points to a pointer sized variable (which is unused by the program, but possibly written by Windows, so the type doesnt matter, only size (and possibly alignment) matters in this case  ;))
However if this is used, the prototype proposed by codetools cannot be used by the compiler, so it must use another prototype which matches and compiles.
BTW, the progam works fine in 32 bit and is not performance critical, so 32 bit is fine, but 64 bit might be needed if snippets of the program are used in 64 bit projects.
Title: Re: [Solved] 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: Peter H on January 22, 2021, 04:28:12 am
Finally - this solution compiles in 32 Bit and 64 Bit and should be compatible with all compilers, where the windows data types are correctly implemented and is compatible with the prototype which is proposed by codetools:
Code: Pascal  [Select][+][-]
  1. procedure EnvInformation.RefreshEnvironment(const Timeout: Cardinal = 5000);
  2. var
  3.   MessageRes: Windows.DWORD_PTR; // this is a pointer sized variable in Pascal and C/C++
  4. begin                           // So it is a DWORD in 32 Bit and a Quadword in 64 Bit
  5.   Windows.SendMessageTimeout(
  6.     Windows.HWND_BROADCAST,
  7.     Messages.WM_SETTINGCHANGE,
  8.     0,
  9.     LPARAM(PChar('Environment')),
  10.     Windows.SMTO_ABORTIFHUNG, Timeout,
  11.     MessageRes //This is a "var" parameter in Pascal and a const pointer (&MessageRes) in C/C++
  12.   );
  13. end;
Title: Re: 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: PascalDragon on January 22, 2021, 08:59:43 am
var lpdwResult: DWORD_PTR, this is argument 7
But "@MessageRes" is a constant pointer.
So, why can I give a constant pointer as argument for a var parameter?
There are more prototypes for this function and some would match.

Yes, there are two overloads as marcov said. The one with var lpdwResult: DWORD_PTR (the one that you found) and the one with lpdwResult: PDWORD_PTR (which I mentioned). If you pass in a DWORD_PTR and not a pointer to a DWORD_PTR then FPC will select the former, otherwise it will select the later.
Title: Re: [Solved] 32 Bit vs 64 Bit Question (Compiling "Path Manager" project)
Post by: Peter H on January 22, 2021, 03:24:49 pm
Yes, I tried again.
Now both cases with and without "@" work correctly and are correctly displayed in the editor, when I use code completion or search for the matching declaration of the function prototype.
This did not work correctly before, probably the code tools where confused by these many $IFDEF, which i removed now.

I did not know all this, when I started, and was confused, so thank anybody for all comments and help, finally everything was helpful!
TinyPortal © 2005-2018