Author Topic: Expose ARX Function to .NET  (Read 31538 times)

0 Members and 1 Guest are viewing this topic.

TR

  • Guest
Expose ARX Function to .NET
« on: August 23, 2007, 01:27:09 PM »
Could anyone give me a pointer on how to to write an ARX wrapper around AcEdCommandStack::addCommand() and expose it .NET?

Is this even possible?

beck's bolero

  • Retired
  • Needs a day job
  • Posts: 7672
  • AKA Daniel
Re: Expose ARX Function to .NET
« Reply #1 on: August 23, 2007, 02:17:29 PM »
Sure, it’s possible and it might already be done for you (at least by example)
In reflector->acmgd do a search for AddCommand and you should find AcMgCommandClass.AddCommand. 
To make the code more readable, you might want to get the C++/CLI plug-in for reflector

Dan
Retired

TR

  • Guest
Re: Expose ARX Function to .NET
« Reply #2 on: August 23, 2007, 04:33:09 PM »
Cant' find it in the 2008 version of acmgd, I can find "AcMgCommandClass" but not the AddCommand method.

MickD

  • King Gator
  • Posts: 3520
  • (x-in)->[process]->(y-out)
Re: Expose ARX Function to .NET
« Reply #3 on: August 23, 2007, 04:42:44 PM »
what you might need to do is write a C++/cli dll class that wraps the native 'add command' function, I have vs2002 loaded on my lap top, I'll see if I can knock something up later or over the weekend if you like, you should then be able to ref it into your project and call it passing the params required for the command...hopefully, the callbacks may be a bit tricky!
« Last Edit: August 23, 2007, 04:44:36 PM by MickD »
Forth is like the Tao: it is a Way, and is realized when followed.
Its fragility is its strength; its simplicity is its direction - Michael Ham

"The E in Javascript stands for 'easy'." Florin Pop tweet

TR

  • Guest
Re: Expose ARX Function to .NET
« Reply #4 on: August 23, 2007, 04:49:38 PM »
I'll take any help I can get. I'm not going to pretend I know C++ well enough to do any ARX stuff.

MickD

  • King Gator
  • Posts: 3520
  • (x-in)->[process]->(y-out)
Re: Expose ARX Function to .NET
« Reply #5 on: August 23, 2007, 04:55:37 PM »
No prob's, if it works you won't have to ;)

On another note (assuming you want to plug python into acad) I have been doing quite a bit with embedding the python interpreter into an application and have it down pretty good. The downside is it would probably be easier to just write wrappers for the functions you need rather than swig the whole lib (same as com has done basically), a lot of work!
If you're interested I could get something going open source and maybe some members or others can contribute where they can in writing the wrappers. It works quite good but it wouldn't be as rich as the .net lib's you're working with now though.
Forth is like the Tao: it is a Way, and is realized when followed.
Its fragility is its strength; its simplicity is its direction - Michael Ham

"The E in Javascript stands for 'easy'." Florin Pop tweet

TR

  • Guest
Re: Expose ARX Function to .NET
« Reply #6 on: August 23, 2007, 05:09:47 PM »
I have been exchanging emails with Kean Walmsley trying to get some kind of functionality going that would allow me to register AutoCAD command line callable commands from IronPython. Basically he told me that I'd have to write a wrapper to the ARX function and expose it to .NET. If I can get that going then I have reached the last step required before I can proceed with my rewrite of PyAcad.NET. Registering commands was the last hurdle, and if I can do that than I should be able to provide a pretty solid development platform for AutoCAD via IronPython.

I like your idea, and it seems like it would be similiar to PyAcad but I'd prefer to keep going with IronPython instead of using the CPython engine. Using IronPython it is fairly simple to manipulate AutoCAD via the .NET API. I'm also a huge fan of using .NET winforms in IronPython.

MickD

  • King Gator
  • Posts: 3520
  • (x-in)->[process]->(y-out)
Re: Expose ARX Function to .NET
« Reply #7 on: August 23, 2007, 05:22:48 PM »
No prob's and it would be easier. I'll load vs onto this machine shortly, I have a bit of spare time later and see what we can do.
Forth is like the Tao: it is a Way, and is realized when followed.
Its fragility is its strength; its simplicity is its direction - Michael Ham

"The E in Javascript stands for 'easy'." Florin Pop tweet

TR

  • Guest
Re: Expose ARX Function to .NET
« Reply #8 on: August 23, 2007, 05:30:42 PM »
Sounds great. What version of AutoCAD are you running these days? Also what version of MS C++ are you going to be using? The C++ 2005 Express Edition or VS.NET 2002?

MickD

  • King Gator
  • Posts: 3520
  • (x-in)->[process]->(y-out)
Re: Expose ARX Function to .NET
« Reply #9 on: August 23, 2007, 05:39:54 PM »
What I'll do is set up a vs2002 project for AC2006, once the framework is up it wouldn't be hard to convert to a later solution.
I have both VS on my other machine but it's 'in the shop' at the moment with mb prob's, should have it going next week I hope.

WRT pyacad, yes it is very similar only he is using ads functions for entity creation. I would start form scratch though if I was to do it and wrap the arx api. I still might yet, Bricscad is pushing really hard to make the new DRX sdk compatible with existing ARX so it may be worth it. The new BricsCAD also uses wxWidgets as its gui and there should be a native Linux version around the corner!
But that's another story, let's see if we can get this going :)
Forth is like the Tao: it is a Way, and is realized when followed.
Its fragility is its strength; its simplicity is its direction - Michael Ham

"The E in Javascript stands for 'easy'." Florin Pop tweet

beck's bolero

  • Retired
  • Needs a day job
  • Posts: 7672
  • AKA Daniel
Re: Expose ARX Function to .NET
« Reply #10 on: August 23, 2007, 10:29:02 PM »
Cant' find it in the 2008 version of acmgd, I can find "AcMgCommandClass" but not the AddCommand method.

It’s is in 2008 as well,

One of the arguments that AcEdCommandStack::addCommand() needs,
is the memory address of the method that gets invoked(your function).This is the signature from the docs

Code: [Select]
addCommand(
const ACHAR * cmdGroupName,
const ACHAR * cmdGlobalName,
const ACHAR * cmdLocalName,
Adesk::Int32 commandFlags,
AcRxFunctionPtr FunctionAddr, /* <<<<<------------ this one */
AcEdUIContext * UIContext = NULL,
int fcode = -1,
HINSTANCE hResourceHandle = NULL,
AcEdCommand** cmdPtrRet = NULL) ;

The wrapped version uses a reference(MethodInfo) to the method that gets invoked

Code: [Select]
void AcMgCommandClass.AddCommand(const const AcMgCommandClass* local1, ICommandLineCallable^ ca, MethodInfo^ mi)
The methods get invoked through one of the helper functions AcMgCommandClass.Invoke or AcMgCommandClass.InvokeWorker, both expect a Reflection.MethodInfo argument.

So my question is, which one of the two arguments would you like to use, Reflection.MethodInfo or AcRxFunctionPtr?

If it’s Reflection.MethodInfo, I already have code that we can use to cook up your command loader.
Retired

TR

  • Guest
Re: Expose ARX Function to .NET
« Reply #11 on: August 23, 2007, 10:52:53 PM »
Maybe it's the couple of beers I have in me but I'm pretty sure you're talking way over my head. If you already have the code using MethodInfo ready then I'll give that a shot. I'm not sure if you can reflect on IronPython compiled code though.

MickD

  • King Gator
  • Posts: 3520
  • (x-in)->[process]->(y-out)
Re: Expose ARX Function to .NET
« Reply #12 on: August 23, 2007, 11:11:46 PM »
this is what I have so far just wrapping the acedRegCmds() that does the hard work for you -

Code: [Select]
void IronPy::CommandReg::IronPyCommandReg::RegisterCommand(String* cmd_group,
   String* cmd_global_name,
   String* cmd_local_name,
   System::Int32 cmd_flags,
   System::IntPtr pyFunction)
{
acedRegCmds->addCommand(StringToCIF(cmd_group),
StringToCIF(cmd_global_name), StringToCIF(cmd_local_name),
(unsigned int)cmd_flags,
/**** This is the bit that needs work maybe!, we have to marshal a delegate/method
for acad to call when the registered command is invoked ------------------*/
(AcRxFunctionPtr)(void*)Marshal::GetUnmanagedThunkForManagedMethodPtr(pyFunction,0,0));
}

this compiles and the dll is attached, ref in the dll into your project and see how it goes. You may need to use the command flags, these have been made visible to the managed wrapper for convenience.
Let me know how it goes...if it does at all, it's just a quick hack to get something to compile and work with.
Forth is like the Tao: it is a Way, and is realized when followed.
Its fragility is its strength; its simplicity is its direction - Michael Ham

"The E in Javascript stands for 'easy'." Florin Pop tweet

MickD

  • King Gator
  • Posts: 3520
  • (x-in)->[process]->(y-out)
Re: Expose ARX Function to .NET
« Reply #13 on: August 23, 2007, 11:18:07 PM »
Just thinking about it, maybe the functionptr needs 'pinning' so it doesn't get GC'd?? Oh well, we'll find out I guess :)

BTW, I tried to add the solution but it was over 9.5 meg zipped!! the project file is over 32meg in release build and cleaned!  :-o

That's pretty big for a one function wrapper  :?
Forth is like the Tao: it is a Way, and is realized when followed.
Its fragility is its strength; its simplicity is its direction - Michael Ham

"The E in Javascript stands for 'easy'." Florin Pop tweet

MickD

  • King Gator
  • Posts: 3520
  • (x-in)->[process]->(y-out)
Re: Expose ARX Function to .NET
« Reply #14 on: August 24, 2007, 12:03:18 AM »
Here's the source of the only two files and code I added to a standard mixed arx/net application wizard solution in VS2002. If you have any trouble creating the solution just holler.

The header file
Code: [Select]
// IronPyCommandReg.h
// Created: 24th Aug. 2007
// By: Mick Duprez DCS Pty Ltd.
// Usage:
// Used to register commands from IronPython scripts using
// the .net wrapper api for AutoCAD.

#pragma once

#include "stdafx.h"
using namespace System ;
using namespace Autodesk::AutoCAD::Geometry ;
using namespace Autodesk::AutoCAD::DatabaseServices ;

namespace IronPy {
namespace CommandReg {
public __gc class IronPyCommandReg
{
public:
IronPyCommandReg(void);
~IronPyCommandReg(void);
static void RegisterCommand(String* cmd_group,
String* cmd_global_name,
String* cmd_local_name,
System::Int32 cmd_flags,
System::IntPtr pyFunction);
// command flag def's exposed to .net, may not be required???
static int CMD_MODAL = ACRX_CMD_MODAL;
static int CMD_TRANSPARENT = ACRX_CMD_TRANSPARENT;
static int CMD_USEPICKSET = ACRX_CMD_USEPICKSET ;
static int CMD_REDRAW = ACRX_CMD_REDRAW;
static int CMD_NOPERSPECTIVE = ACRX_CMD_NOPERSPECTIVE ; 
static int CMD_NOMULTIPLE = ACRX_CMD_NOMULTIPLE;   
static int CMD_NOTILEMODE = ACRX_CMD_NOTILEMODE ;
static int CMD_NOPAPERSPACE = ACRX_CMD_NOPAPERSPACE ;
static int CMD_PLOTONLY = ACRX_CMD_PLOTONLY ;
static int CMD_NOOEM = ACRX_CMD_NOOEM;
static int CMD_UNDEFINED = ACRX_CMD_UNDEFINED;
static int CMD_INPROGRESS = ACRX_CMD_INPROGRESS;
static int CMD_DEFUN = ACRX_CMD_DEFUN ;
static int CMD_NONEWSTACK = ACRX_CMD_NONEWSTACK ;
static int CMD_NOINTERNALLOCK = ACRX_CMD_NOINTERNALLOCK;
static int CMD_DOCREADLOCK = ACRX_CMD_DOCREADLOCK;
static int CMD_DOCEXCLUSIVELOCK = ACRX_CMD_DOCEXCLUSIVELOCK;
static int CMD_INTERRUPTIBLE = ACRX_CMD_INTERRUPTIBLE;
static int CMD_NOHISTORY = ACRX_CMD_NOHISTORY ;
static int CMD_NO_UNDO_MARKER = ACRX_CMD_NO_UNDO_MARKER;
static int CMD_NOBEDIT = ACRX_CMD_NOBEDIT;
};
}
}

The cpp file
Code: [Select]
// IronPyCommandReg.cpp
// Created: 24th Aug. 2007
// By: Mick Duprez DCS Pty Ltd.
// Usage:
// Used to register commands from IronPython scripts using
// the .net wrapper api for AutoCAD.

#include "StdAfx.h"
#include "IronPyCommandReg.h"

//-----------------------------------------------------------------------------
//- Template class that wraps GCHandle from mscorlib.dll
#include <gcroot.h>

//-----------------------------------------------------------------------------
//- autodesk interop header, to aid converting between unmanaged ObjectARX and managed ObjectARX.NET
#include "mgdinterop.h"
using namespace System::Runtime::InteropServices;

IronPy::CommandReg::IronPyCommandReg::IronPyCommandReg(void)
{
}

IronPy::CommandReg::IronPyCommandReg::~IronPyCommandReg(void)
{
}

void IronPy::CommandReg::IronPyCommandReg::RegisterCommand(String* cmd_group,
String* cmd_global_name,
String* cmd_local_name,
System::Int32 cmd_flags,
System::IntPtr pyFunction)
{
acedRegCmds->addCommand(StringToCIF(cmd_group),
StringToCIF(cmd_global_name), StringToCIF(cmd_local_name),
(unsigned int)cmd_flags,
/**** This is the bit that needs work maybe!, we have to marshal a delegate/method
for acad to call when the registered command is invoked ------------------*/
(AcRxFunctionPtr)(void*)Marshal::GetUnmanagedThunkForManagedMethodPtr(pyFunction,0,0));
}

<edit> fixed formatting
« Last Edit: August 24, 2007, 12:05:37 AM by MickD »
Forth is like the Tao: it is a Way, and is realized when followed.
Its fragility is its strength; its simplicity is its direction - Michael Ham

"The E in Javascript stands for 'easy'." Florin Pop tweet