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

0 Members and 1 Guest are viewing this topic.

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: 6144
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

  • Gator
  • Posts: 2507
  • 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

  • Guest
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

  • Gator
  • Posts: 2507
  • 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

  • Gator
  • Posts: 2507
  • 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

  • Gator
  • Posts: 2507
  • 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

  • Gator
  • Posts: 2507
  • 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: 705
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

  • Gator
  • Posts: 2507
  • 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