Guys,
1.While playing with structures i´ve noticed that the compiler throws an misleading error when you pass
a different structure than the callee expects: "error XS1620: Argument 1 must be passed with the 'ref' keyword"
Such an misleading XS1620 error happens when you do something like this:
IF GetVersionEx ( struInfoExW ) // wrong structure - OSVERSIONINFOEXW
instead of:
IF GetVersionEx ( struInfoEx ) // correct structure - OSVERSIONINFOEX
2. I´m able to compile a "_DLL" import not only as "ANSI" or "UNICODE", but with "AUTO" too, though "AUTO" seems to have no effect at runtime. For what is "AUTO" good for in this context ?
There are some more notes in the attached app, so i thing it should be easy to follow what i´m doing and how i´m using the import possibilities
"_DLL" / "[DllImport" and the charsets Ansi,UniCode and Auto.
BTW I. the StructureTest.viaef has no references to any Vulcan dll or X# dll. Also, the warning 0170 is disabeld via "/nowarn:170" . The structure member "szCSDVersion" is only filled when you´re running Win7 SP1 or Vista SPx. Win 8.1 and Win 10 have no servicepacks.
BTW II. very nice to be able to do something like this now:
? "OutPut: " + lStart + " " + dwStart
// Of course, lStart is a LOGIC and dwStart a DWORD
Any comments/corrections are welcome.
regards
Karl-Heinz
Working with structures - the pure .net way
-
- Posts: 774
- Joined: Wed May 17, 2017 8:50 am
- Location: Germany
Working with structures - the pure .net way
- Attachments
-
- StructureTest.zip
- (2.46 KiB) Downloaded 58 times
Working with structures - the pure .net way
Hi Karl-Heinz,
1. About the misleading error, the problem is that the function requires a param by REF:
_DLL FUNCTION GetVersionEx ( struOS REF OSVERSIONINFOEX ) AS LOGIC
But the code calls it (as in VO), without indicating that the param is actually passed by reference:
GetVersionEx ( struInfoEx )
In Roslyn/c# this would throw a compiler error, but it is allowed in X# for compatibility with VO/Vulcan, although the "more correct" or advised syntax to use would be to explicitly specify that you pass the param by reference:
GetVersionEx ( REF struInfoEx )
Now if you had used the wrong structure (struInfoExW) in your case, you would get a proper error message:
error XS1503: Argument 1: cannot convert from 'ref OSVERSIONINFOEXW' to 'ref OSVERSIONINFOEX'
The reason why you do not get a better error message in the original sample, is due to the X# compiler trickery that takes place and tries to decide if you actually intended to pass a param by reference but did not explicitly mentioned that in the code, in which case the compiler silently and adds the "REF" itself. Apparently something goes wrong in that routine in this particular case and confuses the compiler, making it report an error about not using REF yourself, while actually the real problem is that you used the wrong structure. Not sure how easy it will be to fix this, but I will enter it a a bug report.
2. For things like that, it is extremely helpful to check the generated code/function declarations with ILSpy, to see what the compiler did differently in each case. If you do that, you will see that when using ANSI, the compiler generates this attribute for the function/method:
[DllImport("User32", CharSet := CharSet.Ansi, ExactSpelling =: true, SetLastError := true)]
When you use UNICODE, he only difference is the CharSet part which becomes CharSet.Unicode and for AUTO it becomes CharSet.Auto. Having a look at the documentation for CharSet:
https://docs.microsoft.com/en-us/dotnet ... es.CharSet
it explains what Auto stands for in different OSes.
If you want to have full control over things, it's maybe a good idea to not rely on _DLL declarations, but instead always apply the DllImport attribute yourself to class methods, so you always know what gets emitted.
3. Actually I do not like this and would had liked to be disallowed in X# . It is supported by c#/Roslyn so also supported by X# which uses Roslyn as its backend, but in my opinion it is error prone, as it could hide coding errors where the user typed the wrong variable. In my opinion, it would be better to use this also easy syntax instead:
? "OutPut: " + lStart:ToString() + " " + dwStart:ToString()
which I think is more straightforward about what's happening. But of course it's normal that we can have different opinions!
Chris
1. About the misleading error, the problem is that the function requires a param by REF:
_DLL FUNCTION GetVersionEx ( struOS REF OSVERSIONINFOEX ) AS LOGIC
But the code calls it (as in VO), without indicating that the param is actually passed by reference:
GetVersionEx ( struInfoEx )
In Roslyn/c# this would throw a compiler error, but it is allowed in X# for compatibility with VO/Vulcan, although the "more correct" or advised syntax to use would be to explicitly specify that you pass the param by reference:
GetVersionEx ( REF struInfoEx )
Now if you had used the wrong structure (struInfoExW) in your case, you would get a proper error message:
error XS1503: Argument 1: cannot convert from 'ref OSVERSIONINFOEXW' to 'ref OSVERSIONINFOEX'
The reason why you do not get a better error message in the original sample, is due to the X# compiler trickery that takes place and tries to decide if you actually intended to pass a param by reference but did not explicitly mentioned that in the code, in which case the compiler silently and adds the "REF" itself. Apparently something goes wrong in that routine in this particular case and confuses the compiler, making it report an error about not using REF yourself, while actually the real problem is that you used the wrong structure. Not sure how easy it will be to fix this, but I will enter it a a bug report.
2. For things like that, it is extremely helpful to check the generated code/function declarations with ILSpy, to see what the compiler did differently in each case. If you do that, you will see that when using ANSI, the compiler generates this attribute for the function/method:
[DllImport("User32", CharSet := CharSet.Ansi, ExactSpelling =: true, SetLastError := true)]
When you use UNICODE, he only difference is the CharSet part which becomes CharSet.Unicode and for AUTO it becomes CharSet.Auto. Having a look at the documentation for CharSet:
https://docs.microsoft.com/en-us/dotnet ... es.CharSet
it explains what Auto stands for in different OSes.
If you want to have full control over things, it's maybe a good idea to not rely on _DLL declarations, but instead always apply the DllImport attribute yourself to class methods, so you always know what gets emitted.
3. Actually I do not like this and would had liked to be disallowed in X# . It is supported by c#/Roslyn so also supported by X# which uses Roslyn as its backend, but in my opinion it is error prone, as it could hide coding errors where the user typed the wrong variable. In my opinion, it would be better to use this also easy syntax instead:
? "OutPut: " + lStart:ToString() + " " + dwStart:ToString()
which I think is more straightforward about what's happening. But of course it's normal that we can have different opinions!
Chris
Chris Pyrgas
XSharp Development Team
chris(at)xsharp.eu
XSharp Development Team
chris(at)xsharp.eu
-
- Posts: 774
- Joined: Wed May 17, 2017 8:50 am
- Location: Germany
Working with structures - the pure .net way
Hi Chris,
i see the point. Adding "REF" shows the real problem. In a forum sample that uses ref vars i saw that there
the "REF" Keyword is not used, so i did it the same way - without thinking about why . Wouldn´t it be better - and more readable - that the compiler would force us to insert "REF" instead of adding it silently to the IL Code if it´s missing. ?
The code below runs, but if you don´t know the callee signature a quick look at the caller only suggests that in the first case only the first param is a ref var, and in the second case only the second param.
Foo ( REF dw1 , dw2 )
Foo ( dw1 , REF dw2 )
FUNCTION Foo( dwOne REF DWORD , dwTwo REF DWORD ) AS LOGIC
...
>
> ? "OutPut: " + lStart + " " + dwStart
>
I discovered it by accident, and i´ll use this "feature" only where i quickly need an output without to worry about the Datatypes.
regards
Karl-Heinz
i see the point. Adding "REF" shows the real problem. In a forum sample that uses ref vars i saw that there
the "REF" Keyword is not used, so i did it the same way - without thinking about why . Wouldn´t it be better - and more readable - that the compiler would force us to insert "REF" instead of adding it silently to the IL Code if it´s missing. ?
The code below runs, but if you don´t know the callee signature a quick look at the caller only suggests that in the first case only the first param is a ref var, and in the second case only the second param.
Foo ( REF dw1 , dw2 )
Foo ( dw1 , REF dw2 )
FUNCTION Foo( dwOne REF DWORD , dwTwo REF DWORD ) AS LOGIC
...
>
> ? "OutPut: " + lStart + " " + dwStart
>
I discovered it by accident, and i´ll use this "feature" only where i quickly need an output without to worry about the Datatypes.
regards
Karl-Heinz
Working with structures - the pure .net way
Hi Karl-Heinz,
Wolfgang
Maybe because of compatibility to VO and Vulcan? I suspect there is much code dealing with structures in the GUI classes...Wouldn´t it be better - and more readable - that the compiler would force us to insert "REF" instead of adding it silently to the IL Code if it´s missing. ?
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
-
- Posts: 774
- Joined: Wed May 17, 2017 8:50 am
- Location: Germany
Working with structures - the pure .net way
Hi Wolfgang,
>
> Maybe because of compatibility to VO and Vulcan?
>
that doesn´t satisfy me
At least in the described context, the compiler knows exactly if "REF" needs to be added or not.
regards
Karl-Heinz
>
> Maybe because of compatibility to VO and Vulcan?
>
that doesn´t satisfy me
At least in the described context, the compiler knows exactly if "REF" needs to be added or not.
regards
Karl-Heinz
Working with structures - the pure .net way
Hi Karl-Heinz,
AFAIK there is a lot of VO code around that needs to be ported, and I think the development team will make the migration work as easy as possible.
Wolfgang
AFAIK there is a lot of VO code around that needs to be ported, and I think the development team will make the migration work as easy as possible.
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Working with structures - the pure .net way
Hi guys,
Yes, this is also a preference of mine, I was actually strongly suggesting it in the vulcan years, too, because using REF explicitly makes the code a lot more readable/understandable of what it's doing.
Possibly we will make that code requiring REF in X# at some point, but as Wolfgang said we need to support also the "old" way, because there's so much existing code without it, so the new behavior must be optional. Maybe with a /strict compiler option or something like that.
Chris
Yes, this is also a preference of mine, I was actually strongly suggesting it in the vulcan years, too, because using REF explicitly makes the code a lot more readable/understandable of what it's doing.
Possibly we will make that code requiring REF in X# at some point, but as Wolfgang said we need to support also the "old" way, because there's so much existing code without it, so the new behavior must be optional. Maybe with a /strict compiler option or something like that.
Chris
Chris Pyrgas
XSharp Development Team
chris(at)xsharp.eu
XSharp Development Team
chris(at)xsharp.eu
-
- Posts: 50
- Joined: Fri Feb 16, 2018 7:52 am
Working with structures - the pure .net way
Hi,
why does the code have to be compatible? Coulden't this change be handled by the transporter?
/Mathias
why does the code have to be compatible? Coulden't this change be handled by the transporter?
/Mathias
Working with structures - the pure .net way
Hi Mathias,
That could be a possibility, but it is not easy. First of all, it would have to be an optional feature, because some people do not like this change , also we try to keep the changes to as few as possible, because some people need to share code between VO/X# while porting step by step to X#.
The big issue is that in order to implement that, the XPorter needs to become much smarter, it needs to analyze LOCALs and params, to find their types, then lookup the method signatures of that type and figure out if each param should be passed by REF or not. It's doable, but needs a lot of work, so I am afraid this one of those "nice to have" things which have to wait for after we're finished with more pressuring things like the runtime.
Chris
That could be a possibility, but it is not easy. First of all, it would have to be an optional feature, because some people do not like this change , also we try to keep the changes to as few as possible, because some people need to share code between VO/X# while porting step by step to X#.
The big issue is that in order to implement that, the XPorter needs to become much smarter, it needs to analyze LOCALs and params, to find their types, then lookup the method signatures of that type and figure out if each param should be passed by REF or not. It's doable, but needs a lot of work, so I am afraid this one of those "nice to have" things which have to wait for after we're finished with more pressuring things like the runtime.
Chris
Chris Pyrgas
XSharp Development Team
chris(at)xsharp.eu
XSharp Development Team
chris(at)xsharp.eu
-
- Posts: 774
- Joined: Wed May 17, 2017 8:50 am
- Location: Germany
Working with structures - the pure .net way
Hi Chris,
ok, i´ll keep my legs still and wait
But i have another thing:
it´s not a showstopper, but i´m able to write "code" like below. Only when i add to such a textline a space char followed by some more text an "XS9002: Parser: unexpected input" error is thrown. It seems, that this only happens if a dialect other than <core> is selected. Only <core> throws immediately an error.
-----------
CLASS Foo1
classghgghg
forhjhjh
jkjkj
interfacehjhh
END CLASS
INTERFACE iFoo
ISfghfg
hjkhkh
jkjk
END INTERFACE
------------
BTW. Is it possible to add the *.viaef extension to the list of allowed upload files ?
regards
Karl-Heinz
ok, i´ll keep my legs still and wait
But i have another thing:
it´s not a showstopper, but i´m able to write "code" like below. Only when i add to such a textline a space char followed by some more text an "XS9002: Parser: unexpected input" error is thrown. It seems, that this only happens if a dialect other than <core> is selected. Only <core> throws immediately an error.
-----------
CLASS Foo1
classghgghg
forhjhjh
jkjkj
interfacehjhh
END CLASS
INTERFACE iFoo
ISfghfg
hjkhkh
jkjk
END INTERFACE
------------
BTW. Is it possible to add the *.viaef extension to the list of allowed upload files ?
regards
Karl-Heinz
- Attachments
-
- Foo.zip
- (1.03 KiB) Downloaded 69 times