VBA

Introduction to Marshaling in C#

Posted by Nabil Kherouf
on December 3, 2024
Comments Off on Introduction to Marshaling in C#

Using Marshaling to Pass Data between
C# and Native Code (C/C++, Fortran)

Introduction

Does your C#/VB.NET managed code program need to call unmanaged (legacy) code routines in DLL‘s written in C/C++, Fortran, or Assembler

We found ourselves writing a C# program for a customer and needed to call analysis routines in a thermodynamics library called REFPROP which was written in Fortran and compiled with the C-Runtime library. The REFPROP routines expect character, integer, double arrays of data passed as both inputs and outputs. Types can be different (size, allocation, structure…etc) between managed and unmanaged code and hence the main need for marshaling as explained in this post. The following images show a Fortran method wrapped and invoked from within a C# program:

WMOLdll method being called in C#
Importing the WMOLdll method from the Fortran library

Overview

Marshaling in C# is the process of converting data between different languages and environments. One of its most important aspects is managing the data to be passed from unmanaged code to managed code and vice versa. Managed code is code that is handled by a runtime environment such as CLR (Common Language Runtime) whereas unmanaged code is code that is run directly on the hardware and outside any runtime environment. In C#, marshaling is often used to interact with native code, such as code written in C/C++ or Fortran. In this article, we will explain the basics of marshaling in C# and provide examples of how to make use of it.

Why is Marshaling needed ?

Marshaling is needed because the data types and memory are handled very differently between managed and unmanaged code. For example, a typical C-Runtime routine takes as both input/output a pointer to an array of characters, whereas C# provides a string class which does not give access to pointers. Thus, a mechanism is needed to pass data between managed and unmanaged environments. Types of parameters which do not require any conversion/marshaling, for instance int, double, byte, long, are called Blittable. Marshaling for Non-Blittable types in C# is achieved using the MarshalAs attribute. See example below:

Invoking a Fortran method “ABFLSHdll” from C#
Input/output parameters of “ABFLSHdll” in Fortran

Examples

To achieve marshaling in managed code (C#), we have to use the MarshalAsAttribute attribute. It can be applied to either fields of a class, parameters, or return values of a method:

Marshaling of a parameter of a method:

[DllImport(@"C:\Program Files (x86)\REFPROP\REFPRP64.dll", CharSet = CharSet.Ansi)]
public static extern void ABFLSHdll
(
    [MarshalAs(UnmanagedType.VBByRefStr)] ref string ab,
    ref double a,
    ref double b,
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] double[] z,
    ref long iFlag,
    ref double T,
    ref double P,
    ref double D,
    ref double Dl,
    ref double Dv,
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] double[] x,
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] double[] y,
    ref double q,
    ref double e,
    ref double h,
    ref double s,
    ref double Cv,
    ref double Cp,
    ref double w,
    ref long ierr,
    [MarshalAs(UnmanagedType.VBByRefStr)] ref string herr,
    ref long ab_length,
    ref long herr_length
 );


In the code above, the ABFLSHdll routine is being imported (first line) from a library (REFPRP64.dll) written in Fortran and takes strings and arrays among other types which don’t need to be marshaled in-between native and managed code namely: double, int, long parameters. The first parameter is a string and is being marshaled using [MarshalAs(UnmanagedType.VBByRefStr)]. The method also expects an array of doubles for the fourth parameter and is being marshaled using [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)].

Marshaling of a return value of a method:

[return: MarshalAs(UnmanagedType.LPWStr)]
public String SaySomething()
{
    return "Hello World";
}


The return value of type String of the the function “SaySomething” above is marshaled using the keyword return: and the attribute MarshalAs(UnmanagedType.LPWStr).

Marshaling of a field of a class:

class MyClass {
    [MarshalAs(UnmanagedType.LPWStr)]
    public String msg = "Hello World";
}


The member field msg in the class above is marshaled using [MarshalAs(UnmanagedType.LPWStr)].

Conclusion

We’ve briefly explained Marshaling in this article and provided a few examples on how to do it in C#. As stated above, not all types need to be marshaled when importing unmanaged code. The MarshalAsAttribute is therefore optional as each data type has a default marshaling mechanism and the attribute is only necessary when a given type can be marshaled to multiple types within an environment.

Mill Creek Systems, Inc provides software development services in this area. If you have any software needs, please reach out to us by clicking on the “Contact us” button below.

Contact us

Helpful links

Type marshaling in C#: https://learn.microsoft.com/en-us/dotnet/standard/native-interop/type-marshalling

Blittable and Non-Blittable types: https://learn.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types

Common Language Runtime: https://learn.microsoft.com/en-us/dotnet/standard/clr?redirectedfrom=MSDN

Continue Reading

Calling C# Code from VBA

Posted by Nabil Kherouf
on November 8, 2024
Comments Off on Calling C# Code from VBA

Calling C# Code from VBA

Introduction

Do you have code written in C# that you want to invoke in VBA? In order to be able to achieve this, you will have to expose your C# code to COM (Component Object Model). COM is a platform-independent, object-oriented system for creating binary software components that can interact. COM objects can be created in a variety of languages and can be invoked from within any programming language that supports COM. We will show you in this post how to create a C# COM-visible class and invoke it within an Excel VBA project.

Using a C# COM-visible class in Excel VBA

Creating a COM-visible Assembly in C#.NET

We will use Visual Studio 2019, .NET Framework 4.8.1 and Microsoft® Excel® 64-bit in this post.

1- Open Visual Studio 2019 as Administrator and click on Create a new project then select C# Class Library (.NET Framework) from the menu. Make sure you select Class Library (.NET Framework) and not Class Library.


2- Right-click on your project in the Solution Explorer, then go to Properties.


3- Under the Application section, change Target Platform to .NET Framework 4.8.1.


4- Click on Assembly Information and check Make assembly COM-visible. This makes all public types in the library COM-visible. We will show below how to hide types that you wouldn’t want to expose to COM.


5- Then go to the Build section, change Platform target to x64 and click on Register for COM interop.


This option allows Visual Studio to register the dll on your machine and generate keys in HKEY_CLASSES_ROOT in the Registry. If this option is left unchecked, you will have to register the dll manually using the following command:

"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe" /codebase "…\MyComLib.dll"



6- Rename Class1.cs to MyComClass.cs, add following code to your MyComClass.cs file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace MyComLib
{
    [ComVisible(true)]
    public interface IMyComClass
    {
        void Add(ref double result, double x, double y);
    }

    [ComVisible(true)]
    public class MyComClass:IMyComClass
    {
        public void Add(ref double result, double x, double y)
        {
            result =  x + y;
        }
    }
}


The [ComVisible(true)] attribute before the name of the MyComClass class and IMyComClass interface allows you to expose or hide a certain object to COM. If you have a particular object that you don’t want to expose, simply set this attribute to false.

7- Rebuild the solution. If you encounter the following error, make sure you re-run VS2019 as Administrator


At this point, we have created a COM-visible assembly that Visual Studio automatically registers on the user’s machine. If you look under your bin\debug folder in Windows Explorer, you will see that a .tlb (type library) file got created after Visual Studio registered the assembly on your machine.



Calling a COM-visible class in Excel VBA

1- Open Excel and create a new blank workbook, then go to Developer>Visual Basic


2- In the Visual Basic editor go to Tools->References then browse to the Debug folder where .tlb file is and add it.


3- Add a module to your VBA project and add the following code.

Sub Test()

Dim MyComLibObj As New MyComLib.MyComClass

Dim a, b, result As Double

a = CDbl(Range("B3"))
b = CDbl(Range("B4"))

MyComLibObj.Add result, a, b

Range("B5") = result

End Sub

In order to be able to use our COM-visible class in Excel VBA, we have to create an instance of our MyComClass as shown at line 2 in the code above. Please note that MyComlib is the name the tlb/dll file.

Here’s the output from our method MyComClass.Add in the spreadsheet:


Conclusion

We have shown in this post how to create a COM-visible assembly in C# and invoke it in Excel VBA. Please note that such an assembly can be invoked in any Microsoft Product that leverages VBA namely Microsoft Word, Excel, PowerPoint, Access, Publisher, Visio, and Outlook.

Mill Creek Systems, Inc provides software development services in this area. If you have any software needs, please reach out to us by clicking on the “Contact us” button below.

Contact us! Continue Reading
MENU