1
votes

OK, I will try to do my best, but I think that my English is still too bad when it comes to complex subjects/phrases.

I have to build a class (as for now it's not static) that retrieves the information about the external application (software available on customers PC).

The Linux class was really easy to code, but now I have to implement it on Windows.

Basically I've encountered some difficulties with x64 registry reading from x86 version of my application: you can do it only by using [DllImport("advapi32.dll")].

From the beginning I knew that software retirement is strongly depending on target OS, so I've created 4 classes:

public abstract ExternalApplications that defines some basic methods and it is derived by ExternalApplicationsWin, ExternalApplicationsMac and ExternalApplicationsUnix.

Inmy application I create an instance of ExternalApplications and assign it by switching the operating system type:

ExternalApplications ex = null;

switch (Environment.OSVersion.Platform)
{
case PlatformID.Win32NT:
   goto default;
case PlatformID.MacOSX:
   _ex = new ExternalApplicationsMac(); break;
case PlatformID.Unix:
   _ex = new ExternalApplicationsUnix(); break;
default:
   _ex = new ExternalApplicationsWin(); break;
}

I this way the method call will be dispatched automatically to the right ExternalApplicationsXXX type.

Now by introducing [DllImport("advapi32.dll")] I'm obligated to:

  • change the class structure (to static extern)
  • it will probably brake Mono compilation on Unix/Mac OS X compilation (with DllNotFoundException)

There are several options: (1) I can create 3 parallel projects (.cproj) with the same target assembly name for Linux, Mac and Windows. The projects will contain the same classes and methods so I can reference it inside my application without any problem.

This option sucks, it lacks of automation: everything is done manually.

(2) I can accomplish a preventive "DllImport" code exclusion by using C# directives like

#if !MONO
...
#endif

I'm sure that there are many other methods like DLLMAP and others. I think that more experienced programmers will tell me what is the most elegant/reliable solution.

EDIT 1:

I've just discovered that x64 registry hive reading from an x86 application is possible, however only in .NET 4.0 and higher (according to my source, still need to test).

string registryBranchPath = "SOFTWARE\MySoft";
RegistryKey regKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
regKey = regKey.OpenSubKey(registryBranchPath);

This solves only one part of my question, the other part (regarding the possibility to use DllImport in Windows specific non-static classes) is still to be confirmed / tested.

2
Perosnally, if I was doing separate builds, I'd have a single class (or maybe clones with the same name) with the code specified by compile time constants - Deanna
I'm sort of confused. The question seems to be about the Registry, which isn't even available on OSX/Linux. Why not use a 'settings' strategy that works on all platforms as much as possisble? - kenny
In my application I have to detect CAD engine installations. On linux I can read the output of 'which bricscad' command, while in Windows I have to read registry keys. - Salaros

2 Answers

4
votes
  1. Your class doesn't need to be static, only the imported external method is marked as such. Also, the imported method should be marked private, as nothing except the importing class should call it directly.

  2. You don't need any fancy compilation tricks / code splitting, the DllImport is not checked until it's actually invoked.

An example from MSDN documentation for DllImport:

using System;
using System.Runtime.InteropServices;

class Example
{
    // Use DllImport to import the Win32 MessageBox function.
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern int MessageBox(IntPtr hWnd, String text, 
      String caption, uint type);

    public void MessageBox()
    {
        // Call the MessageBox function using platform invoke.
        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
    }
}
0
votes

So the final solution for me will look like this simplified snippet:

public static class UsageClass
{
    private static ExternalApplications _extApps;

    public static void Initialize()
    {
        _extApps = null;

        switch (Environment.OSVersion.Platform)
        {
            case PlatformID.Win32NT:
                goto default;
            case PlatformID.MacOSX:
                _extApps = new ExternalApplicationsMac(); break;
            case PlatformID.Unix:
                _extApps = new ExternalApplicationsUnix(); break;
            default:
                _extApps = new ExternalApplicationsWin(); break;
        }
    }

    internal abstract class ExternalApplications
    {
        public abstract string[] DoSomething();
    }

    internal class ExternalApplicationsWin : ExternalApplications
    {
        public abstract void DoSomething()
        {
            // Windows code
            RegistryKey regKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine,
                (Environment.Is64BitOperatingSystem) ? RegistryView.Registry64 : RegistryView.Registry32).OpenSubKey("SOFTWARE\MySoft");
            ...

        }
    }

    internal class ExternalApplicationsUnix : ExternalApplications
    {
        public abstract void DoSomething()
        {
            // Unix code
        }
    }

    internal class ExternalApplicationsMac : ExternalApplications
    {
        public abstract void DoSomething()
        {
            // Mac code
        }
    }
}