I think this might be more generic, but in this case I try to create a c++ CLR to Microsoft Dynamic NAV.
If I do this in C# it works fine. C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VisualCSharp
{
public class MyTest
{
public MyTest()
{
}
public int AddTest(int a, int b)
{
return a + b;
}
}
}
When I try to add that to Dynamic NAV, I find it:
Select and press OK:
So far so good.
Then I like to do the same in C++ with a CLR class.
I start with creating a C++ CLR Class library and add the code for the test: (I did it inline here, just to keep code simple, but it does not matter if I separate it.)
#pragma once
using namespace System;
namespace VisualCPP
{
public ref class MyTest
{
public:
MyTest()
{
};
int AddTest(int a, int b)
{
return a + b;
};
};
}
Do the same with this. In Assembly list it come up identical to the VisualCSharp addin, but it shows up as Processor "x86", but thats ok. I have C# that also do that (COM wrappers) and they also work fine.
However, when I press "OK" i do get an error telling me that it can not load the type "VisualCPP".
So, question is: What am I missing? I know that the CLR code is different as it is not pure managed (even using the depricated /pure compile option does not work), but is there some kind of setting or declaration that had to be done to make my class visible?
As far is I know you cannot use unmanaged code in NAV.
You assembly also need to be signed - in you example the Public Key Token is null therefore it's not gonna work.
Situations like that I always recommend to create a wrapper dll to handle the unmanaged code though it (for example this is what Microsoft was done with the Office SDK)
I hope it helps.
Cheers!
Related
I am trying to call a function from a c# dll in a flutter desktop app,
In the C# dll I have,
using System;
using System.Runtime.InteropServices;
namespace MathLibrary
{
// NOTE: I have set set [assembly: ComVisible(true)] in AssemblyInfo.cs
[ComVisible(true)]
[Guid("66DE2FB9-7A3B-4C33-AF26-9AD5EDD4C71F")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IMathLibrary
{
[DispId(1)]
string multiply(int a, int b);
};
[ComVisible(true)]
[Guid("021E950E-3612-4FAD-9F15-F61632A95BD8")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("MathLibrary.MathCalc")]
public class MathCalc : IMathLibrary
{
public string multiply(int a, int b)
{
return "Product is " + (a * b).ToString();
}
}
}
I used this repo as the base flutter app.
In the app I have used platform channel to communicate between dart and c++ code. In c++ code I am trying to call the c# function (in file windows/runner/custom_channel.cpp).
After some googling, I came up with the following
First I added an import to the tlb file (had to add import to generated tlh file for IntelliSense to work)
#import "bin/MathLibrary.tlb"
using namespace MathLibrary;
And the following function is supposed to call the c# function
CoInitialize(NULL);
MathLibrary::IMathLibraryPtr IcalcPtr;
HRESULT hr = ::CoCreateInstance(__uuidof(MathLibrary::MathCalc), NULL,
CLSCTX_INPROC_SERVER,
__uuidof(MathLibrary::IMathLibrary),
(void**)&IcalcPtr);
_bstr_t calcVal;
if (FAILED(hr) || IcalcPtr == nullptr) {
// CoCreateInstance failed
// THIS CONDITION IS MET
(*resPointer)->Error("Cannot Create COM Object");
return;
}
//IcalcPtr->multiply(a, b, &calcVal);
calcVal = IcalcPtr->multiply(a, b);
// not sure how to convert bstr to std::string
const char* calcStr((const char*) calcVal.GetBSTR());
c.assign(calcStr);
CoUninitialize();
The CoCreateInstance fails.
Since I have no experience with c++, I am confused,
what is IMathLibraryPtr(I didn't define in c#)
Intellisense showed that a and b in IcalcPtr->multiply(a, b) are long but I thought it would be int
When I make a release build do I need to include the tlb or dll
what is tlh file, it got generated during build and I got Intellisense support only if I add an import to that file
I would like to understand in general how to interact with c# com-interface from c++ and also how to make it work in my case. Sample code and document links would be helpful
The problem was with the way the c# dll was built and not with the way it was called from c++. I had built the dll for "Any CPU" once I built it for x64 it worked.
I wanna call a uwp app with Uri in a win32 C++ console application.The first thing I thought is using LaunchUriAsync, but I couldn't find Windows.System.Launcher.LaunchUriAsync in win32 C++. So I wanna create a uwp class library to call LaunchUriAsync and win32 call this library. I find an example and now I can load the library sucessfully, but GetProcAddress always returns null. Not sure whether it is feasible calling uwp class library in win32 console. Pls help me out. The project is at https://github.com/vincent1000/win32CallUwpLibrary
The code is very simple:
UWP Classy Library:
namespace ExportedCodeSolution
{
public class Class1
{
[DllExport(ExportName = "callUri", CallingConvention = CallingConvention.StdCall)]
static public async void callUri()
{
Console.WriteLine("call URI start");
await Launcher.LaunchUriAsync(new Uri("www.bing.com"));
}
}
}
And Win32 Console:
using CallUriFn = void(__stdcall*) ();
int main()
{
HMODULE mod = LoadLibraryA("ExportedCodeSolution.dll");
CallUriFn fn = reinterpret_cast<CallUriFn>(GetProcAddress(mod, "callUri"));
fn();
}
Also, is any other method to call LaunchUriAsync in win32? I have searched some methods but none works for me.
The solution is trivial: Simply call Launcher.LaunchUriAsync from your console application. The easiest route is to use C++/WinRT. Assuming that you are using Visual Studio with the C++/WinRT extension installed, create a "Windows Console Application (C++/WinRT)", and replace the wizard generated code with this:
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.System.h>
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::System;
int main()
{
init_apartment();
Uri uri(L"www.bing.com");
Launcher::LaunchUriAsync(uri).get();
}
This will compile, but fail at runtime due to "www.bing.com" not being a valid URI. This needs to be replaced with a valid URI (e.g. "https://www.bing.com").
How can I call C# functions (a DLL) from D?
I have tried or am looking at the following:
Using the Derelict Mono package
(https://github.com/kubasz/derelict-mono)
Using Unmanaged Exports (See
Calling C# from C), D
to C to C#, eventually maybe eliminating the C.
The Derelict Mono approach works well for Hello World programs, however a larger DLL (with references to lots of other assemblies, each of which may or may not use genuine Windows API calls) fails as the DLL is not properly loaded.
Initial experiments with Unmanaged Exports result in errors with MSBUILD.
Try the following. First the D code:
module main;
import std.stdio;
import std.conv;
extern (C++) ulong receiveMe(ulong i);
extern (C++) ulong freeMe(ulong i);
void main() {
ulong l = receiveMe(0);
char* p = cast(char*)l;
char[] s = to!(char[])(p);
byte[] b = cast(byte[])(s.dup);
writeln("The answer is " ~ to!string(s));
ulong m = freeMe(0);
}
Then a C++/CLI shim:
#include "stdafx.h"
#using "...\CS-Interop\bin\x64\Debug\netstandard2.0\CS-Interop.dll"
using namespace System;
UInt64 sendMe(UInt64 arg) {
return CS_Interop::Test::receiveMe(42);
}
UInt64 freeMe(UInt64 arg) {
return CS_Interop::Test::freeMe(42);
}
Lastly the C#:
using System.Runtime.InteropServices;
using System.Text;
namespace CS_Interop {
public class Test {
public static byte[] buffer;
public static GCHandle gcbuf;
public static ulong receiveMe(ulong arg) {
string s = "I was a string " + arg;
s = (s.Length + 2) + s;
buffer = Encoding.ASCII.GetBytes(s);
gcbuf = GCHandle.Alloc(buffer, GCHandleType.Pinned);
ulong l = (ulong)gcbuf.AddrOfPinnedObject();
return l;
}
public static ulong freeMe(ulong arg) {
gcbuf.Free();
return 42;
}
}
}
I'm still looking at ways to get rid of that C++/CLI shim.
This code is written in such a way that you can poke around with the VS debugger.
This is very simple to set up and test in Visual Studio. With Visual D installed, first set up a C++/CLI project (NOT a Visual D project) and park the D and C++ code there. Then setup a C# DLL project under the D project.
It is one thing to call C# code from D, but another thing to get data back unless you are using only simple scalar types like int. The key lines of C# are
gcbuf = GCHandle.Alloc(buffer, GCHandleType.Pinned);
ulong l = (ulong)gcbuf.AddrOfPinnedObject();
where you first need to pin the thing you are sending back then send the address back to D. There is no tedious mucking about with marshaling in the C++ part, your D code just needs to be able to deal with whatever sits behind the pointer.
Be sure also to free the pinned pointer once you're done with it. Comment out the freeMe line in the D code and watch the memory usage grow (and grow) in VS.
Personally, I find the pin process a bit fickle as GCHandle.Alloc will only work when its first argument, be it a byte array or structure, contains blittable items.
See also https://learn.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types
I have a preliminary string passing solution from D to (a small amount of) C++ to C# based on the following articles: (I gave up on unmanaged exports from Robert Giesecke)
C# "Unmanaged Exports" (tutorial from Hans Passant)
Calling C# function from C++/CLI - Convert Return C# string to C String
The D to C++ integration with Visual D just works.
https://rainers.github.io/visuald/visuald/vcxproject.html (See Visual C/C++ Project Integration)
https://dlang.org/spec/cpp_interface.html
You can use Unmanaged Exports to call C# from D. I've done it without problems.
See https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports
However, when I tried Unmanaged Exports with Visual Studio 2017, I also could not get it to work. Unmanaged Exports worked well with VS2015. Considering the link is from July 2009, other aspects could have become stale.
Be sure to read the instructions carefully, and most importantly make sure you are building for x86 or x64, and not "any CPU". Marshalling the data will be another challenge.
I'm working in a native C++ enviroment on WP8.1.
Let's say I want to call a functions like
Microsoft.Phone.Info::DeviceExtendedProperties.GetValue( "DeviceUniqueId" );
The problem is no matter how I try, it didn't pass compile.
I know "Microsoft.Phone.Info" is a name space,
in C# ppl wrote:
using Microsoft.Phone.Info;
but in C++, I tried
using namespace Microsoft.Phone.Info;
void func()
{
DeviceExtendedProperties.GetValue("DeviceUniqueId");
}
didn't pass compile. or
void func()
{
Microsoft.Phone.Info::DeviceExtendedProperties.GetValue("DeviceUniqueId");
}
didn't pass compile.
It keeps telling me something like this
'Microsoft' : illegal use of namespace identifier in expression
so how do I use this namespace properly?
Thank you guys for the reading and answering. :-)
I can't find a C++ example for retriving the device ID. :-P
Although many of the properties from DeviceInformation can be found on EasClientDeviceInformation, the device ID is not one of them. Instead, you should look at using the ASHWID which will get you an app-specific hardware ID.
In native WP runtime, using a namespace follows this syntax
using namespace Windows::Phone::Info;
But you will find out that the namespace does not exist in runtime. Only in WP Silverlight.
So you can look at this MSDN webpage that tells you where they moved all the functionality from 8.0 SL to 8.1 runtime.
Windows Phone Silverlight to Windows Runtime namespace and class mappings
Then if you search for "Windows.Phone.Info" it will lead you to the new functions you should call.
Here is the ID : EasClientDeviceInformation class
I guess I got it:
EasClientDeviceInformation^ deviceInfo = ref new EasClientDeviceInformation();
deviceInfo->Id; //<- is what I want
The MSDN on templates and template parameters is completely f****ng useless.. I can't even follow the instructions because it implies I have access to files that don't exist yet (like .vstemplate), and god forbid it provides some directories for this stuff.
Anyway I tried to make do and I thought I figured it out.. But apparently not:
I made a class file to export to a template that looks like this:
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using NovaTech.Framework;
using NovaTech.Utilities;
namespace $rootnamespace$
{
public class $safeitemname$ : BaseComponent
{
public $safeitemname$() : base()
{
}
}
}
But when i export it to a template and reimport it, it looks like this:
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using **NovaTech.NED.AssetComponents**.Framework;
using **NovaTech.NED.AssetComponents**.Utilities;
namespace NovaTech.NED.AssetComponents
{
public class NEDComponent1 : BaseComponent
{
public NEDComponent1() : base()
{
}
}
}
(EDIT: Those parts surrounded in ** ** were supposed to be bolded..)
EDIT: (fixed one of the problems I had, only 1 left):
Notice the two usings there changing despite them not being marked as parameters in the template. The two usings are not supposed to be parameters, yet for some reason it treats them like they are. How can I stop this from happening?
Managed to find a solution. I figured out that it replaces my usings with parameters (why, I still don't know) during EXPORTING, not importing. So I just exported the template, replaced the .cs file with a CORRECT one, and when I reimported it everything was as it should be.