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.
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)
Apologies if there is a duplicate - I have struggled to find an answer (I have found a few questions around C++ functions that use Function callbacks, and some answers that use Classes as callbacks when called from C/C++ but..
I am in C#.
I am calling a C++ function
I can not change the signature of the C++ function.
Also, I am using the dynamic method of p/invoke rather then the static binding (my example below);
I can handle the situation where a function takes a simple value, or a struct containing simple values, and returns simple values, but in this instance I have a C function which is taking a call-back object.
Following some ideas online, I tried to make a class that had the same signature, then pinned that class, and passed it in., but I get the C# error of 'Object is non-Blittable' (which it doesn't have any variables in it!).
Header file:
again apologies if there are any mistakes in my example, I've tried to strip all non relevant code and explode the macros, but I hope you understand the essence of what is going on
struct someData_t
{
int length; /**< JSON data length */
char* pData; /*< JSON data */
};
namespace FOO {
class ICallback
{
public: virtual ~ICallback() {}
virtual void Callback(const someData_t &response) = 0;
};
}
extern "C" __declspec(dllexport) void process(const someData_t *inData, FOO::ICallback *listener);
My C# file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace Scratchpad {
class Program {
static void Main(string[] args) {
Console.Out.WriteLine("I'm in Managed C#...");
IntPtr user32 = NativeMethods.LoadLibrary(#"somelongpath\my_c.dll");
IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(user32, "process");
process proc = (process)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(process));
String someJson = "{ \"type\":\"someTestJson\"}";
byte[] rawdata = Encoding.UTF8.GetBytes(someJson);
someData myData = new someData();
int dataLength = rawdata.Length * Marshal.SizeOf(typeof(byte)); // I realise byte is size 1 but..
myData.length = rawdata.Length;
myData.pData = Marshal.AllocHGlobal(dataLength);
Marshal.Copy(rawdata, 0, myData.pData, dataLength);
Console.Out.WriteLine("Size of mydata: " + Marshal.SizeOf(myData));
IntPtr unmanagedADdr = Marshal.AllocHGlobal(Marshal.SizeOf(myData));
Marshal.StructureToPtr(myData, unmanagedADdr, true);
// ################################################################
// FIXME: This area still working
Callbacker myCallback = new Callbacker();
GCHandle gch = GCHandle.Alloc(myCallback, GCHandleType.Pinned);
IntPtr mycallbackPtr = gch.AddrOfPinnedObject();
// FIXME: close of working area.
// ################################################################
// CALL THE FUNCTION!
proc(unmanagedADdr, mycallbackPtr);
myData = (someData) Marshal.PtrToStructure(unmanagedADdr, typeof(someData));
Marshal.FreeHGlobal(unmanagedADdr);
Marshal.FreeHGlobal(myData.pData);
gch.Free();
unmanagedADdr = IntPtr.Zero;
bool result = NativeMethods.FreeLibrary(user32);
Console.Out.WriteLine("Fini!)");
}
private delegate void process(IntPtr data, IntPtr callback);
[StructLayout(LayoutKind.Sequential)]
private struct someData {
public int length;
public IntPtr pData;
}
private class Callbacker {
public void Callback(someData response) {
Console.WriteLine("callback Worked!!!");
}
}
}
static class NativeMethods {
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
}
}
Any suggestions welcome
You could create some un-managed wrapper for Your managed Callbacker class which implements ICallback interface.
Something like this:
typedef void (*PointerToManagedFunctionToInvoke)(const someData_t&);
class UnmanagedDelegate : public FOO::ICallback {
private:
PointerToManagedFunctionToInvoke managedCallback;
public:
UnmanagedDelegate(PointerToManagedFunctionToInvoke inManagedCallback)
: managedCallback(inManagedCallback) {}
virtual void Callback(const someData_t &response)
{
managedCallback(response);
}
};
// Export this to managed part
UnmanagedDelegate* CreateUnmanagedDelegate(PointerToManagedFunctionToInvoke inManagedCallback)
{
return new UnmanagedDelegate(inManagedCallback);
}
Then at C# part You could create a delegate to marshal as PointerToManagedFunctionToInvoke, pass it to CreateUnmanagedDelegate receive unmanaged implementation of ICallback and use that to pass to your process
Please be aware that managedCallback should stay allocated at C# side while UnmanagedDelegate class object is alive. And You should delete the UnmanagedDelegate object when it is not used anymore.
OR You could use thin C++/CLI to implement this wrapper.
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.
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
developers!
I have very strange problem. My project has DLL writen in C++ and a GUI writen in C#. And I have implemented callback for some interoperability. I planed that C++ dll will call C# code in some circumstances. It works... but not long and I cant understand why. The problem marked in comment in C# part
Here the complete code of simplified sample:
C++ DLL:
#include <SDKDDKVer.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
extern "C"
{
typedef void (*WriteSymbolCallback) (char Symbol);
WriteSymbolCallback Test;
_declspec(dllexport) void InitializeLib()
{
Test = NULL;
}
_declspec(dllexport) void SetDelegate(WriteSymbolCallback Callback)
{
Test = Callback;
}
_declspec(dllexport) void TestCall(const char* Text,int Length)
{
if(Test != NULL)
{
for(int i=0;i<Length;i++)
{
Test(Text[i]);
}
}
}
};
C# part:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace CallBackClient
{
class Program
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void WriteToConsoleCallback(char Symbol);
[DllImport("CallbackSketch.dll",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
private static extern void InitializeLib();
[DllImport("CallbackSketch.dll",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
private static extern void SetDelegate(WriteToConsoleCallback Callback);
[DllImport("CallbackSketch.dll",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
private static extern void TestCall(string Text,int Length);
private static void PrintSymbol(char Symbol)
{
Console.Write(Symbol.ToString());
}
static void Main(string[] args)
{
InitializeLib();
SetDelegate(new WriteToConsoleCallback(PrintSymbol));
string test = "Hello world!";
for (int i = 0; i < 15000; i++)
{
TestCall(test, test.Length);// It crashes when i == 6860!!!! Debugger told me about System.NullReferenceException
}
}
}
}
The problem is that it crashes in 6860th iteration! I believe that the problem is lack of my knowlege in the subject. Could sombody help me?
SetDelegate(new WriteToConsoleCallback(PrintSymbol));
Yes, this cannot work properly. The native code is storing a function pointer for the delegate object but the garbage collector cannot see this reference. As far as it is concerned, there are no references to the object. And the next collection destroys it. Kaboom.
You have to store a reference to the object yourself. Add a field in the class to store it:
private static WriteToConsoleCallback callback;
static void Main(string[] args)
{
InitializeLib();
callback = new WriteToConsoleCallback(PrintSymbol);
SetDelegate(callback);
// etc...
}
The rule is that the class that stores the object must have a lifetime at least as long as native code's opportunity to make the callback. It must be static in this particular case, that's solid.