But that is exactly the point. The .NET Runtime can use the Windows API if it is installed for Windows and use the "Linux API" if it is installed under Linux.
The difference with pure C++ is that you would have to re-compile it. Which not all users can do that. That is why you have a different version for different system.s
Of course Linux don't use .exe files so OK. You see the limitations. Don't know how you can run your Windows Forms application. This would have been a good example for Java and .jar files. Except if there is a similar mechanic for C#...
So you have to differentiate if the program is portable and if the code if portable. You can use a library, like QT, for GUIs that are portable in Windows, Linux and Mac OS. But you need to compile it for all those systems. And for 32bit and 64bit versions of those.
To understand the above think of this
Code:
//FormLinux.cpp
class Form
{
...
}
Code:
//FormWindows.cpp
class Form
{
...
}
Code:
//Your code
int main()
{
Form myForm();
}
So if you use the above in QT, as an example, where Form is supposed to be something like a Windows Form, your code is portable. If you compile it in Linux it will use FormLinux.cpp if you compile it under windows FormWindows.cpp. But your code won't change at all.
Something similar, but without compiling, can be down with a VM. You just tell it "built a Windows Form", the what API to use and how exactly to do it you don't care.