Pass object from C# to CLR (Visual C++) - c#

I have two projects. One in C# and one in CLR (Visual C++). The CLR has User.h which defines the class User. An instance of User, is created and initialised in C# and has to be passed into the CLR project. Therefore, I have the following:
User.h (CLR)
public ref class User : IDisposable
{
private:
long id;
String^ name;
String^ password;
int user_type;
List<int> permissions;
//Some more fields here
public:
User();
~User();
!User();
}
In a C# class there is the following:
User user = new User ("Name",.....)
controllerClr.addUser(user);
where the user is actually of class User as defined in User.h
and in CLR I have the following method
void ControllerClr::MainClr::addUser(ControllerClr::User^ user)
{
Console::WriteLine("Passed");
//Do some processing
}
However, when calling the method controllerClr.addUser(user); I get the following:
Unhandled exception at 0x00007FFFE4C3879B (ntdll.dll) in
platform.exe:0xC0000374: A heap has been corrupted (parameters:
0x00007FFFE4C9C6E0)
Please also note that Passed is not printed on the Console, so I guess the error has to be something with the call.
What am I doing wrong and how to resolve it?

Related

How to use a common Class/Interface between a Console Program and a DLL?

I"m developing an application (.NET framework + C#) where I have a bunch of DLLs developed and sent to me by clients, which I put in a folder and read from my main console program. For the sake of simplicity, I'll explain it using a rather abstract example which properly represent my original problem.
I want the DLLs to have one function, which creates and returns an object of the data type "PersonData". PersonData has three variables, namely, personName, personAge, and personNationality. The data type PersonData, which is a structure, look like this:
namespace CommonStuff
{
public static class Common
{
public struct PersonData
{
string name;
int age;
string nationality;
// Properties go here.
// Constructor goes here.
}
}
}
The above is saved in a file called Common.cs.
Then I have an interface, which is saved in IPerson.cs which looks like this:
namespace CommonStuff
{
public interface IPerson
{
Common.PersonData GetPerson();
}
}
Basically, my interface has one function that returns an object of type PersonData.
I want to distribute these two files to my clients, so they can code their DLLs based on these. So one client would create a DLL which returns a PersonData object, with nationality set to "American", and another would return a PersonData object with nationality set to Japanese. For instance, a DLL would look like this:
namespace CreateAmerican
{
public class CreateAmerican : CommonStuff.IPerson
{
public CommonStuff.Common.PersonData GetPerson()
{
CommonStuff.Common.PersonData person = new CommonStuff.Common.PersonData();
person.Name = "Jennifer";
person.Age = 23;
person.Nationality = "American";
return person;
}
}
}
Then, in my Main program, I want to read those DLLs - which I've put in a folder in advance - at runtime, and get the object returned by GetPerson() in each DLL. That means, I want to have the same two IPerson.cs and Common.cs files included in my Main program, then create an object of type PersonData, and get the return value of GetPerson() method into it, and then do some work with it. My main looks like this:
static void Main(string[] args)
{
List<object> dllResultList = null;
LoadDllClassesToList(Directory.GetCurrentDirectory(), out dllResultList);
foreach(object item in dllResultList)
{
CommonStuff.Common.PersonData person = (CommonStuff.Common.PersonData)item;
Console.WriteLine(String.Format("Name : {0}, Age : {1}, Nationality : {2}", person.Name, person.Age, person.Nationality));
}
Console.ReadLine();
}
The LoadDllClassesToList() method reads the DLLs at runtime, and put the return value of GetPerson() method into a list of "object" type.
When I run the program, everything works fine until I reach the line:
CommonStuff.Common.PersonData person = (CommonStuff.Common.PersonData)item;
There, it throws an exception as follows:
An unhandled exception of type 'System.InvalidCastException' occurred
in MainProgram.exe
From what I understand, even though I have the same data type PersonData in my main program, the program sees it as something different to the PersonData object that my DLL returns.
So here's my question; how would I use an interface like IPerson, and a common data type such as PersonData, which both my DLLs and main program can share? My actual program has more complex data types, so I can't return something like an array of standard data types. I need to be able to return a user-defined data type like PersonData.
Thanks in advance!
Provide assembly instead of source for shared classes and interfaces. Clients and you will link to the same assembly and code will work fine.
Alternatively you can give up type safety and use reflection or dynamic to use client's objects.
The best option would be to set your interfaces in separated library, which could have been used by Clients.
If sharing of your assembly is impossible because of any reasons, I'm affraid the only option is using of dynamic type or reflection.
dynamic person = item;
Same structure of class or interface like in 3rd party DLL is still different class or interface, as long as those classes/interfaces coming from different assembly.
You may like to give a try to AutoMapper which could later translate type to your local one. here

Using .NET wrapper for COM object: Non-invocable member cannot be used like a method

I need some way to use ActiveX component in .NET (C#). I know it can be done using dynamic object. But I would like to use typed wrapper (Runtime Callable Wrapper), generated by VS 2015 or TlbImp.exe. I tried use it this way:
var connector = new Connector(); // Connector is _interface_ with CoClass attribute applied. Yep, it works
connector.Login(connStr, flags); // compile-time error
Connector object can be successfully created, but I can't call Login method because of compile-time error: CS1955 Non-invocable member 'IConnector.Login[string, int]' cannot be used like a method. Autogenerated wrapper looks like this (via IL Spy):
[Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"), TypeLibType(TypeLibTypeFlags.FDual | TypeLibTypeFlags.FDispatchable)]
[ComImport]
public interface IConnector
{
[DispId(201)]
int Login
{
[DispId(201)]
[MethodImpl(MethodImplOptions.InternalCall)]
get;
}
...
}
[CoClass(typeof(ConnectorClass)), Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]
[ComImport]
public interface Connector : IConnector
{
// nothing here
}
[ClassInterface(ClassInterfaceType.None), Guid("yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"), TypeLibType(TypeLibTypeFlags.FCanCreate)]
[ComImport]
public class ConnectorClass : IConnector, Connector
{
[DispId(201)]
public virtual extern int Login
{
[DispId(201)]
[MethodImpl(MethodImplOptions.InternalCall)]
get;
}
...
}
Note: this ActiveX-component is successfully called from VBScript. Also I tried to generate .NET wrapper for other random COM-component in the same way and I could call methods. So why it doesn't work now?

Returning Managed C# List to Unmanaged C++ code

I have a C# dll ( for which register for COM interop option is set).
This C# dll has the below interface and class
interface IMyInterface
{
bool IsNameExists(string name);
List<string> GetNameList();
}
public class MyClass : IMyInterface
{
public bool IsNameExists(string name)
{
//DO Something
}
public List<string> GetNameList()
{
// DO something
}
}
I need to call the methods IsNameExists and GetNameList from unmanaged C++.
#import "..\..\ProdCon\bin\ProdCon.tlb" raw_interfaces_only
void main()
{
HRESULT hr =::CoInitialize(NULL);
IMyInterface pIMyInterface(__uuidof(MyClass));
VARIANT_BOOL bRet = FALSE;
BSTR bstrName = ::SysAllocString(_T("RAJESH"));
hr = pIMyInterface->IsNameExists(bstrName,&bRet);
}
So I created COM object as above and called the IsNameExists mehtod without any issue.
Since GetNameList method returns list, I am getting the below warning
'MyDll.IMyInterface.GetNameList(#0), MyDll'. Warning: Type library
exporter encountered a generic type instance in a signature. Generic
code may not be exported to COM.
Please help me how to return C# list to unmanaged C++ code. So that unmanaged C++ code can use this list.
'Generic code may not be exported to COM.' => hence its the type parameter string in public List GetNameList(). Thus essentially you need access to a non-generic c# method to get the data.
If you have control of the MyClass codebase you could (for example) add a:
public string[] GetNameArray()
{
return GetNameList.ToArray();
}
If not then you'll need to write a proxy/wrapper class to do something similar to the above and expose that via COM, either as a one off or a 'general' methodology using reflection say.
See for example http://weblog.west-wind.com/posts/2007/Jul/10/Generics-and-COM-Interop-dont-mix

How to replace an instance of a base class with a derived one during runtime

I have a simple Error base class that has for the purposes of this question an int representing a possible error value.
I have a derived from this Error base class an ErrorMultiple class which has a List of ints so that I can hold multiple errors values.
There is an Event class that I wish to hold amongst other things an single instance of the Error base class.
So I create an instance of an Event and pass it around to many methods. One of these decides that “Oh no. We have discovered that there multiple errors to report."
How can I write code to get my Event object to discard it’s Error base class instance and replace it with an instance of the ErrorMultiple class via a Event.ReplaceErrorObjWithMultipleErrorObj()
public class Error
{
protected int ErrorNo;
}
public class ErrorMultiple : Error
{
Protected List<int> MultipleErrorNos;
}
public class Event
{
Error * errorObject;
}
Public Event()
{
errorObject = new Error();
}
void Event.ReplaceErrorObjWithMultipleErrorObj()
{
errorObject = new ErrorMultiple();
}
Of course the Event declaration is rubbish in true C# syntax. I don't know how to declare a reference to a replaceable object instance.
Issue #01 - Value Types and Reference Types
In C++, objects are value types - structures of their components. To make a reference to the value as it lives in memory, you use a pointer. C# is not like this. In C#, objects are references by default, there are no values types as far as objects are concerned. (That was a bit of a lie to make this simpler)
That means that in C# this:
ErrorClass error = new ErrorClass();
Is the equivalent of this in C++:
ErrorClass* error = new ErrorClass();
The key difference however, is that there is no way to dereference a C# reference like you can dereference a pointer in C++.
Additionally this in C#:
error.ErrorNumber = 0;
Is equivalent to this in C++:
error->ErrorNumber = 0;
Effectively what is happening is that all the pointer magic is being concealed from the user in order to prevent pointer errors and to ensure that no pointer management interferes with the managed language's garbage collection (a system that deletes objects when they are finished with, ie they fall out of scope).
Issue #02 - Syntax
In C#, the compiler is quite a bit different to the C++ one. It's more intelligent and less strict, though it lacks a few features like true defines and macros.
Firstly, classes must fully contain all their fields and methods in one block, you cannot declare the basic structure of a class and declare the definitions of things afterwards. ie, you cannot do this:
class ErrorClass { public: void PrintError(); }
void ErrorClass::PrintError() { printf("error"); }
Because the definition for the method must be inside the body of the class, like so:
class ErrorClass { public void PrintError() { Console.Write("error"); } }
You do not however have to declare the name of a function before it is used, the compiler does not necessarily read your code in the order it is written.
To sum everything up, I shall rewrite your code in proper C# form so you may see how things should look:
public class Error
{
protected int ErrorNo;
}
public class ErrorMultiple : Error
{
protected List<int> MultipleErrorNos = new List<int>();
}
public class Event
{
Error errorObject = new Error();
public void MultiplyErrors()
{
errorObject = new ErrorMultiple();
}
}
Please note that firstly, the error numbers cannot be read because they are protected, and that secondly C# has an exception-based system using the try,catch,finally and throw statements, similar to more modern C++.
Suggested reading:
Reference Types: http://msdn.microsoft.com/en-us/library/490f96s2(v=vs.100).aspx
Value Types: http://msdn.microsoft.com/en-us/library/s1ax56ch(v=vs.100).aspx
Classes: http://msdn.microsoft.com/en-us/library/vstudio/x9afc042(v=vs.100).aspx
Exceptions: http://msdn.microsoft.com/en-us/library/ms173160(v=vs.100).aspx

Can I use Carbon APIs to launch an application via jna?

In brief: is Carbon valid for this task, or should I dump it and look for a Cocoa solution?
Trying to write an applet which interrogates the client system (Snow Leopard and later) to list applications claim to be able to edit a single given file. User selects an application, and my applet then calls on Launch Services to launch the application, with the file as an argument.
I can get the list of eligible applications by calling LSCopyApplicationURLsForURL.
I can convert a file path to a FSRef object by calling FSPathMakeRef.
What I can't do is to construct and use a LSApplicationParameters object (one of whose members is a FSRef) in order to successfully call LSOPenURLsWithRole (one of whose arguments is a LSApplicationParameters).
What I done so far:
interface MyCarbonWrapper extends com.sun.jna.Library
{
public static final MyCarbonWrapper INSTANCE =
(MyCarbonWrapper) Native.loadLibrary("Carbon", MyCarbonWrapper.class);
// .. various function declarations, including
com.sun.jna.Pointer LSCopyApplicationURLsForURL(Object curlRef, int rolesMask);
int FSPathMakeRef(Object path, PointerByReference ref, Void isDirectory);
int LSOpenURLsWithRole(Pointer ptrArray, int roles, Void inAEParam,
Structure myLSApplicationParams, Void outPsns, int inMaxPSCount);
}
// unsuccessful attempt to define a mapped LSApplicationParameters
public static class LSApplicationParameters
{
public int version;
public int flags;
public Pointer Application;
public Void asyncLaunchRefCon;
public Void environment;
public Void argv;
public Void initialEvent;
public static final int sizeof = 28;
}
public void openWith(String filePath)
{
// create a CURLRef for the selected application - OK
// Create a FSRef from the CURLRef - OK
// Create a CFArray to contain the file argument - OK
// create and attempt to populate a LSApplicationParameters instance - problematic
// call LSOpenURLsWithRole - failure. Returned error code is -50
}
The returned error code I usually get I understand to map to the message:
"Error in user parameter list".
As far as I can tell, Snow Leopard seems to have dropped support for a range of APIs that take a FSRef as an argument. It's not at all clear to me just where I stand with what's supported and what isn't.
So I should conclude Carbon is a dead duck for this activity? Or am I closer than I think?
Tx
Having given up on Carbon's LSOpenApplication, I've implemented a solution which uses Rococoa to bridge Objective-C and Java. Not the the necessity of translating Cocoa composite method names eg openFile:withApplication to openFile_withApplication.
// Declare an interface which will call on a rococoa class:
public interface NSWorkspace extends NSObject
{
public static final _Class CLASS = Rococoa.createClass("NSWorkspace", _Class.class);
public interface _Class extends NSClass
{
// static method to get the workspace in
NSWorkspace sharedWorkspace();
}
boolean openFile_withApplication(NSString fullPath, NSString appName);
}
// then we can call on it to do stuff
final NSWorkspace nsWorkspace = NSWorkspace.CLASS.sharedWorkspace();
boolean isRunning = nsWorkspace.openFile_withApplication(
NSString.stringWithString(targetFilePathStr),
NSString.stringWithString(executableApplicationPathStr));
I'm still using Carbon for a number of other LaunchApplication services.
java.net/projects/rococoa/ is its home, with some useful if minimal chatter on java.net/projects/rococoa/lists/users/archive

Resources