According to the release note of Xcode 9.3, macOS will remove the support of running 32-bit application without compromise in future version, and they include a 'no32exec' mode in the beta version of macOS 10.13.4 which 32-bit applications will no longer run on.
System.Windows.Forms haven't been port to Cocoa API, so all WinForms apps won't runs on future macOS.
Recently I referenced to XplatUICarbon and managed to make a Cocoa version of XplatUI, but I haven't understand how it works, it would be better if anyone who can give me some thoughts.
Correct, at the moment we don't support WinForms when running with 64bit Mono on macOS. We'd really appreciate a pull request making this work.
You can use mono32 to run the app with 32bit Mono for now.
@CNAmira you said you managed to make a Cocoa version of XplatUI, do you mean something that was able to run a simple WinForms Hello World? Is the code available somewhere?
Even if it's just a first step it would be amazing to have it 😄
Not yet.
I looked into this yesterday
The good news is that Carbon now works on 64 bits. I have long assumed Carbon died with 32, so either my memory is faulty or Apple at some point reversed course and added 64 bit support to it.
Anyways, we now have a path to 64 but support.
What needs to be done is to create a 64bit backend. It should probably be a replica of the 32 bit one, with some bits shared where possible.
The 64 bit version needs to have most of the structures in Structs.xs upgraded to 64 bits. I did a partial review last night and I got a good sense of what needs doing. Will continue the audit tonight.
Beyond the 64bit structure update we probably need to upgrade many calls that we make into Carbon that were suitable or requires back in 2005/2006 (either due to platform requirements and back compact needs) and replace them with new ones.
I haven’t found replacement for all, but WebKit and an old fork From adobe of WebKit have enough clues about what to do here.
So those would be the two stages.
In my memory most Carbon functions on 64bits are just stubs.
I built a simple Carbon application in Xcode, it only worked when I switched the architecture to i386 other than x86_64.
we will have to wait until we find those, we only use a handful of Carbon apis.
An alternative is to build a new backend on top of Cocoa, about 5000 lines of code. To do this, I would dynamically load Xamarin.Mac, but it would be an external dependency and we would need to figure out how to distribute this.
In my memory most Carbon functions on 64bits are just stubs.
@CNAmira The first error I encountered when running a WinForms app on 64bits was that GetWindowPort was not found:
System.EntryPointNotFoundException: GetWindowPort
at (wrapper managed-to-native) System.Drawing.MacSupport.GetWindowPort(intptr)
at System.Drawing.MacSupport.GetCGContextForView (System.IntPtr handle) [0x0007f] in <fff295db00f34819a42b2efd9e41c64c>:0
at System.Drawing.Graphics.FromHwnd (System.IntPtr hwnd) [0x00042] in <fff295db00f34819a42b2efd9e41c64c>:0
at System.Windows.Forms.Control.CreateGraphics () [0x00019] in <2c6cbf9dc9fa4171bf0a705a710f3831>:0
at (wrapper remoting-invoke-with-check) System.Windows.Forms.Control.CreateGraphics()
We can see that GetWindowPort is external (uppercase T) in 32bits but is non-external on 64bits (lowercase t):
$ nm --arch=i386 /System/Library/Frameworks/Carbon.framework/Versions/Current/Frameworks/HIToolbox.framework/HIToolbox | grep GetWindowPort$
002b2eb1 T _GetWindowPort
$ nm --arch=x86_64 /System/Library/Frameworks/Carbon.framework/Versions/Current/Frameworks/HIToolbox.framework/HIToolbox | grep GetWindowPort$
00000000002386eb t _GetWindowPort
I don't know how DllImport is implemented on macOS, but I’m not surprised that it can't bind a non-external symbol.
So, it’s not a strictly a stub, but it has been changed to be inaccessible from outside.
An alternative is to build a new backend on top of Cocoa, about 5000 lines of code. To do this, I would dynamically load Xamarin.Mac, but it would be an external dependency and we would need to figure out how to distribute this.
@migueldeicaza Before creating XplatUICocoa.cs and blindly starting to implement it I thought I’d check GitHub to see someone already started to work on it. Do you already have a prototype of a Cocoa backend or are these 5000 lines of code you are talking about just an estimation?
Also, I really think the way forward is to use Cocoa, not Carbon. Qt for example took that direction in 2009 if I remember correctly.
@0xced We have a fork (https://github.com/filipnavara/mac-playground) that has XplatUICocoa mostly implemented. However it's not matter of simple cut & paste to plug it in back to Mono. We had some design choices early on, such as using the System.Drawing implementation on top of Cocoa, that make it complicated to use it as drop-in binary replacement. It works somewhat as a source-level replacement where applications link to our System.Window.Forms and System.Drawing libraries.
That said, large parts of the code were back-ported to official repositories in one way or another. The System.Drawing changes were backported for the Cocoa version (https://github.com/mono/sysdrawing-coregraphics). I have a pending patch to fix System.Drawing on 64-bit Cocoa, but it depends on patches to libgdiplus that were not merged yet. And the non-Cocoa changes to SWF were 90% back-ported to official Mono repository.
The next step is taking the XplatUICocoa from our repository and making it a separate library that can be loaded as external driver from SWF (like https://github.com/migueldeicaza/CocoaDriver). I have done this locally as a proof of concept, so I know it's possible, but my effort was largely unfinished. There was still some glue code missing to support creating Graphics objects for non-client and client parts of the controls, so the rendering of controls was not perfect.
I've just been struggling with this issue, because of problems compiling libgdiplus (and dependencies!) as 32-bit. Either way, I gave up on that, and tried XQuartz. It seems to work OK in my specific application after a very brief test. For anyone else wanting to try it, here are the steps I followed (I already had Mono compiled and installed as 64-bit):
CPPFLAGS=-I/opt/X11/include LDFLAGS=-L/opt/X11/lib ./configureLD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/X11/lib MONO_MWF_MAC_FORCE_X11=true mono ___.exeIt's not necessarily perfect, but I hope it might be useful if anyone else has problems before a suitable driver is finished.
Since Microsoft has released the full sources of WinForms and WPF, is it possible to combine those sources and Wine's 'native' libs to provide a better WinForms experience?
Assuming it is possible, it'd probably be no better than running .Net on Wine, which does work but not particularly well.
Exactly, that is a different solution, it would not help in this scenario.
The baseline is still there. That said, since this was originally started, we did merge a lot of the work from @filipnavara which should get us on a path to have a Cocoa backend, but I have not checked recently on how much was missing or was completed.
What's the current status on this issue?
I was interested in fixing couple of issues in WF but the Carbon API is so deprecated I can't even find any documentation and knowing Apple I have a feeling they will soon knock it off completely.
The work from @filipnavara sounds very promising. It's not clear to me if it can be merged though.
The biggest issues I use the mono on macOS are visibility and z-order issue (It is also said in https://semisignal.com/mono-winforms-on-os-x/).
I tried mac-playground from @filipnavara. It works great. The visibility and z-order issues disappear. Also I can use many native components on macOS.
@PreferLinux Did you get 64-bits compiling of Windows.Forms working with Xquartz?
Yes, I did. Just make sure libgdiplus is compiled with X11 (https://github.com/mono/libgdiplus#build-instructions), and run the application with LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/X11/lib MONO_MWF_MAC_FORCE_X11=true mono ___.exe
In my use-case it seems to work reliably, but is somewhat slow. Obviously, if XQuartz isn't running, it has to be started when starting the application – in my experience that takes at least 5 – 10 seconds. Drawing in general seems pretty slow as well – e.g. owner-drawn ListBox. And after installing XQuartz it requires a log out and back in before it'll get used, which could make application installation more awkward.
But the main thing is, it works.
@PreferLinux Well, I’m probably not as experienced as you are on this topic so don’t understand really how to do the things you said.
Will this solution also work when building and compiling within Visual Studio?
Basically you have to install libgdiplus (a native component of Mono used by System.Drawing, a substitute for Window's GDI+) from source, so that it is compiled with the correct options. And to use that you probably have to install the rest of Mono from source too, with the option --with-libgdiplus=sibling so that it finds the correct libgdiplus. It probably also requires installing Cairo from source so that it can use X11 too (PKG_CONFIG_PATH="/opt/X11/lib/pkgconfig" ./configure --with-x --enable-xlib=yes --enable-xcb=no), and possibly Pango as well (no special options needed) because it uses Cairo; and that would probably need to be done before compiling libgdiplus... So yeah, it is a bit complicated, and I can't recall what is actually required! (I distribute Mono and its dependencies with my application, all compiled from source; so while I've got it all sorted now I don't remember the exact details of what I had to compile myself before I was simply compiling _everything_ myself!)
The application itself can be compiled in Visual Studio the same as for Mono on Linux, which may well be exactly the same as for .Net on Windows.
Then when you run the application from Terminal, instead of just doing mono whatever.exe you need to run it like I posted above – which basically sets the environment variables LD_LIBRARY_PATH (so that libgdiplus can find XQuartz's libraries) and MONO_MWF_MAC_FORCE_X11 (so that Mono uses X11 rather than native macOS), and also does the mono whatever.exe part.
@akoeplinger Do you know where this landed re. patches to libgdiplus, System.Drawing, and System.Windows.Forms?
@filipnavara Was XplatUICocoa ever merged?
@alexchandel as far as I know the current situation is still the same: Mono's WinForms doesn't work on 64bit macOS.
Coincidentally I just tested this in the current version of visual studio for Mac yesterday. Yeah, you get a warning about the carbon API not being 64-bit and WinForms apps don’t run.
I’m using a manually built copy of the port linked earlier in this thread instead.
@akoeplinger But were the libgdiplus patches merged upstream?
And does Mono now compile libgdiplus with the right options for this? If not, is it a simple change to do this?
I'm speaking in reference to @filipnavara's comments:
That said, large parts of the code were back-ported to official repositories in one way or another. The System.Drawing changes were backported for the Cocoa version (https://github.com/mono/sysdrawing-coregraphics). I have a pending patch to fix System.Drawing on 64-bit Cocoa, but it depends on patches to libgdiplus that were not merged yet. And the non-Cocoa changes to SWF were 90% back-ported to official Mono repository.
And is Mono preparing to adopt the Cocoa System.Drawing fork?
@alexchandel libgdiplus is maintained by us so we are the upstream in this case and we ship the latest version :)
I'm not sure how much work is still left to adopt @filipnavara's work or complete the Cocoa backend.
In the mean time would it make sense for the official mono builds to start building libgdiplus with X11 enabled on macOS?
It already does, at least the ones from www.mono-project.com.
However there seems to still be a problem since I tried and it crashes when trying to find GdipCreateFromXDrawable_linux:
$ DISPLAY=:0 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/X11/lib MONO_MWF_MAC_FORCE_X11=true mono helloworld.exe
Unhandled Exception:
System.EntryPointNotFoundException: GdipCreateFromXDrawable_linux assembly:<unknown assembly> type:<unknown type> member:(null)
at (wrapper managed-to-native) System.Drawing.GDIPlus.GdipCreateFromXDrawable_linux(intptr,intptr,intptr&)
at System.Drawing.Graphics.FromXDrawable (System.IntPtr drawable, System.IntPtr display) [0x00000] in /Users/builder/jenkins/workspace/build-package-osx-mono/2019-08/external/bockbuild/builds/mono-x64/mcs/class/System.Drawing/System.Drawing/Graphics.cs:1790
at System.Drawing.Graphics.FromHwnd (System.IntPtr hwnd) [0x000ef] in /Users/builder/jenkins/workspace/build-package-osx-mono/2019-08/external/bockbuild/builds/mono-x64/mcs/class/System.Drawing/System.Drawing/Graphics.cs:1748
at System.Windows.Forms.XplatUIX11.GetAutoScaleSize (System.Drawing.Font font) [0x00010] in /Users/builder/jenkins/workspace/build-package-osx-mono/2019-08/external/bockbuild/builds/mono-x64/mcs/class/System.Windows.Forms/System.Windows.Forms/XplatUIX11.cs:3890
at System.Windows.Forms.XplatUI.GetAutoScaleSize (System.Drawing.Font font) [0x00000] in /Users/builder/jenkins/workspace/build-package-osx-mono/2019-08/external/bockbuild/builds/mono-x64/mcs/class/System.Windows.Forms/System.Windows.Forms/XplatUI.cs:677
at System.Windows.Forms.Form.GetAutoScaleSize (System.Drawing.Font font) [0x00000] in /Users/builder/jenkins/workspace/build-package-osx-mono/2019-08/external/bockbuild/builds/mono-x64/mcs/class/System.Windows.Forms/System.Windows.Forms/Form.cs:1531
at System.Windows.Forms.Form..ctor () [0x00018] in /Users/builder/jenkins/workspace/build-package-osx-mono/2019-08/external/bockbuild/builds/mono-x64/mcs/class/System.Windows.Forms/System.Windows.Forms/Form.cs:353
at HelloWorld..ctor () [0x00000] in <f9a7b878c7c84b6ab16bdf7aa6b87fdd>:0
at (wrapper remoting-invoke-with-check) HelloWorld..ctor()
at HelloWorld.Main () [0x00001] in <f9a7b878c7c84b6ab16bdf7aa6b87fdd>:0
This is because we're building the Cairo library without X support on macOS: https://github.com/mono/bockbuild/blob/5b5e5d686742b38e089416afcfa5be0003db1157/packages/cairo.py#L24-L30
@filipnavara do you happen to know whether enabling that would cause any issues?
There is no easy way to build libgdiplus with X11 support on macOS. You have to build many of dependent libraries yourself (cairo, pango, harfbuzz at least) without sourcing them from Homebrew. I know the Mono builds may be doing that anyway but I tried doing it outside of Bockbuild and it was pain in the ass difficult.
Just wondering if there is any hope for a solution to this?
If anyone knows how to resolve this, please contact me as I would be happy to pay for a solution (if that would expedite completion).
I would cheap in as well if somebody can help with this please. @filipnavara would building mono 2.10.5 or 2.10.9 as a 64 bit package which were the last builds I believe with X11 support work and be simple enough as a workaround solution? https://xamarin.github.io/bugzilla-archives/24/2474/bug.html
@filipnavara 's https://github.com/filipnavara/mac-playground did the trick for us! If you are struggling making your winforms app work on Catalina, it should work.
@zukzuk2020 @filipnavara unfortunately mac-playground is really not sustainable as it only covers the base WinForms assembly. Worse still is that it's inoperable with cross-dependencies (i.e. if your project references another dependency which references System.Drawing or anything like that, you would have to rebuild everything from scratch).
Any news on this front? Is anything expected to change with the move to .NET 5? Or are all WinForms projects simply dead on MacOS?
@zukzuk2020 @filipnavara unfortunately mac-playground is really not sustainable as it only covers the base WinForms assembly. Worse still is that it's inoperable with cross-dependencies (i.e. if your project references another dependency which references
System.Drawingor anything like that, you would have to rebuild everything from scratch).
That's not entirely accurate. It is possible to use 3rd-party code with the included System.Drawing library but it's hell to manage in the build system. You basically have to convince the build system to prefer certain assets over others. It is even possible to get the System.Drawing.Common facade over System.Drawing from a NuGet with carefully crafted PackageReference. As long as the exported API is compatible it works at runtime. Definitely not for the faint of heart and not something I would recommend though.
Any news on this front? Is anything expected to change with the move to .NET 5? Or are all WinForms projects simply dead on MacOS?
Unless someone takes action to revive it I would consider it dead. We continue to maintain our code internally and use it in our product but we have no resources to invest into to it aside from maintaining the GitHub mac-playground mirror (and even that one tracks the wrong internal branch now :-/).
For what it's worth, I've still been using it (mac-playground) in multiple projects. It's better than nothing at all, even though I have to roll custom builds of every single dependency that references WinForms & related in order for it to work. In general I've been using it for ports of open source code because writing a brand new UI isn't worth my time and otherwise there would be no Mac build at all.
I was hoping .NET 5 moving everything to Nuget would make the problem easier instead of harder, but it's a shame to know I'll have continue with a complicated work-around.
I'm not able to help either, but just wanted to show my support for even some kind of basic not-very-good support like we had before x64 was a requirement.
@filipnavara @Sappharad I agree the mac-playground is an incredibly useful contribution and in no way I wanted to convey the idea I am not grateful for it.
However, for better or worse, WinForms continues to have a future as part of .NET 5 on windows, and so making a sustainable solution is valuable work which will remain relevant for the foreseeable future. Is there a path forward to merge mac-playground contributions back to Mono? Are contributions being accepted still with .NET Core and .NET Fx merger?
I just wanted to understand a bit better what are the hurdles involved in integrating it with the current Mono codebase. It looks like there might be a window of opportunity for doing this in time for .NET 5.
I cannot understand why the fork of mac-playground cannot be merged here.
.NET are a great platform, and project mono are something vital for it.
Today, we are on 2020, not on 2002, when .net first release go to public and Mac OS and Linux have low market share. Now, yes, windows have a lot of market, but Mac OS have their users and Linux too. Even Microsoft are moving .NET to a "full cross-platform" (with the next .NET 5 and leaving Framework support), but, designer and frontend are windows only. Does that have some sense? "Ey, you can use .NET to do your app for something, but only for console apps if you want to use cross-platform. If you want to use it on any system, please search some different."
I cannot understand why.
And, I can understand Mono project members doesn't have time to refactor more than 5000 lines of source, but, why not Microsoft directly?
Today you can use VS on mac/linux. Today you can have SQLServer on linux. But you cannot compile a simple hello world with graphics on a mac.
"Ey, you can use .NET to do your app for something, but only for console apps if you want to use cross-platform. If you want to use it on any system, please search some different."
Today you can use VS on mac/linux. Today you can have SQLServer on linux. But you cannot compile a simple hello world with graphics on a mac.
Your statements imply that you don't understand the process of developing .NET Apps for Mac then. You can already do these things, you just need to use the native UI tools and classes provided by Xamarin and included with Visual Studio. You can build fully native graphical applications, you just need to build them using native tools.
This thread is about using Windows Forms, which was created for native Windows UI's on Windows. The people who want it on macOS (including me) just want it for ease of porting so I don't need to re-create a massive open source app that gets changes faster than I have time to keep up with myself.
If someone is building a graphical application from scratch and they want to support multiple platforms, they design for that from the start by using something like Eto Framework (comparable to using WinForms) or Avalonia (comparable to WPF).
Most helpful comment
I looked into this yesterday
The good news is that Carbon now works on 64 bits. I have long assumed Carbon died with 32, so either my memory is faulty or Apple at some point reversed course and added 64 bit support to it.
Anyways, we now have a path to 64 but support.
What needs to be done is to create a 64bit backend. It should probably be a replica of the 32 bit one, with some bits shared where possible.
The 64 bit version needs to have most of the structures in Structs.xs upgraded to 64 bits. I did a partial review last night and I got a good sense of what needs doing. Will continue the audit tonight.
Beyond the 64bit structure update we probably need to upgrade many calls that we make into Carbon that were suitable or requires back in 2005/2006 (either due to platform requirements and back compact needs) and replace them with new ones.
I haven’t found replacement for all, but WebKit and an old fork From adobe of WebKit have enough clues about what to do here.
So those would be the two stages.