# .NET

Source: https://docs.interop.io/desktop/getting-started/how-to/interop-enable-your-apps/net/index.html

## Overview

**io.Connect Desktop** provides .NET libraries that enable you to access programmatically the available io.Connect features. There are three versions of the io.Connect .NET library for the following frameworks:

- io.Connect .NET for [.NET 5+](https://www.nuget.org/packages/GlueNetCore);
- io.Connect .NET for [.NET Framework](https://www.nuget.org/packages/io.Connect.NET);
- io.Connect .NET for [.NET Standard](https://www.nuget.org/packages/GlueBase);

The libraries provide identical APIs and are initialized in the same way.

> ⚠️ *Note that the io.Connect .NET library for [.NET Standard](https://www.nuget.org/packages/GlueBase) is meant to provide a cross-platform support (e.g., for Android, Linux, Blazor) and therefore doesn't work with window handles. This means that you can use the library to control other windows registered in the io.Connect framework, but you can't use it to [register your own window as an io.Connect Window](https://docs.interop.io/desktop/capabilities/windows/window-management/net/index.md#registering_windows).*

To use the **io.Connect Desktop** features, you must reference the io.Connect .NET library (either as a standalone DLL file or as a NuGet package), and instantiate and initialize the `Glue42` class (for [.NET 5+](https://www.nuget.org/packages/GlueNetCore) and [.NET Framework](https://www.nuget.org/packages/io.Connect.NET)) or the `Glue42Base` class (for [.NET Standard](https://www.nuget.org/packages/GlueBase)) to gain access to the io.Connect functionalities.

## Referencing

### NuGet Package

The io.Connect .NET library is available as a NuGet package for [.NET 5+](https://www.nuget.org/packages/GlueNetCore), [.NET Framework](https://www.nuget.org/packages/io.Connect.NET) and [.NET Standard](https://www.nuget.org/packages/GlueBase), which you can include and configure as a standard NuGet package in your projects.

### Standalone File

Another option is to reference the DLL library files delivered with the **io.Connect Desktop** installer and located in the `<installation_location>/interop.io/io.Connect Desktop/SDK/.NET/lib/` folder.

Referencing the DLL library file for .NET 5+:

```xml
<Reference Include="GlueNetCore">
    <HintPath>..\lib\GlueNetCore.dll</HintPath>
</Reference>
```

Referencing the DLL library file for .NET Framework:

```xml
<Reference Include="ioConnectNET">
    <HintPath>..\lib\ioConnectNET.dll</HintPath>
</Reference>
```

## Initialization

The .NET io.Connect library is initialized asynchronously with an optional `InitializeOptions` object in which you can specify various settings and io.Connect features you want your app to use.

> ⚠️ *Note that after initializing the library, you may want to register the windows of your .NET app as io.Connect Windows in order to be able to use io.Connect functionalities. For more details on how to do that correctly and the pitfalls to avoid during window registration, see the [Capabilities > Window Management > .NET](https://docs.interop.io/desktop/capabilities/windows/window-management/net/index.md) section.*

### .NET 5+

The following example demonstrates initializing the io.Connect .NET library for [.NET 5+](https://www.nuget.org/packages/GlueNetCore) and specifying initialization options:

```csharp
var options = new InitializeOptions()
{
    ApplicationName = "my-app",
    IncludedFeatures = GDFeatures.UseAppManager | GDFeatures.UseGlueWindows
};

// The initialization options aren't required.
// If skipped, the library will be initialized with the default options
// (default assembly name for `ApplicationName` and with all features included).
Glue42 io = await Glue42.InitializeGlue(options);
```

#### Logging

When initializing the io.Connect .NET library for [.NET 5+](https://www.nuget.org/packages/GlueNetCore), you can provide a logger factory implementation to be used as a logging mechanism instead of the built-in one:

```csharp
var options = new InitializeOptions() { LoggerFactory = new Log4NetLoggerFactory() };

Glue42 io = await Glue42.InitializeGlue(options);
```

The following is an example implementation of a logger factory using the standard `log4net` library:

```csharp
using log4net;

public class Log4NetLoggerFactory : IGlueLoggerFactory
{
    public IGlueLog GetLogger(string name)
    {
        return new Log4NetLog(LogManager.GetLogger(name));
    }

    public IGlueLog GetLogger(Type type)
    {
        return new Log4NetLog(LogManager.GetLogger(type));
    }

    public static Level GetLog4NetLevel(GlueLogLevel level)
    {
        return level switch
        {
            GlueLogLevel.All => Level.All,
            GlueLogLevel.Trace => Level.Trace,
            GlueLogLevel.Debug => Level.Debug,
            GlueLogLevel.Info => Level.Info,
            GlueLogLevel.Warn => Level.Warn,
            GlueLogLevel.Error => Level.Error,
            GlueLogLevel.Fatal => Level.Fatal,
            GlueLogLevel.Off => Level.Off,
            _ => throw new ArgumentOutOfRangeException(nameof(level), level, null)
        };
    }

    public class Log4NetLog : IGlueLog
    {
        private readonly ILog log_;

        public Log4NetLog(ILog log)
        {
            log_ = log;
        }

        public void Trace(Func<string> messageCtor, Exception e = null)
        {
            if (log_.Logger.IsEnabledFor(Level.Trace))
            {
                log_.Logger.Log(new LoggingEvent(new LoggingEventData
                {
                    Level = Level.Trace,
                    Message = messageCtor(),
                    LoggerName = log_.Logger.Name,
                    ThreadName = Thread.CurrentThread.Name,
                    TimeStampUtc = DateTime.UtcNow,
                    ExceptionString = e?.ToString()
                }));
            }
        }

        public void Info(string message, Exception e = null)
        {
            log_.Info(message, e);
        }

        public void Error(string message, Exception e = null)
        {
            log_.Error(message, e);
        }

        public void Debug(string message, Exception e = null)
        {
            log_.Debug(message, e);
        }

        public void Debug(Func<string> message, Exception e = null)
        {
            if (log_.IsDebugEnabled)
            {
                log_.Debug(message(), e);
            }
        }

        public bool IsEnabledFor(GlueLogLevel level)
        {
            return log_.Logger.IsEnabledFor(GetLog4NetLevel(level));
        }

        public void Warn(string message, Exception e = null)
        {
            log_.Warn(message, e);
        }

        public void Log(GlueLogLevel level, string message, Exception exception)
        {
            log_.Logger.Log(typeof(LogImpl), GetLog4NetLevel(level), message, exception);
        }
    }
}
```

### .NET Framework

The following example demonstrates initializing the io.Connect .NET library for [.NET Framework](https://www.nuget.org/packages/io.Connect.NET) and specifying initialization options:

```csharp
var initializeOptions = new InitializeOptions()
{
    ApplicationName = "my-app",
    IncludedFeatures = GDFeatures.UseAppManager | GDFeatures.UseGlueWindows
};

// The initialization options aren't required.
// If skipped, the library will be initialized with the default options
// (default assembly name for `ApplicationName` and with all features included).
Glue42 io = await Glue42.InitializeGlue(initializeOptions);
```

#### Logging

The io.Connect .NET library for [.NET Framework](https://www.nuget.org/packages/io.Connect.NET) can use your custom proxy logging facade implementation as a logging mechanism instead of the built-in one.

First, you must initialize your logging library and bind the proxy implementation to the built-in logging mechanism:

```csharp
// Initializing your custom logging library.
log4net.Config.XmlConfigurator.Configure();

// Binding the proxy logging facade implementation to the built-in logging mechanism.
DotLoggingFacade.Instance = new ProxyLoggingFacade();
```

After that, initialize the io.Connect .NET library and provide initialization options that will instruct it to use your custom logging facade:

```csharp
var options = new InitializeOptions() { LogLibrary = LogLibrary.UseCustomFacade };

Glue42 io = await Glue42.InitializeGlue(options);
```

The following is an example implementation of a proxy logging facade using the standard `log4net` library:

```csharp
public class ProxyLoggingFacade : IDotLoggingFacade
{
    public IDotLog GetLogger(string loggerName)
    {
        return new DotLog(loggerName);
    }

    public List<string> GetLogFolders()
    {
        // ensure repo is initialized
        var logger = GetLogger(GetType().FullName);

        var fileAppenders = GetFileAppenders();

        var dirs = new HashSet<string>(fileAppenders.Select(fa => Path.GetDirectoryName(fa.File)));

        return dirs.ToList();
    }

    public List<string> GetLogFileNames()
    {
        // ensure repo is initialized
        var logger = GetLogger(GetType().FullName);

        var fileAppenders = GetFileAppenders();

        var files = new HashSet<string>(fileAppenders.Select(fa => fa.File));

        return files.ToList();
    }

    private static IEnumerable<FileAppender> GetFileAppenders()
    {
        var repository = LogManager.GetRepository();

        var fileAppenders =
            repository.GetAppenders().Where(iAppender => iAppender is FileAppender).Cast<FileAppender>();
        return fileAppenders;
    }

    private class DotLog : IDotLog
    {
        static DotLog()
        {
            DeclaringType = typeof(DotLog);
        }

        public DotLog(string loggerName)
        {
            Log4NetLogger = LogManager.GetLogger(loggerName);
        }

        internal DotLog(ILog log4NetLogger)
        {
            Log4NetLogger = log4NetLogger;
        }

        private static Type DeclaringType { get; }

        private ILog Log4NetLogger { get; }

        public bool IsEnabled(DotLogLevel logLevel)
        {
            return Log4NetLogger.Logger.IsEnabledFor(GetLevel(logLevel));
        }

        public void Log(DotLogLevel logLevel, object message)
        {
            Log4NetLogger.Logger.Log(DeclaringType, GetLevel(logLevel), message, null);
        }

        public void Log(
            DotLogLevel logLevel,
            object message,
            Exception exception)
        {
            Log4NetLogger.Logger.Log(DeclaringType, GetLevel(logLevel), message, exception);
        }

        public void FlushBuffers()
        {
            ILoggerRepository rep = LogManager.GetRepository();
            foreach (IAppender appender in rep.GetAppenders())
            {
                if (appender is BufferingAppenderSkeleton buffered)
                {
                    buffered.Flush();
                }
            }
        }

        public bool IsTraceEnabled => Log4NetLogger.Logger.IsEnabledFor(Level.Trace);

        public bool IsDebugEnabled => Log4NetLogger.IsDebugEnabled;

        public bool IsInfoEnabled => Log4NetLogger.IsInfoEnabled;

        public bool IsWarnEnabled => Log4NetLogger.IsWarnEnabled;

        public bool IsErrorEnabled => Log4NetLogger.IsErrorEnabled;

        public bool IsFatalEnabled => Log4NetLogger.IsFatalEnabled;

        public DotLogLevel SetLogLevel(DotLogLevel logLevel)
        {
            var logger = (Logger)Log4NetLogger.Logger;
            var oldLevel = logger.Level;
            logger.Level = GetLevel(logLevel);
            return GetLevel(oldLevel);
        }

        private static DotLogLevel GetLevel(Level logLevel)
        {
            if (logLevel == Level.All)
            {
                return DotLogLevel.All;
            }

            if (logLevel == Level.Trace)
            {
                return DotLogLevel.Trace;
            }

            if (logLevel == Level.Debug)
            {
                return DotLogLevel.Debug;
            }

            if (logLevel == Level.Info)
            {
                return DotLogLevel.Info;
            }

            if (logLevel == Level.Warn)
            {
                return DotLogLevel.Warn;
            }

            if (logLevel == Level.Error)
            {
                return DotLogLevel.Error;
            }

            if (logLevel == Level.Fatal)
            {
                return DotLogLevel.Fatal;
            }

            return DotLogLevel.Off;
        }

        private static Level GetLevel(DotLogLevel logLevel)
        {
            switch (logLevel)
            {
                case DotLogLevel.All:
                    return Level.All;
                case DotLogLevel.Trace:
                    return Level.Trace;
                case DotLogLevel.Debug:
                    return Level.Debug;
                case DotLogLevel.Info:
                    return Level.Info;
                case DotLogLevel.Warn:
                    return Level.Warn;
                case DotLogLevel.Error:
                    return Level.Error;
                case DotLogLevel.Fatal:
                    return Level.Fatal;
                case DotLogLevel.Off:
                default:
                    return Level.Off;
            }
        }
    }
}
```

### .NET Standard

The following example demonstrates initializing the io.Connect .NET library for [.NET Standard](https://www.nuget.org/packages/GlueBase) and specifying initialization options:

```csharp
var initializeOptions = new InitializeOptions()
{
    ApplicationName = "my-app",
    IncludedFeatures = GDFeatures.UseAppManager | GDFeatures.UseGlueWindows
};

// The initialization options aren't required.
// If skipped, the library will be initialized with the default options
// (default assembly name for `ApplicationName` and with all features included).
Glue42Base io = await Glue42Base.InitializeGlue(initializeOptions);
```

#### Logging

The procedure for providing a custom logging mechanism for the io.Connect .NET library for [.NET Standard](https://www.nuget.org/packages/GlueBase) is identical to the one for the io.Connect .NET library for [.NET 5+](https://www.nuget.org/packages/GlueNetCore) For more details, see the [Initialization > .NET 5+ > Logging](#initialization-net_5-logging) section.

## App Definition

To add your .NET app to the [io.Connect launcher](https://docs.interop.io/desktop/capabilities/launcher/index.md), you must create a JSON [app definition](https://docs.interop.io/desktop/developers/configuration/application/index.md) file for it. Place this file in the `<installation_location>/interop.io/io.Connect Desktop/UserData/<ENV>-<REG>/apps` folder where `<ENV>-<REG>` represents the environment and region of **io.Connect Desktop** (e.g., `DEMO-INTEROP.IO`).

> ⚠️ *Note that you can also launch manually your interop-enabled .NET app and it will automatically register itself and its child windows in the **io.Connect Desktop** in-memory store. In this case, your app will be available in the io.Connect environment until the next restart of **io.Connect Desktop**. This is useful if you have a multi window interop-enabled app with dynamic interop-enabled child windows (e.g., different user configurations, software updates, etc.) - when your main window starts its child windows, they will be registered automatically.*

The following is an example definition of a .NET app:

```json
{
    "title": "My .NET App",
    "type": "exe",
    "name": "my-net-app",
    "details": {
        "path": "%IO_CD_ROOT_DIR%/../Demos/MyNETApp/",
        "command": "MyNETApp.exe",
        "parameters": " --mode=1"
    }
}
```

The `"name"`, `"type"`, and `"command"` properties are required and `"type"` must be set to `"exe"`. The `"path"` property points to the app working directory. The `"command"` property accepts the actual command to execute - the EXE file name.

The value of the `"title"` property will be used as a name for the app in the io.Connect launcher and as a window title.

> ℹ️ *For more details on defining apps, see the [Developers > Configuration > Application](https://docs.interop.io/desktop/developers/configuration/application/index.md#app_definition-exe) section.*

> ℹ️ *See the [.NET examples](https://github.com/InteropIO/net-examples) on GitHub which demonstrate various **io.Connect Desktop** features.*

## ClickOnce

**io.Connect Desktop** offers support for ClickOnce apps. The following example demonstrates how to initialize the io.Connect .NET library in your ClickOnce app and how to register a ClickOnce app in **io.Connect Desktop**.

### Initialization

In a ClickOnce app, the library is initialized the same way as in other .NET apps:

```csharp
var initializeOptions = new InitializeOptions()
{
    ApplicationName = "ClientProfileDemo",
    IncludedFeatures = GDFeatures.UseAppManager | GDFeatures.UseGlueWindows
};

// The initialization options aren't required.
// If skipped, the library will be initialized with the default options
// (default assembly name for `ApplicationName` and with all features included).
Glue42 io = await Glue42.InitializeGlue(initializeOptions);
```

### Registering ClickOnce Apps

To add your ClickOnce app to the [io.Connect launcher](https://docs.interop.io/desktop/capabilities/launcher/index.md), you must create a JSON file with app definition.

The following is an example definition of a ClickOnce app:

```json
{
    "title": "My ClickOnce App",
    "type": "clickonce",
    "name": "my-clickonce-app",
    "details": {
        "url": "https://example.com/my-clickonce-app.application",
        "width": 1000,
        "height": 400
    }
}
```

The `"name"`, `"type"`, and `"url"` properties are required and `"type"` must be set to `"clickonce"`. The `"url"` property points to the physical location where the ClickOnce app is deployed and from where it will be installed on the user machine.

Available since io.Connect Desktop 9.8

To specify command line arguments for a ClickOnce app, use the `"appParameters"` property of the `"details"` top-level key in the [app definition](https://docs.interop.io/desktop/developers/configuration/application/index.md). The `"appParameters"` property accepts as a value an object with key/value pairs describing the names and the values of the command line arguments to pass to the ClickOnce app:

```json
{
    "type": "clickonce",
    "name": "my-clickonce-app",
    "details": {
        "url": "https://example.com/my-clickonce-app.application",
        "appParameters": {
            "parameter1": "value1",
            "parameter2": "value2"
        }
    }
}
```

> ℹ️ *For more details, see the [Developers > Configuration > Application](https://docs.interop.io/desktop/developers/configuration/application/index.md) section.*

## Silverlight

**io.Connect Desktop** offers a Silverlight library as a version of the io.Connect .NET library. The Silverlight library offers the same features and functionalities as the io.Connect .NET library, it's only tailored to meet the specifications of the Microsoft Silverlight framework.

## io.Connect .NET Concepts

Once the io.Connect .NET library has been initialized, your app has access to all io.Connect functionalities. For more detailed information on the different io.Connect capabilities and APIs, see:

- [Shared Contexts](https://docs.interop.io/desktop/capabilities/data-sharing/shared-contexts/net/index.md)
- [Channels](https://docs.interop.io/desktop/capabilities/data-sharing/channels/net/index.md)
- [Interop](https://docs.interop.io/desktop/capabilities/data-sharing/interop/net/index.md)
- [Intents](https://docs.interop.io/desktop/capabilities/data-sharing/intents/net/index.md)
- [Pub/Sub](https://docs.interop.io/desktop/capabilities/data-sharing/pub-sub/net/index.md)
- [Window Management](https://docs.interop.io/desktop/capabilities/windows/window-management/net/index.md)
- [Workspaces](https://docs.interop.io/desktop/capabilities/windows/workspaces/net/index.md)
- [Layouts](https://docs.interop.io/desktop/capabilities/windows/layouts/net/index.md)
- [App Management](https://docs.interop.io/desktop/capabilities/app-management/net/index.md)
- [App Preferences](https://docs.interop.io/desktop/capabilities/app-preferences/net/index.md)
- [Notifications](https://docs.interop.io/desktop/capabilities/notifications/net/index.md)
