Array of Objects/Classes fails for user defined Class - c#

I am hoping someone with some more C++ knowledge might be able to help me. I am trying to create an array of objects in C# from a Class I've created in a Managed C++ DLL. I haven't any clue what is going on. I am able to run the application and build it setting up the array of classes appears to work perfectly fine but when I call a function from the array it never researches the Managed DLL. I've traced it and it simply doesn't work. The application doesn't fail with any errors either. Interestingly enough when I removed the array of classes and only initiated the class once it works all fine and dandy. Please help me figure out how to fix this.
//C#
public ClientBridge[] netlobby;
private void connectToLobby(int lobbyIndex)
{
//lobbyIndex = 0
netlobby[lobbyIndex] = new ClientBridge();
connectLobby[lobbyIndex] = netlobby[lobbyIndex].MMK_Connect(host, lobbyport);
}
//C++ DLL
// This class is the managed reference class
public ref class ClientBridge
{
public:
ClientBridge();
virtual ~ClientBridge();
bool MMK_Connect(String^ hostpass, UInt16 port);
};

doesn't look like you ever initialize the array
public ClientBridge[] netlobby = new ClientBridge[MAX_BRIDGES]; // <- gotta initialize
private void connectToLobby(int lobbyIndex)
{
netlobby[lobbyIndex] = new ClientBridge();
connectLobby[lobbyIndex] = netlobby[lobbyIndex].MMK_Connect(host, lobbyport);
}

Related

C#: Looping through member objects of nested structs

Hi all you c# wizards!
I need to store all the memory offset values of (packed) nested structs within these respective structs.
Recusively looping through all the members works fine so far. Also, i get the appropriate memory offset values.
This struct contraption might contain several dozends of structs, and several hundreds of other members in the end.
But i do this whole thing at initialization time, so CPU performance won't be an issue here.
But:
In this iteration process, it seems i have trouble accessing the actual instances of those structs. As it turns out, when i try to store these offset values, they don't end up where i need them (of course, i need them in the instance "SomeStruct1" and its containing other struct instances, but the debugger clearly shows me the init values (-1)).
I suspect "field_info.GetValue" or "obj_type.InvokeMember" is not the proper thing to get the object reference? Is there any other way to loop through nested struct instances?
Please help! I've desperately debugged and googled for three days, but i'm so out of ideas now...
Thanks for your efforts!
-Albert
PS - the reason i do this unusual stuff:
I communicate between two embedded CPU cores via the mentioned nested struct (both are mixed c/c++ projects). This works like a charm, as both cores share the same memory, where the struct resides.
Additionally, i have to communicate between a c# host application and theses embedded cores, so i thought it could be a neat thing, if i implement a third instance of this struct. Only this time, i oviously can't use shared RAM. Instead, i implement value setters and getters for the data-holding members, find out the memory offset as well as the lenght of the data-holding members, and feed this information (along with the value itself) via USB or Ethernet down to the embedded system - so the "API" to my embedded system will simply be a struct. The only maintenance i have to do every thime i change the struct: i have to copy the holding .h file (of the embedded project) to a .cs file (host project).
I know it's crazy - but it works now.
Thanks for your interest. -Albert
This is a simplified (buggy, see below) example that should compile and execute (WinForms, c#7.3):
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace CodingExample
{
public interface Interf
{
Int32 Offset {get; set; }
}
[StructLayout (LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct sSomeStruct2 : Interf
{
public sSomeStruct2 (bool dummy)
{
Offset = -1;
SomeMember3 = 0;
}
public Int32 Offset {get; set; }
public Int32 SomeMember3;
// much more various-typed members (e. g. nested structs)...
}
[StructLayout (LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct sSomeStruct1 : Interf
{
public sSomeStruct1 (bool dummy)
{
Offset = -1;
SomeMember1 = 0;
SomeStruct2 = new sSomeStruct2 (true);
SomeMember2 = 0;
}
public Int32 Offset {get; set; }
public Int32 SomeMember1;
public sSomeStruct2 SomeStruct2;
public Int16 SomeMember2;
// much more various-typed members...
}
public partial class Form1 : Form
{
void InitializeOffsets (object obj)
{
Console.WriteLine ("obj: {0}", obj);
Type obj_type = obj.GetType ();
foreach (FieldInfo field_info in obj_type.GetFields ())
{
string field_name = field_info.Name;
Int32 offset = (Int32) Marshal.OffsetOf (obj_type, field_name);
Type field_type = field_info.FieldType;
bool is_leafe = field_type.IsPrimitive;
// none of theses three options seem to give me the right reference:
// object node_obj = field_info.GetValue (obj);
// object node_obj = field_info.GetValue (null);
object node_obj = obj_type.InvokeMember (field_name, BindingFlags.GetField, null, obj, null);
Console.WriteLine ("field: {0}; field_type: {1}; is_leafe: {2}; offset: {3}", field_name, field_type, is_leafe, offset);
if (! is_leafe)
{
// this writes not as expected:
(node_obj as Interf).Offset = offset;
InitializeOffsets (node_obj);
}
}
}
sSomeStruct1 SomeStruct1;
public Form1 ()
{
InitializeComponent ();
SomeStruct1 = new sSomeStruct1 (true);
InitializeOffsets (SomeStruct1);
}
}
}
Meanwhile i found out, what i did wrong:
i have to do boxing, so i can use "ref" when i call my initialize function:
// instead of this:
SomeStruct1 = new sSomeStruct1 (true);
// i have to do it this way:
object boxed_SomeStruct1 = new sSomeStruct1 (true);
InitializeOffsets (ref boxed_SomeStruct1);
SomeStruct1 = (sSomeStruct1) boxed_SomeStruct1;
Within the "InitializeOffsets" function, "field_info.GetValue (obj)" delivers a copy of my member object. That's why i have to copy the modified copy back at the very end of the foreach loop:
field_info.SetValue (obj, node_obj);
After these changes, the code works as intended.
Thanks for your interest. -Albert

WCF Generated Classes

I'm converting a sample VB Client (from a 3rd party SDK which consumes its WCF Services) to C#. The client in VB when executed, works correctly.
I'm writing a small wrapper in C# Windows Form in vs2015 to consume the same WCF Services. I was able to add the service references in VS2015.
I am having a problem making an equivalent call in VB which works but for some reason in C#, it does not work, as if the generated C# classes is incorrect.
in the working sample VB,
I have a call ContinueGetList
Dim siteList() As SRAdminSys.MW_DataContractCommon
Dim iRecCount As Integer = 20
Dim Status As SRAdminSys.MW_ContinueStatusEnum
Status = m_wcfSysDB.ContinueGetList(iRecCount, siteList)
The call returns a list in siteList which is an array of SRAdminSys.MW_DataContractCommon
note: I think* the actual The array returned by the webservice , is of type MW_Site which inherits DataContractCommon - (* when I made the same call in a C# equivalent call, I get an error and when I checked the error , it mentioned that it is casting MW_Site to MW_DataContractCommon )- See further test below.
Partial Public Class MW_Site
Inherits SRAdminSys.MW_DataContractCommon
The Reference.vb from the Generated Proxy Class:
Public Function ContinueGetList(ByVal iCount As Integer, ByRef list() As SRAdminSys.MW_DataContractCommon) As Integer Implements SRAdminSys.IMW_KWSAdminSysDatabase.ContinueGetList
Return MyBase.Channel.ContinueGetList(iCount, list)
End Function
Now in the Non-Working prototype in C# ( it does compile though ) ,
int[] lst_SiteIds = new int[] { };
int iRecCount = 20;
SRAdminSys.MW_ContinueStatusEnum status;
status = (SRAdminSys.MW_ContinueStatusEnum)adminSysClient.ContinueGetList(iRecCount,ref siteList);
The return value status indicated that the call failed and when I check the error - a WCF method GetLastError(), I get
Unable to cast object of type 'MW_DataContractCommon[]' to type 'MW_Site[]'.
When I check the MW_Site Partial class generated by C# Service Reference, I notice it inherits DataContractCommon, so I expected it to work:
public partial class MW_Site : SRAdminSys.MW_DataContractCommon {
Reference.cs
public int ContinueGetList(int iCount, ref TestUtil.SRAdminSys.MW_DataContractCommon[] list) {
TestUtil.SRAdminSys.ContinueGetListRequest inValue = new TestUtil.SRAdminSys.ContinueGetListRequest();
inValue.iCount = iCount;
inValue.list = list;
TestUtil.SRAdminSys.ContinueGetListResponse retVal = ((TestUtil.SRAdminSys.IMW_KWSAdminSysDatabase)(this)).ContinueGetList(inValue);
list = retVal.list;
return retVal.ContinueGetListResult;
}
CWUtility.SRAdminSys.ContinueGetListResponse retVal = ((CWUtility.SRAdminSys.IMW_KWSAdminSysDatabase)(this)).ContinueGetList(inValue);
list = retVal.list;
return retVal.ContinueGetListResult;
}
In relation to the partial class and inheritance of DataContractCommon - On another sample code which works in VB but not in C# when both is pointing to the same WCF service. The only difference is a VB App vs a C# App which points to their respective generated proxy classes from VS Service Reference.
Dim sitekey__ As New SRAdminSite.MW_Key
bRet = m_wcfSiteDB.Add(1, eKEY_COLLECTION, sitekey__)
VB, Reference.vb
Public Function Add(ByVal iSiteID As Integer, ByVal iCollectionType As Integer, ByRef iRecord As SRAdminSite.MW_DataContractCommon) As Boolean Implements SRAdminSite.IMW_KWSAdminSiteDatabase.Add
Return MyBase.Channel.Add(iSiteID, iCollectionType, iRecord)
End Function
SRAdminSite.MW_Key sitekey__ = new SRAdminSite.MW_Key();
//COMPILE ERROR on sitekey__ although MW_Key is a partial class which inherits MW_DataContractCommon
bRet = adminSiteClient.Add(1, (int)SRAdminSite.MW_SiteCollectionTypesEnum.eKEY_COLLECTION, ref sitekey__);
C#, Reference.cs
public bool Add(int iSiteID, int iCollectionType, ref SRAdminSite.MW_DataContractCommon iRecord)
{
SRAdminSite.AddRequest inValue = new SRAdminSite.AddRequest();
inValue.iSiteID = iSiteID;
inValue.iCollectionType = iCollectionType;
inValue.iRecord = iRecord;
SRAdminSite.AddResponse retVal = ((SRAdminSite.IMW_KWSAdminSiteDatabase)(this)).Add(inValue);
iRecord = retVal.iRecord;
return retVal.AddResult;
}
So both these examples seems to be related to the way the proxy classes the WCF Service was generated and inheriting other classes but unable to work. In the 2nd sample the error was at compile time...
Can anyone shed some light, why in VB the default generated class works when called, but in c#, the same equivalent call to the same method does not work?
I've consumed other 3rd party WCF services and normally, importing the Service Reference and consuming the methods were a breeze...
For some reason, this one, I cant wrap my head around why it will not work. Is there something I'm missing or need to modify in the way the Reference.cs is generated before I consume them?

C# Namespace not found for DLL

Ok, I will preface this by saying I did do a look up and read a lot of similar questions and answers on here before posting this.
Background Info
Using Visual Studio Professional 2013
Goal:
This project is for my own amusement and to try to learn as much as I can.
I have a native C++ header file called BinaryTree.h which is just a simple data structure that uses recursive logic to build a binary search tree. It works quite well on its own.
I want to build a GUI in C# that uses this. (Its not really useful or practical, I just choose it, well because I wanted to. Also, while the logic inside the binary tree class is complex(ish), I only need to call 2 methods, a addNode method, and a toString method which return the max depth and number of nodes).
I choose using a c++/cli wrapper to accomplish this. Everything seemed to go well, the build was successful and a .dll file was created in the debug directory of my project.
Now, I started in on the C# side of things. I added the .dll file to references. However, when I typed in " using filename.dll;" I got an error saying "Type or namespace not found...".
To reiterate I did some researching. I found (it seemed in VS2010) that different target frameworks could cause this error. I checked mine, targets for both were net4.5, so that is not the problem.
Here is the code from my c++/cli wrapper. Perhaps it has something to do with using templates? Any help is appreciated.
#pragma once
#include "D:\Schoolwork 2015\Test Projects\CPPtoC#WrapperTest\CPPLogic\CPPLogic\BinaryTree.h"
using namespace System;
namespace BinaryTreeWrapper {
template<class Data>
public ref class BinaryTreeWrapperClass
{
public:
BinaryTreeWrapperClass(){ tree = new BinaryTree(); }
~BinaryTreeWrapperClass(){ this->!BinaryTreeWrapperClass(); }
!BinaryTreeWrapperClass(){ delete tree; }
//methods
void wrapperAddNode(Data)
{
tree->addNode(Data);
}
std::string wrapperToString()
{
return tree->toString();
}
private:
BinaryTree* tree;
};
}
Screenshot of the error:
EDIT
Ok, so here is a weird thing... my original file built just fine with the new code and produced a .dll file. However, I decided to try a fresh project since the namespace was still not being found. Upon moving the code over and trying to build, I've run into 4 errors:
Error 1 error C2955: 'BinaryTree' : use of class template requires template argument list
Error 2 error C2512: 'BinaryTree' : no appropriate default constructor available
Error 3 error C2662: 'void BinaryTree::addNode(Data)' : cannot convert 'this' pointer from 'BinaryTree' to 'BinaryTree &'
Error 4 error C2662: 'std::string BinaryTree::toString(void) const' : cannot convert 'this' pointer from 'BinaryTree' to 'const BinaryTree &'
I copied the code exactly, only changing the namespace to "TreeWrapper' and the class name to 'TreeWrapperClass'.
To help, I've include a snippet from my BinaryTree.h file. There is a bunch more that defines the 'NODE' class, but I didn't want to clutter it up more than i needed.
After further investigation, it appears the problem lies with using 'generic'. If I switch it all to 'template' it builds just fine, but then it can't be used as a reference in C# (getting the namespace error). I built a test project using very simple methods (no templates) and was able to use the .dll wrapper I made in C#. So the problem lies with templates and generics.
Last Edit
I've found if I change the code to initiate the template as 'int' it works just fine, and I can use it in C#. For example:
...
BinaryTreeWrapperClass(){ tree = new BinaryTree<int>(); }
....
private:
BinaryTree<int>* tree;
BinaryTree.h
template<class Data>
class BinaryTree
{
private:
Node<Data>* root;
unsigned int nNodes;
unsigned int maxDepth;
unsigned int currentDepth;
void traverse(Node<Data>*& node, Data data);
public:
BinaryTree();
~BinaryTree();
void addNode(Data);
std::string toString() const
{
std::stringstream sstrm;
sstrm << "\n\t"
<< "Max Depth: " << maxDepth << "\n"
<< "Number of Nodes: " << nNodes << "\n";
return sstrm.str(); // convert the stringstream to a string
}
};
template<class Data>
BinaryTree<Data>::BinaryTree() //constructor
{
//this->root = NULL;
this->root = new Node<Data>(); //we want root to point to a null node.
maxDepth = 0;
nNodes = 0;
}
template<class Data>
BinaryTree<Data>::~BinaryTree() //destructor
{
}
template<class Data>
void BinaryTree<Data>::addNode(Data data)
{
traverse(root, data); //call traverse to get to the node
//set currentDepth to 0
currentDepth = 0;
}
template<class Data>
void BinaryTree<Data>::traverse(Node<Data>*& node, Data data)
{
//increment current depth
currentDepth++;
if (node == NULL) //adds new node with data
{
node = new Node<Data>(data);
//increment nNode
nNodes++;
//increment maxDepth if current depth is greater
if (maxDepth < currentDepth)
{
maxDepth = currentDepth - 1; //currentDepth counts root as 1, even though its 0;
}
return;
}
else if (node->getData() >= data) //case for left, getData must be bigger. The rule is, if a number is equal to getData or greater, it is added to the left node
{
Node<Data>* temp = node->getLeftNode();
traverse(temp, data); //recursive call, going down left side of tree
node->setLeftNode(temp);
}
else if (node->getData() < data) //case for right, getData must be less
{
Node<Data>* temp = node->getRightNode();
traverse(temp, data);
node->setRightNode(temp);
}
return;
}
You're declaring a template, but are not actually instantiating it. C++/CLI templates are just like C++ templates - if you don't instantiate them, they just don't exist outside of the compilation unit.
You're looking for generics here (yes, C++/CLI has both templates and generics). And here's how you declare a generic in C++/CLI:
generic<class Data>
public ref class BinaryTreeWrapperClass
{
// ...
}
But you'll get stuck at this point for several reasons.
First, I'll include the parts which are OK:
generic<class Data>
public ref class BinaryTreeWrapperClass
{
public:
BinaryTreeWrapperClass(){ tree = new BinaryTree(); }
~BinaryTreeWrapperClass(){ this->!BinaryTreeWrapperClass(); }
!BinaryTreeWrapperClass(){ delete tree; }
private:
BinaryTree* tree;
};
You've got this right.
Next, let's look at:
std::string wrapperToString()
{
return tree->toString();
}
That's no good, since you're returning an std::string - you don't want to use that from C#, so let's return a System::String^ instead (using marshal_as):
#include <msclr/marshal_cppstd.h>
System::String^ wrapperToString()
{
return msclr::interop::marshal_as<System::String^>(tree->toString());
}
Here, that's much better for use in C#.
And finally, there's this:
void wrapperAddNode(Data)
{
tree->addNode(Data);
}
See... here you'll have to do some real interop. You want to pass a managed object to a native one for storage. The GC will get in your way.
The GC is allowed to relocate any managed object (move it to another memory location), but your native code is clueless about this. You'll need to pin the object so that the GC won't move it.
There are several ways to do this, and I don't know what BinaryTree::addNode looks like, but I'll just suppose it's BinaryTree::addNode(void*).
For long-term object pinning, you can use a GCHandle.
The full code looks like this:
generic<class Data>
public ref class BinaryTreeWrapperClass
{
public:
BinaryTreeWrapperClass()
{
tree = new BinaryTree();
nodeHandles = gcnew System::Collections::Generic::List<System::Runtime::InteropServices::GCHandle>();
}
~BinaryTreeWrapperClass(){ this->!BinaryTreeWrapperClass(); }
!BinaryTreeWrapperClass()
{
delete tree;
for each (auto handle in nodeHandles)
handle.Free();
}
void wrapperAddNode(Data data)
{
auto handle = System::Runtime::InteropServices::GCHandle::Alloc(
safe_cast<System::Object^>(data),
System::Runtime::InteropServices::GCHandleType::Pinned);
nodeHandles->Add(handle);
tree->addNode(handle.AddrOfPinnedObject().ToPointer());
}
System::String^ wrapperToString()
{
return msclr::interop::marshal_as<System::String^>(tree->toString());
}
private:
BinaryTree* tree;
System::Collections::Generic::List<System::Runtime::InteropServices::GCHandle>^ nodeHandles;
};
This allocates a GCHandle for each node, and stores it in a list in order to free it later. Freeing a pinned handle releases a reference to the object (so it becomes collectable if nothing else references it) as well as its pinned status.
After digging around I think I found the answer, although not definitively, so if anyone can chime in I would appreciate it. (Apologies in advance for any misuse of vocabulary, but I think I can get the idea across).
It seems the problem lies in the fundamental difference between templates in native C++ and generics. Templates are instantiated at compilation, and are considered a type. They cannot be changed at runtime, whereas generics can. I don't think there is an elegant way to solve that.
At least I accomplished one goal of my project, which was learning as much as I can haha. I was able to get c++/cli wrappers working for things without templates, and if I choose a type for the template before building the .dll (see above)
If anyone else has an idea please let me know.

Assigning value to class method with parameters in C#

I'm working in an assembly in C# that will replace an old DLL registered as COM. That old DLL allowed COM enabled applications (like VB or Perl) or do things like in the following VBS example:
dim All_Domains
set All_Domains = WScript.CreateObject("MailServerX.LocalDomains")
dim Specific_Domain
set Specific_Domain = All_Domains.Items(3)
dim Domain_Aliases
set Domain_Aliases = WScript.CreateObject("MailServerX.Lines")
Domain_Aliases.Add "one.com"
Domain_Aliases.Add "two.com"
Specific_Domain.Domain_Aliases = Domain_Aliases
All_Domains.Items(3) = Specific_Domain
As you can see in the last line, the property/method LocalDomains.Items is being assigned while passing the parameter "3".
I need to maintain the same interface in the new assembly in order to keep compatibility with all existing scripts that access the old DLL. I have this (very summarized) C# class:
public class LocalDomains
{
private List<LocalDomain> itemsList = new List<LocalDomain>();
# Assume the list is now loaded
public LocalDomain Items(int index)
{
return itemsList[index];
}
}
How can I write the method Items in the class LocalDomains so it can not only return a value from the list, but also receive a value assignment so it can do some processing with itemsList[index] including assigning the new value to it?
By that I mean, keeping the last line of my first code block valid with the new code.
Thanks in advance for any advice!

ValueType to IntPtr to use during application lifetime

Lets say we have a native library that works with data like this:
double *pointer = &globalValue;
SetAddress(pointer);
//Then we can change value and write it to disk
globalValue = 5.0;
FlushValues(); // this function writes all values
// registered by SetAddress(..) functions
... //Then we change our value (or values)
FlushValues(); //and do write any time we need
Now I have to interop it to C#. I would like to avoid using unsafe... but... I dont know =)
So on C# side we will have some class wich fields we will write. And we can do writing like:
public class Data
{
[Serializable] <-- somehow we mark what we are going to write
double myValue;
...
[Serializable]
double myValueN;
}
public class LibraryInterop
{
IntPtr[] pointers; //Pointers that will go to SetAddressMethod
...
public void RegisterObject(Data data)
{
... //With reflection we look for [Serializable] values
//create a pointer for each and some kind of BindingInfo
//that knows connection of pointers[i] to data.myValueN
//Then add this pointers to C++ library
foreach(IntPtr pointer in pointers) { SetAddress(pointer);}
}
public void Flush()
{
//Loop through all added pointers
for(int i=0; i<pointers.Length; i++)
{
double value = ... //With reflections and BindingInfo we find data.myValueN
// that corresponds to pointers[i]
// then we write this value to memory of IntPtr
// we have to brake value to bytes and write it one by one to memory
// we could use BitConverter.DoubleToInt64Bits() but double - is just
// an example, so in general case we will use GetBytes
int offset = 0;
foreach(byte b in BitConverter.GetBytes(value))
{
Marshal.WriteByte(pointer[i],offset,byte);
offset++;
}
}
//Save values of IntPtr to disk
FlushValues();
}
}
Then the user code looks like this then
var library = new LibraryInterop();
var data1 = new Data();
var data2 = new AnotherData();
library.RegisterObject(data1);
library.RegisterObject(data2);
...//change data
library.Flush();
...//change data
library.Flush();
...//change data
library.Flush();
So in C++ we have a very neat structure. We have pointers, we fill data behind this pointers and on FlushValues all this values are writed.
C# part cannot have SetAddress(ref double value). Since address may change, we have to pin pointers - use unsafe+fixed or IntPtr, and have SO many headache.
On the other hand, we can have "managed pointers" by boxing|unboxing data.myValue to Object. So if it would be possible to somehow bind this ValueType data.myValue to IntPtr without this coping and reflection - it would be much much neater.
Is it possible to do this interop and have less ugly and slow C# part then the one I listed here?
(There are some major downsides to this...)
You can use GCHandle.Alloc(data1, GCHandleType.Pinned) to "pin" an object, and then get an IntPtr from GCHandle.AddrOfPinnedObject. If you do this, you'll be able to pass this IntPtr to your native code, which should work as expected.
However, this is going to cause a lot of issues in terms of undermining the efficiency of the garbage collector. If you decide to do something like this, I'd recommend allocating all of the objects very early on in your program (potentially immediately after calling a GC.Collect(), which is something I really don't normally recommend), and leaving them alone for the lifetime of your program. This would (slightly) mitigate the GC issues, as it'd allocate them into the "best" possible spot early on, and leave them where they'll only be touched in Gen2 collections.

Categories

Resources