Author Topic: Mixed/Managed C++ dll's, passing objects.  (Read 14717 times)

0 Members and 1 Guest are viewing this topic.

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Mixed/Managed C++ dll's, passing objects.
« on: September 13, 2009, 09:34:28 PM »
It's been a while since I played with mixed/managed C++ and I tried to avoid it by using native arx to handle user input etc. but now I need to do things in a better fashion to make my dll more of a library instead of an application addon.

How would 'you' pass objects like acad pointers such as db's, entities and object id arrays.

Any tips/clues/tricks/samples would be great.

Thanks,
Mick.
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.
- J.R.R. Tolkien

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Mixed/Managed C++ dll's, passing objects.
« Reply #1 on: September 13, 2009, 09:41:04 PM »
for example, this compiles -
Code: [Select]
void MyApp::MyLib::Utils::SetDb(Database* db)
{
if(db != NULL)
{
_pDb = (reinterpret_cast<AcDbDatabase*>(&(db)));
}
}

but so does this -
Code: [Select]
void MyApp::MyLib::Utils::SetDb(Database* db)
{
if(db != NULL)
{
_pDb = (reinterpret_cast<AcDbDatabase*>(&(db->UnmanagedObject)));
}
}

and is this way of passing objects the most elegant??
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.
- J.R.R. Tolkien

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8690
  • AKA Daniel
Re: Mixed/Managed C++ dll's, passing objects.
« Reply #2 on: September 13, 2009, 09:52:59 PM »
does this compile?

Code: [Select]
void MyApp::MyLib::Utils::SetDb(Database^ db)
{
  if(db != nullptr)
   {
     _pDb =  reinterpret_cast<AcDbDatabase*>(db->UnmanagedObject);
   }
}

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Mixed/Managed C++ dll's, passing objects.
« Reply #3 on: September 13, 2009, 10:01:00 PM »
hmmm, changed the properties to /clr instead of /clr oldsyntax and I now have a swag of '*' errors in my other code, but that part seemed to compile :)

there's also a swag of deprecated errors, may need to set this one up again from scratch...

Do you have a favourite tutorial on this stuff Daniel I could brush up on?

"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.
- J.R.R. Tolkien

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8690
  • AKA Daniel
Re: Mixed/Managed C++ dll's, passing objects.
« Reply #4 on: September 13, 2009, 10:04:52 PM »
Ah sorry, your using the old syntax. which would you prefer using?
What version of Acad are you targeting?

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Mixed/Managed C++ dll's, passing objects.
« Reply #5 on: September 13, 2009, 10:14:42 PM »
2k7 and up, I guess the newer syntax may make it easier to see what's managed and what's not (??)
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.
- J.R.R. Tolkien

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8690
  • AKA Daniel
Re: Mixed/Managed C++ dll's, passing objects.
« Reply #6 on: September 14, 2009, 02:49:54 AM »
I would use the new coding style. I don't know of too many tutorials covering this,
but I have a lot of samples wrapping native code I can send to you. I'm not sure what your end goal is.
Are you wrapping native stuff?  who is the consumer of your library, .NET or native?



MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Mixed/Managed C++ dll's, passing objects.
« Reply #7 on: September 14, 2009, 05:57:17 AM »
I'm wrapping native stuff for use in .net rather than using P/Invoke. I have my class/s all sorted out for the mixed/managed part, just need to handle the marshalling of the objects to be dealt with between .net and my m/m dll.

I'll pm you with further info Daniel if needed but I thought it would be helpful for others as well. For instance, if you have some methods that would benefit from the use of native code to speed things up this type of back and forth protocol is very handy.

It's been a while and my C++/CLI is very basic and I still suffer from straight C tendencies but I'll have to deal with those if I want to get this done properly :)
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.
- J.R.R. Tolkien

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8690
  • AKA Daniel
Re: Mixed/Managed C++ dll's, passing objects.
« Reply #8 on: September 14, 2009, 08:48:55 AM »
...I thought it would be helpful for others as well..

Absolutely! let me dig for a few samples

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8690
  • AKA Daniel
Re: Mixed/Managed C++ dll's, passing objects.
« Reply #9 on: September 14, 2009, 10:45:06 AM »
Lets where to start, Of course basic types (int, double...) we don't need to anything special.
here are a couple of samples doing strings and arrays, note I prefer using the CString for the strings and Marshal::Copy for arrays

Code: [Select]
static void ArxMdg_doit1(void)
 {
  CString cstr = _T("To Managed and back");
  System::String ^str = gcnew System::String(cstr);
  acutPrintf(CString(str));
 }


 static void ArxMdg_doit2(void)
 {
  TCHAR *tchar = _T("To Managed and back");
  System::String ^str = gcnew System::String(tchar);
  pin_ptr<const TCHAR> pstr = PtrToStringChars(str);
  acutPrintf(pstr);
 }

 static void ArxMdg_doit3(void)
 {
  double npt[3] = {10.0,100.0, 1000.0};
  array<double> ^mpt = gcnew array<double>(3);

  System::Runtime::InteropServices::Marshal::Copy
     ( System::IntPtr(&npt[0]),mpt, 0, mpt->Length);

  acutPrintf(_T("%g, %g ,%g"),mpt[0], mpt[1], mpt[2]);
 }

 static void ArxMdg_doit4(void)
 {
  array<double> ^mpt = gcnew array<double>{10.0,100.0, 1000.0};
  pin_ptr<const double> npt = &mpt[0];
  acutPrintf(_T("%g, %g ,%g"),npt[0], npt[1], npt[2]);
 }

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8690
  • AKA Daniel
Re: Mixed/Managed C++ dll's, passing objects.
« Reply #10 on: September 14, 2009, 11:02:51 AM »
Next, let's play with value classes, objects that we want to use on the stack.

.h
Code: [Select]
#pragma once
#pragma managed(push, off)
class CNative
{
 LONG_PTR m_num;
public:
 CNative(LONG_PTR val);
 ~CNative(void);
  bool isNull(void);
  bool isGreaterThan(LONG_PTR val);
  bool isLuckySeven(void);
  LONG_PTR Num() const { return m_num; }
  void Num(LONG_PTR val) { m_num = val; }
};
#pragma managed(pop)

.cpp
Code: [Select]
#include "StdAfx.h"
#include "Native.h"

CNative::CNative(LONG_PTR val):m_num(val){}
CNative::~CNative(void){}

bool CNative::isNull( void )
{
 return this->m_num == NULL;
}

bool CNative::isGreaterThan( LONG_PTR val )
{
  return this->m_num > val;
}

bool CNative::isLuckySeven( void )
{
 return this->m_num == 7L;
}


now the managed wrapper

.h
Code: [Select]
#pragma once
#include "Native.h"
using namespace System;

#define UNMNGD_CLASS(X) (*reinterpret_cast<CNative*>(&(X)))

namespace ArcMdg
{
 public value class CManaged
 {
  LONG_PTR m_num;

 public:
  CManaged(LONG_PTR val);
  CManaged(const CNative *native);

  bool isGreaterThan(LONG_PTR val)
  {
   return UNMNGD_CLASS(*this).isGreaterThan(val);
  }

  property bool isNull
  {
   bool get() { return UNMNGD_CLASS(*this).isNull() ; }
  }

  property bool isLuckySeven
  {
   bool get() { return UNMNGD_CLASS(*this).isLuckySeven() ; }
  }

  virtual System::String^ ToString() override;
 };
}

.cpp
Code: [Select]
#include "StdAfx.h"
#include "Managed.h"

namespace ArcMdg
{
 CManaged::CManaged(LONG_PTR val) : m_num(val)
 {
 }

 CManaged::CManaged( const CNative *native )
 {
  m_num = native->Num();
 }

 System::String^ CManaged::ToString()
 {
  return String::Format("{0}", m_num);
 }
}

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8690
  • AKA Daniel
Re: Mixed/Managed C++ dll's, passing objects.
« Reply #11 on: September 14, 2009, 11:05:29 AM »
and the test

Code: [Select]
static void ArxMdg_doit5(void)
 {
   CNative n(7);
   ArcMdg::CManaged m(&n);

   if(m.isLuckySeven)
    acutPrintf(_T("\nisLuckySeven"));

    if(m.isGreaterThan(2))
     acutPrintf(_T("\nisGreaterThan 2"));

    acutPrintf(_T("\nand the value is %s "), CString(m.ToString()));
 }


MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Mixed/Managed C++ dll's, passing objects.
« Reply #12 on: September 14, 2009, 05:17:21 PM »
Thanks Dan, let me soak that in for a bit. :)
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.
- J.R.R. Tolkien

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Mixed/Managed C++ dll's, passing objects.
« Reply #13 on: September 14, 2009, 06:54:29 PM »
Ok, I think I've got the managed to native ok, I'm still stuck on the native to managed though, I'd like to pass back managed objects so it's seemless on the .net side.

here's what I have so far but the native to managed methods don't work but should give an idea of what I want to achieve.
Code: [Select]
// mixman.h

#pragma once

using namespace System;
using namespace Autodesk::AutoCAD::ApplicationServices;
using namespace Autodesk::AutoCAD::DatabaseServices;
using namespace Autodesk::AutoCAD::Runtime;

// conversions from managed to native:
#define GETDB(database) (*reinterpret_cast<AcDbDatabase*>(&(database)))
#define GETIDARRAY(objidarray) (*reinterpret_cast<AcDbObjectIdArray*>(&(objidarray)))

// conversion functions from native to managed:
inline Database SetMgdDatabase ( AcDbDatabase* pDb)
{
Database db =  gcnew Database(pDb);
return db;
}
inline ObjectIdCollection SetIdCollection(AcDbObjectIdArray* nativeArray)
{
ObjectIdCollection idcoll = gcnew ObjectIdCollection(nativeArray);
return idcoll;
}
« Last Edit: September 14, 2009, 07:09:57 PM by MickD »
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.
- J.R.R. Tolkien

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8690
  • AKA Daniel
Re: Mixed/Managed C++ dll's, passing objects.
« Reply #14 on: September 14, 2009, 07:23:15 PM »
I think you need to use DisposableWrapper::Create. Most all AutoCAD's managed classes derive from this. 
Try these


Code: [Select]
inline Database^ SetMgdDatabase ( AcDbDatabase* pDb)
{
 return (Database^)DisposableWrapper::
  Create(Database::typeid, IntPtr(pDb),false);
}

inline ObjectIdCollection^ SetIdCollection(AcDbObjectIdArray* nativeArray)
{
 return (ObjectIdCollection^)DisposableWrapper::
  Create(ObjectIdCollection::typeid, IntPtr(nativeArray),false);
}