Thread overview
Using stdin/out in a windows application bugs only when compiled to 64bit.
Jun 07, 2018
realhet
Jun 07, 2018
realhet
Jun 08, 2018
realhet
Jun 08, 2018
Mike Parker
Jun 08, 2018
realhet
Jun 08, 2018
Mike Franklin
Jun 08, 2018
realhet
June 07, 2018
Hi,

The following narrow test program works fine when compiled with DMD to 32bit target:

import std.stdio, core.sys.windows.windows, core.runtime;
extern(Windows) int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
{
	Runtime.initialize;
	writeln("Hello");
	stdout.flush; //exception
        readln; //exception
	return 0;
}

It shows the console window and waits for user input at readln.

If I try to compile this to 64bit target or with LDC (both 32 and 64) it gets privileged instruction exception at stdout.flush. If I comment out flush then it fails at readln.

Is there a way to fix this? I don't wanna lose the console window even on 64bit.

Thanks!

June 07, 2018
On 6/7/18 3:19 PM, realhet wrote:
> Hi,
> 
> The following narrow test program works fine when compiled with DMD to 32bit target:
> 
> import std.stdio, core.sys.windows.windows, core.runtime;
> extern(Windows) int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
> {
>      Runtime.initialize;
>      writeln("Hello");
>      stdout.flush; //exception
>          readln; //exception
>      return 0;
> }
> 
> It shows the console window and waits for user input at readln.
> 
> If I try to compile this to 64bit target or with LDC (both 32 and 64) it gets privileged instruction exception at stdout.flush. If I comment out flush then it fails at readln.
> 
> Is there a way to fix this? I don't wanna lose the console window even on 64bit.
> 
> Thanks!
> 

Are you just compiling the 32-bit dmd version with default flags?

If so, it's likely an issue with MSVC runtime library not being properly set up. DMD 32 bit by default uses DMC runtime.

To verify, use the switch -m32mscoff to do 32-bit result but link against MSVC library.

I know this isn't really an answer, but it at least narrows it down. Been a while since I did windows development, but it sounds like you are trying to print to or read from a console that isn't open. A windows program that has gui-only flag doesn't by default allocate a console. Most likely dmc just does it for you.

-Steve
June 07, 2018
On Thursday, 7 June 2018 at 19:42:05 UTC, Steven Schveighoffer wrote:
> Are you just compiling the 32-bit dmd version with default flags?
Yes, no flags at all and it defaults to a 32bit target. I can use the console and able to make windows, and able to setup an opengl window too.
The console (stdin/stdout/SetConsoleAttribute) stuff crashes only when I compile to 64bit with DMD or to 32/64 with LDC.

> If so, it's likely an issue with MSVC runtime library not being properly set up. DMD 32 bit by default uses DMC runtime.
I just tried to put up msvcrt2017 runtime but doesnt solved the issue.

> To verify, use the switch -m32mscoff to do 32-bit result but link against MSVC library.
Compiling this way is impossible: It can't link even basic stuff like GetDC().

>> I know this isn't really an answer, but it at least narrows it
> down. Been a while since I did windows development, but it sounds like you are trying to print to or read from a console that isn't open. A windows program that has gui-only flag doesn't by default allocate a console. Most likely dmc just does it for you.

To be honest I don't use gui-only flag as I don't even know about where to put it :D (.def file maybe, but I don't). I use core.Runtime.initialize only. That is enough in 32bit but not in 64bit.

My goal is to have a fully functional exe that contains every necessary things inside it. So far this is given when I use DMD and compile to win32.
The error I'm getting is not a 'friendly' exception. It is a privileged instruction crash.
And it caused only by adding the -m64 option, not changing anything else. The linking is done by DMD itself also. (If I link with msvcenv.bat && MSLink.exe, it produces also the same fail.)
June 07, 2018
On 6/7/18 7:16 PM, realhet wrote:
> On Thursday, 7 June 2018 at 19:42:05 UTC, Steven Schveighoffer wrote:
>> Are you just compiling the 32-bit dmd version with default flags?
> Yes, no flags at all and it defaults to a 32bit target. I can use the console and able to make windows, and able to setup an opengl window too.
> The console (stdin/stdout/SetConsoleAttribute) stuff crashes only when I compile to 64bit with DMD or to 32/64 with LDC.

So that is definitely dmc initializing the console window in that context.

> 
>> If so, it's likely an issue with MSVC runtime library not being properly set up. DMD 32 bit by default uses DMC runtime.
> I just tried to put up msvcrt2017 runtime but doesnt solved the issue.

Sorry, I'm not familiar with Windows enough to help.

> 
>> To verify, use the switch -m32mscoff to do 32-bit result but link against MSVC library.
> Compiling this way is impossible: It can't link even basic stuff like GetDC().

Also lost with this one.

> 
>>> I know this isn't really an answer, but it at least narrows it
>> down. Been a while since I did windows development, but it sounds like you are trying to print to or read from a console that isn't open. A windows program that has gui-only flag doesn't by default allocate a console. Most likely dmc just does it for you.
> 
> To be honest I don't use gui-only flag as I don't even know about where to put it :D (.def file maybe, but I don't). I use core.Runtime.initialize only. That is enough in 32bit but not in 64bit.

I don't know what happens nowadays. I literally haven't tried to build non-console Windows applications since like 2008 or so? I thought it was a linker flag, but I'm not sure.

> My goal is to have a fully functional exe that contains every necessary things inside it. So far this is given when I use DMD and compile to win32.
> The error I'm getting is not a 'friendly' exception. It is a privileged instruction crash.

Well, you are telling the linker, when you create your WinMain function that you are handling all the runtime setup yourself, and you are not using a console. So somehow you have to initialize the console.

Alternatively, you can define extern(C) main function, and handle the D initialization from there.

Is there a reason you want the D runtime but don't want to have D initialize it for you?

> And it caused only by adding the -m64 option, not changing anything else. The linking is done by DMD itself also. (If I link with msvcenv.bat && MSLink.exe, it produces also the same fail.)

Just to be clear, it's not the 32-bit vs 64-bit, it has to do with which C runtime you are using.

-Steve
June 08, 2018
On Thursday, 7 June 2018 at 23:25:45 UTC, Steven Schveighoffer wrote:
>...

The WinMain exported function works alone well and on 32bit it also does the console.
On 64 I also tried AllocConsole, but fail. I get the Console handle with GetConsoleHandle, it sends back a nonzero value.

But as I play with it, now I can broke it even when I only use main() as export.

This bat file runs(dmd and visual-d installed to default c:\d path) (It only affects LDC & win64)
----------------------------------------------------
echo void main(){ import std.stdio; writeln(123); } > test.d
ldmd2 -vcolumns -c -op -allinst -w -m64 -release -O -inline -boundscheck=off test.d
call msvcenv amd64
cd c:\d
link /LIBPATH:C:\d\ldc2\lib64 /OUT:test.exe /MACHINE:X64 /MAP legacy_stdio_definitions.lib test.obj druntime-ldc.lib phobos2-ldc.lib msvcrt.lib
test.exe
----------------------------------------------------

And when I want to run the same exe from outside with totalcommander, it brings up an "application was unable to start: 0xc00000fb exception in a small window o.O.

UPDATE: The exe only starts when "msvcenv.bat amd64" was called before. That enironment setter bat file is needed for the exe as well o.O

I really thank you for trying to help me. Maybe that other different runtime you mentioned is using environment variables to be able to start up. Tomorrow I will test it.
June 08, 2018
On Thursday, 7 June 2018 at 19:19:55 UTC, realhet wrote:
> Hi,
>
> The following narrow test program works fine when compiled with DMD to 32bit target:
>
> import std.stdio, core.sys.windows.windows, core.runtime;
> extern(Windows) int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
> {
> 	Runtime.initialize;
> 	writeln("Hello");
> 	stdout.flush; //exception
>         readln; //exception
> 	return 0;
> }
>
> It shows the console window and waits for user input at readln.
>
> If I try to compile this to 64bit target or with LDC (both 32 and 64) it gets privileged instruction exception at stdout.flush. If I comment out flush then it fails at readln.

I get no exceptions with this code, but if I run if I compile with no flags, it prints hello and waits for input. With -m32mscoff or -m64, there's no output. If I run from Windows Explorer, the no-flag version pops up a console, prints and waits, while the -m32mscoff and do nothing.

As Steven mentioned earlier, this is a runtime issue. The standard I/O streams are not available in the MSVC runtime by default when compiling as a GUI application. You don't have to enable any flags to get that -- it's automatic when WinMain is present. The DititalMars runtime, which is what you get with the default compile, apparently does initialize the standard I/O streams and makes sure the console pops up when you write to it.

>
> Is there a way to fix this? I don't wanna lose the console window even on 64bit.
>

If you want to keep WinMain and still have the console stuff available from the start with the MSVC runtime, you can get it with this:

dmd -m64 -L/SUBSYSTEM:console -L/ENTRY:WinMainCRTStartup foo.d

With this (for -m32mscoff as well), the above code runs the same as it does with the DigitalMars runtime.



June 08, 2018
On Thursday, 7 June 2018 at 19:19:55 UTC, realhet wrote:
> Hi,
>
> The following narrow test program works fine when compiled with DMD to 32bit target:
>
> import std.stdio, core.sys.windows.windows, core.runtime;
> extern(Windows) int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
> {
> 	Runtime.initialize;
> 	writeln("Hello");
> 	stdout.flush; //exception
>         readln; //exception
> 	return 0;
> }
>
> It shows the console window and waits for user input at readln.
>
> If I try to compile this to 64bit target or with LDC (both 32 and 64) it gets privileged instruction exception at stdout.flush. If I comment out flush then it fails at readln.
>
> Is there a way to fix this? I don't wanna lose the console window even on 64bit.
>
> Thanks!

You might be running into this issue:  https://issues.dlang.org/show_bug.cgi?id=6880

I recall similar issues with a project I was working on, but I don't remember all the details now.  Anyway, I ended up with this in the end.

https://github.com/JinShil/Dsus2/blob/b08c66c6a6efb46134c409aac9f1c600d62f99fa/Dsus2/main.d#L300-L331

Mike
June 08, 2018
On Friday, 8 June 2018 at 02:44:13 UTC, Mike Parker wrote:
>...you can get it with this:
>
> dmd -m64 -L/SUBSYSTEM:console -L/ENTRY:WinMainCRTStartup foo.d

Thank You! It works with LDC -m64 as well.
And now that I repaired my VCRedist2015 the bat file test (my second code example) is working too.
June 08, 2018
On Friday, 8 June 2018 at 03:08:21 UTC, Mike Franklin wrote:
> I recall similar issues with a project I was working on, but I don't remember all the details now.  Anyway, I ended up with this in the end.

Using main() instead of WinMain() did the trick too. Also it's simpler, so I choose this way. But it was also important to learn about the SUBSYSTEM and ENTRY linker options.

Thank you all very much!