Skip to content

Commit

Permalink
experimental .NET 5+ support
Browse files Browse the repository at this point in the history
  • Loading branch information
vnau committed Jan 24, 2024
1 parent cf1403a commit 13a27ed
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 90 deletions.
6 changes: 0 additions & 6 deletions App.config

This file was deleted.

66 changes: 66 additions & 0 deletions GacLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using Ivi.Visa.ConflictManager;
using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace IviVisaNetSample
{
#if NET5_0_OR_GREATER
/// <summary>
/// Class used to load .NET Framework assemblies located in GAC from .NET 5+
/// Requred only for expiremental using VISA.NET library in .NET 5+
/// </summary>
internal static class GacLoader
{
/// <summary>
/// Load an assembly from the GAC.
/// </summary>
/// <param name="assemblyName"></param>
/// <returns>Loaded assembly</returns>
/// <exception cref="FileNotFoundException"></exception>
public static Assembly Load(AssemblyName assemblyName)
{
var gacPaths = new[]
{
$"{Environment.GetFolderPath(Environment.SpecialFolder.Windows)}\\Microsoft.NET\\assembly\\GAC_MSIL\\{assemblyName.Name}",
$"{Environment.GetFolderPath(Environment.SpecialFolder.Windows)}\\assembly\\GAC_MSIL\\{assemblyName.Name}",
};

foreach (var folder in gacPaths.Where(f => Directory.Exists(f)))
{
foreach (var subfolder in Directory.EnumerateDirectories(folder))
{
if (subfolder.Contains(Convert.ToHexString(assemblyName.GetPublicKeyToken()), StringComparison.OrdinalIgnoreCase)
&& subfolder.Contains(assemblyName.Version.ToString(), StringComparison.OrdinalIgnoreCase))
{
var assemblyPath = Path.Combine(subfolder, assemblyName.Name + ".dll");
if (File.Exists(assemblyPath))
return Assembly.LoadFrom(assemblyPath);
}
}
}
throw new FileNotFoundException($"Assembly {assemblyName} not found.");
}

/// <summary>
/// Preloading installed VISA implementation assemblies for NET 5+
/// </summary>
public static void LoadInstalledVisaAssemblies() {
var installedVisas = new ConflictManager().GetInstalledVisas(ApiType.DotNet);
foreach (var visaLibrary in installedVisas)
{
try
{
var inst = GacLoader.Load(new AssemblyName(visaLibrary.Location.Substring(visaLibrary.Location.IndexOf(",") + 1)));
Console.WriteLine($"Loaded assembly \"{visaLibrary.FriendlyName}\".");
}
catch (Exception exception)
{
Console.WriteLine($"Failed to load assembly \"{visaLibrary.FriendlyName}\": {exception.Message}");
}
}
}
}
#endif
}
61 changes: 8 additions & 53 deletions IviVisaNetSample.csproj
Original file line number Diff line number Diff line change
@@ -1,58 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{181CDD33-8577-4F46-AF93-2A02660E51AC}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>IviVisaNetSample</RootNamespace>
<AssemblyName>IviVisaNetSample</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
<TargetFrameworks>net48;net5.0;net8.0</TargetFrameworks>
<Platforms>AnyCPU;x64;x86</Platforms>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Kelary.Ivi.Visa" Version="7.*" />
<PackageReference Include="Kelary.Ivi.Visa" Version="7.2.0" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

</Project>
46 changes: 25 additions & 21 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,70 +9,74 @@ static class Program
{
static void Main()
{
// Get a VISA.NET library version.
Version VisaNetSharedComponentsVersion = typeof(GlobalResourceManager).Assembly.GetName().Version;
Console.WriteLine("VISA.NET Shared Components version {0}.", VisaNetSharedComponentsVersion);
// Get VISA.NET Shared Components version.
Version visaNetSharedComponentsVersion = typeof(GlobalResourceManager).Assembly.GetName().Version;
Console.WriteLine($"VISA.NET Shared Components version {visaNetSharedComponentsVersion}.");

// Check whether VISA Shared Components is installed before using VISA.NET.
// If access VISA.NET without the visaConfMgr.dll library, an unhandled exception will
// be thrown during termination process due to a bug in the implementation of the
// VISA.NET Shared Components, andthe application will crash.
FileVersionInfo VisaSharedComponentsInfo;
try
{
// Get an available version of the VISA Shared Components.
VisaSharedComponentsInfo = FileVersionInfo.GetVersionInfo(Path.Combine(Environment.SystemDirectory, "visaConfMgr.dll"));
Console.WriteLine("VISA Shared Components version {0} detected.", VisaSharedComponentsInfo.ProductVersion);
FileVersionInfo visaSharedComponentsInfo = FileVersionInfo.GetVersionInfo(Path.Combine(Environment.SystemDirectory, "visaConfMgr.dll"));
Console.WriteLine($"VISA Shared Components version {visaSharedComponentsInfo.ProductVersion} detected.");
}
catch (FileNotFoundException)
{
Console.WriteLine("VISA implementation compatible with VISA.NET Shared Components {0} not found. Please install corresponding vendor-specific VISA implementation first.", VisaNetSharedComponentsVersion);
Console.WriteLine($"VISA implementation compatible with VISA.NET Shared Components {visaNetSharedComponentsVersion} not found. Please install corresponding vendor-specific VISA implementation first.");
return;
}

#if NET5_0_OR_GREATER
// Preloading installed VISA implementation assemblies for NET 5+
GacLoader.LoadInstalledVisaAssemblies();
#endif

try
{
// Connect to the instrument.
using (IVisaSession res = GlobalResourceManager.Open("TCPIP0::localhost::5025::SOCKET", AccessModes.ExclusiveLock, 2000))
var resourceName = "TCPIP::localhost::5025::SOCKET";
using (IVisaSession resource = GlobalResourceManager.Open(resourceName, AccessModes.ExclusiveLock, 2000))
{
if (res is IMessageBasedSession session)
if (resource is IMessageBasedSession session)
{
// Ensure termination character is enabled as here in example we use a SOCKET connection.
session.TerminationCharacterEnabled = true;

// Request information about an instrument.
session.FormattedIO.WriteLine("*IDN?");
string idn = session.FormattedIO.ReadLine();
Console.WriteLine("Instrument information: {0}", idn);
string instrumentInfo = session.FormattedIO.ReadLine();
Console.WriteLine($"Instrument information: {instrumentInfo}");
}
else
{
Console.WriteLine("Not a message-based session.");
}
}
}
catch (Exception ex)
catch (Exception exception)
{
if (ex is TypeInitializationException && ex.InnerException is DllNotFoundException)
if (exception is TypeInitializationException && exception.InnerException is DllNotFoundException)
{
// VISA Shared Components is not installed.
Console.WriteLine("VISA implementation compatible with VISA.NET Shared Components {0} not found. Please install corresponding vendor-specific VISA implementation first.", VisaNetSharedComponentsVersion);
Console.WriteLine($"VISA implementation compatible with VISA.NET Shared Components {visaNetSharedComponentsVersion} not found. Please install corresponding vendor-specific VISA implementation first.");
}
else if (ex is VisaException
&& ex.Message == "No vendor-specific VISA .NET implementation is installed.")
else if (exception is VisaException && exception.Message == "No vendor-specific VISA .NET implementation is installed.")
{
// Vendor-specific VISA.NET implementation is not available.
Console.WriteLine("VISA implementation compatible with VISA.NET Shared Components {0} not found. Please install corresponding vendor-specific VISA implementation first.", VisaNetSharedComponentsVersion);
Console.WriteLine($"VISA implementation compatible with VISA.NET Shared Components {visaNetSharedComponentsVersion} not found. Please install corresponding vendor-specific VISA implementation first.");
}
else if (ex is EntryPointNotFoundException)
else if (exception is EntryPointNotFoundException)
{
// Installed VISA Shared Components is not compatible with VISA.NET Shared Components
Console.WriteLine("Installed VISA Shared Components version {0} does not support VISA.NET {1}. Please upgrade VISA implementation.", VisaSharedComponentsInfo.ProductVersion, VisaNetSharedComponentsVersion);
// Installed VISA Shared Components are not compatible with VISA.NET Shared Components.
Console.WriteLine($"Installed VISA Shared Components version {visaNetSharedComponentsVersion} does not support VISA.NET. Please upgrade VISA implementation.");
}
else
{
// Handle remaining errors.
Console.WriteLine("Exception: {0}", ex.Message);
Console.WriteLine($"Exception: {exception.Message}");
}
}

Expand Down
40 changes: 30 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,47 @@

[![Build Status Github](https://github.com/vnau/IviVisaNetSample/actions/workflows/build.yml/badge.svg)](https://github.com/vnau/IviVisaNetSample/actions/workflows/build.yml) [![Build Status AppVeyor](https://ci.appveyor.com/api/projects/status/dag3r35u0sn1sci3?svg=true)](https://ci.appveyor.com/project/vnau/IviVisaNetSample)

In traditional approach [VISA.NET Shared Components](http://www.ivifoundation.org/shared_components/) distributed only as part of a vendor's installer for its VISA implementation.
This approach suppose installation of vendor's VISA library implementation on CI server environment even if communication with instruments is not required on this stage.
In the traditional approach, [VISA.NET Shared Components](http://www.ivifoundation.org/shared_components/) are distributed solely as part of a vendor's installer for its VISA implementation.
This approach necessitates the installation of the vendor's VISA library implementation on the CI server environment, even if communication with instruments is not required at this stage.

If developed application assumed to work with various third-party VISA implementations and no VISA libraries were installed it would be nice if it could provide only a part of the functionality or report VISA library necessity.
If a developed application is intended to work with various third-party VISA implementations, and no VISA libraries are installed, it would be beneficial if it could provide only a part of the functionality or report the necessity of the VISA library.

This is simple example of application bypassing those limitations using unofficial NuGet VISA.NET Shared Components distribution [Kelary.Ivi.Visa](https://www.nuget.org/packages/Kelary.Ivi.Visa/).
This is a simple example of an application bypassing those limitations by using the unofficial NuGet VISA.NET Shared Components distribution, [Kelary.Ivi.Visa](https://www.nuget.org/packages/Kelary.Ivi.Visa/).

### VISA.NET implementations ###
### Using VISA.NET in .NET 5+ solutions ###

In most cases, an application built with VISA.NET Shared Components version 5.5 will work with latter implementations of Shared Components.
.NET Standard, introduced in 2016, marked a pivotal moment in the evolution of the .NET ecosystem.
Since then, many versions of .NET have emerged, diminishing the relevance of the traditional .NET Framework for new projects.

Below is a non-exhausting list of vendor-specific VISA implementations with VISA.NET support.
Despite these advancements, VISA.NET remains tied to .NET Framework 2.0, with no indication from the IVI Foundation of plans to embrace modern technologies.

A notable challenge is VISA.NET's limited integration into projects with contemporary .NET runtimes.
However, compatibility has improved since .NET Core 2, allowing referencing of .NET Framework assemblies in compatibility mode. This suggests potential usability of the VISA.NET library and .NET Framework implementations within a modern .NET runtime.

Another challenge arises as the modern .NET runtime no longer uses the GAC, which VISA.NET relies on.
This issue is mitigated by preloading VISA.NET implementations.

In this repository, an **experimental** .NET 8 example application showcases potential VISA.NET usage with various vendors implementations.
Despite progress, successful execution is not guaranteed, as the .NET runtime may lack support for all .NET Framework APIs.
Basic functions have been tested with Rohde & Schwarz and Keysight implementations, but some functions may require further testing.

We look forward to the IVI Foundation moving the VISA.NET library from the .NET Framework to the .Net Standard, which will allow VISA.NET to become not only interchangeable, but also cross-platform.

### Existing VISA.NET implementations ###

In most cases, an application built with VISA.NET Shared Components version 5.5 will work with later implementations of Shared Components.
For .NET 5+ projects, the Shared Components version must match those that the implementation depends on.

Below is a non-exhaustive list of vendor-specific VISA implementations with VISA.NET support.

| Implementation | Size | VISA.NET Shared Components Version |
| --- | ---- | ---- |
| [Rohde & Schwarz VISA 7.2.3 for Windows](https://scdn.rohde-schwarz.com/ur/pws/dl_downloads/dl_application/application_notes/1dc02___rs_v/RS_VISA_Setup_Win_7_2_3.exe)| 63 MB | 7.2 |
| [NI-VISA 2023 Q4](https://www.ni.com/en/support/downloads/drivers/download.ni-visa.html)| 1.41 GB | 7.2 |
| [NI-VISA 2023 Q4](https://www.ni.com/en/support/downloads/drivers/download.ni-visa.html#494653)| 1.41 GB | 7.2 |
| [Rohde & Schwarz VISA 5.12.3 for Windows](https://www.rohde-schwarz.com/us/applications/r-s-visa-application-note_56280-148812.html) | 59 MB | 5.11 |
| [Keysight IO Libraries Suite 2019](https://www.keysight.com/main/software.jspx?id=2175637) | 251 MB | 5.11 |
| [NI-VISA 20.0](https://www.ni.com/ru-ru/support/downloads/drivers/download.ni-visa.html#346210)| 1.16 GB | 5.11 |
| [NI-VISA 20.0](https://www.ni.com/en/support/downloads/drivers/download.ni-visa.html#346210)| 1.16 GB | 5.11 |
| [Keysight IO Libraries Suite 2018](https://www.keysight.com/main/software.jspx?id=2175637) | 260 MB | 5.8 |
| [Keysight IO Libraries Suite 17.2](https://www.keysight.com/main/software.jspx?id=2784293) | 191 MB | 5.6 |
| [NI-VISA 15.0,17.5](http://www.ni.com/download/ni-visa-17.5/7220/en/) | 756 MB | 5.6 |
| [NI-VISA 15.0,17.5](https://www.ni.com/en/support/downloads/drivers/download.ni-visa.html#306106) | 122 MB | 5.6 |
| [Kikusui KI-VISA 5.5](https://www.kikusui.co.jp/en/download/en/?fn=com_kivisa) | 89 MB | 5.5 |

0 comments on commit 13a27ed

Please sign in to comment.