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

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.

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;
}

Load dynamic delegate function from text file? [duplicate]

I have a WPF C# application that contains a button.
The code of the button click is written in separate text file which will be placed in the applications runtime directory.
I want to execute that code placed in the text file on the click of the button.
Any idea how to do this?
Code sample for executing compiled on fly class method:
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Net;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
string source =
#"
namespace Foo
{
public class Bar
{
public void SayHello()
{
System.Console.WriteLine(""Hello World"");
}
}
}
";
Dictionary<string, string> providerOptions = new Dictionary<string, string>
{
{"CompilerVersion", "v3.5"}
};
CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);
CompilerParameters compilerParams = new CompilerParameters
{GenerateInMemory = true,
GenerateExecutable = false};
CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, source);
if (results.Errors.Count != 0)
throw new Exception("Mission failed!");
object o = results.CompiledAssembly.CreateInstance("Foo.Bar");
MethodInfo mi = o.GetType().GetMethod("SayHello");
mi.Invoke(o, null);
}
}
}
You can use Microsoft.CSharp.CSharpCodeProvider to compile code on-the-fly. In particular, see CompileAssemblyFromFile.
I recommend having a look at Microsoft Roslyn, and specifically its ScriptEngine class.
Here are a few good examples to start with:
Introduction to the Roslyn Scripting API
Using Roslyn ScriptEngine for a ValueConverter to process user input.
Usage example:
var session = Session.Create();
var engine = new ScriptEngine();
engine.Execute("using System;", session);
engine.Execute("double Sin(double d) { return Math.Sin(d); }", session);
engine.Execute("MessageBox.Show(Sin(1.0));", session);
Looks like someone created a library for this called C# Eval.
EDIT: Updated link to point to Archive.org as it seems like the original site is dead.
What you need is a CSharpCodeProvider Class
There are several samples to understand how does it work.
1 http://www.codeproject.com/Articles/12499/Run-Time-Code-Generation-I-Compile-C-Code-using-Mi
The important point of this example that you can do all things on flay in fact.
myCompilerParameters.GenerateExecutable = false;
myCompilerParameters.GenerateInMemory = false;
2 http://www.codeproject.com/Articles/10324/Compiling-code-during-runtime
This example is good coz you can create dll file and so it can be shared between other applications.
Basically you can search for http://www.codeproject.com/search.aspx?q=csharpcodeprovider&x=0&y=0&sbo=kw&pgnum=6 and get more useful links.

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.

execute c# code at runtime from code file

I have a WPF C# application that contains a button.
The code of the button click is written in separate text file which will be placed in the applications runtime directory.
I want to execute that code placed in the text file on the click of the button.
Any idea how to do this?
Code sample for executing compiled on fly class method:
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Net;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
string source =
#"
namespace Foo
{
public class Bar
{
public void SayHello()
{
System.Console.WriteLine(""Hello World"");
}
}
}
";
Dictionary<string, string> providerOptions = new Dictionary<string, string>
{
{"CompilerVersion", "v3.5"}
};
CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);
CompilerParameters compilerParams = new CompilerParameters
{GenerateInMemory = true,
GenerateExecutable = false};
CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, source);
if (results.Errors.Count != 0)
throw new Exception("Mission failed!");
object o = results.CompiledAssembly.CreateInstance("Foo.Bar");
MethodInfo mi = o.GetType().GetMethod("SayHello");
mi.Invoke(o, null);
}
}
}
You can use Microsoft.CSharp.CSharpCodeProvider to compile code on-the-fly. In particular, see CompileAssemblyFromFile.
I recommend having a look at Microsoft Roslyn, and specifically its ScriptEngine class.
Here are a few good examples to start with:
Introduction to the Roslyn Scripting API
Using Roslyn ScriptEngine for a ValueConverter to process user input.
Usage example:
var session = Session.Create();
var engine = new ScriptEngine();
engine.Execute("using System;", session);
engine.Execute("double Sin(double d) { return Math.Sin(d); }", session);
engine.Execute("MessageBox.Show(Sin(1.0));", session);
Looks like someone created a library for this called C# Eval.
EDIT: Updated link to point to Archive.org as it seems like the original site is dead.
What you need is a CSharpCodeProvider Class
There are several samples to understand how does it work.
1 http://www.codeproject.com/Articles/12499/Run-Time-Code-Generation-I-Compile-C-Code-using-Mi
The important point of this example that you can do all things on flay in fact.
myCompilerParameters.GenerateExecutable = false;
myCompilerParameters.GenerateInMemory = false;
2 http://www.codeproject.com/Articles/10324/Compiling-code-during-runtime
This example is good coz you can create dll file and so it can be shared between other applications.
Basically you can search for http://www.codeproject.com/search.aspx?q=csharpcodeprovider&x=0&y=0&sbo=kw&pgnum=6 and get more useful links.

Categories