Access a subclass function inside C# .dll in node.js - c#

I'm trying to access some functions inside my C# .dll library and I'm doing it in node.js. I managed to load the library but having some troubles accessing the functions because they are inside a namespace "CCTalkReader" which contains a class "CREDIT_MANAGER" and inside this class I have my method INIT. How do I call it correctly?
I tried like this:
const CCTalk = new ffi.Library("./CCTalkNoteBill.dll", {
'CCTalkReader':{
'CREDIT_MANAGER':{
'INIT': ["void", []]
}
}
});
CCTalk.INIT();
and this
const CCTalk = new ffi.Library("./CCTalkNoteBill.dll", {
'INIT': ["void", []]
});
CCTalk.INIT();
But I always get
Dynamic Symbol Retrieval Error: Win32 error 127
which I found out means that I don't write the second parameter (function name) correctly. Any Ideas?

Related

WebView2 AddHostObjectToScript in UWP crashes

I'm trying to pass a C# object to a WebView2 using AddHostObjectToScript. After not succeeding to retrieve the object from the webview, I've used the debugger and found out that the AddHostObjectToScript call is never completing.
Here is the full code snippet:
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class Example
{
public string Prop { get; set; } = "example";
}
namespace Example_UWP
{
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
InitializeAsync();
}
public async Task InitializeAsync()
{
await ExampleView.EnsureCoreWebView2Async();
ExampleView.Source = new Uri("http://localhost:3000");
ExampleView.CoreWebView2.OpenDevToolsWindow();
ExampleView.CoreWebView2.AddHostObjectToScript("example", new Example());
}
}
}
The example object is as a result not available in chrome.webview.hostObjects or chrome.webview.hostObjects.sync. The function throws the following error:
The group or resource is not in the correct state to perform the requested operation.
I've tried different alternatives without success, such as:
Keeping a reference to the Example instance in an attribute inside Example_UWP to avoid potential GC
Adding the host object before and after each of the previous steps within InitializeAsync
Wait for the event NavigationCompleted to add the host object.
Wait for 5 seconds before adding the host object.
I'm using Microsoft.Web.WebView2 version 1.0.1264.42
In order to interact with your third-party lib, you need to add a very specific C++ project, Windows Runtime Component (C++/WinRT), to your solution that must be called WinRTAdapter.
Next, you must install a lib to your C++ project from NuGet called Microsoft.Web.WebView2:
After this is done, you must your third-party lib as a reference.
Next, go to your C++ project properties go to Common Properties and choose WebView2:
Here you have to do four changes:
Set Use WebView2 WinRT APIs to No.
Set Use the wv2winrt tool to Yes.
Set Use Javascript case to Yes.
Edit Include filters and add the following ones:
Windows.System.UserProfile
Windows.Globalization.Language
CallJSInterface
CallJSInterface is the name of my third-party's namespace.
You click on OK and build your C++ lib.
After you have built your C++ lib (WinRTAdapter), you must add it to your main project as a reference.
Now, we need to do some changes to be able to invoke the functions from our third-party lib. The first one is to register it. We do it in the same LoadLocalPage() function from before or on NavigationCompleted:
var namespacesName = "CallJSInterface";
var dispatchAdapter = new WinRTAdapter.DispatchAdapter();
core_wv2.AddHostObjectToScript(namespacesName, dispatchAdapter.WrapNamedObject(namespacesName, dispatchAdapter));
Where CallJSInterface is your namespace. After this, you need to register your function in your JS like this:
var callJS;
if (chrome && chrome.webview) {
chrome.webview.hostObjects.options.defaultSyncProxy = true;
chrome.webview.hostObjects.options.forceAsyncMethodMatches = [/Async$/];
chrome.webview.hostObjects.options.ignoreMemberNotFoundError = true;
window.CallJSInterface = chrome.webview.hostObjects.sync.CallJSInterface;
callJS = new CallJSInterface.CallJSCSharp();
}
Where CallJSInterface is one more time your namespace. Now, you can invoke JS like this (the async() is mandatory):
callJS.async().KeepScreenOn()
If you need more details, I have a full tutorial on my website:
https://supernovaic.blogspot.com/2022/10/from-webview-to-webview2-in-uwp.html

How do I access static members of a c# library from c++ code?

I have been given a library written in C# and I need to use it in a C++ project. The C# library has been exported to a .tlb type library, which I can successfully import into my C++ project by using the #import directive.
Being utterly unfamiliar with COM I can't for the life of me figure out how to get at static member functions on any classes. Here's how I access it in C#:
void Function()
{
StaticClass.StaticMethod();
}
And then you get into the C++ side, what gets generated in the .tlh file is:
struct __declspec(uuid("some big long thing"))
/* dual interface */_StaticClass;
//long while later
_COM_SMARTPTR_TYPEDEF(_StaticClass, __uuidof(_StaticClass));
So I'm trying to figure out how to get use of the static class and haven't had any luck with Google. The only example anywhere else in any other project I have access to gives me something similar to this:
_StaticClassPtr s = _StaticClassPtr(__uuidof(_StaticClass));
but the example I have isn't for a static class anyway.
Basically I'm stuck with nowhere to even really start. This fails with "Unhandled exception at in <executable>: Microsoft C++ exception: _com_error at memory location <location>"
Edit: Since #dxiv informed me static methods aren't usable with COM interop, there's another option marked 'obsolete' that does not use static members -- problem is I get exactly the same exception when I construct the instance with similar syntax:
IInstanceClassPtr p = _IInstanceClassPtr(__uuidof(_InstanceClass));
The same exception is thrown, "_com_error at memory location"
Reading your question you would like to use C# using COM Interop. What I done is something like this. Starting from the assumption that COM is the acronym of Component Object Model and to use it you need an instantiable, local or remote, object. The example below creates an "in-proc" instance of the CLR object.
Create an interface which is exposed to COM:
namespace MyNamespace
{
/// <summary>
/// Provides an entry point for COM clients.
/// </summary>
[ComVisible(true)]
[Guid("A9E6D7FE-34FD-4A6B-9EB2-DC91F4AE567B")]
public interface IMyAccessor
{
void ExecuteStaticMethod();
// add anything else like methods, property,
}
}
then implement the interface in a C# class:
namespace MyNamespace
{
/// <summary>
/// The implementation of IMyAccessor.
/// </summary>
[ComVisible(true)]
[Guid("C65A7F81-641C-4F17-B34A-DEB88B4158E8")]
[ProgId("MyCompany.MyAccessor")]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IMyAccessor))]
public sealed class MyAccessor: IMyAccessor
{
public void ExecuteStaticMethod() { StaticClass.StaticMethod(); }
}
}
export the TLB and import it in C++ project (MyAccessor is only a name I used here) in the header file of your C++ class using the following clause:
#import "MyAccessor.tlb"
Within the class header add a line like the following:
MyNamespace::IMyAccessorPtr m_IMyAccessor;
And in the class implementation use the following:
HRESULT hr = m_IMyAccessor.CreateInstance(__uuidof(MyNamespace::MyAccessor));
if (FAILED(hr))
{
// do something if failed
}
m_IMyAccessor->ExecuteStaticMethod(); // this will execute your static method in C#
NOTE: when exporting the TLB use the correct switchs. In an x64 environment (/win64) must be used to have the right pointer size: normally tlbexp returns pointer usable in a 32bit environment. This is important if you want to extend the class with more sofistcated methods.
NOTE 2: if the returned HRESULT from CreateInstance is something like "class not registered", remember to execute the registration of the TLB wih REGTLIB.

iOS Plug ins for Unity

I know the basic premise for creating a plug ins for Unity is to create a wrapper for all the classes you want to call from within Unity. However, all of the docs and examples I have found have all expected a corresponding .cpp (if c++) or .mm (if objective-c) file.
However I'm trying to create a plug in for some native iOS code that doesn't contain any source files. All I have access to is four header files and a single .a file. Which I have gotten from here.
Now, because I don't have any .mm files to wrap I'm a bit confused as to how I can go about bringing in these files into Unity so that I can call them from within. Has anyone ever done this before?
Can someone point me to some documentation, or anything that may help me, in bringing in 4 header files and .a file into Unity as a plug in?
Please remember, there are no source files that I have access to. Only the header files themselves.
You don't need source files, even the header files are not needed as long as you know the functions' declarations.
As described in Building Plugins for iOS:
1.) Put your .a file in Assets/Plugins/iOS
2.) Look at the header files to get the function signatures you need e.g.
void RegisterUnityIPodCallbackListener (const char* gameObject, const char* method);
3.) Declare RegisterUnityIPodCallbackListener within a C# class by:
public class IPodHandler {
[DllImport("__Internal")]
private static extern void RegisterUnityIPodCallbackListener (string gameObject, string method);
public static void MyRegisterUnityIPodCallbackListener () {
if (Application.platform == RuntimePlatform.IPhonePlayer) {
RegisterUnityIPodCallbackListener (GAME_OBJECT, METHOD);
}
}
}
4.) Call at an appropriate location:
IPodHandler.MyRegisterUnityIPodCallbackListener ("MyCallbackGameObject", "MyCallbackMethod");
Common pitfalls:
You should not build directory structures within Assets/Plugins/iOS otherwise files don't get copied to the generated Xcode project
Provide a fallback solution when testing in editor player
Here is another guide: How to build Unity3d Plugin for iOS
A few notes if you get an error something like "Undefined symbols for architecture..."
1) Make sure the used architecture in .a-file matches yours (for example armv7)
2) Make sure the .a-file is compiled with libstdc++ (GNU C++ standard library) since Unity requires that.
The Estimote SDK you're are using has ObjectiveC classes. Functions in ObjectiveC can't be called directly via a Unity plugin (either can C++ classes for that matter). The reason is that function names are managled when you compile.
The interface between a Unity C# class and a library must a pure C interface. That is why there is often .mm or .cpp files along side a .a file. These are to wrap a C++ or ObjectiveC class in a pure C wrapper.
Your job is a little easier because most of the Estimote functions seem to be class functions. So you don't need to write wrapper functions to create and delete NSObjects.
Here is an example I wrote, a pure C wrapper around the TestFlightSDK. https://github.com/notoes/TestFlightUnity/blob/master/src/TestFlightCBinding.mm
Notice the extern "C" block, forcing the file the code to be compiled without name mangling. A c function signature and then an ObjectiveC call within the function.
So using the Esitmote ESTBeacon class as an example, the connectToBeacon call could like this:
extern "C" {
void ESTBeacom_Connect()
{
[ESTBeacon connectToBeacon];
}
}
And the .cs function would look like this:
class ESTBeacon {
[DllImport ("__Internal")]
private static extern void ESTBeacon_Connect();
public static void Connect() {
if( Application.platform == RuntimePlatform.IPhonePlayer )
ESTBeacon_Connect()
}
}
Put the .cs, .mm and .a files in Plugins/iOS and you'll be all good.
Use this reference to find out how to pass various data types. http://www.mono-project.com/Interop_with_Native_Libraries

How to redirect a method call?

I have got a DLL which works fine in Windows, but Inside one of its private functions the static System.IO.File.ReadAllBytes() is called. I have to use this DLL on a windows CE smart Device Project with Compact Framework 3.5 and there is no such method in the System.IO.File. I have tried to create a class named "File" inside the project like this:
public class File
{
internal static byte[] ReadAllBytes(string path)
{
}
internal static void WriteAllBytes(string path, byte[] bytes)
{
}
}
My own calls to the static Methods of the class File are redirected here But the calls inside the DLL methods still go to the System.Io.File class and I still get the MissingMethodException. I tried the methods with public modifiers but saw no change.
I even tried to rewrite the public method that calls the private method inside which the ReadAllbytes was invoked and used MethodInfo.Invoke with no success.
The question: Is there a way to force the method inside the Dll to accept my ReadAllbytes Method instead of System.File.IO.ReadAllBytes()? The invocation inside the DLL is like this:
using System.IO.File;
namespace Something
{
class SomeClass
{
public Boolean someMethod()
{
byte[] myBytes = File.ReadAllBytes(filePath);
}
}
}
Static methods like File.ReadAllBytes are resolved at compile time to a specific Assembly name and class. You will need to modify the DLL to change the call. Prehaps you could decompile and recompile it, or edit the IL.
*It is possible to redirect the call using the profiling hooks (the sameone the debugger uses), and that is how the Moles Mocking framework works. However it would not be suitable for production use.

Compile error when calling managed C++ from C#

I am new to .net .
I have a managed C++ library. It looks like this.
// header file
namespace cppnetdll
{
public __gc class Class1
{
public:
static int foo_int(void);
};
}
// source file
namespace cppnetdll
{
int Class1::foo_int(void)
{
return 123;
}
}
I can call this from a managed c++ program. When I try to call it from
a C# program, I get the compiler error: "The type or namespace name
'Class1' could not be found (are you missing a using directive or an
assembly reference?)" The error refers to the DllImport line below.
Here is the C# code
[code:1:a72c1df571]
namespace csuser
{
public class xxx
{
[DllImport("cppnetdll.dll")] extern
int Class1.foo_int();
private void yyy() { int i =
foo_int(); }
}
}[/code:1:a72c1df571]
I have tried various approaches but no success. What is the magic
syntax ?
It's funny that I can call unmanaged C++ functions from C# fairly
easily by declaring the functions as "C" and exporting from the DLL.
I expected calling managed code to be easier. Maybe it's so easy
that no one thought of documenting it !
You do not use a [DllImport] directive to call code that's written in managed C++. That is only intended for native DLLs that export their functions. Neither of which applies to yours, it isn't native and you don't export the function.
You built a managed assembly, you can treat it just like one you'd have written in C#. Project + Add Reference, Browse tab, navigate to the DLL. Or better yet, put both projects in one solution, use the Projects tab to select the reference.
Instead of using the [DllImport ...]
Try just adding a reference, here is a how to from MSDN:
http://msdn.microsoft.com/en-us/library/7314433t%28v=VS.90%29.aspx

Categories