- Running Windows Forms on Linux with Mono
- Step 1 — Install Mono
- Step 2 — Create an Application
- Step 3 — Compile and Run
- Taking it Further
- About Mark Heath
- Sound Code
- Friday, 6 June 2014
- Running Windows Forms on Linux with Mono
- Step 1 — Install Mono
- Step 2 — Create an Application
- Step 3 — Compile and Run
- Taking it Further
- Mono Basics
- Console Hello World
- HTTPS connections
- WinForms Hello World
- ASP.NET Hello World
- Gtk# Hello World
- Porting Winforms Applications
- The Project
- Getting Started
- MoMA Summary
- Trying It Out
- Porting Strategies
- Remove / Comment Code
- Compiler Conditionals
- Runtime Conditionals
- Rewriting Code
- Porting NClass
- Continuing the Port
- Running on Linux
- Linux Platform Differences
- Win32 P/Invokes
- Case Sensitivity
- The Path Separator
- Conclusion
Running Windows Forms on Linux with Mono
Although WinForms may be “dead”, it does have one trick up its sleeve that WPF doesn’t, and that is you can run WinForms apps on mono. Here’s a simple guide to running a Windows Forms application on Ubuntu
Step 1 — Install Mono
Open a terminal window, and make sure everything is up to date with the following commands:
Now you can install mono with the following command:
Step 2 — Create an Application
Now we need to create our C# source file. You can use any text editor you like, but if like me you aren’t familiar with Linux text editors like vi or emacs, gedit is a simple notepad-like application which is easy to use. Launch it with the following command: (the ampersand at the end tells the terminal not to wait for gedit to close before letting us continue)
Now let’s create a very simple application:
Step 3 — Compile and Run
Now we’re ready to compile. The C# compiler in mono is gmcs. We’ll need to tell it we’re referencing the Windows Forms DLL:
To run the application, simply call mono, passing in the executable:
And that’s all there is to it! We have a WinForms app running on Linux.
Although mono doesn’t support everything in WinForms, you can use most standard controls, so you can easily add further UI elements:
Taking it Further
Obviously writing applications by hand like this is a bit cumbersome, but there is an IDE you can use for Linux called monodevelop. You install it like this:
This then gives you a nice editing environment, allowing you to debug, and manage project references (you’d usually add System.Windows.Forms and System.Drawing). Unfortunately it doesn’t offer a WinForms designer – for desktop apps it prefers you to use GTK#. Nevertheless, it’s a nice free IDE allowing you to experiment with getting your existing Windows Forms applications working cross-platform on Linux. (It seems this will also work on OS X with mono installed but I don’t have a Mac so I haven’t tried it out)
About Mark Heath
I’m a Microsoft MVP and software developer based in Southampton, England, currently working as a Software Architect for NICE Systems. I create courses for Pluralsight and am the author of several open source libraries. I currently specialize in architecting Azure based systems and audio programming. You can find me on:
Источник
Sound Code
a place for me to talk about programming and .NET . because my friends and family aren’t interested
Friday, 6 June 2014
Running Windows Forms on Linux with Mono
Although WinForms may be “dead”, it does have one trick up its sleeve that WPF doesn’t, and that is you can run WinForms apps on mono. Here’s a simple guide to running a Windows Forms application on Ubuntu
Step 1 — Install Mono
Open a terminal window, and make sure everything is up to date with the following commands:
Now you can install mono with the following command:
Step 2 — Create an Application
Now we need to create our C# source file. You can use any text editor you like, but if like me you aren’t familiar with Linux text editors like vi or emacs, gedit is a simple notepad-like application which is easy to use. Launch it with the following command: (the ampersand at the end tells the terminal not to wait for gedit to close before letting us continue)
Now let’s create a very simple application:
Step 3 — Compile and Run
Now we’re ready to compile. The C# compiler in mono is gmcs. We’ll need to tell it we’re referencing the Windows Forms DLL:
gmcs wf.cs –r:System.Windows.Forms.dll
To run the application, simply call mono, passing in the executable:
And that’s all there is to it! We have a WinForms app running on Linux.
Although mono doesn’t support everything in WinForms, you can use most standard controls, so you can easily add further UI elements:
Taking it Further
Obviously writing applications by hand like this is a bit cumbersome, but there is an IDE you can use for Linux called monodevelop. You install it like this:
Источник
Mono Basics
After you get Mono installed, it’s probably a good idea to run a quick Hello World program to make sure everything is set up properly. That way you’ll know that your Mono is working before you try writing or running a more complex application.
Console Hello World
To test the most basic functionality available, copy the following code into a file called hello.cs.
To compile, use csc:
Note: csc compiler is not available on all platforms or in very old Mono versions, in such cases use mcs instead.
The compiler will create “hello.exe”, which you can run using:
The program should run and output:
HTTPS connections
To make sure HTTPS connections work, run the following command to check whether you can connect to nuget.org:
The program prints the website contents if everything works or throws an exception if it doesn’t.
WinForms Hello World
The following program tests writing a System.Windows.Forms application.
To compile, use csc with the -r option to tell the compiler to pull in the WinForms libraries:
The compiler will create “hello.exe”, which you can run using:
NOTE: on macOS you’ll have to wait around a minute the very first time you run this command. You also need to use mono32 since WinForms isn’t supported on 64bit yet. As of macOS 10.15 Catalina there’s no 32bit support anymore so WinForms doesn’t work there at the moment.
ASP.NET Hello World
Create a text file with the name hello.aspx and the content:
Then run the xsp4 command from that directory:
Gtk# Hello World
The following program tests writing a Gtk# application.
To compile, use mcs with the -pkg option to tell the compiler to pull in the Gtk# libraries (note that Gtk# must be installed on your system for this to work):
Источник
Porting Winforms Applications
by Jonathan Pobst
The amount of effort required to get an existing Winforms app running on Mono can vary greatly. Although many small apps will run on Mono unmodified, many apps will require some work on the developer’s part to run smoothly on Mono. This guide will attempt to port a non-trivial open source application to document several of the issues a developer may run into while porting their app to Mono.
The Project
For this guide, the open source application NClass has been chosen. It is a UML compliant class designer that looks very much like the one that ships in Visual Studio 2005. It is licensed under the GPL and LGPL, and has not been written with Mono in mind. We will be using Mono 1.2.4 to port NClass to run on Linux.
NClass running on .NET with included example file:
Getting Started
For the purpose of this guide, we will make the assumption that the developer is more familiar with Windows and Visual Studio than the Linux counterparts, given that the app is originally a Windows app.
Because we grabbed the source version, first we need to load up NClass.sln in Visual Studio and compile the solution.
Once we have a compiled executable, we can run MoMA on it to give us an idea of what kinds of issues we may run into.
The results from the MoMA scan can be seen here.
MoMA Summary
Methods we are using that are not in Mono 1.2.4:
- void ListView.add_ItemSelectionChanged (ListViewItemSelectionChangedEventHandler)
- void TreeView.set_ShowNodeToolTips (bool)
- void TreeNode.set_ToolTipText (string)
- void PrintDialog.set_UseEXDialog (bool)
Methods that are marked with a [MonoTODO] attribute in Mono.1.2.4:
- void Control.set_AutoSize (bool) — This method currently does nothing
- void ContainerControl.set_AutoScaleMode (AutoScaleMode) — Call scaling method
- void ComboBox.set_AutoCompleteMode (AutoCompleteMode) — AutoCompletion algorithm is currently not implemented.
- void ComboBox.set_AutoCompleteSource (AutoCompleteSource) — AutoCompletion algorithm is currently not implemented.
Looking at the report, we can immediately see several places we will most likely need to make adjustments for our program to work.
The TODOs look pretty harmless, we can infer that if we rely on AutoSize, it’s probably not going to work, and we are not going to have auto completion for our ComboBox. Most of these missing methods won’t be too big of a problem either once we work around them. We can expect that we will not have ToolTips in our TreeView, and we can’t set our PrintDialog to use the Windows XP dialog. The missing event ListView.ItemSelectionChanged however will likely be a problem. Because that event does not exist, it will not get fired, so whatever code we are doing in the event handler will not get called.
Trying It Out
Even though MoMA shows there could be problems running the application, sometimes those problems aren’t hit until certain features of the program are executed. So let’s try to run the application.
Open “Mono-1.2.4 Command Prompt” that was created in the start menu by the Mono installer. Go to the directory that contains the executable “NClass.NClass.exe”, and try running it with Mono using:
Unfortunately, nothing seemed to happen.
What actually happened is that the application ran, but hit an error and quit. To see the error, we need to redirect the error output to a file, so we run:
Looking at the log that was created, we see this error:
This isn’t really unexpected. It is telling us that we tried to set the property PrintDialog.UseEXDialog, which MoMA already told us didn’t exist in Mono. So it’s time to start porting our source code to work around these issues.
Porting Strategies
There are several approaches to porting code, depending on your goals.
- The unsupported code can simply be removed or commented out if it is not needed.
- Compiler conditional directives (#if) can be used to create separate executables for .NET and Mono.
- The runtime (.NET or Mono) can be detected and use different code.
- The code can be rewritten to use supported methods in Mono.
Here is the section of code that is setting PrintDialog.UseEXDialog in MainForm.Designer.cs:
Remove / Comment Code
If the code isn’t really needed, the easiest thing to do is to remove it:
or comment it out:
However, there will be plenty of cases when this is not feasible.
Compiler Conditionals
Another strategy is to create different assemblies for .NET and Mono. This can be done by wrapping all code that is not supported by Mono in conditional directives and compiling with and without the directive.
When compiling the version for Mono, we would specify the flag MONO in the Build tab of the GUI project properties.
The downside of this approach is that we must ship separate assemblies for different platforms.
Runtime Conditionals
In order to have only one assembly for all platforms, but run different code on different platforms, we can detect at runtime whether we are using Mono or .NET. First, create a function to detect if the assembly is running on Mono (from the Technical FAQ):
Then use the function to determine which code to run:
Notice that we moved the code into a separate function, as this will prevent the JIT engine from throwing an exception as it JITs the code and determines that the method did not exist. By moving the code into a different method we prevent the JIT engine from hitting it.
Rewriting Code
Sometimes the best option is to simply rewrite the problem code in a way that is supported by Mono. For example, MoMA told us that this application uses the ListView.ItemSelectionChanged event, which is not implemented in Mono. However, the event ListView.SelectedIndexChanged event is implemented, and could be used for the same purpose.
Here is the problem code:
Which can be modified to:
We then modify lstItems_ItemSelectionChanged from:
When possible (which isn’t always the case), this method is the cleanest, as it uses the same code and provides the same functionality on both the Mono and .NET runtime.
Porting NClass
For our NClass application, we will comment out PrintDialog.UseEXDialog, TreeView.ShowNodeToolTips, and TreeNode.ToolTipText. We will rewrite both occurrences of ListView.ItemSelectionChanged.
MainForm.Designer.cs — Line 1196
TreeDialog.Designer.cs — Line 66
TreeDialog.cs — Line 74
MembersDialog.designer.cs — Line 238
Rewriting the listMembers_ItemSelectionChanged method takes a little bit more effort. One way is to change it from:
Also, make the changes to lstItems.ItemSelectionChanged and lstItems_ItemSelectionChanged outlined in the “Rewriting Code” section above.
With these changes made, rebuild the solution in Visual Studio. Then run the NClass.NClass.exe executable again. This time we get much better results:
Loading up the example file:
Class members dialog:
Continuing the Port
Now that the application runs, you have a good starting point to work from. However, it needs to be thoroughly tested for issues that MoMA can’t find. When exercising the application’s code, it is possible that you will run into things that do not function as expected or the application may crash unexpectedly. If these issues are bugs in Mono, please file the bug with a small test case (not your entire application) so we can fix them. See Bugs for details. Other issues may just be poorly written code that can be fixed by rewriting it to be more robust and error-proof.
There are still many issues with the NClass port, but for the sake of brevity, we will not attempt to fix them in this guide.
Running on Linux
Ultimately, the goal is to run the application on Linux, so let’s look at running NClass on Linux. If you already have a Linux installation, you can look for Mono packages in your distro’s package repositories.
If they are not there or are out of date, there are packages for many popular distros on Mono’s Downloads page. If you cannot find pre-made packages, you can also compile from the source code. Instructions are available here.
If you do not have a Linux installation, another option is to run the openSUSE/Mono VMWare image. This is the method we will use, which requires the following:
After installing the VMWare player and unzipping the VMWare image, start the Mono image. It should bring you to the openSUSE desktop:
Enabling User Shares
To transfer the application to the Mono image, we need to create a NClass directory and turn on directory sharing.
- Right click the desktop, choose Create Directory, name the directory NClass.
- Click the “Computer” menu in the bottom left, choose Control Center.
- Choose “YaST” at the bottom, the password is “mono”.
- Choose “Network Services”, then “Samba Server”.
- Check “Allow Users to Share Their Directories” on the “Shares” tab.
- Choose “Finish”.
- Back on the desktop, Right click the “NClass” folder you created and choose “Sharing Options”.
- Check “Share this folder” and “Allow other people to write to this folder”.
- Click “Modify Share”.
Now go back to Windows, and access your new share by typing “\\mono\nclass” into the Run dialog. Copy your NClass project to the Mono image share.
On the Mono image desktop, double click your NClass folder, and navigate to your compiled NClass.NClass.exe application. Double click it and the application should run.
Linux Platform Differences
There are two classes of issues you may run into during your port. The first class are issues stemming from differences between the .NET and Mono runtime. These can be worked out while still on Windows as shown above. However, there is a second class of issues that you may come across. These are issues stemming from differences between Windows and Linux (or macOS, etc.).
Common OS Porting Issues
- Use of Win32 P/Invokes
- Case sensitivity in file names.
- Use of “\” in paths.
As an example, lets say you have a subdirectory in your application called “Sounds” that contains the file “Finished.wav”. To play this sound, you have the following function:
This function exhibits all three mentioned porting issues.
Mono has a special execution mode that will help you get your port moving faster (if you do not want to change the filename casing or path separators, see the IOMap page for details.
Win32 P/Invokes
The PlaySound function is declared in the Win32 API dll “winmm.dll”, which does not exist on Linux. There are two ways around this. One is to find the equivalent function in your target platform’s API, and use runtime detection to determine which API to call. If you are lucky, the easy way is to replace your API call with managed code. In this case, we can use the System.Media.SoundPlayer class to play the sound for us, like this:
Case Sensitivity
Another difference between Windows and many other operating systems such as Linux is that the file system is case sensitive. That is, in Windows the files “readme.txt” and “README.TXT” are the same, but in Linux those are distinct files. Looking at our example, our file is called “Finished.wav”, but we are referencing it in code as “finished.wav”. Although this will work on Windows, it will generate a FileNotFoundException on Linux. So we must be consistent about the case of our file names:
The Path Separator
Another issue you may run across is the path separator (“\”) used in file paths. In many other operating systems, such as Linux, the path separator is a forward slash (“/”) instead of a backwards slash like Windows. In our example, we have hard coded a backwards slash that will cause our file to not be found.
We can correct this using Path.DirectorySeparatorChar:
or using Path.Combine:
Either method will ensure that the correct path separator is used on the correct operating system.
For some more issues you might run into and a more detailed look at options for dealing with them, see Guidelines:Application Portability.
Conclusion
Using this guide, we have managed to port a non-trivial winforms app to start up on Mono on Linux in a couple of hours. Although it is not complete, once the application is starting up on Mono, it’s a good beginning to test and see what issues remain.
Источник