Using unmanaged C#-DLL in C++/FORTRAN - c#

I am working on my master thesis and need your help! Btw I am studying mechanical enginneering... so my programming skills are limited.
Here my problem:
I have a DLL, which is created in C# ( I cannot post it, because it is a part of an unpublished research). But it gives me some Arrays ( 1D-Array [], 2DArray[,] ).
For a simulation with ABAQUS I need to import that C#-DLL in C++ and/or FORTRAN.
I found the solution from Robert Giesecke to create a unmanaged DLL. I think this is the easiest solution for me. (Of course if someone has another solution for me, a wrapper or something, please post it)
Here my 1D Array example for a unmanaged C#-DLL created with R.Giesecke Template:
using System;
using System.Text;
using RGiesecke.DllExport;
using System.Runtime.InteropServices;
namespace Testme
{
class Test
{
[DllExport("Get1DArray", CallingConvention = CallingConvention.StdCall)]
public static double Get1DArray([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] double[] STRESS, int i)
{
return STRESS[i];
}
}
}
and here my 2D Array code:
using System;
using System.Text;
using RGiesecke.DllExport;
using System.Runtime.InteropServices;
namespace Testme
{
class Test
{
public static int idx(int a, int b) { int cols = 2; return a * cols + b; }
[DllExport("Set2DArray", CallingConvention = CallingConvention.StdCall)]
public static int Set2DArray([In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] int[] STRAIN, int len)
{
STRAIN[idx(0, 0)] = 0;
STRAIN[idx(0, 1)] = 1;
STRAIN[idx(1, 0)] = 2;
STRAIN[idx(1, 1)] = 3;
STRAIN[idx(2, 0)] = 4;
STRAIN[idx(2, 1)] = 5;
return 0;
}
}
}
The Build have succeeded at both. How can I import the DLLs in C++ and/or FORTRAN?
Thx in advance!

When you compile your C#-DLL with tool from R. Giesecke, you should also get a *.lib file with it.
You need to reference this lib in your FORTRAN linker settings as additional library dependency. It contains all the code required to load the DLL and make the functions in the DLL available.
In your FORTRAN code you need to declare the imported methods with the following statements:
!DEC$ ATTRIBUTES DLLIMPORT, ALIAS:'_MethodeName::MethodName
BTW: COM visibility is not needed if using RGiesecke. The access to the C# is native and not via COM (also making it considerably faster).

Related

IronPython C# linkage

Can anyone tell me, how to use methods from C# in ironpython?
I know some basics but don't understand what to do if I don't want to build my C# project as .dll?
Code for the ConsoleApplication1.exe:
using System;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
namespace ConsoleApplication1 {
class Program
{
static void Main()
{
ScriptEngine engine = Python.CreateEngine();
engine.ExecuteFile("C:/Users/Vlad/Desktop/kal.py");
}
public int Plus(int a, int b)
{
int z = a + b;
Console.WriteLine(z);
return z;
}
}
}
//kal.py
print 'test1'
import clr
clr.LoadAssemblyFromFile("C://Users/Vlad/Documents/Visual Studio 2015/Projects/ConsoleApplication1/ConsoleApplication1/bin/Debug/ConsoleApplication1.exe")
import ConsoleApplication1
arith = ConsoleApplication1.Program.Plus(10,20)
print arith
just change type of C# application to Class library and reference it to your
python app.
from solution explorer right click on C# project note and click Properties and change appliction type to class library.
you can reference a library to the project from Project>add reference menu.

Exported functions from C# DLL not working

I have to export 3 basic methods from my DLL in C#, so it becomes accessible in C++:
OnPluginStart
OnPluginStop
PluginUpdate
So I found Unmanaged Exports a nice C# library that makes that easier.
So I went ahead with a sample code to test:
using System;
using System.IO;
using RGiesecke.DllExport;
namespace Plugins
{
public class Plugins
{
[DllExport("OnPluginStart", CallingConvention = CallingConvention.StdCall)]
public static void OnPluginStart()
{
using (var file = new StreamWriter(#"pluginLog.txt", true))
{
file.WriteLine("OnPluginStart");
}
}
[DllExport("OnPluginStop", CallingConvention = CallingConvention.StdCall)]
public static void OnPluginStop()
{
using (var file = new StreamWriter(#"pluginLog.txt", true))
{
file.WriteLine("OnPluginStop");
}
}
[DllExport("PluginUpdate", CallingConvention = CallingConvention.StdCall)]
public static void PluginUpdate(float dt)
{
using (var file = new StreamWriter(#"pluginLog.txt", true))
{
file.WriteLine("PluginUpdate");
}
}
}
}
However, when I compile my DLL and use DLL Exporter Viewer it doesn't list any of the exported functions and the application the DLL is loaded into also never runs my plugin.
What am I doing wrong here which makes my functions not being exported at all?
Your code works fine, apart from the fact that the code you posted does not compile. You omitted the using System.Runtime.InteropServices line. Dependency Walker for an x86 class library build of your (fixed) code says this:
The most obvious cause for the problem could be the following from the NuGet page for the library:
You have to set your platform target to either x86, ia64 or x64. AnyCPU assemblies cannot export functions.

Consuming a .NET WebService using a C++ client

I have a webservice that queries a DB and returns the results of those queries.
However the webservice does not have direct access to the DB, instead, it queries it using a user created library, with specific methods for retrieving Users, names, etc.
I've included the library in the webservice project by selecting add > reference and I get no compiling errors or warnings on my webservice code.
I have created the client libraries using svcutil.exe and wsutil.exe and everything went OK, without warnings.
On my client code I get a "There was an error processing tempuri.org.xsd","Error generating code for DataSet ''.", and "Unable to convert input xml file to DataSet. The nested table 'Utilizador' which inherits the respective namespace cannot have multiple tables in different namespaces".
The last error was translated to Portuguese, and I did my best to translate it back to English, so YMMV... 'Utilizador' is a table name on my DB and a class name on the library that accesses the DB.
I'm using this code to consume the WebService:
#include "stdafx.h"
#include "WebServices.h"
#include "schemas.microsoft.com.2003.10.Serialization.xsd.h"
#include "tempuri.org.xsd.h"
#include "tempuri.org.wsdl.h"
#include <string>
#include <iostream>
#include <stdlib.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
unsigned int *listSize;
Utilizador **arrayUser;
HRESULT hr = ERROR_SUCCESS;
WS_ERROR* error = NULL;
WS_HEAP* heap = NULL;
WS_SERVICE_PROXY* proxy = NULL;
WS_ENDPOINT_ADDRESS address = {};
WS_STRING url= WS_STRING_VALUE(L"http://localhost:51765/Graph4Social.svc");
address.url = url;
hr = WsCreateHeap(2048, 512, NULL, 0, &heap, error);
WS_HTTP_BINDING_TEMPLATE templ = {};
//criação do proxy para o serviço
//hr = BasicHttpBinding_IGraph4WebService_CreateServiceProxy(&templ, NULL, 0, &proxy, error);
//WsCreateServiceProxy(&templ, NULL, 0, &proxy, error);
hr = BasicHttpBinding_IGraph4Social_CreateServiceProxy(&templ, NULL, 0, &proxy, error);
hr = WsCreateServiceProxy(WS_CHANNEL_TYPE_REQUEST,WS_HTTP_CHANNEL_BINDING,NULL,NULL,0,NULL,0,&proxy,error);
hr = WsOpenServiceProxy(proxy,&address,NULL,error);
hr = BasicHttpBinding_IGraph4Social_getUserList(proxy,listSize,&arrayUser,heap, NULL,0, NULL,error);
for (unsigned int i = 0; i < *listSize; i++)
cout << "ID Utilizador: " + arrayUser[i]->idUtilizador;
return 0;
}
This is the WebService code for the Interface:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using TM_TDG.WithDataSets.BLL;
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IGraph4Social" in both code and config file together.
[ServiceContract]
public interface IGraph4Social
{
[OperationContract]
IList<Utilizador> getUserList();
}
And this is the code for the Class that implements the interface:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using TM_TDG.WithDataSets.BLL;
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Graph4Social" in code, svc and config file together.
public class Graph4Social : IGraph4Social
{
public IList<Utilizador> getUserList()
{
RedeSocial rede = RedeSocial.getRedeSocial();
IList<Utilizador> userList = rede.getUserList(true); //so queremos os utilizadores activos
return userList;
}
}
I have googled this error and can't seem to find an answer. I hope someone can help me with this, as I've been stuck for days on this issue!
Thanks!
I actually solved this last night by mere chance! It turns out I was placing all files generated by svcutil.exe and wsutil.exe on the project page(including the .xsd and .wsdl generated by svcutil.exe). I was also adding all items to the visual studio project, although, obviously, I was only including the .h files. I thought that the compiler was only going to look at the .h and the corresponding .c files, but it turns out, it was looking at the .xsd and .wsdl too. When I deleted those files, the compiler worked flawlessly!
So, PEBKAC...
Dunno if this is Visual Studio related or if this behaviour can be reproduced with gc++, for example.

Unity3D load an not exist DLL file , still work but no effect to the result

I tried to integrate my own c++ plugin with Unity3D project. But it doesn't work. So i wrote some small code to test, I found some strange problems, make me #$%##$^##$^#^
First I create a small dll project by code
#include "stdafx.h"
#define EXPORT_API __declspec(dllexport)
extern "C"
{
EXPORT_API void testFloatPoint(float *mFloat)
{
for(int i = 0 ; i<13;i++)
{
*mFloat = (float)i;
mFloat++;
}
mFloat = mFloat-13;
}
}
Then I tried to invoke this dll file in unity3D c# script by following code:
using UnityEngine;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections;
using System.IO;
public class TestDLL : MonoBehaviour
{
float[] pose_float;//
#region import c++ dll
[DllImport ("TestDLL2")]
public static extern void testFloatPoint ([In,Out] float[] pose_float);
#endregion
void Start ()
{
pose_float = new float[13];
testFloatPoint(pose_float);
print ("get pose_float data!!!");
for(int i=0;i<13;i++)
{
print(pose_float[i]);
}
}
}
but the print out msg are all zero. It's a bit strange, because before I got the result is "0,1,2,3....12". I can't figure out where the problem is . So i tried to remove the dll file from my unity3D project folder. Much more strange problem is, it can still run with all zero output result. There is no TestDLL2 file under the project folder and there is no dllnotfound problem.
Is there some advice from U? It bother me around one week , Thank you very much!

COM Interop (how to pass an array to the com) via classic ASP

I need to create a com object for my classic asp, since i can create a .net Assembly and have it 'Interop' with com, so i proceeded to create a .net Assembly like this:-
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Configuration;
using System.Web;
namespace LMS
{
[ComVisible(true)]
public class Calc
{
public int Add(int val1, int val2, out string[] outputz)
{
int total = val1 + val2;
outputz = new string[5];
outputz[1] = "test2";
outputz[2] = "test3";
outputz[3] = "test4";
outputz[4] = "test5";
return total;
}
}
}
Next i did the usual, build, ran: gacutil & RegAsm
and in my classic asp page i had this:-
Dim params
dim objPassport3
set objPassport3 = Server.CreateObject("LMS.Calc")
comTest2 = objPassport3.Add(1,1,params)
and i get error:
Error Type:
Microsoft VBScript runtime (0x800A0005)
Invalid procedure call or argument: 'Add'
/eduservice/test.asp, line 25
But if i modify the assembly not to use an array, it all just work, i can even send normal string or int to and from the assembly to classic asp.
i read so many things but i get the same error,
anyone tried this before and was successful, please do share your solution
thanks
ASP can only handle arrays that are variant, rather than arrays of strings or ints. So try using an object instead, e.g.,
public int Add(int val1, int val2, out object outputz)
{
int total = val1 + val2;
outputz = new object[5]
{
"test1",
"test2",
"test3",
"test4",
"test5"
};
return total;
}

Categories