Author Topic: NET P/INVOKE Routines  (Read 16546 times)

0 Members and 1 Guest are viewing this topic.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11653
  • class keyThumper<T>:ILazy<T>
NET P/INVOKE Routines
« on: January 27, 2010, 06:49:43 PM »
LIBRARY THREAD for  AutoCAD P/INVOKE
 Members are encouraged to post any functions, methods, snips regarding
AutoCAD P/INVOKE in .NET : C# ,  VB , F# , Python , etc

Feel free to include comments, descriptive notes, limitations,  and images to document your post.

Please post questions in a regular thread.


added Note:
Be aware that the p/invoke methods sometimes have :
different entry points in different ACAD Versions due to name mangling
and
different entry point signatures dependant on x32 or x64 platforms.
« Last Edit: January 29, 2010, 05:45:35 PM by Kerry Brown »
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

--> Donate to theSwamp<--

MickD

  • Gator
  • Posts: 3232
  • !false...it's funny 'cause it's true!
Re: NET P/INVOKE Routines
« Reply #1 on: January 27, 2010, 07:40:27 PM »
Here's a few to kick off with, some or all may be obselete now but they may be of use just for reference.

Code: [Select]

        [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl,
             EntryPoint = "?acedSetCurrentView@@YA?AW4ErrorStatus@Acad@@PAVAcDbViewTableRecord@@PAVAcDbViewport@@@Z")]
        private static extern int acedSetCurrentView(IntPtr pVtr, /*IntPtr.Zero*/IntPtr pVP);

        [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedTrans")]
        static extern int acedTrans(double[] point, IntPtr fromRb, IntPtr toRb, int disp, double[] result);

        [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl,
             EntryPoint = "?acedVports2VportTableRecords@@YA?AW4ErrorStatus@Acad@@XZ")]
        private static extern bool acedVports2VportTableRecords();

        [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl,
             EntryPoint = "?acedVportTableRecords2Vports@@YA?AW4ErrorStatus@Acad@@XZ")]
        private static extern bool acedVportTableRecords2Vports();
A programmer's wife tells him, "Run to the store and pick up a loaf of bread. If they have eggs, get a dozen."

The programmer comes home with 12 loaves of bread.

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6905
  • AKA Daniel
Re: NET P/INVOKE Routines
« Reply #2 on: January 27, 2010, 08:50:15 PM »
Since p/invoke methods sometimes have different entry points due to name mangling, maybe it would be a good idea to specify the Acad version.

fixo

  • Guest
Re: NET P/INVOKE Routines
« Reply #3 on: January 28, 2010, 04:06:56 AM »
Since p/invoke methods sometimes have different entry points due to name mangling, maybe it would be a good idea to specify the Acad version.
Would be interesting to see the example of usage as well...:)

~J'~

LE3

  • Guest
Re: NET P/INVOKE Routines
« Reply #4 on: April 13, 2010, 12:52:43 PM »
Just by any chance...
Anyone have done a PInvoke for std:map ?

Like from unmanaged:
Code: [Select]
extern "C" __declspec(dllexport)
void steelMEPcollisions (map<AcDbObjectId, double> pipeDiamMap) { ... }

I wish the above could be to easy like to managed:
Code: [Select]
[DllImport(STEEL_ARX, CallingConvention = CallingConvention.Cdecl, EntryPoint = "steelMEPcollisions ")]
public static extern void CollisionsSteelMEP ( Dictionary<ObjectId, Double> pipeDiamMap );

Thanks.
« Last Edit: August 13, 2010, 02:34:03 PM by LE »

LE3

  • Guest
Re: NET P/INVOKE Routines
« Reply #5 on: April 13, 2010, 01:50:03 PM »
On the mean time, will go into this route:
> To use separate arguments for the ids and the doubles and make sure to keep the same location in their AcArray's - have done some with these types, so guess will be much easier.
 
Like from unmanaged:
Code: [Select]
extern "C" __declspec(dllexport)
void steelMEPcollisions (map<AcDbObjectId, double> pipeDiamMap) { ... }

I which the above could be to easy like to managed:
Code: [Select]
[DllImport(STEEL_ARX, CallingConvention = CallingConvention.Cdecl, EntryPoint = "steelMEPcollisions ")]
public static extern void CollisionsSteelMEP ( Dictionary<ObjectId, Double> pipeDiamMap );

LE3

  • Guest
Re: NET P/INVOKE Routines
« Reply #6 on: April 13, 2010, 08:45:18 PM »
In case someone end up smashing on the same wall:

The ticket (or the easier route for me) was to use resbuf* and ResultBuffer

Code: [Select]
// C++ - unmanaged
extern "C" __declspec(dllexport)
void steelMEPcollisions ( resbuf *pArgs ) { ... }

// C# - managed    
public static class SteelFunctions        
{
    [DllImport(STEEL_ARX, CallingConvention = CallingConvention.Cdecl, EntryPoint = "steelMEPcollisions ")]
    public static extern void CollisionsSteelMEP ( IntPtr pArgs );
}
        
public static void CollisionsMEPSteel ( ResultBuffer args )    
{        
    SteelFunction.CollisionsSteelMEP( args.UnmanagedObject );        
}

ResultBuffer args = new ResultBuffer();                        
args.Add(new TypedValue((int)LispDataType.ListBegin, -1));                        
args.Add(new TypedValue((int)LispDataType.ListBegin, -1));                        
args.Add(new TypedValue((int)LispDataType.Int32, oldIdPtr.ToInt32()));                        
args.Add(new TypedValue((int)LispDataType.Double, diam));                      
args.Add(new TypedValue((int)LispDataType.ListEnd, -1));                        
args.Add(new TypedValue((int)LispDataType.ListEnd, -1));

PInvoke.CollisionsMEPSteel(args);
« Last Edit: April 14, 2010, 10:37:52 AM by LE3 »

jgr

  • Guest
Re: NET P/INVOKE Routines
« Reply #7 on: May 25, 2010, 08:16:17 PM »
FindText "API", with acad 2007 32-bit

Code: [Select]
'#define AC_SRCH_BLOCK         0x01
'#define AC_SRCH_DIM_TEXT      0x02
'#define AC_SRCH_TEXT          0x04
'#define AC_SRCH_LINK_DESC     0x08
'#define AC_SRCH_LINK_URL      0x10
'#define AC_SRCH_MATCH_CASE    0x20
'#define AC_SRCH_WHOLE_WORD    0x40

'#define AC_SRCH_DEFAULT       0x1F

'bool acdbTextFind(AcDbDatabase* pDatabase,
'                  AcDbObjectIdArray& resultSet,
'                  const ACHAR* findString,
'                  const ACHAR* replaceString = NULL,
'                  Adesk::UInt8 searchOptions = AC_SRCH_DEFAULT,
'                  const AcDbObjectIdArray& selSet = 0);

Public Const AC_SRCH_BLOCK As Integer = 1

Public Const AC_SRCH_DIM_TEXT As Integer = 2

Public Const AC_SRCH_TEXT As Integer = 4

Public Const AC_SRCH_LINK_DESC As Integer = 8

Public Const AC_SRCH_LINK_URL As Integer = 16

Public Const AC_SRCH_MATCH_CASE As Integer = 32

Public Const AC_SRCH_WHOLE_WORD As Integer = 64

Public Const AC_SRCH_DEFAULT As Integer = 31


<DllImport("acdb17.dll", CallingConvention:=CallingConvention.Cdecl, CharSet:=CharSet.Unicode, EntryPoint:="?acdbTextFind@@YA_NPAVAcDbDatabase@@AAV?$AcArray@VAcDbObjectId@@V?$AcArrayMemCopyReallocator@VAcDbObjectId@@@@@@PB_W2EABV2@@Z")> _
Private Shared Function acdbTextFind(ByVal pDb As System.IntPtr, _
                                 ByVal resultSet As System.IntPtr, _
                           <System.Runtime.InteropServices.InAttribute(), System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)> ByVal findString As String, _
                           <System.Runtime.InteropServices.InAttribute(), System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)> ByVal replaceString As String, _
                           ByVal searchOptions As Integer, _
                           ByVal selSet As System.IntPtr) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.I1)> Boolean
End Function



Public Shared Sub ZZ()

    Dim oidIn As New ObjectIdCollection
    Dim oidOut As New ObjectIdCollection
    Dim ret As Boolean

    Dim db As Database = Application.DocumentManager.MdiActiveDocument.Database

    ret = acdbTextFind(db.UnmanagedObject, oidOut.UnmanagedObject, "hello", Nothing, AC_SRCH_DEFAULT, oidIn.UnmanagedObject)

    If ret Then
        For Each id As ObjectId In oidOut
            '
        Next
    End If

    oidIn.Dispose()
    oidOut.Dispose()
End Sub


can someone explain to me why different entry point signatures dependant on x32 or x64 platforms? i read this in first post, and i do not understand (I come from vba/vb6)
« Last Edit: May 25, 2010, 08:24:59 PM by jgr »

Peter Jamtgaard

  • Guest
Re: NET P/INVOKE Routines
« Reply #8 on: May 28, 2010, 05:41:38 PM »
I have been playing with this for using lisp.

Code: [Select]

Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.ApplicationServices
Imports System.Runtime.InteropServices 'for DllImport()
Imports System.Security

<System.Security.SuppressUnmanagedCodeSecurity(), DllImport("acad.exe", CallingConvention:=CallingConvention.Cdecl)> _
    Private Shared Function acedInvoke(ByVal rbIn As IntPtr, <Out()> ByRef rbOut As IntPtr) As Integer
    End Function
    Public Shared Function InvokeLisp(ByVal resbuf As ResultBuffer) As ResultBuffer
        Dim rb As IntPtr = IntPtr.Zero
        Class1.acedInvoke(resbuf.UnmanagedObject, rb)
        Return DirectCast(DisposableWrapper.Create(GetType(ResultBuffer), rb, True), ResultBuffer)
    End Function

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11653
  • class keyThumper<T>:ILazy<T>
Re: NET P/INVOKE Routines
« Reply #9 on: May 28, 2010, 06:27:58 PM »

Peter,

Isn't that the code gile posted for you elsewhere ??

Regards,

Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

--> Donate to theSwamp<--

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6905
  • AKA Daniel
Re: NET P/INVOKE Routines
« Reply #10 on: May 28, 2010, 08:44:43 PM »

Peter,

Isn't that the code gile posted for you elsewhere ??

Regards,



looks like a vb version of  http://www.theswamp.org/index.php?topic=33352.msg387539#msg387539 ?

gile

  • Water Moccasin
  • Posts: 2191
  • Marseille, France
Re: NET P/INVOKE Routines
« Reply #11 on: May 29, 2010, 03:39:43 AM »
Hi,

The code I posted "elsewhere" was greatly inspired by Daniel's and Tony's (here) ones.
Speaking English as a French Frog

Peter Jamtgaard

  • Guest
Re: NET P/INVOKE Routines
« Reply #12 on: May 29, 2010, 08:37:08 AM »

Peter,

Isn't that the code gile posted for you elsewhere ??

Regards,



Yes, I just thought I would add it here, for discussion.

LE3

  • Guest
Re: NET P/INVOKE Routines
« Reply #13 on: August 13, 2010, 02:25:34 PM »
Here is one, I just found out:
Code: [Select]
[DllImport("acad.exe")]
public static extern void acedPostCommandPrompt ( );

Just place this at the end of your function: acedPostCommandPrompt(); and will do the trick.

Have been fighting on re-display the Command: line prompt after calling/executing a function from a modeless form.
i.e:
Command:
Table position:

And after finished with the function, and going back to the form (focus) you will still see in the command line:
Table position:

Until you go to the drawing screen area and do a click with your mouse, for example, and that will bring back the prompt:
Command:

Jeff H

  • Needs a day job
  • Posts: 6014
Re: NET P/INVOKE Routines
« Reply #14 on: August 13, 2010, 05:04:29 PM »
2011 preview icon for blocks if misssing

<DllImport("acad.exe", CallingConvention:=CallingConvention.Cdecl, CharSet:=CharSet.Unicode)> _
   Private Shared Function acedCommand(ByVal type1 As Integer, ByVal command As String, ByVal type2 As Integer, ByVal blockName As String, ByVal [end] As Integer) As Integer

End Function
 acedCommand(5005, "BLOCKICON", 5005, blkName, 5000)

jgr

  • Guest
Re: NET P/INVOKE Routines
« Reply #15 on: September 01, 2010, 05:21:33 PM »
Returns the preview bitmap from a DWG file, without opening the file (database)

For AutoCAD 2007 ~ 2011. 32-bit and 64-bit

VB.NET:

Code: [Select]
Imports System.Drawing
Imports System.Runtime.InteropServices

#If ACAD_17 Then
    Const ACDBDLL_NAME = "acdb17.dll"
#Else
    Const ACDBDLL_NAME = "acdb18.dll"
#End If

    <System.Runtime.InteropServices.DllImportAttribute(ACDBDLL_NAME, CallingConvention:=CallingConvention.Cdecl, CharSet:=CharSet.Unicode, EntryPoint:="?acdbGetPreviewBitmapFromDwg@@YA_NPB_WPAPAUHBITMAP__@@PAPAUHPALETTE__@@@Z")> _
    Private Shared Function acdbGetPreviewBitmapFromDwg32(<System.Runtime.InteropServices.InAttribute(), System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)> ByVal pszDwgfilename As String, ByRef pPreviewBmp As System.IntPtr, ByRef pRetPal As System.IntPtr) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.I1)> Boolean
    End Function

    <System.Runtime.InteropServices.DllImportAttribute(ACDBDLL_NAME, CallingConvention:=CallingConvention.Cdecl, CharSet:=CharSet.Unicode, EntryPoint:="?acdbGetPreviewBitmapFromDwg@@YA_NPEB_WPEAPEAUHBITMAP__@@PEAPEAUHPALETTE__@@@Z")> _
    Private Shared Function acdbGetPreviewBitmapFromDwg64(<System.Runtime.InteropServices.InAttribute(), System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)> ByVal pszDwgfilename As String, ByRef pPreviewBmp As System.IntPtr, ByRef pRetPal As System.IntPtr) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.I1)> Boolean
    End Function

    Private Function GetPreviewBitmapFromDwg(ByVal fileName As String) As Bitmap
        Dim hBmp As System.IntPtr
        Dim hPal As System.IntPtr

        If IntPtr.Size > 4 Then ' 64-bit
            acdbGetPreviewBitmapFromDwg64(fileName, hBmp, hPal)

        Else ' 32-bit
            acdbGetPreviewBitmapFromDwg32(fileName, hBmp, hPal)

        End If

        If hBmp.Equals(IntPtr.Zero) Then
            Return Nothing
        Else
            Return Bitmap.FromHbitmap(hBmp)
        End If


    End Function


CSHARP (using a translator)

Code: [Select]
#if ACAD_17
    private const string ACDBDLL_NAME = "acdb17.dll";
#else
    private const string ACDBDLL_NAME = "acdb18.dll";
#endif

    [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.I1)]
    [System.Runtime.InteropServices.DllImportAttribute(ACDBDLL_NAME, CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Unicode, EntryPoint="?acdbGetPreviewBitmapFromDwg@@YA_NPB_WPAPAUHBITMAP__@@PAPAUHPALETTE__@@@Z")]
    private extern static bool acdbGetPreviewBitmapFromDwg32([System.Runtime.InteropServices.InAttribute(), System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string pszDwgfilename, ref System.IntPtr pPreviewBmp, ref System.IntPtr pRetPal);

    [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.I1)]
    [System.Runtime.InteropServices.DllImportAttribute(ACDBDLL_NAME, CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Unicode, EntryPoint="?acdbGetPreviewBitmapFromDwg@@YA_NPEB_WPEAPEAUHBITMAP__@@PEAPEAUHPALETTE__@@@Z")]
    private extern static bool acdbGetPreviewBitmapFromDwg64([System.Runtime.InteropServices.InAttribute(), System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string pszDwgfilename, ref System.IntPtr pPreviewBmp, ref System.IntPtr pRetPal);

    private Bitmap GetPreviewBitmapFromDwg(string fileName)
    {
System.IntPtr hBmp = default(System.IntPtr);
System.IntPtr hPal = default(System.IntPtr);

if (IntPtr.Size > 4) // 64-bit
{
   acdbGetPreviewBitmapFromDwg64(fileName, ref hBmp, ref hPal);

}
else // 32-bit
{
   acdbGetPreviewBitmapFromDwg32(fileName, ref hBmp, ref hPal);

}

if (hBmp.Equals(IntPtr.Zero))
{
   return null;
}
else
{
   return Bitmap.FromHbitmap(hBmp);
}

    }
« Last Edit: September 01, 2010, 11:17:47 PM by jgr »

Jeff H

  • Needs a day job
  • Posts: 6014
Re: NET P/INVOKE Routines
« Reply #16 on: September 01, 2010, 05:36:40 PM »
I think they already have a wrapper for that one

Code: [Select]

    private  void  AddToImageList(string filename)
        {
            Database db = new Database(false, false);
            db.ReadDwgFile(filename, FileOpenMode.OpenForReadAndAllShare, false, null);
            Bitmap bitmap = db.ThumbnailBitmap;
            if (!(DetailsLargeImage.Images.ContainsKey(filename)))
            {
                DetailsLargeImage.Images.Add(filename, bitmap);
                DetailsSmallImage.Images.Add(filename, bitmap);
               
            }
        }

mohnston

  • Bull Frog
  • Posts: 305
  • CAD Programmer
Re: NET P/INVOKE Routines
« Reply #17 on: February 10, 2011, 12:28:28 PM »
For determining if a drawing has unsaved changes even if that drawing is not currently active. (Don't have to get the "DBMOD" system variable value)

http://www.theswamp.org/index.php?topic=37022.msg420606#msg420606
It's amazing what you can do when you don't know what you can't do.
CAD Programming Solutions

Odoshi

  • Guest
Re: NET P/INVOKE Routines
« Reply #18 on: December 11, 2011, 12:17:17 PM »
Little thread bump here :)

I am using the code below, but get an error on the Return DirectCast... line.

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: Operation is not valid due to the current state of the object.   at Autodesk.AutoCAD.Runtime.DisposableWrapper.Attach(IntPtr unmanagedPointer, Boolean autoDelete)

Is it a permissions thing? Or is it because the LISP may not be returning a value? Any ideas are appreciated.

Thanks,
Mike


I have been playing with this for using lisp.

Code: [Select]

Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.ApplicationServices
Imports System.Runtime.InteropServices 'for DllImport()
Imports System.Security

<System.Security.SuppressUnmanagedCodeSecurity(), DllImport("acad.exe", CallingConvention:=CallingConvention.Cdecl)> _
    Private Shared Function acedInvoke(ByVal rbIn As IntPtr, <Out()> ByRef rbOut As IntPtr) As Integer
    End Function
    Public Shared Function InvokeLisp(ByVal resbuf As ResultBuffer) As ResultBuffer
        Dim rb As IntPtr = IntPtr.Zero
        Class1.acedInvoke(resbuf.UnmanagedObject, rb)
        Return DirectCast(DisposableWrapper.Create(GetType(ResultBuffer), rb, True), ResultBuffer)
    End Function

gile

  • Water Moccasin
  • Posts: 2191
  • Marseille, France
Re: NET P/INVOKE Routines
« Reply #19 on: December 11, 2011, 01:20:46 PM »
Hi,

I don't think the error is in the code you posted.
It may come from the argument (result buffer) you pass to the InvokeLisp() method or from the LISP function you call.
To be accessible for external applications, the LISP function symbol must use the 'c:' prefix or passed to the vl-acad-defun function:
Code: [Select]
(defun c:foo ...)or
Code: [Select]
(defun foo ...) (vl-acad-defun 'foo)
« Last Edit: December 11, 2011, 01:24:33 PM by gile »
Speaking English as a French Frog

kaefer

  • Swamp Rat
  • Posts: 572
Re: NET P/INVOKE Routines
« Reply #20 on: December 11, 2011, 02:24:28 PM »
Hi,

I don't think the error is in the code you posted.

Is it an error not to check for an error?

arxref.chm makes interesting reading; it indicates acedInvoke returns an int. As far as I know, there's no enum exposed corresponding to possible return values, like the one for Autodesk.AutoCAD.Runtime.LispDataType. Instead, acedads.h says:

Code: [Select]
/* The following RT codes are for the ADS program interface.
   They are used to signal success or failure (error) of the
   ADS library functions.  RTFAIL in particular means that
   the link has failed (most likely because AutoLISP has
   died or exited), and the application should cleanup and exit.
*/
#define RTNORM    5100 /* Request succeeded */
 
/* Various error codes returned to ADS application by library
 */
#define RTERROR          (-5001) // Some other error
#define RTCAN            (-5002) // User cancelled request -- Ctl-C
#define RTREJ            (-5003) // AutoCAD rejected request -- invalid
#define RTFAIL           (-5004) // Link failure -- Lisp probably died
#define RTKWORD          (-5005) // Keyword returned from getxxx() routine
#define RTINPUTTRUNCATED (-5008) // Input didn't all fit in the buffer

So prudence would have it that you check for RTNORM as well as you test against an invalid result pointer.

gile

  • Water Moccasin
  • Posts: 2191
  • Marseille, France
Re: NET P/INVOKE Routines
« Reply #21 on: December 11, 2011, 03:33:31 PM »
Quote
Is it an error not to check for an error?

In the sample TonyT gave here, he shows how to use the int returned by acedInvoke as error checking.

Remind that since A2011 .NET SDK there's an Application.Invoke() method which wraps the ObjectARX function acedInvoke().
Speaking English as a French Frog

Odoshi

  • Guest
Re: NET P/INVOKE Routines
« Reply #22 on: December 11, 2011, 08:14:19 PM »
That's great! I will use the wrapper, which has now shown my it was Invalid Input.

So, part 2:

I want to send something like this:

args.Add(New TypedValue(5005, "(c:ace_update_WFRM2ALL 1)"))

Dim rbRes As ResultBuffer = Autodesk.AutoCAD.ApplicationServices.Application.Invoke(args)

Could anyone help me with the resbuf to send? (I'll be perusing the help files in the meanwhile)

Thanks so much,
Mike


Quote
Is it an error not to check for an error?

In the sample TonyT gave here, he shows how to use the int returned by acedInvoke as error checking.

Remind that since A2011 .NET SDK there's an Application.Invoke() method which wraps the ObjectARX function acedInvoke().

gile

  • Water Moccasin
  • Posts: 2191
  • Marseille, France
Re: NET P/INVOKE Routines
« Reply #23 on: December 12, 2011, 12:48:57 AM »
Code: [Select]
args.Add(New TypedValue((int)LispDataType.Text, "c:ace_update_WFRM2ALL"))
args.Add(New TypedValue((int)LispDataType.Int16, 1))

Dim rbRes As ResultBuffer = Autodesk.AutoCAD.ApplicationServices.Application.Invoke(args)
Speaking English as a French Frog

Odoshi

  • Guest
Re: NET P/INVOKE Routines
« Reply #24 on: December 12, 2011, 10:24:00 AM »
Yep, figured out the same last night.

Thanks again!

Code: [Select]
args.Add(New TypedValue((int)LispDataType.Text, "c:ace_update_WFRM2ALL"))
args.Add(New TypedValue((int)LispDataType.Int16, 1))

Dim rbRes As ResultBuffer = Autodesk.AutoCAD.ApplicationServices.Application.Invoke(args)

fixo

  • Guest
Re: NET P/INVOKE Routines
« Reply #25 on: December 12, 2011, 12:07:11 PM »
@Odoshi
Just I'm wondering, have you read the very first post in this thread?
Please, next time follow the rules, this thread is not for question

gile

  • Water Moccasin
  • Posts: 2191
  • Marseille, France
Re: NET P/INVOKE Routines
« Reply #26 on: January 22, 2012, 12:48:12 PM »
Inspired by the well known CommandLine by Tony Tanzillo.

 
Code - C#: [Select]
  1.        [System.Security.SuppressUnmanagedCodeSecurity]
  2.        [DllImport("acad.exe", EntryPoint = "acedCmd",
  3.            CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
  4.        extern static private int acedCmd(IntPtr resbuf);
  5.  
  6.        /// <summary>
  7.        /// Call an AutoCAD command (works synchronously).
  8.        /// </summary>
  9.        /// <param name="args">A ResultBuffer containing the command name followed by command inputs.</param>
  10.        public static void Command(ResultBuffer args)
  11.        {
  12.            acedCmd(args.UnmanagedObject);
  13.        }
  14.  
  15.        /// <summary>
  16.        /// Call an AutoCAD command (works synchronously).
  17.        /// </summary>
  18.        /// <param name="args">The command name followed by command inputs.</param>
  19.        public static void Command(params object[] args)
  20.        {
  21.            ResultBuffer resbuf = new ResultBuffer();
  22.            foreach (object obj in args)
  23.            {
  24.                switch (obj.GetType().Name)
  25.                {
  26.                    case "String":
  27.                        resbuf.Add(new TypedValue((int)LispDataType.Text, obj)); break;
  28.                    case "Int16":
  29.                        resbuf.Add(new TypedValue((int)LispDataType.Int16, obj)); break;
  30.                    case "Int32":
  31.                        resbuf.Add(new TypedValue((int)LispDataType.Int32, obj)); break;
  32.                    case "Double":
  33.                        resbuf.Add(new TypedValue((int)LispDataType.Double, obj)); break;
  34.                    case "Point2d":
  35.                        resbuf.Add(new TypedValue((int)LispDataType.Point2d, obj)); break;
  36.                    case "Point3d":
  37.                        resbuf.Add(new TypedValue((int)LispDataType.Point3d, obj)); break;
  38.                    case "ObjectId":
  39.                        resbuf.Add(new TypedValue((int)LispDataType.ObjectId, obj)); break;
  40.                    case "ObjectId[]":
  41.                        foreach (ObjectId id in (ObjectId[])obj)
  42.                            resbuf.Add(new TypedValue((int)LispDataType.ObjectId, id));
  43.                        break;
  44.                    case "ObjectIdCollection":
  45.                        foreach (ObjectId id in (ObjectIdCollection)obj)
  46.                            resbuf.Add(new TypedValue((int)LispDataType.ObjectId, id));
  47.                        break;
  48.                    case "SelectionSetDelayMarshalled":
  49.                    case "SelectionSetFullyMarshalled":
  50.                        resbuf.Add(new TypedValue((int)LispDataType.SelectionSet, obj)); break;
  51.                    default:
  52.                        throw new InvalidOperationException("Unsupported type in Command() method");
  53.                }
  54.            }
  55.            acedCmd(resbuf.UnmanagedObject);
  56.        }
« Last Edit: January 22, 2012, 01:43:46 PM by gile »
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2191
  • Marseille, France
Relative Path
« Reply #27 on: April 12, 2015, 01:16:50 PM »
Relative Path

Code - C#: [Select]
  1.        [DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
  2.        static extern bool PathRelativePathTo(
  3.             StringBuilder path,
  4.             string fromPath,
  5.             FileAttributes fromAttr,
  6.             string toPath,
  7.             FileAttributes toAttr);
  8.  
  9.        static string MakeRelativePathTo(string fromPath, string toPath)
  10.        {
  11.            StringBuilder builder = new StringBuilder(260);
  12.            bool done = PathRelativePathTo(
  13.                builder,
  14.                fromPath,
  15.                FileAttributes.Directory,
  16.                toPath,
  17.                FileAttributes.Normal);
  18.            return done ? builder.ToString() : toPath;
  19.        }

MakeRelativePathTo(@"Z:\Drawings\Blocks", @"Z:\Drawings\Blocks\Arch\Door.dwg");    -> ".\Arch\Door.dwg"
MakeRelativePathTo(@"Z:\Drawings\Blocks", @"Z:\Docs\Help.html");                               -> ..\..\Docs\Help.html"
Speaking English as a French Frog

MexicanCustard

  • Swamp Rat
  • Posts: 704
Re: NET P/INVOKE Routines
« Reply #28 on: April 13, 2015, 07:51:44 AM »
Relative Path

Code - C#: [Select]
  1.        [DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
  2.        static extern bool PathRelativePathTo(
  3.             StringBuilder path,
  4.             string fromPath,
  5.             FileAttributes fromAttr,
  6.             string toPath,
  7.             FileAttributes toAttr);
  8.  
  9.        static string MakeRelativePathTo(string fromPath, string toPath)
  10.        {
  11.            StringBuilder builder = new StringBuilder(260);
  12.            bool done = PathRelativePathTo(
  13.                builder,
  14.                fromPath,
  15.                FileAttributes.Directory,
  16.                toPath,
  17.                FileAttributes.Normal);
  18.            return done ? builder.ToString() : toPath;
  19.        }

MakeRelativePathTo(@"Z:\Drawings\Blocks", @"Z:\Drawings\Blocks\Arch\Door.dwg");    -> ".\Arch\Door.dwg"
MakeRelativePathTo(@"Z:\Drawings\Blocks", @"Z:\Docs\Help.html");                               -> ..\..\Docs\Help.html"

There is an easier way in managed code Uri.MakeRelativeUri
Revit 2019, AMEP 2019 64bit Win 10

gile

  • Water Moccasin
  • Posts: 2191
  • Marseille, France
Re: NET P/INVOKE Routines
« Reply #29 on: April 13, 2015, 01:09:43 PM »
Thanks MexicanCustard,

I knew this method but I prefer the one I posted because the returned path is correctly formated for a filename or a folder (with dots and back slashes prefix).
To get the same result with Uri.MakeRelativeUri() you'd have some additional work (and it won't be so much easier).
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2191
  • Marseille, France
Re: NET P/INVOKE Routines
« Reply #30 on: March 08, 2019, 11:22:41 AM »
Mimic the 'textbox' LISP function by P/Invoking acedTextBox but avoiding to P/Invoke acdbGetAdsName32/64 and acdbEntGet by building the ResultBuffer from the DBText properties.
The minimum required data is DBText.TextString, the default for other data is got from the Database current settings (TestSize, TextStyle).

Code - C#: [Select]
  1.        [DllImport("accore.dll", CharSet = CharSet.Unicode,
  2.            CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedTextBox")]
  3.        static extern System.IntPtr acedTextBox(IntPtr rb, double[] point1, double[] point2);
  4.  
  5.        /// <summary>
  6.        /// Gets the extents of a DBText (mimics the 'textbox' LISP function)
  7.        /// </summary>
  8.        /// <param name="rb">DXF data decribing the DBText</param>
  9.        /// <returns>The diagonal of the non rotated text with insertion point at (0, 0, 0).</returns>
  10.        public static Extents3d TextBox(ResultBuffer rb)
  11.        {
  12.            var point1 = new double[3];
  13.            var point2 = new double[3];
  14.            acedTextBox(rb.UnmanagedObject, point1, point2);
  15.            return new Extents3d(new Point3d(point1), new Point3d(point2));
  16.        }
  17.  
  18.        /// <summary>
  19.        /// Gets the WCS points of the text bounding box.
  20.        /// </summary>
  21.        /// <param name="dbText">Instance of DBText the method applies to.</param>
  22.        /// <returns>The bounding box points in counterclockwise sense.</returns>
  23.        public static Point3d[] GetTextBoxCorners(this DBText dbText)
  24.        {
  25.            if (dbText == null)
  26.                throw new ArgumentNullException("dbText");
  27.  
  28.            int mirrored = dbText.IsMirroredInX ? 2 : 0;
  29.            mirrored |= dbText.IsMirroredInY ? 4 : 0;
  30.            var rb = new ResultBuffer(
  31.                    new TypedValue(1, dbText.TextString),
  32.                    new TypedValue(40, dbText.Height),
  33.                    new TypedValue(41, dbText.WidthFactor),
  34.                    new TypedValue(51, dbText.Oblique),
  35.                    new TypedValue(7, dbText.TextStyleName),
  36.                    new TypedValue(71, mirrored),
  37.                    new TypedValue(72, (int)dbText.HorizontalMode),
  38.                    new TypedValue(73, (int)dbText.VerticalMode));
  39.  
  40.            var extents = TextBox(rb);
  41.            var xform =
  42.                Matrix3d.Displacement(dbText.Position.GetAsVector()) *
  43.                Matrix3d.Rotation(dbText.Rotation, dbText.Normal, Point3d.Origin) *
  44.                Matrix3d.WorldToPlane(new Plane(Point3d.Origin, dbText.Normal));
  45.  
  46.            return new[]
  47.            {
  48.                extents.MinPoint.TransformBy(xform),
  49.                new Point3d(extents.MaxPoint.X, extents.MinPoint.Y, 0.0).TransformBy(xform),
  50.                extents.MaxPoint.TransformBy(xform),
  51.                new Point3d(extents.MinPoint.X, extents.MaxPoint.Y, 0.0).TransformBy(xform)
  52.            };
  53.        }
Speaking English as a French Frog