calling a method in c++ from c#
-
this is in my c++ file
public ref class Class1
{
public:
int read_file
(const char * file_path, /* Path to file */
hdf_call_vars_t & ret_vals) ;};
and from c# I call it like so (unsafe code)
hdf\_call\_vars\_t ret\_vals; string str = "C:\\\\a.h5"; byte\[\] bytes = Encoding.ASCII.GetBytes(str); fixed (byte\* p = bytes) { sbyte\* sp = (sbyte\*)p; DoAT.Class1 cl = new DoAT.Class1(); cl.read\_file(sp, ref ret\_vals);
It does not compile sayiing the c++ is expecting a pointer to a structure. I thought making it an & is the same as passing by ref? Input appreciated. thanks, sb p.s. the c++ is a managed dll and I add it as a reference in the c# program.
-
this is in my c++ file
public ref class Class1
{
public:
int read_file
(const char * file_path, /* Path to file */
hdf_call_vars_t & ret_vals) ;};
and from c# I call it like so (unsafe code)
hdf\_call\_vars\_t ret\_vals; string str = "C:\\\\a.h5"; byte\[\] bytes = Encoding.ASCII.GetBytes(str); fixed (byte\* p = bytes) { sbyte\* sp = (sbyte\*)p; DoAT.Class1 cl = new DoAT.Class1(); cl.read\_file(sp, ref ret\_vals);
It does not compile sayiing the c++ is expecting a pointer to a structure. I thought making it an & is the same as passing by ref? Input appreciated. thanks, sb p.s. the c++ is a managed dll and I add it as a reference in the c# program.
bonosa wrote:
It does not compile sayiing the c++ is expecting a pointer to a structure. I thought making it an & is the same as passing by ref?
It is indeed passing by ref. How is your
hdf_call_vars_t
looks like? For me the following code compiles and runs fine.struct hdf_call_vars_t {
int foo;
};public ref class Class1 {
public:
int read_file(const char * file_path, hdf_call_vars_t & ret_vals);
};int Class1::read_file(const char * file_path, hdf_call_vars_t & ret_vals)
{
Console::WriteLine(ret_vals.foo);
return 1;
}int main(array ^args)
{
hdf_call_vars_t instance;
instance.foo = 10;
Class1^ c = gcnew Class1;
c->read_file("A string", instance);
return 0;
}If you are using C++/CLI, you don't have to use unsafe code in C#. Just wrap the C++ specific code in C++/CLI and return well defined managed objects which can be consumed directly from C#. After all, that's what C++/CLI is best at.
Best wishes, Navaneeth
-
bonosa wrote:
It does not compile sayiing the c++ is expecting a pointer to a structure. I thought making it an & is the same as passing by ref?
It is indeed passing by ref. How is your
hdf_call_vars_t
looks like? For me the following code compiles and runs fine.struct hdf_call_vars_t {
int foo;
};public ref class Class1 {
public:
int read_file(const char * file_path, hdf_call_vars_t & ret_vals);
};int Class1::read_file(const char * file_path, hdf_call_vars_t & ret_vals)
{
Console::WriteLine(ret_vals.foo);
return 1;
}int main(array ^args)
{
hdf_call_vars_t instance;
instance.foo = 10;
Class1^ c = gcnew Class1;
c->read_file("A string", instance);
return 0;
}If you are using C++/CLI, you don't have to use unsafe code in C#. Just wrap the C++ specific code in C++/CLI and return well defined managed objects which can be consumed directly from C#. After all, that's what C++/CLI is best at.
Best wishes, Navaneeth
thank you Navaneeth. The struct is [StructLayout(LayoutKind.Sequential, Pack = 1)] /* Returned values from read_file */ unsafe struct hdf_call_vars_t { public channel_vars p_vars; public channel_vars s_vars; FILE_VERSION file_vers; public int fetch_n; public s_line_header_t * n_addr; /* malloc'd address of n data */ public UInt32 n_lines; c_file_header_t hdr; } I added the structlayout line in the c# world. Mine still doesn't compile. I'm new to this so I don't know what is meant by wrapping unsafe code in c++ yet. thanks so much, saroj
-
bonosa wrote:
It does not compile sayiing the c++ is expecting a pointer to a structure. I thought making it an & is the same as passing by ref?
It is indeed passing by ref. How is your
hdf_call_vars_t
looks like? For me the following code compiles and runs fine.struct hdf_call_vars_t {
int foo;
};public ref class Class1 {
public:
int read_file(const char * file_path, hdf_call_vars_t & ret_vals);
};int Class1::read_file(const char * file_path, hdf_call_vars_t & ret_vals)
{
Console::WriteLine(ret_vals.foo);
return 1;
}int main(array ^args)
{
hdf_call_vars_t instance;
instance.foo = 10;
Class1^ c = gcnew Class1;
c->read_file("A string", instance);
return 0;
}If you are using C++/CLI, you don't have to use unsafe code in C#. Just wrap the C++ specific code in C++/CLI and return well defined managed objects which can be consumed directly from C#. After all, that's what C++/CLI is best at.
Best wishes, Navaneeth
-
thank you Navaneeth. The struct is [StructLayout(LayoutKind.Sequential, Pack = 1)] /* Returned values from read_file */ unsafe struct hdf_call_vars_t { public channel_vars p_vars; public channel_vars s_vars; FILE_VERSION file_vers; public int fetch_n; public s_line_header_t * n_addr; /* malloc'd address of n data */ public UInt32 n_lines; c_file_header_t hdr; } I added the structlayout line in the c# world. Mine still doesn't compile. I'm new to this so I don't know what is meant by wrapping unsafe code in c++ yet. thanks so much, saroj
Wrapping native objects means, you create a managed class in C++/CLI and hide your
hdf_call_vars_t
struct inside this managed class. Your library will only deal with this managed object and at the C# side you only have to deal with this managed object. This approach hides all the complexities of using native structs and simplifies your library's interface. Here is working example of wrapping native object in a managed object.// ClassLibrary.h
using namespace System;
namespace Example {
// Native struct struct hdf\_call\_vars\_t { int foo; }; // This wraps the native hdf\_call\_vars\_t and provides managed class public ref class HdfCallVars { private: hdf\_call\_vars\_t\* native; public: HdfCallVars() { native = new hdf\_call\_vars\_t; } property int Foo { int get() { return native->foo; } void set(int value) { native->foo = value; } } }; // This is your libraries main class public ref class YourLibrary { public: // This method now takes managed objects int ReadFile(String^ file\_path, HdfCallVars^ ret\_vals) { // your code goes here return ret\_vals->Foo; } };
}
To use this library from a C# application,
using System;
using Example;namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
HdfCallVars vars = new HdfCallVars();
vars.Foo = 10; // Doing native call through well defined interfaceYourLibrary library = new YourLibrary(); int status = library.ReadFile("filepath", vars); Console.WriteLine(status); Console.Read(); } }
}
Hope that helps.
Best wishes, Navaneeth
-
Wrapping native objects means, you create a managed class in C++/CLI and hide your
hdf_call_vars_t
struct inside this managed class. Your library will only deal with this managed object and at the C# side you only have to deal with this managed object. This approach hides all the complexities of using native structs and simplifies your library's interface. Here is working example of wrapping native object in a managed object.// ClassLibrary.h
using namespace System;
namespace Example {
// Native struct struct hdf\_call\_vars\_t { int foo; }; // This wraps the native hdf\_call\_vars\_t and provides managed class public ref class HdfCallVars { private: hdf\_call\_vars\_t\* native; public: HdfCallVars() { native = new hdf\_call\_vars\_t; } property int Foo { int get() { return native->foo; } void set(int value) { native->foo = value; } } }; // This is your libraries main class public ref class YourLibrary { public: // This method now takes managed objects int ReadFile(String^ file\_path, HdfCallVars^ ret\_vals) { // your code goes here return ret\_vals->Foo; } };
}
To use this library from a C# application,
using System;
using Example;namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
HdfCallVars vars = new HdfCallVars();
vars.Foo = 10; // Doing native call through well defined interfaceYourLibrary library = new YourLibrary(); int status = library.ReadFile("filepath", vars); Console.WriteLine(status); Console.Read(); } }
}
Hope that helps.
Best wishes, Navaneeth
Thank you so very much for taking the time to answer in such detail, Navaneeth! I learned a lot from your code. I do have a question though. Since my hdf_call_vars_t is a complex structure containing members whiuch are instances of other structs, how do I treat those? For example, it has a member 'struct channel_vars p_data.' A channel_vars type struct instance contains in it ints, doubles, enums and a void * . I'll of course create get set accessors for Channel_vars as you have shown for the int foo in your example.But do I need to make Channel Vars a wrapper class also? thank you, saroj
-
Thank you so very much for taking the time to answer in such detail, Navaneeth! I learned a lot from your code. I do have a question though. Since my hdf_call_vars_t is a complex structure containing members whiuch are instances of other structs, how do I treat those? For example, it has a member 'struct channel_vars p_data.' A channel_vars type struct instance contains in it ints, doubles, enums and a void * . I'll of course create get set accessors for Channel_vars as you have shown for the int foo in your example.But do I need to make Channel Vars a wrapper class also? thank you, saroj
bonosa wrote:
But do I need to make Channel Vars a wrapper class also?
If that is something which user has to create, you need to make it a managed class. All your internal structures don't need to be wrapped because you handle the creation of those inside your main managed object.
Best wishes, Navaneeth
-
bonosa wrote:
But do I need to make Channel Vars a wrapper class also?
If that is something which user has to create, you need to make it a managed class. All your internal structures don't need to be wrapped because you handle the creation of those inside your main managed object.
Best wishes, Navaneeth
Thanks Navaneeth, that almost compiled! Now I have a problem!
int atClass1::read_file
(String^ file_path, /* Path tofile */
HdfCallVars % ret_vals)I did the above as you advised. However ret_vals has to be fed to an HDF5 function which needs a void* as one of it's arguments. So I am stuck at the following line of code not knowing what to do:
/* Iterate through the links, filling in needed data as discovered. */
io_err = H5Literate (group_id, H5_INDEX_NAME, H5_ITER_NATIVE,&i, get\_data, (void\*)&ret\_vals);
the compiler doesn't like (void*)&ret_vals.... What to do? thanks, saroj
-
Thanks Navaneeth, that almost compiled! Now I have a problem!
int atClass1::read_file
(String^ file_path, /* Path tofile */
HdfCallVars % ret_vals)I did the above as you advised. However ret_vals has to be fed to an HDF5 function which needs a void* as one of it's arguments. So I am stuck at the following line of code not knowing what to do:
/* Iterate through the links, filling in needed data as discovered. */
io_err = H5Literate (group_id, H5_INDEX_NAME, H5_ITER_NATIVE,&i, get\_data, (void\*)&ret\_vals);
the compiler doesn't like (void*)&ret_vals.... What to do? thanks, saroj
bonosa wrote:
the compiler doesn't like (void*)&ret_vals....
Add a method to
HdfCallVars
so that it can give you the underlying native struct.Best wishes, Navaneeth
-
Thanks Navaneeth, that almost compiled! Now I have a problem!
int atClass1::read_file
(String^ file_path, /* Path tofile */
HdfCallVars % ret_vals)I did the above as you advised. However ret_vals has to be fed to an HDF5 function which needs a void* as one of it's arguments. So I am stuck at the following line of code not knowing what to do:
/* Iterate through the links, filling in needed data as discovered. */
io_err = H5Literate (group_id, H5_INDEX_NAME, H5_ITER_NATIVE,&i, get\_data, (void\*)&ret\_vals);
the compiler doesn't like (void*)&ret_vals.... What to do? thanks, saroj