I have a question about calling a fortran DLL from C# (using VS 2010).
I cannot get this program to run. When stepping into the Fortran code from C#, at the line where z is calculated (sum of x and y), a message box pops up:
An unhandled exception of type 'System.AccessViolationException' occurred in WinApp_FortranDLLStruct2.exe
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt"
How do I fix this?
The Fortran DLL is called "TestDLL.DLL"
Fortran code:
MODULE TESTING
TYPE Point
REAL*8 :: x(10)
REAL*8 :: y(10)
REAL*8 :: z(10)
ENDTYPE Point
end module
!DEC$ ATTRIBUTES DLLEXPORT::CalcPoint
!DEC$ ATTRIBUTES ALIAS : "CalcPoint" :: CalcPoint
SUBROUTINE CalcPoint(myPts)
use TESTING
IMPLICIT NONE
INTEGER*4 I,J,NPTS
REAL*8 Sum_Out
TYPE(Point) :: myPts
do i = 1,10
myPts.z(i) = myPts.x(i) + myPts.y(i)
enddo
END SUBROUTINE CalcPoint
C# code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WinApp_FortranDLLStruct2 {
public partial class Form1 : Form {
[StructLayout(LayoutKind.Sequential)]
public unsafe struct myPoint {
public fixed double x[10];
public fixed double y[10];
public fixed double z[10];
}
public class FortranCall {
[DllImport("TestDLL.dll")]
public unsafe static extern void CalcPoint([Out] myPoint t);
}
public Form1()
{
InitializeComponent();
}
private unsafe void button1_Click(object sender, EventArgs e) {
int i;
double d1 = 1.0;
myPoint T = new myPoint();
for (i = 0; i < 10; i++) {
T.x[i] = (i+1)*d1;
T.y[i] = (i+2)*d1;
}
FortranCall.CalcPoint(T);
}
}
}
Adding C calling conventions solved my stack imbalance problems. Also change the [Out] to ref in order to point at the same memory and not copy values around. Here is my code
FORTRAN
MODULE TESTING
INTEGER, PARAMETER :: SIZE = 10
TYPE Point
SEQUENCE
REAL*8 :: x(SIZE), y(SIZE) , z(SIZE)
ENDTYPE Point
end module
SUBROUTINE CalcPoint(myPts)
!DEC$ ATTRIBUTES DLLEXPORT::CalcPoint
!DEC$ ATTRIBUTES ALIAS : 'CalcPoint' :: CalcPoint
use TESTING
IMPLICIT NONE
! Arguments
TYPE(Point), INTENT(INOUT) :: myPts
! Local variables
INTEGER*4 I
do i = 1,SIZE
myPts%z(i) = myPts%x(i) + myPts%y(i)
enddo
END SUBROUTINE CalcPoint
C#
[StructLayout(LayoutKind.Sequential)]
public unsafe struct myPoint
{
public const int size = 10;
public fixed double x[size];
public fixed double y[size];
public fixed double z[size];
public void Initialize(double d1)
{
fixed (double* x_ptr=x, y_ptr=y, z_ptr=z)
{
for (int i=0; i<size; i++)
{
x_ptr[i]=(i+1)*d1;
y_ptr[i]=(i+1)*d1;
z_ptr[i]=0.0;
}
}
}
}
class Program
{
[DllImport(#"FortranDll1.dll", CallingConvention=CallingConvention.Cdecl)]
public unsafe static extern void CalcPoint(ref myPoint t);
unsafe Program()
{
double d1=1.0;
var T=new myPoint();
T.Initialize(d1);
Program.CalcPoint(ref T);
// T.z = {2,4,6,...}
}
static void Main(string[] args)
{
new Program();
}
}
Appendix
To convert a fixed array to managed array and vise versa use the following code
public unsafe struct myPoint
{
public const int size=10;
public fixed double x[size];
...
public double[] X
{
get
{
double[] res=new double[size];
fixed (double* ptr=x)
{
for (int i=0; i<size; i++)
{
res[i]=ptr[i];
}
}
return res;
}
set
{
if (value.Length>size) throw new IndexOutOfRangeException();
fixed (double* ptr=x)
{
for (int i=0; i<value.Length; i++)
{
ptr[i]=value[i];
}
}
}
}
}
Try:
[DllImport("TestDLL.dll")]
public unsafe static extern void CalcPoint(ref myPoint t);
If that doesn't work, try creating a really simple FORTRAN method that takes X/Y values and adds them together and returns the sum. This will allow you to diagnose that the overall process is working correctly. Work up from there, adding more complexity in until it breaks. That will tell you if something else is wrong.
Related
I coded a simple sum function using Rhapsody Developer in C and declared it as __declspec(dllexport) int Class_Sum_sum(Class_Sum* const me, int a, int b); in my C file. I am a total beginner in C# programming.
My C# program looks like this:
using System.Runtime.InteropServices;
namespace Test1_C_Sharp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
int x = Win32.Class_Sum_sum(5, 8);
textBox1.Text = x.ToString();
}
}
public class Win32
{
[DllImport("CalcSum.dll", CharSet = CharSet.Auto)]
public static extern int Class_Sum_sum(int a, int b);
}
}
When i execute this code, i get a Form with a textbox and "sum" button, as expected, when i press the "sum" button, an exception is thrown saying that
PInvokeStackImbalance was detected
which actually makes sense because i have three arguments in my C function (Class_sum *,int,int) and I do not know what exactly the first argument in my C# code should look like.
Does anyone know the right way to do this?
EDIT: I modelled my class "Class_sum" in IBM Rhapsody which translates to a struct in C. A snippet from my H file like this:
/*## class Class_Sum */
typedef struct Class_Sum Class_Sum;
struct Class_Sum {
RiCReactive ric_reactive;
int op1; /*## attribute op1 */
int op2; /*## attribute op2 */
int sum; /*## attribute sum */
/*#[ ignore */
int rootState_subState;
int rootState_active;
int MainState_subState;
/*#]*/
......
......
};
Rhapsody generates its own functions and structures like me for instance, which translates to this in an OOP Language.
you have to put the third parameter and because it is a pointer in C# you have to use the word ref
public static extern int Class_Sum_sum(ref Class_Sum parameterName,int a, int b)
I have setup a c# console mode program which calls a clr/dll to access a MFC c++ dll and that works for accessing functions in the MFC c++ dll. But I want to pass back a delegate function from c# so that when a function in the MFC c++ dll needs to call the c# program it has the callback to do that. But I can't get it setup right...this is my attempt:
program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using dd_clr; // this is my clr.dll
namespace dd_gui
{
public delegate int GUI_CallbackDelegate(string fn);
class Program
{
int GUI_callback(string fn)
{
Console.WriteLine("Begin GUI_callback()");
Console.WriteLine("GUI_callback fn=" + fn);
Console.WriteLine("End GUI_callback()");
return (1);
}
static GCHandle gch;
static void Main(string[] args)
{
Console.WriteLine("begin GUI");
dd_clr.DDManaged instance = new dd_clr.DDManaged();
GUI_CallbackDelegate ^ fp = gcnew GUI_CallbackDelegate(GUI_Callback); // this does not compile for some reason ; expected after gcnew ???
gch = GCHandle.Alloc(fp);
instance.Set_GUICallback(fp); // this I am trying to get to work.
instance.batch_run("test.dap"); // this call works.
Console.WriteLine("end GUI");
gch.Free();
}
}
}
From your code it's not obvious what you are trying to do, but you can pass a delegate to C++ this way:
server.h:
extern "C"
{
typedef int(__stdcall * ComputeCallback)(int);
__declspec(dllexport) int Sum(ComputeCallback computeCallback);
}
server.cpp:
__declspec(dllexport) int Sum(ComputeCallback computeCallback)
{
int sum = 0;
for (int i = 0; i < 4; i++)
{
int x = computeCallback(i);
sum += x;
}
return sum;
}
client.cs:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate int ComputeCallback(int value);
class Program
{
[DllImport("server.dll")]
public static extern int Sum(ComputeCallback callback);
static void Main(string[] args)
{
ComputeCallback callback = x =>
{
Console.WriteLine("Computing. X = {0}", x);
return x*x;
};
Console.WriteLine("Result: {0}", Sum(callback));
}
}
I've a C# DLL below like. I want to use this C# DLL in C++ Builder.
But I don't know C# Struct and C++ Struct marshalling:
using RGiesecke.DllExport;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace TestLibrary
{
[ComVisible(true)]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MyStruct
{
public int X;
public int Y;
}
public class Class1
{
[DllExport("DoSomething", CallingConvention = CallingConvention.StdCall)]
public static int DoSomething(int x, int y, ref MyStruct myStruct)
{
myStruct.X = 50;
myStruct.Y = 75;
return x + y;
}
}
}
I want to pass "myStruct" parameter from C++ Builder below like.
void __fastcall TForm1::FormCreate(TObject *Sender)
{
struct MyStruct
{
int X;
int Y;
};
int (__stdcall *DoSomething)(int,int,MyStruct);
HINSTANCE dllHandle = NULL;
dllHandle = LoadLibrary( edtdllPath->Text.c_str());
if(dllHandle == NULL) return;
int status = -1;
try
{
DoSomething =(int (__stdcall *)(int,int,MyStruct)) GetProcAddress(dllHandle, "DoSomething");
}
catch(Exception &Err)
{
ShowMessage(Err.Message);
}
if(DoSomething != NULL)
{
try
{
MyStruct myStruct;
status = DoSomething(5,5,myStruct);
String strStatus = status;
ShowMessage(strStatus);
ShowMessage(myStruct.X);
ShowMessage(myStruct.Y);
}
catch(EAccessViolation &err)
{
ShowMessage(err.Message);
}
}
}
When I debug code,myStruct.X and myStruct.Y value is wrong.
Where is my wrong ?
You are not passing your struct as pointer to c#, yet in c# you told that it would be a pointer (ref).
The C# project declares the struct parameter like this:
ref MyStruct myStruct
That marshals as a pointer to the struct. In C++ that means
MyStruct*
So change the declaration of the function pointer variable to be like so:
int (__stdcall *DoSomething)(int,int,MyStruct*);
And use the same type when you cast:
DoSomething =(int (__stdcall *)(int,int,MyStruct*)) GetProcAddress(dllHandle, "DoSomething");
Note that a typedef would serve you better here to avoid repeating yourself. And note also that GetProcAddress does not raise exceptions. It signals errors by returning NULL. You don't check for errors properly.
When you call the function pass the address of the struct:
status = DoSomething(5,5,&myStruct);
It's also a little pointless to declare status and initialise it to -1, but then overwrite that value later. It would be more idiomatic to declare and initialise it like this:
int status = DoSomething(5,5,&myStruct);
I have a C DLL that I am call from C#
C Code
// The real DLL entry point
__declspec(dllexport) int Do_7plus(char *cmd_line)
char *p1, *p2;
char **argv;
int argc = 0;
int i, l;
int ret;
/*
* Count the args.
*/
l = strlen(cmd_line);
C# Code
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices; // DLL support
namespace Packet
{
public partial class _7PlusFrm : Form
{
[DllImport("7plus.dll")]
public static extern void Do_7plus(string args);
public _7PlusFrm()
{
InitializeComponent();
}
private void button_ok_Click(object sender, EventArgs e)
{
Do_7plus("c:\\temp\\7plus.zip -SAVE \"c:\\temp\\\" -SB 5000");
}
}
}
I get an exception error (Access Violation)
How do I fix it?
Is the problem the pointer in C and string in C#?
You need to marshal the string to the c-string type.
Something like this should do the trick:
[DllImport("7plus.dll", Charset = Charset.Ansi)]
public static extern void Do_7plus([MarshalAs(UnmanagedType.LCPStr)]string args);
b.t.w. you can solve this problem without PInvoke and without the massive security vulnerability of that shell injection you are doing.
I am trying to do 2 things: get a return value from a C dll function, and have that same function modify 1 of the members of a structure that is passed to it. After much experimentation I was able to get the function to return a value, but I am still unable to get it to return a modified value to the C# code; the value remains (unmodified) as 0.
I've tried lots of variations (ref, [In,Out], etc) to no avail
using System;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
namespace Vexing.Problem{
public class myIdx : VexingObject {
public myIdx(object _ctx) : base(_ctx) { }
private IPlotObject plot1;
[StructLayout(LayoutKind.Sequential)]
public class PLEX { public int yowser; }
[DllImport("my.dll", CharSet = CharSet.Unicode)]
public static extern int cFunction(
[MarshalAs(UnmanagedType.LPStruct)] PLEX mPlex);
PLEX a;
protected override void Create() { a = new PLEX(); }
protected override void CalcBar() {
int mf = cFunction(a);
plot1.Set(a.yowser); }
}}
// pertinent c dll code
typedef struct s_plex { int yowser;} cplex;
extern "C" __declspec( dllexport )
int cFunction(cplex *Cplex){ Cplex->yowser = 44; return 1;}
Your import declaration is wrong.
Setting the CharSet in your case doesn't make any sense (there are no string parameters in the native function declaration).
If you want to pass class instance, ref/out also must be thrown away (classes being passed by reference).
And the main point: extern "C" __declspec( dllexport ) means CallingConvention.Cdecl.
UPDATE. Here's complete working code sample:
C++ (header):
struct CStruct
{
int myField;
};
extern "C" __declspec( dllexport ) int MyFunction(CStruct *pStruct);
C++ (code):
int MyFunction(CStruct *pStruct)
{
pStruct->myField = 100;
return 1;
}
C#:
[StructLayout(LayoutKind.Sequential)]
class MyStruct
{
public int myField;
};
class Program
{
MyStruct myStruct = new MyStruct();
[DllImport("MyLib.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int MyFunction(MyStruct pStruct);
static void Main(string[] args)
{
var p = new Program();
var result = MyFunction(p.myStruct);
Console.WriteLine("Result: {0}, MyStruct.myField = {1}", result, p.myStruct.myField);
}
}
Prints:
Result: 1, MyStruct.myField = 100