views:

260

answers:

4

I have a WinForms Application that I want to run at Mono at some point. However, it is using some P/Invoke against user32.dll, which will cause an obvious problems.

However, this is not a critical functionality, just some flavor stuff. So I would like to ignore it when running Mono.

I know I could just use an #if directive, but then I have to offer two different assemblies, which is bad. I know I can check on Runtime if I am running in Mono, but that won't help me removing the declarations to the functions.

So I wonder: Is there a way where I can say "If running Mono, ignore this completely"? If it helps: The P/Invoke stuff is in a separate .cs file and implemented as a Partial Class.

The source code in question is here: http://pastie.org/588940
It is part of my Main Form, overriding the WndProc message to add an item to the system menu. (Some other stuff snipped). My problem is that while the WndProc stuff is easy, I do not know what to do with the two private extern declarations - can I put them into another (static) class that I just never call on Mono? Or would that be russian roulette?

+6  A: 

Why not encapsulate the platform-dependent stuff in an interface, then have some way of getting the "right" implementation for the current platform. Then the calling code can use it blithely, and you can gradually fill in bits to run on Mono as and when you want. I would at least hope that if you never even load the class containing the P/Invoke bits, you should be okay...

EDIT:

I don't see why this approach shouldn't work, although you may not even need the factory. Here's what I'd do:

MainForm.cs:

PlatformServicesFacade.InitializeSystemMenu();

IPlatformServices.cs:

public interface IPlatformServices
{
    void InitializeSystemMenu();
}

MonoPlatformServices.cs:

public class MonoPlatformServices : IPlatformServices
{
    // Prevent early type initialization
    static WindowsPlatformServices() {}

    public void InitializeSystemMenu()
    {
        // Maybe log what you would have done?
    }
}

WindowsPlatformServices.cs:

public class WindowsPlatformServices : IPlatformServices
{
    // Prevent early type initialization
    static WindowsPlatformServices() {}

    public const Int32 SystemMenuAboutSWikiId = 1000;
    [DllImport("user32.dll")]
    private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
    [DllImport("user32.dll")]
    private static extern bool InsertMenu(IntPtr hMenu, Int32 wPosition, 
                                          Int32 wFlags, Int32 wIDNewItem,
                                          string lpNewItem);

    public void InitializeSystemMenu()
    {
        const Int32 MF_SEPARATOR = 0x800;
        const Int32 MF_BYPOSITION = 0x400;

        IntPtr systemMenuPtr = GetSystemMenu(Handle, false);
        InsertMenu(systemMenuPtr, 5, MF_BYPOSITION | MF_SEPARATOR, 0, "");
        InsertMenu(systemMenuPtr, 6, MF_BYPOSITION, SystemMenuAboutSWikiId, 
                   "About SWiki...");
    }
}

PlatformServicesFacade.cs:

public class PlatformServicesFacade
{
    private static readonly IPlatformServices services;

    static PlatformServiceFacade()
    {
        services = RunningOnWindows() ? new WindowsPlatformServices()
            : (IPlatformServices) new MonoPlatformServices();
    }

    public static void InitializeSystemMenu()
    {
        services.InitializeSystemMenu();
    }
}

I think that should work... if it doesn't, please tell us what's going wrong :)

Jon Skeet
Not sure if that would work, I've amended my question with some actual code and description of intent.
Michael Stum
Having a static constructor prevents early type initialization? That sounds interesting and exactly what I need. I'll give it a try right now.
Michael Stum
@Michael: See http://pobox.com/~skeet/csharp/beforefieldinit.html
Jon Skeet
cool, looks good. had to change a few things (tenary operator and different return types...). It's now live: http://swiki.codeplex.com/SourceControl/changeset/view/40698 (SWiki/PlatformSpecific folder)
Michael Stum
Ooh, will edit.
Jon Skeet
+2  A: 

Checking the runtime seems like a viable solution. Is there any reason you would like to remove the P/Invoke function declarations if running under Mono even if they are never used?

Darin Dimitrov
I just want to be sure that they are not called and randomly crash my application.
Michael Stum
Just don't make them public - wrap them in a function that will check which platform-specific variant should be called.
skolima
+3  A: 

Some Environment property probably has this info, for example System.Environment.OSVersion.Platform. Or use if (Type.GetType("Mono.Runtime") != null)

See "How to detect the execution platform?" and "How can I detect if am running in Mono?": http://www.mono-project.com/FAQ:_Technical

Axl
Thanks. My problem was more about "How to I put the _declaration_ of an external method into a conditional block that is executed at runtime?"
Michael Stum
+2  A: 

I think your best bet will be M/Invoke from the Mono guys. It is still pretty new (only in Mono SVN). But I'm sure they'd love help getting it into a Mono Distribution :)

Justin Rudd
I guess so :) But P/Invoke is one of those things that I only want to use if there is really no other way, and even then only to the most minimal extent.
Michael Stum