Exported functions from C# DLL not working - c#

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.

Related

Optional Argument of COM Add-in vs Automation Add-in Written in C#

I am working on a library of COM Add-in and Excel Automation Add-in, whose core codes are written in C#. I'd like to set an optional argument for the function and I know that this is legal for both C# and VBA, and even Excel WorksheetFunction. But I find that finally the optional argument works exclusively for COM and Automation add-in, meaning that if one add-in is run first, then works well but the optional argument of the other one will not work.
Below please see the example:
In the VS 2013 solution, I have two projects: one is called TestVBA and another one is called TestExcel.
TestVBA is for the COM add-in and built through the "Excel 2013 Add-in" and there are two .cs files:
ThisAddIn.cs
This file is generated automatically and modified a little bit. The codes are
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Excel;
namespace TestVBA
{
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
private ExcelVBA oExcelVBA;
protected override object RequestComAddInAutomationService()
{
if (oExcelVBA == null)
{
oExcelVBA = new ExcelVBA();
}
return oExcelVBA;
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
}
TestVBA.cs
This file is the main calculation file of COM add-in. The codes are
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
namespace TestVBA
{
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class ExcelVBA
{
public int TestAddVBA(int a = 1, int b = 1)
{
return a + b;
}
}
}
Another TestExcel is for the Excel Automation add-in and built through the C# "Class Library" and there are two .cs files either:
BaseUDF.cs
This file defines the decoration of two attributes. The codes are
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace BaseUDF
{
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public abstract class BaseUDF
{
[ComRegisterFunctionAttribute]
public static void RegisterFunction(Type type)
{
// Add the "Programmable" registry key under CLSID.
Registry.ClassesRoot.CreateSubKey(
GetSubKeyName(type, "Programmable"));
// Register the full path to mscoree.dll which makes Excel happier.
RegistryKey key = Registry.ClassesRoot.OpenSubKey(
GetSubKeyName(type, "InprocServer32"), true);
key.SetValue("",
System.Environment.SystemDirectory + #"\mscoree.dll",
RegistryValueKind.String);
}
[ComUnregisterFunctionAttribute]
public static void UnregisterFunction(Type type)
{
// Remove the "Programmable" registry key under CLSID.
Registry.ClassesRoot.DeleteSubKey(
GetSubKeyName(type, "Programmable"), false);
}
private static string GetSubKeyName(Type type,
string subKeyName)
{
System.Text.StringBuilder s =
new System.Text.StringBuilder();
s.Append(#"CLSID\{");
s.Append(type.GUID.ToString().ToUpper());
s.Append(#"}\");
s.Append(subKeyName);
return s.ToString();
}
// Hiding these methods from Excel.
[ComVisible(false)]
public override string ToString()
{
return base.ToString();
}
[ComVisible(false)]
public override bool Equals(object obj)
{
return base.Equals(obj);
}
[ComVisible(false)]
public override int GetHashCode()
{
return base.GetHashCode();
}
}
}
TestExcel.cs
This file is the main calculation file of Excel Automation add-in. The codes are
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
using Extensibility;
namespace TestExcel
{
[Guid("7127696E-AB87-427a-BC85-AB3CBA301CF3")]
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class TestExcel : BaseUDF.BaseUDF
{
public int TestAddExcel(int a = 1, int b = 1)
{
return a + b;
}
}
}
After building, the two add-ins have been registered in the system and in Excel we can use them successfully.
For the Automation add-in, we call them in the spreadsheet as =TestAddExcel(2,3) and =TestAddExcel() both of them work very well and give the right result 5 and 2. However, when I try to call the COM add-in via
Sub TestVBA_Click()
Dim addIn As COMAddIn
Dim TesthObj As Object
Set addIn = Application.COMAddIns("TestVBA")
Set TestObj = addIn.Object
Range("Output").Value2 = TestObj.TestAddVBA(2, 3)
Range("Output").Offset(1, 0).Value2 = TestObj.TestAddVBA()
End Sub
The first call with all arguments existing works well, but for the second one with arguments missing shows the error Type mismatch.
The interesting thing is, when I close the test excel file and open it again, this time I test the COM add-in first, still via the above VBA codes, both two calls work very well. Then when I test the two spreadsheet functions which used to work well, only the first one is good, the second one with arguments missing =TestAddExcel() fails with #VALUE!.
It would be very nice if someone can help with this strange issue.
I am not sure how you Referenced the class library without Registering for COM? I see now, you're using Late Binding. I didnt know you could do that (didn't think it would let you) and suspect that is the problem, it also matches the Type mismatch error.
Follow the second solution in my canonical answer here on the 3 methods to call .Net from Excel or VBA and make sure you Register for COM:
Click on the Build tab and check the check box that says “Register for COM Interop”. At this point you have an extra step if you are running on Windows Vista or higher. Visual Studio has to be run with administrator privileges to register for COM Interop. Save your project and exit Visual Studio. Then find Visual Studio in the Start menu and right click on it and choose “Run as Administrator”. Reopen your project in Visual Studio. Then choose “Build” to build the add-in.
Optionally if the above doesn't work, follow the third solution in my answer and reference the Automation Add-In and use Early Binding, I've tested this and it works perfectly:
Sub TestVBA1_Click()
Dim addIn As COMAddIn
Dim TesthObj As Object
Set addIn = Application.COMAddIns("TestVBA")
Set TestObj = addIn.Object
Debug.Print TestObj.TestAddVBA(2, 3)
Debug.Print TestObj.TestAddVBA()
Dim dotNetClass As TestExcel.TestExcel
Set dotNetClass = New TestExcel.TestExcel
Debug.Print dotNetClass.TestAddExcel(7, 3)
Debug.Print dotNetClass.TestAddExcel()
End Sub
This is a total stab in the dark, but can you create overloaded versions of the method to mimic the way you would have accomplished this before C# had optional parameters and see if that would work?
public int TestAddExcel(int a, int b)
{
return a + b;
}
public int TestAddExcel(int a)
{
return a + 1;
}
public int TestAddExcel()
{
return 2;
}

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.

Native C++ program crashes on startup with C++/CLI & C# interop DLL

I'm in a situation where I need to wrap C# WPF UserControls in a C++/CLI wrapper to use in a native C++ program. The native program uses MFC heavily and is compiled in VS2010. I've properly generated the C++/CLI .lib and .dll files in a separate solution and linked to them from the MFC application, which compiles and links successfully, generating the executable as usual. However, when I go to launch the MFC application, I get an access violation error 0xc0000005 before it even gets to main(). When I comment out the wrapper code, the program launches normally.
To try and isolate the problem, I tried creating a completely separate native C++ project whose sole purpose is to call the wrapper class with dummy data. This project reaches main() but throws an exception (0xe0434352) when I call the constructor for the wrapper class.
I've tried searching for any possible solutions, but the one the most closely matches my situation says to check the function signatures to make sure I'm passing in the right number of arguments (I am). I've also looked up the 0xe0434352 exception from the standalone project and it appears to be a generic .NET exception.
I also saw this question posted, but was hoping the solution would be simpler than that. I've been following the instructions on this blog to create the wrapper class.
I'm including all code but the MFC application code, since it's not even loading to main(). I'm hoping that if I can manage to resolve the issue in the brand new project, the MFC application will start.
C# WPF:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace ManagedWPFLink
{
public struct TestResultData
{
public string inspectionType;
public string testResult;
public string measuredValue;
public string passCondition;
public string comments;
}
/// <summary>
/// Interaction logic for TestResultsDialog.xaml
/// </summary>
public partial class TestResultsDialog : UserControl
{
private Window window;
public TestResultsDialog()
{
InitializeComponent();
DataGridTextColumn col1 = new DataGridTextColumn();
DataGridTextColumn col2 = new DataGridTextColumn();
DataGridTextColumn col3 = new DataGridTextColumn();
DataGridTextColumn col4 = new DataGridTextColumn();
DataGridTextColumn col5 = new DataGridTextColumn();
this.testResultsGrid.Columns.Add(col1);
this.testResultsGrid.Columns.Add(col2);
this.testResultsGrid.Columns.Add(col3);
this.testResultsGrid.Columns.Add(col4);
this.testResultsGrid.Columns.Add(col5);
col1.Binding = new Binding("inspectionType");
col2.Binding = new Binding("testResult");
col3.Binding = new Binding("measuredValue");
col4.Binding = new Binding("passCondition");
col5.Binding = new Binding("comments");
col1.Header = "Inspection Type";
col2.Header = "Test Result";
col3.Header = "Measured Value";
col4.Header = "Pass Condition";
col5.Header = "Comments";
this.window = new Window
{
Title = "Inspection Results",
Content = this,
SizeToContent = SizeToContent.WidthAndHeight,
ResizeMode = ResizeMode.NoResize
};
}
public void addRow(string inspectionType, string testResult, string measuredValue, string passCondition, string comments)
{
TestResultData data = new TestResultData();
data.inspectionType = inspectionType;
data.testResult = testResult;
data.measuredValue = measuredValue;
data.passCondition = passCondition;
data.comments = comments;
this.testResultsGrid.Items.Add(data);
}
public void deleteAllRows()
{
this.testResultsGrid.Items.Clear();
}
public void showDialog()
{
this.window.ShowDialog();
}
}
}
C# XAML:
<UserControl x:Class="ManagedWPFLink.TestResultsDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="600">
<Grid>
<DataGrid AutoGenerateColumns="True" Height="376" HorizontalAlignment="Left" Margin="12,12,0,0" Name="testResultsGrid" VerticalAlignment="Top" Width="576" ItemsSource="{Binding}" MinColumnWidth="40">
</DataGrid>
</Grid>
</UserControl>
C++/CLI header:
// ManagedWPFLink.h
#pragma once
namespace WPFLink
{
class WPFPrivate;
class __declspec(dllexport) WPFWrapper
{
private:
WPFPrivate *internalWrapper;
public:
WPFWrapper();
~WPFWrapper();
void addItems(const char **data);
void deleteAllRows();
void showDialog();
};
}
C++/CLI source:
// This is the main DLL file.
#include "stdafx.h"
#using <WindowsBase.dll>
#using <PresentationCore.dll>
#using <PresentationFramework.dll>
#using "ManagedWPFLink.dll"
#include "WPFLink.h"
using namespace System::Runtime::InteropServices;
namespace WPFLink
{
public class WPFPrivate
{
public:
msclr::auto_gcroot<System::String^> inspectionType;
msclr::auto_gcroot<System::String^> testResult;
msclr::auto_gcroot<System::String^> measuredValue;
msclr::auto_gcroot<System::String^> passCondition;
msclr::auto_gcroot<System::String^> comments;
msclr::auto_gcroot<ManagedWPFLink::TestResultsDialog^> testResultDialog;
public:
WPFPrivate()
{
}
~WPFPrivate()
{
delete testResultDialog;
}
};
WPFWrapper::WPFWrapper()
{
this->internalWrapper = new WPFLink::WPFPrivate();
}
WPFWrapper::~WPFWrapper()
{
delete this->internalWrapper;
}
void WPFWrapper::addItems(const char **data)
{
this->internalWrapper->inspectionType = gcnew System::String(data[0]);
this->internalWrapper->testResult = gcnew System::String(data[1]);
this->internalWrapper->measuredValue = gcnew System::String(data[2]);
this->internalWrapper->passCondition = gcnew System::String(data[3]);
this->internalWrapper->comments = gcnew System::String(data[4]);
this->internalWrapper->testResultDialog->addRow(this->internalWrapper->inspectionType, this->internalWrapper->testResult,
this->internalWrapper->measuredValue, this->internalWrapper->passCondition, this->internalWrapper->comments);
}
void WPFWrapper::deleteAllRows()
{
this->internalWrapper->testResultDialog->deleteAllRows();
}
void WPFWrapper::showDialog()
{
this->internalWrapper->testResultDialog->showDialog();
}
}
Native C++ code:
#include "..\WPFLink\WPFLink.h"
int main()
{
WPFLink::WPFWrapper wrapper; // Generates 0xe0434352 error
return 0;
}
After much hair-pulling, I resorted to using the method in this post here: http://www.codeguru.com/cpp/cpp/cpp_managed/nfc/article.php/c14589/Hosting-WPF-Content-in-an-MFC-Application.htm
I used the relevant code to generate an HWND and the program worked flawlessly with my own native C++ code. However, I'm still running into an access violation on startup in our main MFC application which I haven't managed to narrow down at all. The only information I have is that if I attempt to link to my DLL at all, the program crashes. I will have to abandon this method of linking to a stub .lib file and loading the necessary DLL and instead redo the interface to only use extern "C" functions and manually call LoadLibrary on my DLL.
Thanks everyone who tried to help.

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

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).

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!

Categories