Author Topic: P/Invoke acedNEntSelPEx, ie select modelspace entity from paperspace  (Read 13117 times)

0 Members and 1 Guest are viewing this topic.

Chumplybum

  • Newt
  • Posts: 97
Hi all,

i know i'm in way over my head, but it seems to be the best way for me to learn anything... anyway, i'm trying to select a modelspace entity from paperspace (without being inside a floating viewport) similar to the way that the linear dimension routine and object selection in the fields dialog works.

so far i've tracked down acedNEntSelPEx, which is apparently an undocumented function that does exactly what i'm after (see http://through-the-interface.typepad.com/through_the_interface/2006/08/selecting_the_e.html, http://discussion.autodesk.com/thread.jspa?messageID=399784). The next thing i found was the entrypoint "?acedNEntSelPEx@@YAHPB_WQAJQANHQAY03NPAPAUresbuf@@IPAH@Z" via Dependency walker... which brings me to the following code, that unfortunately doesn't work :|

'start code

    Public Class PInvoke
        <CommandMethod("Test")> _
        Public Sub Test()

            Dim temp As Object

            ' int     gsmarker = -1;
            Dim gsmarker As Integer = -1
            ' ads_name  ename;
            Dim ename As Object
            ' struct resbuf *rbChain;
            Dim rbChain As ResultBuffer
            ' ads_point  selPt;
            Dim selPt As Object
            ' ads_matrix tranMat;
            Dim tranMat As Object

            temp = acedNEntSelPEx("test", ename, selPt, 0, tranMat, rbChain, 1, -1)

        End Sub


        ''' <summary>
        ''' extern int acedNEntSelPEx (const char *str, ads_name entres, ads_point ptres, int pickflag, ads_matrix xformres, struct resbuf **refstkres, unsigned int uTransSpaceFlag, int* gsmarker);
        '''
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        <System.Security.SuppressUnmanagedCodeSecurity()> _
        <Runtime.InteropServices.DllImport("acad.exe", CallingConvention:=Runtime.InteropServices.CallingConvention.Cdecl, CharSet:=Runtime.InteropServices.CharSet.Unicode, EntryPoint:="?acedNEntSelPEx@@YAHPB_WQAJQANHQAY03NPAPAUresbuf@@IPAH@Z")> _
        Public Shared Function acedNEntSelPEx(ByVal msg As String, ByRef entres As Object, ByRef ptres As Object, ByVal pickflag As Integer, ByVal xformres As Object, ByVal resbuf As Object, ByVal TransSpaceFlag As Integer, ByVal gsmarker As Integer) As Integer
        End Function

    End Class

'endcode


for a while i had the routine prompting to select an object (which crashed, but atleast i was getting somewhere) but now thats not even happening.
can anyone point me in the right direction as to what i'm doing wrong, or have any suggestions of what to try next???

any help would be great


cheers

Mark

Glenn R

  • Guest
Hmmm...from memory, an ads_name is represented in .NET p/invoke stuff as:
Code: [Select]
long [] yourVarNameHere;

However, I would not have thought declaring it as Object would have mattered.
What I suspect is wrong is your translation of this:

resbuf **refstkres

Unfortunately, I don't even have acad installed on this work box, let alone an IDE, so I can't check at this point in time.

Someone else might chime in before I can get to a properly configured box.

Cheers,
Glenn.

Chumplybum

  • Newt
  • Posts: 97
thanks glenn, my conversion from c++ to .net was a guess at best

i have changed some of the arguments to be ByRef and this atleast is now asking me to pick an object (or in case of my previous code 'test' is entered at the command line and ACAD is waiting for me to pick an object) but this crashes autocad as mention in the original post

'start new code
        <System.Security.SuppressUnmanagedCodeSecurity()> _
        <Runtime.InteropServices.DllImport("acad.exe", CallingConvention:=Runtime.InteropServices.CallingConvention.Cdecl, CharSet:=Runtime.InteropServices.CharSet.Unicode, EntryPoint:="?acedNEntSelPEx@@YAHPB_WQAJQANHQAY03NPAPAUresbuf@@IPAH@Z")> _
        Public Shared Function acedNEntSelPEx(ByVal msg As String, ByRef entres As Object, ByRef ptres As Object, ByVal pickflag As Integer, ByRef xformres As Object, ByRef resbuf As ResultBuffer, ByVal TransSpaceFlag As Integer, ByVal gsmarker As Integer) As Integer
        End Function

'end new code


i'll keep plugging away and see what i can come up, who knows, might even learn c++ in the process :-)


cheers

Mark


MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Just a guess but in this function declaration -

<System.Security.SuppressUnmanagedCodeSecurity()> _
        <Runtime.InteropServices.DllImport("acad.exe", CallingConvention:=Runtime.InteropServices.CallingConvention.Cdecl, CharSet:=Runtime.InteropServices.CharSet.Unicode, EntryPoint:="?acedNEntSelPEx@@YAHPB_WQAJQANHQAY03NPAPAUresbuf@@IPAH@Z")> _
        Public Shared Function acedNEntSelPEx(ByVal msg As String, ByRef entres As Object, ByRef ptres As Object, ByVal pickflag As Integer, ByVal xformres As Object, ByVal resbuf As Object, ByVal TransSpaceFlag As Integer, ByVal gsmarker As Integer) As Integer

the items in bold you might want to try ByRef.
ByVal will probably only work with built in types such as ints etc, even the string may need changing to ByRef as a string is not a built in type in C

Might be worth a shot.
"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: 8704
  • AKA Daniel
I tried it a couple of times and it didn’t work, It seems to be blowing up when filling the resultbuffer.

Chumplybum

  • Newt
  • Posts: 97
ok, so i'm a little closer to disassembling the acedNEntSelPEx function... as previous posts have mentioned, its this 'ByRef resbuf As ResultBuffer' thats more than likely causing the crashes, and after reading up on double asterics in c++ (http://bytes.com/forum/thread435304.html), the double asterics is a double pointer, ie a pointer to a memory location which in turn is a pointer to another memory location.

this doesn't really get me much closer to a solution, but i was wondering if anyone knows of a .net (c# or vb) equivalent to a double pointer


cheers

Mark

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
ByRef resbuf As IntPtr??? (if IntPtr's are available to VB)
"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

Chumplybum

  • Newt
  • Posts: 97
thanks mick, vb.net does have the intPtr's but still with the same result, ie ACAD crashes...

i have a feeling that i need to use the system.runtime.interopservices.marshal class but as i know little about c++ and the marshal class i'm just throwing darts in the dark at the moment, although i could be wrong as well.

or it may be the case, that i need to switch to c#... the following is taken from http://dnjonline.com/article.aspx?ID=mar05_vbvscsharp

Better Safe Than Sorry?
More significantly, C# gives programmers the option of using pointers. Pointers are variables which refer or ‘point’ to specific locations in memory. They are so widely used in C and C++ that many programmers may find it difficult to conceive of programming without them.
      The trouble with pointers is that they are inherently unsafe. They make it easy to corrupt memory, overwrite data and crash your applications. C# discourages the use of pointers but does permit their use within blocks of code marked with the unsafe directive.
      Code marked with the unsafe directive is ‘unmanaged’. This means that it is run as native machine code without benefiting from the services of the CLR. The use of pointers may be useful for certain special programming tasks that require the direct manipulation of memory or interaction with COM. In most cases, however, the .NET Framework provides all the services your program requires without having to resort to the hazards of unmanaged code. VB.NET is more restrictive than C# in this respect. It does not permit the use of unsafe code under any circumstances.
      Even so, it is not quite true to say that pointers can never be used. VB.NET provides access to pointers using the IntPtr type. This allows a limited range of pointer operations. For example, an IntPtr variable can be used to store a handle to a file or a window.
      If you prefer to program in VB.NET but have an occasional need to use pointers, you can, of course, add a C# or even a C++ project to your solution in order to do all the ‘dirty work’.




MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
The easiest option (unless you work out the marshaling) is to write a managed wrapper in C++, I don't have time at present - maybe next week though.

So, do you just want the object id of the ent? That would be the easiest to pass back, from there you can get other data as required. If you want the pick point also, that's a bit harder.
"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

Glenn R

  • Guest
Don't know if this will help any, but DisposableWrapper.Create for the resultbuffer?

Search on this forum - I think Dan has posted some examples of it.

Cheers,
Glenn.

Chumplybum

  • Newt
  • Posts: 97
Re: P/Invoke acedNEntSelPEx, ie select modelspace entity from paperspace
« Reply #10 on: May 08, 2008, 02:25:43 AM »
at this stage just i was just looking at getting ObjectID back, i can't see any need for the point picked, at this stage...

i have a few other ideas of how to get the objectID, but wouldn't mind getting acedNEntSelPEx sorted for future reference.
idea1: if in paperspace change to a floating viewport, select the object, then return to paperspace
idea2: pick a modelspace entity from paperspace, if nothing is selected, transform the point picked (possibly even the apperture box size) to modelspace and perform a selection based on either the point or the apperture size.

but to me, these ideas seem like a fudge at best



It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8704
  • AKA Daniel
Re: P/Invoke acedNEntSelPEx, ie select modelspace entity from paperspace
« Reply #11 on: May 08, 2008, 09:32:48 AM »

I’ll wrap acedNEntSelPEx for you this weekend, if you can wait.  :-)

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8704
  • AKA Daniel
Re: P/Invoke acedNEntSelPEx, ie select modelspace entity from paperspace
« Reply #12 on: May 08, 2008, 12:48:53 PM »
Had a little free time, the Resultbuffer was still being a pain in the arse, so I nixed it.
Anyway here is the C++ code, followed by C# code, then a little picky, then attached is the solution
and compiled dll (in the release folder) let me know if it works

Code: [Select]
   bool Utilities::NEntSelPEx
    (
    String^ prompt,
    [Out]ObjectId %objectid,
    Point3d %point,
    int pickflag,
    [Out]Matrix3d %matrix,
    [Out]ResultBuffer ^%buffer,
    unsigned int transSpaceFlag,
    int %gsmarker
    )
  {
    CString _prompt(prompt);
    ads_name _name;
    ads_point _point = {point.X,point.Y,point.Z};
    ads_matrix _matrix;
    resbuf *_buffer = NULL;
    int *_gsmarker;

    if(acedNEntSelPEx(_prompt,_name,_point,pickflag,_matrix,&_buffer,transSpaceFlag,_gsmarker) == RTNORM)
    {

      objectid = ObjectId(_name[0]);
      point = Point3d(_point[0],_point[1],_point[2]);

      array<double> ^_tmparray = gcnew array<double>(16)
      {
        _matrix[0][0],_matrix[0][1],_matrix[0][2],_matrix[0][3],
        _matrix[1][0],_matrix[1][1],_matrix[1][2],_matrix[1][3],
        _matrix[2][0],_matrix[2][1],_matrix[2][2],_matrix[2][3],
        _matrix[3][0],_matrix[3][1],_matrix[3][2],_matrix[3][3]
      };

      matrix = Matrix3d(_tmparray);

      if(_buffer != NULL)
      {
        {
          //stupid prick resbuf !@#@!#$%!#%
          //IntPtr ptr(_buffer);
          //buffer = (ResultBuffer^)DisposableWrapper::Create(ResultBuffer::typeid, ptr, true);
        }
        acutRelRb(_buffer);
      }
      gsmarker = *_gsmarker;
      return true;
    }
    else
    {
      return false;
    }
  }
.
Code: [Select]
   [CommandMethod("doit")]
    public void doit()
    {
      Editor ed = AcAp.Application.DocumentManager.MdiActiveDocument.Editor;


      string prompt = "select object";
      ObjectId id;
      Point3d point;
      int pickflag = 0;
      Matrix3d matrix;
      ResultBuffer buff;
      uint transSpaceFlag = Convert.ToUInt32(AcAp.Application.GetSystemVariable("CVPORT"));
      int marker = -1;

      try
      {
        AcMgdWrprs.Utilities.NEntSelPEx(prompt,
                                        out id,
                                        ref point,
                                        pickflag,
                                        out matrix,
                                        out buff,
                                        transSpaceFlag,
                                        ref marker);
        //
        ed.WriteMessage("\nObjectId: " + id.ToString());
        ed.WriteMessage("\nPoint3d:  " + point.ToString());
        ed.WriteMessage("\nMatrix3d: " + matrix.ToString());
        ed.WriteMessage("\nmarker:   " + marker.ToString());
      }
      catch (System.Exception ex)
      {
        ed.WriteMessage(ex.Message);
        ed.WriteMessage(ex.StackTrace);
      }
    }


MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: P/Invoke acedNEntSelPEx, ie select modelspace entity from paperspace
« Reply #13 on: May 08, 2008, 06:11:28 PM »
resbuf **_buffer perhaps???

Nice work as always Daniel.

<edit> or even IntPtr ptr(*_buffer);??
« Last Edit: May 08, 2008, 07:26:33 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

Chumplybum

  • Newt
  • Posts: 97
Re: P/Invoke acedNEntSelPEx, ie select modelspace entity from paperspace
« Reply #14 on: May 08, 2008, 07:08:02 PM »
thanks a million Daniel... and to think that i was going to try and do that :-)

for anyone who is interested in the vb code...
Code: [Select]

        Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor


        Dim prompt As String = "select object"
        Dim id As AADS.ObjectId
        Dim point As AAG.Point3d
        Dim pickflag As Integer = 0
        Dim matrix As AAG.Matrix3d
        Dim buff As AADS.ResultBuffer
        Dim transSpaceFlag As UInteger = Convert.ToUInt32(Application.GetSystemVariable("CVPORT"))
        Dim marker As Integer = -1

        Try
            AcMgdWrprs.Utilities.NEntSelPEx(prompt, id, point, pickflag, matrix, buff, _
            transSpaceFlag, marker)
            '
            ed.WriteMessage("" & Chr(10) & "ObjectId: " + id.ToString())
            ed.WriteMessage("" & Chr(10) & "Point3d: " + point.ToString())
            ed.WriteMessage("" & Chr(10) & "Matrix3d: " + matrix.ToString())
            ed.WriteMessage("" & Chr(10) & "marker: " + marker.ToString())
        Catch ex As System.Exception
            ed.WriteMessage(ex.Message)
            ed.WriteMessage(ex.StackTrace)
        End Try



every now and then i get the following error:
Attempted to read or write protected memory. This is
often an indication that other memory is corrupt.   at acedNEntSelPEx(Char* ,
Int32* , Double* , Int32 , $ArrayType$$$BY03N* , resbuf** , UInt32 , Int32* )
   at AcMgdWrprs.Utilities.NEntSelPEx(String prompt, ObjectId& objectid,
Point3d& point, Int32 pickflag, Matrix3d& matrix, ResultBuffer& buffer, UInt32
transSpaceFlag, Int32& gsmarker)


i tried giving the byref values (objectid, matrix, buffer, gsmarker) a value of nothing (null), but that hasn't fixed the error