How to connect c++ library to blazor project? - c#

I've been trying to use a c++ library in my blazor project, so I firstly created a small function to test if it is possible:
#include <emscripten.h>
EMSCRIPTEN_KEEPALIVE
int mult(int i)
{
return i * 4;
}
i compiled it using em++ with this command from a guide:
em++ test.cpp -o lib.wasm -Oz -s SIDE_MODULE=1 -s WASM=1 -s "STANDALONE_WASM=1"
Also I added reference to my csproj:
<NativeFileReference Include="lib.wasm" />
And now I am getting an error when try to buil it: lib.wasm: not a relocatable wasm file
Also I tried compile this file as C code with emcc test.c -shared -o lib.o and it worked fine with added reference: <NativeFileReference Include="lib.o" /> When I changed emcc to em++ I got an runtime blazor error when tried to use this function: missing function: mult
Page code where I tested both of them:
#page "/counter"
#using Microsoft.AspNetCore.Components.Web
#using System.Runtime.InteropServices
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status"> <i>Current count: </i> #currentCount</p>
<button class="btn btn-primary" #onclick="IncrementCount">Click me</button>
#code {
private int currentCount = 2;
private void IncrementCount()
{
[DllImport("lib")]
static extern int mult(int n);
currentCount = mult(currentCount);
}
}
What am I doing wrong?

Related

How to set variables for `dotnet new`?

Standard template for command
dotnet new console
contains #if for the preprocessor:
#if (csharpFeature_TopLevelProgram)
// See https://aka.ms/new-console-template for more information
#endif
#if (!csharpFeature_ImplicitUsings)
using System;
#endif
#if (csharpFeature_TopLevelProgram)
Console.WriteLine("Hello, World!");
#else
namespace Company.ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
}
#endif
What command line switches should I specify to dotnet new so that the variable "csharpFeature_TopLevelProgram" is not defined and so that when I execute dotnet new console the file is generated not for Net6 but without TopLevelProgram as before in the good old Net5?
If you use the -h command line option, it tells you how to use the template and that you can specify the --use-program-main parameter:
For example:
dotnet new console --use-program-main true
Note: You may need to update to a newer version of .NET 6 for this command line option to exist. This was tested with v6.0.401

Using React form submission in C# .NET Controller class

I'm learning how to use React with .NET and am using the Visual Studio templates provided to play around (I'm using a Mac, if that is useful for context). I am starting with very simple back end applications and trying to get my React front end to link to these. I am very new to using React and .NET so I apologise if the questions I ask seem simple. Here is my current issue:
I have the following C# Controller class which retrieves and returns the middle letters of a string:
MiddleLetterController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace TestingReactDotNet.Controllers
{
public class MiddleLetterController : Controller
{
public string GetMiddle(string word)
{
if (word.Length % 2 == 0)
{
return word.Substring(word.Length / 2 - 1, 2);
}
else
{
return word.Substring(word.Length / 2, 1);
}
}
}
}
I am using React on the front end and want whatever the user types and submits in the form to be the word passed as an argument to the GetMiddle() method and for this to be displayed on screen.
My current MiddleLetter.js file is:
import React, { Component } from "react";
export class MiddleLetter extends Component {
state = {
word: "",
};
updateInput(key, value) {
// update react state
this.setState({
[key]: value
});
}
render() {
return (
<center>
<div className="App">
<div>
Type a word...
<br />
<input
type="text"
placeholder="Type word here ..."
value={this.state.word}
onChange={e => this.updateInput("word", e.target.value)}
/>
<button onClick={() => ?????)}>Submit</button>
<br />
</div>
</div>
</center>
);
}
}
I have looked at the React documentation but cannot work out how / the best way to link the React form submission to the backend application. I would be really grateful if anyone had any suggestions please :) Thank you.
You're probably going to want to pull in a library like axios or fetch into your react project "yarn add axios" in your react folder.
Then at the top of your MiddleLetter.js you'll need to import axios from axios;
create an instance of axios in MiddleLetter.js, something like:
const instance = axios.create({
baseURL: "localhost:{whatever port you're running on}",
)}
Then in your "updateInput" function where your comments are add:
const response = await instance.get("/middleletter/" + value);
your response should have the return value from the controller.
Then for your controller.
add [HttpGet({word})]
over public string GetMiddle(string word)
This will let the controller get the word from your URL.
There are better ways to do all of this. But this should get you where you need to go.

Unable to load DLL the specified procedure could not be found. (Exception from HRESULT: 0x8007007F)

For a long time I have a problem with a project that I have to implement in mine, I'll explain it to you.
My goal is to call a C ++ class from a C # application (Project 1), the problem is that the C ++ project (Project 3) is not compatible with CLR.
What I have done so far has been created an intermediate project also in unmanaged C ++ (Project 2) to be compatible with the project 3.
Project 2 consists of a very simple method that initializes a class from project 3 and uses this object for different operations.
I'm working in Visual Studio and it does not give me an error when compiling, but at run time I get the following error:
Unable to load the DLL file 'PROJECT-ROUTE \ Project2.dll': The specified procedure could not be found. (Exception from HRESULT: 0x8007007F)
in project1.process ()
The thing is that the previous error comes out only when within the project2 method I initialize the class from project 3, if I comment the initialization line then it goes well, I can not understand why between 2 C ++ projects of the same type gives me this type of problems.
Can somebody help me?
thank you
C# Code (Project 1)
private const string DllFilePath = #"PATH_TO_DLL\Proyect2.dll";
[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl, EntryPoint = "process")]
public extern static void process();
[HandleProcessCorruptedStateExceptions]
public static string Prox(string a, string b)
{
string str = "OK";
try
{
process();
}
catch (System.AccessViolationException exception)
{
return exception.Message + " " + exception.StackTrace;
}
catch (Exception exception)
{
return exception.Message + " " + exception.StackTrace + " ";
}
return str;
}
}
Middle proyect Unamanged C++ Code (Project 2)
Project2.h
#include <stdexcept>
#include "Project3.h"
using namespace std;
namespace FinalProcess
{
extern "C" { __declspec(dllexport) void __cdecl process(); }
}
Project2.cpp
#include "Project2.h"
#include <iostream>
#include <chrono> // To measure execution time
namespace FinalProcess
{
void process()
{
OCTA::Analyzer& ld = OCTA::Analyzer::getInstance(); // <-- Singleton
// if I comment this line then it goes well
}
}
Assuming that the information that you present here is correct (and I have my doubts because you've not copied it verbatim as can be seen from the DLL name of Proyect2.dll) then the error cannot be that the function process is not found. In which case the error has to be in the linking to Project3.
Your Project2.dll is presumably attempting to link to Project3.dll and to a function that is not exported from Project3.dll. That would explain the behaviour that you report. This would happen typically if the Project3.lib file that you linked when building Project2.dll did not match the Project3.dll file found by the executable at runtime.
Make sure that the version of Project3.dll that is being loaded is up to date and matches the .h and .lib files you used to build Project2.dll.
Rather than give an absolute path of the DLL in your C# code, you just the file name of the DLL. Place both DLLs in the same directory as your C# executable.

Autofac is not loading module from Multi Target .Net Standard library

I'm developing a windows service and I'm referencing a .NET Standard library where I have an Autofac Module, I'm going call this library as A. I have the following PropertyGroup in the csproj:
<PropertyGroup>
<TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
</PropertyGroup>
And this is the Autofac module quoted earlier:
public class DefaultModule:Module
{
protected override void Load(ContainerBuilder builder)
{
#if net461
builder.Register(context => {
return new BusSettings
{
HostAddress = System.Configuration.ConfigurationManager.AppSettings["HostAddress"],
Username = System.Configuration.ConfigurationManager.AppSettings["Username"],
Password = System.Configuration.ConfigurationManager.AppSettings["Password"],
QueueName=System.Configuration.ConfigurationManager.AppSettings["QueueName"]
};
}).AsSelf();
#else
builder.Register(context => {
var configuration = context.Resolve<IConfiguration>();
return new BusSettings
{
HostAddress = configuration["BusSettings:HostAddress"],
Username = configuration["BusSettings:Username"],
Password = configuration["BusSettings:Password"],
QueueName = configuration["BusSettings:QueueName"]
};
}).AsSelf();
#endif
Now I created .NET Framework console app using 4.61 as Target Framework.And this is the code I use to load the modules:
//Library A is loaded By ExtractCustomAssemblyModules
List<Assembly> assemblies = ExtractCustomAssemblyModules();
containerBuilder.RegisterAssemblyModules(assemblies.ToArray());//Register custom modules
When I execute containerBuilder.Build() I'm not seeing Autofac loading the module and registering the services I have in my custom module, so is giving me an exception because it couldn't found a dependency. Now, I created a .NET Core 2 Console application and did exactly the same, at the time to call containerBuilder.Build() the code jump to the module and I see the services been registered and no exception this time
Why is not loading the Autofac Module in the .NET framework Console App?
PS: I found this blog really useful, I switched the first target framework to .NET 4.61 as you can see in the PropertyGroup but still I'm seeing in grey 4.61 code inside the if.
Small Test
Lets build a sample library with
namespace MyClassLibrary
{
public class Foo
{
public static string Info { get; } = "Conditionals"
#if net461
+ " net461"
#endif
#if NET461
+ " NET461"
#endif
#if NETCORE
+ " NETCORE"
#endif
#if NETSTANDARD
+ " NETSTANDARD"
#endif
#if NETSTANDARD2_0
+ " NETSTANDARD2_0"
#endif
+ "";
}
}
and
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
</PropertyGroup>
</Project>
If we reference this library in a .Net Framework 4.6.1+ console application with a simple
using System;
namespace ConsoleApp.NetCore
{
class Program
{
static void Main( string[] args )
{
Console.WriteLine( MyClassLibrary.Foo.Info );
}
}
}
the output is
Conditionals NET461
github: Complete Solution
Conclusion
The directive net461 is unknown but NET461 is known.
As you can see, size does matters :o)

Auto inject javascript in blazor application

I'm developing a Blazor extension library.
One thing in this library would be the reuse of the javascript alert() method.
I know how to do this, this involves adding this in a .cshtml page:
<script>
Blazor.registerFunction('Alert', (message) => {
alert(message);
});
</script>
and this in my code:
public void Alert(string message)
{
RegisteredFunction.Invoke<object>("Alert", message);
}
I would like the javascript part somehow to be auto injected in the html if you use my package (or maybe always). Not sure if this is possible (yet)
Any ideas on this?
Edit
Since blazor 0.2 there is a more supported way to do this. Look at the blazorlib template for an example:
dotnet new -i Microsoft.AspNetCore.Blazor.Templates
dotnet new blazorlib
0.1 answer
I ended up with creating my own blazor component containing my javascript, like this:
public class BlazorExtensionScripts : Microsoft.AspNetCore.Blazor.Components.BlazorComponent
{
protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder)
{
builder.OpenElement(0, "script");
builder.AddContent(1, "Blazor.registerFunction('Alert', (message) => { alert(message); });");
builder.CloseElement();
}
}
And adding it to my app.cshtml like this:
#addTagHelper *, BlazorExtensions
<Router AppAssembly=typeof(Program).Assembly />
<BlazorExtensionScripts></BlazorExtensionScripts>

Categories

Resources