   Writing a DLL in C++ to be used in C#.NET
  I'm starting out in C++, but I'm a solid coder in C#.NET and C (although, of these two, I'm infinitely better at C). So out of curiosity, I thought I'd write a small library of general data structures that I use fairly often. I then thought I'd import this library (by adding a reference to it) into a C#.NET project in order to test the library (.NET is very nice for RAD, IMMO). I've only completed the linked-list class, but I wanted to test that I hadn't screwed up majorly before I got too far in.Anyway, when I go to add a reference to the DLL in C#.NET (I'm using the C++ Express Edition and C#.NET Express Edition IDEs), I get the following error:"A reference to 'C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\DataStructures\release\DataStructures.dll' could not be added. Please make sure that the file is accessible, and that it is a valid assembly or COM component."Here's the code:LinkedList.h#pragma once/* class LinkedListDefines a singly-linked list object */class LinkedList{public:LinkedList(void);// Constructor~LinkedList(void);// Destructor/* struct nodeDefines a node in the list */struct node{unsigned long data;// While this is only an unsigned long, we can// store anything up to (and including) a// 32-bit integer in here. Assuming// sizeof(float) == 4, then even floating point// numbers can be stored in here. Also, in the// event of a larger data structure needing to// be stored, a pointer can be be stored here// to point to anything. Also, note that all of// the previous assumes a 32-bit architecture.node *next;// Pointer to next node in list};unsigned long findEnd(void);// Finds the last node in the list, returning a// index into the list as type unsigned long.unsigned long getNode(unsigned long);// Finds the node requested, given as a 0-based// index into the list. Returns the value of// the 'data' field of that node.void appendNode(unsigned long, unsigned long);// Appends a node to the given index (i.e. the// new node will be added after the given node)void prependNode(unsigned long, unsigned long);// Prepends a node to the given index (i.e. the// new node will be added before the given// node)void deleteNode(unsigned long);// Deletes the node at the given indexprivate:node *head;// Points to the first node in the linked-list,// as without this pointer we'll lose the list.node* getNodePtr(unsigned long);// Returns a pointer to the requested node// index - for use internally. We don't make// this externally available because then it// becomes very easy for a bug to allow// pointers to arbitrary locations - undefined// behaviour.};LinkedList.cpp#include "LinkedList.h"#include &lt;cstddef&gt;LinkedList::LinkedList(void){/* LinkedList::LinkedList(void)Sets the list-pointer to NULL. That's all the initalisation that we need. */head = NULL;}LinkedList::~LinkedList(void){/* LinkedList::~LinkedList(void)Deallocates the memory (on the heap) for the linked list. Finds the end node, andcycles backwards from here, 'delete'ing each node on the way. */}unsigned long LinkedList::findEnd(void){/* unsigned long LinkedList::findEnd(void)Finds the last node in the list and returns an unsigned long index to this node.Returns 0xFFFFFFFF if the list has not yet been created. */node *conductor = head;unsigned long index = 0;if(!conductor){/* If the head pointer is NULL then either something is quite seriously wrong, or   the list hasn't been created yet - either way, return maximum value of a 32-bit   integer. */return 0xFFFFFFFF;}while(conductor-&gt;next){/* While the next node pointer is not NULL (indicating that there *is* a next node),   just increment the conductor by one node, and increment our count */index++;conductor = conductor-&gt;next;}return index;}unsigned long LinkedList::getNode(unsigned long index){/* void LinkedList::findNode(unsigned long)Finds the requested node (given as a 0-based index into the list), and returnsthe 'data' field of that node. Returns 0 if the node doesn't exist (including ifthe list doesn't exist). */node *conductor = head;if((!conductor) || (index &gt; LinkedList::findEnd())){/* Check if the node doesn't exist, return 0 if so */return 0;}for(unsigned long i = 0; i &lt; index; i++){/* Otherwise, loop through to requested index */conductor = conductor-&gt;next;}return conductor-&gt;data; // And return the requested data}LinkedList::node* LinkedList::getNodePtr(unsigned long index){/* void LinkedList::findNode(unsigned long)Finds the requested node (given as a 0-based index into the list), and returnsa pointer to that node. Returns NULL if the node doesn't exist (including ifthe list doesn't exist). Only for internal use. */node *conductor = head;if((!conductor) || (index &gt; LinkedList::findEnd())){/* Check if the node doesn't exist, return 0 if so */return 0;}for(unsigned long i = 0; i &lt; index; i++){/* Otherwise, loop through to requested index */conductor = conductor-&gt;next;}return conductor; // And return the requested data}void LinkedList::appendNode(unsigned long index, unsigned long data){/* void LinkedList::appendNode(unsigned long)Appends a node with the given 'data' field after the given node index. Thisfunction is not heavily commented because it is relatively simple. */node *iNode = LinkedList::getNodePtr(index);node *nNode = new node;nNode-&gt;data = data;nNode-&gt;next = iNode-&gt;next;iNode-&gt;next = nNode;}void LinkedList::prependNode(unsigned long index, unsigned long data){/* void LinkedList::prependNode(unsigned long)Prepends a node with the given 'data' field before the given node index. */if(index == 0){/* If index = 0 then we can't simply append a node to (index - 1) as we're using   unsigned longs - so we can't store -1. So we need to handle this as a special   case */node *nNode = new node;nNode-&gt;data = data;nNode-&gt;next = head;head = nNode;}else{LinkedList::appendNode(index - 1, data);}}void LinkedList::deleteNode(unsigned long index){/* void LinkedList::deleteNode(unsigned long)Deletes the node at the given index */node *dNode = LinkedList::getNodePtr(index);if(index == 0){/* If we're deleting the head node, we do things a little differently because we   need to update the head pointer */head = dNode-&gt;next;delete dNode;}else{node *pNode = LinkedList::getNodePtr(index - 1);pNode-&gt;next = dNode-&gt;next;delete dNode;}}Now, I'm presuming that there is some sort of stuff I need to add in order to make this DLL compatible with .NET. I know with C, I would need the header file - but I have no idea what to do for .NET. In case it matters, I didn't create this project as a 'CLR Class Library'. I created an empty project, and added this class; then changed the Configuration Type in the project properties from Application (.exe) to Dynamic Library (.dll).So where am I going wrong?EDIT: And if someone could tell me how to reduce those code quotes to the scrollable box, I'd be much obliged. I thought it was the BBCode code tags, but obviously not.[Edited by - TheUnbeliever on June 23, 2006 6:03:45 PM]
Quote:Original post by TheUnbelieverEDIT: And if someone could tell me how to reduce those code quotes to the scrollable box, I'd be much obliged. I thought it was the BBCode code tags, but obviously not.It's [ source ] and [ /source ] (Without the spaces).I've no idea about the main question though, unfortunately (I'm a C++ only person). One thing I noticed is that your DLL doesn't actually export anything (You need to use __declspec(dllexport) - assuming MSVC). You can also use a program (dumpbin.exe?) to list the exports from a DLL or EXE.Also, I'd be a bit surprised about using classes exported from a C++ DLL in a C# app, but again I'm not a C# person. Exporting classes from a DLL is usually something to be avoided, since it's compiler dependant (someone shout at me if I'm wrong)   ;  Gah, this is a lot more complicated than I originally thought then, I guess! :DI'll try your suggestions: the exporting thing. I can now import it into C#.NET Express, having found the Common Language Runtime option in the properties - compiling with /MD /clr lets me import it - but no classes are available. That said, if I need to export things, then that would appear to be sensible...   ;  There are two ways to get a C++ class recognized by C#. The first way is to compile the source as a Manged C++ or C++/CLI assembly. The second way is to create the class as a COM class, which can then be accessed like any other COM class from C#. Both are pretty ugly methods that really have nothing to do with standard C++. If you are trying to learn standard C++ and not one of the managed variants, I highly suggest that you don't try to export a C++ class to .NET as a learning exercise.   ;  Wow, fast reply (faster than it took me to find the same information on Google actually).Thanks for the recommendation, I'll just do this all in C++. That's the point, anyway! Heh. It lets me know what a no-no in future, too. Thanks guys. :)   