Reputation: 171
Is there a simply way to load and call functions of a C# dll from F# both located in the same solution?
I've set up this very simple example:
"CSharpComm\CSharpComm.csproj"
"FSharpInterop\FSharpInterop.fsproj"
The C# library provides this simple service:
using System;
namespace CSharpComm
{
public class CSharpService
{
public string Request()
{
return "Hello World provided by C#";
}
}
}
After adding the C# project in the F# reference (right click on the References icon Add Reference... > Projects > Select CSharpComm), I tried to run this line in F#:
open CSharpComm
Error message: InteropHelloWorld.fsx(1,6): error FS0039: The namespace or module 'CSharpComm' is not defined.
Tried to run:
#r "C:\src\FSharpInterop\CSharpComm\bin\Debug\CSharpComm.dll"
or
#r "C:\src\FSharpInterop\CSharpComm\bin\Debug\CSharpComm"
This ends up with the error message: Invalid directive. Expected '#r "<file-or-assembly>"'.
I've tried using the interop service library:
open System.Runtime.InteropServices
module InteropWithNative =
[<DllImport(@"C:\src\FSharpInterop\CSharpComm\bin\Debug\CSharpComm.dll", CallingConvention = CallingConvention.Cdecl)>]
extern void Request()
InteropWithNative.Request()
Error message: > System.EntryPointNotFoundException: Unable to find an entry point named 'Request' in DLL 'C:\src\FSharpInterop\CSharpComm\bin\Debug\CSharpComm.dll'. at FSI_0005.InteropWithNative.Request() at <StartupCode$FSI_0005>.$FSI_0005.main@()
Here I guess I need to instantiate a CSharpService
object first but the Interop doesn't let me create a ctor in F#. Also, even if I manage to call the C# service that way, that looks a bit overkill for two technologies that supposes to cohabit easily.
Following this question Import/open DLLs in F#, I tried to add an <ItemGroup>
in my F# project but didn't find where and how I can do that (I thought Visual Studio would take care of that when adding the reference project).
F# App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
</configuration>
F# packages.config
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FSharp.Core" version="5.0.1" targetFramework="net48" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
</packages>
[Edit] FSharpInterop.fsproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.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')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>425bd4f6-3577-4c20-9ceb-3f95609706d8</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>FSharpInterop</RootNamespace>
<AssemblyName>FSharpInterop</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<UseStandardResourceNames>true</UseStandardResourceNames>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<Tailcalls>false</Tailcalls>
<OutputPath>bin\$(Configuration)\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget>
<DocumentationFile>bin\$(Configuration)\$(AssemblyName).XML</DocumentationFile>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<Tailcalls>true</Tailcalls>
<OutputPath>bin\$(Configuration)\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget>
<DocumentationFile>bin\$(Configuration)\$(AssemblyName).XML</DocumentationFile>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(FSharpTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets') ">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
<Import Project="$(FSharpTargetsPath)" />
<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
<Compile Include="Program.fs" />
<None Include="App.config" />
<Content Include="packages.config" />
<None Include="InteropHelloWorld.fsx" />
</ItemGroup>
<ItemGroup>
<Reference Include="FSharp.Core">
<HintPath>..\packages\FSharp.Core.5.0.1\lib\netstandard2.0\FSharp.Core.dll</HintPath>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
<Reference Include="System.ValueTuple">
<Private>True</Private>
</Reference>
<Reference Include="CSharpComm">
<HintPath>..\CSharpComm\bin\Debug\CSharpComm.dll</HintPath>
</Reference>
</ItemGroup>
<!-- 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>
Upvotes: 0
Views: 735
Reputation: 12351
Disclaimer: This is my first ever attempt at F#
Add a reference to the C# project from the F# project
Unsure why this "doesn't work" (reference the project, not the dll as @user1981 stated)
C# Library Project in Solution:
using System;
namespace SampleLibInCSharp
{
public class CsharpService
{
// a static function to do trivial test....
public static string Request()
{
return "Hello World provided by C#";
}
}
}
Trivial F# Console App (from VS 2019 template), set as startup app in VS: Program.fs
open System
open SampleLibInCSharp
// Define a function to construct a message to print
let from whom =
sprintf "from %s" whom
[<EntryPoint>]
let main argv =
let message = from "F#" // Call the function
printfn "Hello world %s" message
let cs = CsharpService.Request()
printfn "ola %s" cs
0 // return an integer exit code
Run the F# console app:
Hello world from F#
ola Hello World provided by C#
Note: I did see some odd red squiglies denoting some syntax error with the message you posted, but it seems more like a VS artifact or bug. Rebuilding and/or running is fine (as above).
As stated, this is a trivial F#/C# test only...
Hth...
Upvotes: 3