• Industries
  • Products
  • Buy
  • Services & Support
  • Communities
  • Discussion Groups

    .NET

    Reply
    *Tony Tanzillo

    DelayLoad in .NET ?

    83 Views, 6 Replies
    06-14-2005 05:14 PM
    I'm trying to port a custom form class that was
    originally developed in Delphi to .NET.

    The class provides support for modeless floating
    forms in AutoCAD, as well as various other AutoCAD-
    specific functionality that's commonly used in UI
    based applications.

    The problem I'm running into, is that in the Form
    class, I cannot calll any Interop code because the
    form must be instantiated in the IDE designer, which
    means that Visual Studio will attempt to load any
    referenced assemblies.

    I see this as a massive impediment to component
    based development of AutoCAD-based solutions,
    because no AutoCAD interop code can be used in
    any compoent that must be instantiated in the
    Visual Studio IDE.

    Here is an example of where the functionality is
    needed. This is an override of Form.CreateParams
    which sets the Form's owner window to the AutoCAD
    MainFrame, and adds the WS_POPUP window style,
    so AutoCAD fires WM_KEEPFOCUS messages at the
    Window. Note that I can get around this specific
    problem using P/Invoke (adsi_AcadMainWnd), but
    this is only an _example_ for illustration purposes.

    Because the form calls Interop code, I cannot derive
    new Form classes from this form class, and use them
    in the IDE designer:

    namespace AcadUI
    {
    public class AcadModelessForm : System.Windows.Forms.Form
    {
    const UInt32 WS_OVERLAPPED = 0x00000000;
    const UInt32 WS_POPUP = 0x80000000;
    const UInt32 WS_CHILD = 0x40000000;

    const Int32 WM_USER = 0x0400;
    const Int32 WM_ACADKEEPFOCUS = WM_USER + 0x6D01;

    // Handle of the AutoCAD MainFrame:
    static IntPtr s_OwnerWnd = IntPtr.Zero;

    // An event to query if we want to keep the focus
    public delegate void OnQueryKeepFocusEvent(object sender, ref bool bKeepFocus );
    public event OnQueryKeepFocusEvent OnQueryKeepFocus = null;


    // This code makes the form's owner window the
    // AutoCAD Mainframe, and adds the WS_POPUP style
    // so AutoCAD sends us WM_KEEPFOCUS messages

    protected override CreateParams CreateParams
    {
    get
    {
    if( DesignMode )
    return base.CreateParams; // design-time behavior

    /////////////////////////////////////////////////////////
    // The following prevents this from from being used
    // in the IDE designer (as a base for Inherited Forms):

    if( s_OwnerWnd == IntPtr.Zero )
    s_OwnerWnd = Autodesk.AutoCAD.ApplicationServices.Application.MainWindow.Handle;

    Params.Parent = s_OwnerWnd;

    /////////////////////////////////////////////////////////

    this.TopLevel = false;

    CreateParams Params = base.CreateParams;

    unchecked
    {
    Params.Style |= (int) (WS_POPUP | WS_OVERLAPPED);
    Params.Style &= ~ (int) WS_CHILD;

    // Params.ExStyle |= (int) WS_EX_PALETTEWINDOW; // optional
    }
    return Params;
    }
    }

    // Need to do this, because Control.Bounds is
    // returning the bounds in owner/parent window
    // client coordinates. Once I get rid of the WS_CHILD
    // window style for the parking window this may no
    // longer be necessary

    public new Rectangle Bounds
    {
    get
    {
    return base.Bounds;

    // TODO: Translate bounds rectangle to screen coords
    }
    }

    protected virtual bool OnKeepFocus()
    {
    bool bKeepFocus = true;
    if( this.OnQueryKeepFocus != null )
    this.OnQueryKeepFocus(this, ref bKeepFocus);
    else
    {
    // TODO: Implement default behavior (keep focus
    // only if the mouse is in our window bounds)

    }
    return bKeepFocus;
    }

    protected override void WndProc( ref Message m )
    {
    if( ! this.DesignMode && m.Msg == WM_ACADKEEPFOCUS )
    m.Result = (IntPtr) (OnKeepFocus() ? 1 : 0);
    else
    base.WndProc( ref m );

    }

    // A modeless Form is automatically disposed of when closed.
    // We want to stop this from happening, so instead we hide
    // the form which allows it to be redisplayed by calling Show(),
    // or by setting the Visible property to true;

    protected override void OnClosing(CancelEventArgs e)
    {
    base.OnClosing( e );
    if( ! DesignMode )
    {
    e.Cancel = true;
    this.Hide();
    }
    }

    }
    }


    --
    http://www.caddzone.com

    AcadXTabs: MDI Document Tabs for AutoCAD 2004/2005/2006
    http://www.acadxtabs.com
    Please use plain text.
    *Albert Szilvasy

    Re: DelayLoad in .NET ?

    06-14-2005 06:09 PM in reply to: *Tony Tanzillo
    Yes. I understand that this is a problem in general. I think you can get
    around this with careful coding. The reason VS tries to resolve the
    dependency because you have the dependency in a function that is called
    (hence jitted) in the designer. Try refactoring the design time and runtime
    behavior into 2 separate functions like I did below. This way
    GetCreateParams won't be jitted in the designer and the dependency won't
    have to be resolved.

    Albert

    protected override CreateParams CreateParams
    {
    get
    {
    if( DesignMode )
    return base.CreateParams; // design-time behavior
    return GetCreateParams(); //runtime behavior in separate function
    so it won't be jitted in designer
    }
    }
    private CreateParams GetCreateParams()
    {
    /////////////////////////////////////////////////////////
    // The following prevents this from from being used
    // in the IDE designer (as a base for Inherited Forms):

    if( s_OwnerWnd == IntPtr.Zero )
    s_OwnerWnd =
    Autodesk.AutoCAD.ApplicationServices.Application.MainWindow.Handle;

    Params.Parent = s_OwnerWnd;

    /////////////////////////////////////////////////////////

    this.TopLevel = false;

    CreateParams Params = base.CreateParams;

    unchecked
    {
    Params.Style |= (int) (WS_POPUP | WS_OVERLAPPED);
    Params.Style &= ~ (int) WS_CHILD;

    // Params.ExStyle |= (int) WS_EX_PALETTEWINDOW; // optional
    }
    return Params;

    }

    "Tony Tanzillo" wrote in message
    news:4874992@discussion.autodesk.com...
    I'm trying to port a custom form class that was
    originally developed in Delphi to .NET.

    The class provides support for modeless floating
    forms in AutoCAD, as well as various other AutoCAD-
    specific functionality that's commonly used in UI
    based applications.

    The problem I'm running into, is that in the Form
    class, I cannot calll any Interop code because the
    form must be instantiated in the IDE designer, which
    means that Visual Studio will attempt to load any
    referenced assemblies.

    I see this as a massive impediment to component
    based development of AutoCAD-based solutions,
    because no AutoCAD interop code can be used in
    any compoent that must be instantiated in the
    Visual Studio IDE.

    Here is an example of where the functionality is
    needed. This is an override of Form.CreateParams
    which sets the Form's owner window to the AutoCAD
    MainFrame, and adds the WS_POPUP window style,
    so AutoCAD fires WM_KEEPFOCUS messages at the
    Window. Note that I can get around this specific
    problem using P/Invoke (adsi_AcadMainWnd), but
    this is only an _example_ for illustration purposes.

    Because the form calls Interop code, I cannot derive
    new Form classes from this form class, and use them
    in the IDE designer:

    namespace AcadUI
    {
    public class AcadModelessForm : System.Windows.Forms.Form
    {
    const UInt32 WS_OVERLAPPED = 0x00000000;
    const UInt32 WS_POPUP = 0x80000000;
    const UInt32 WS_CHILD = 0x40000000;

    const Int32 WM_USER = 0x0400;
    const Int32 WM_ACADKEEPFOCUS = WM_USER + 0x6D01;

    // Handle of the AutoCAD MainFrame:
    static IntPtr s_OwnerWnd = IntPtr.Zero;

    // An event to query if we want to keep the focus
    public delegate void OnQueryKeepFocusEvent(object sender, ref bool
    bKeepFocus );
    public event OnQueryKeepFocusEvent OnQueryKeepFocus = null;


    // This code makes the form's owner window the
    // AutoCAD Mainframe, and adds the WS_POPUP style
    // so AutoCAD sends us WM_KEEPFOCUS messages

    protected override CreateParams CreateParams
    {
    get
    {
    if( DesignMode )
    return base.CreateParams; // design-time behavior

    /////////////////////////////////////////////////////////
    // The following prevents this from from being used
    // in the IDE designer (as a base for Inherited Forms):

    if( s_OwnerWnd == IntPtr.Zero )
    s_OwnerWnd =
    Autodesk.AutoCAD.ApplicationServices.Application.MainWindow.Handle;

    Params.Parent = s_OwnerWnd;

    /////////////////////////////////////////////////////////

    this.TopLevel = false;

    CreateParams Params = base.CreateParams;

    unchecked
    {
    Params.Style |= (int) (WS_POPUP | WS_OVERLAPPED);
    Params.Style &= ~ (int) WS_CHILD;

    // Params.ExStyle |= (int) WS_EX_PALETTEWINDOW; // optional
    }
    return Params;
    }
    }

    // Need to do this, because Control.Bounds is
    // returning the bounds in owner/parent window
    // client coordinates. Once I get rid of the WS_CHILD
    // window style for the parking window this may no
    // longer be necessary

    public new Rectangle Bounds
    {
    get
    {
    return base.Bounds;

    // TODO: Translate bounds rectangle to screen coords
    }
    }

    protected virtual bool OnKeepFocus()
    {
    bool bKeepFocus = true;
    if( this.OnQueryKeepFocus != null )
    this.OnQueryKeepFocus(this, ref bKeepFocus);
    else
    {
    // TODO: Implement default behavior (keep focus
    // only if the mouse is in our window bounds)

    }
    return bKeepFocus;
    }

    protected override void WndProc( ref Message m )
    {
    if( ! this.DesignMode && m.Msg == WM_ACADKEEPFOCUS )
    m.Result = (IntPtr) (OnKeepFocus() ? 1 : 0);
    else
    base.WndProc( ref m );

    }

    // A modeless Form is automatically disposed of when closed.
    // We want to stop this from happening, so instead we hide
    // the form which allows it to be redisplayed by calling Show(),
    // or by setting the Visible property to true;

    protected override void OnClosing(CancelEventArgs e)
    {
    base.OnClosing( e );
    if( ! DesignMode )
    {
    e.Cancel = true;
    this.Hide();
    }
    }

    }
    }


    --
    http://www.caddzone.com

    AcadXTabs: MDI Document Tabs for AutoCAD 2004/2005/2006
    http://www.acadxtabs.com
    Please use plain text.
    *Tony Tanzillo

    Re: DelayLoad in .NET ?

    06-15-2005 12:20 AM in reply to: *Tony Tanzillo
    Albert - Thanks for the advice.

    However, it doesn't seem to work. I tried refactoring exactly as
    you show, and also tried putting the code that gets the AutoCAD
    window handle into a separate class and even a separate source
    file, and it still happens.

    The problem is not that I can't open that form in the designer.
    I can do that. What I can't do, is create a form class derived from
    it via the IDE (e.g., Project->Add Inherited Form), using that
    form as the base class. When I try to do that, I get the exception
    as soon as the IDE tries to create the derived form class.

    If anyone wants to reproduce the problem, I've attached the
    .cs and .resx from (Visual Studio 2003) for the form.

    Just create a new ClassLibrary; reference acmgd and acdbmgd;
    and add the attached form class to the project (remove any
    bland class that the IDE creates).

    Then build the project. Then try to create an Inherited Form
    that derives from the attached one, and you'll get the exception.


    --
    http://www.caddzone.com

    AcadXTabs: MDI Document Tabs for AutoCAD 2004/2005/2006
    http://www.acadxtabs.com

    "Albert Szilvasy" wrote in message news:4875015@discussion.autodesk.com...
    Yes. I understand that this is a problem in general. I think you can get
    around this with careful coding. The reason VS tries to resolve the
    dependency because you have the dependency in a function that is called
    (hence jitted) in the designer. Try refactoring the design time and runtime
    behavior into 2 separate functions like I did below. This way
    GetCreateParams won't be jitted in the designer and the dependency won't
    have to be resolved.

    Albert

    protected override CreateParams CreateParams
    {
    get
    {
    if( DesignMode )
    return base.CreateParams; // design-time behavior
    return GetCreateParams(); //runtime behavior in separate function
    so it won't be jitted in designer
    }
    }
    private CreateParams GetCreateParams()
    {
    /////////////////////////////////////////////////////////
    // The following prevents this from from being used
    // in the IDE designer (as a base for Inherited Forms):

    if( s_OwnerWnd == IntPtr.Zero )
    s_OwnerWnd =
    Autodesk.AutoCAD.ApplicationServices.Application.MainWindow.Handle;

    Params.Parent = s_OwnerWnd;

    /////////////////////////////////////////////////////////

    this.TopLevel = false;

    CreateParams Params = base.CreateParams;

    unchecked
    {
    Params.Style |= (int) (WS_POPUP | WS_OVERLAPPED);
    Params.Style &= ~ (int) WS_CHILD;

    // Params.ExStyle |= (int) WS_EX_PALETTEWINDOW; // optional
    }
    return Params;

    }
    Please use plain text.
    *Tony Tanzillo

    Re: DelayLoad in .NET ?

    06-15-2005 03:57 AM in reply to: *Tony Tanzillo
    I was puzzled enough by this problem to run Visual Studio
    under Depends.exe's profiler, and it looks like the problem is
    that the DesignMode property is returning false, and the code
    is trying to call into the Interop to get the AutoCAD window
    handle. That also explains why it doesn't fail when I create
    an instance of that form class, but does when I try to derive
    a new form class from it.

    As it turns out, DesignMode returns true only when called from
    the class that is actually instantiated in the designer, but does
    not return true when called from a base class.

    That is supposedly because an instance of a base class is not
    what is being 'designed'.

    So, I have to find another queue to see if the form is actually
    running in AutoCAD or not. I can stick the call to get the Window
    handle inside of a try/catch block, and deal with it that way, but
    by the time the exception bubbles up, all kinds of stuff has
    already happened in the IDE (e.g., the AutoCAD interop DLLs are
    loaded; and AutoCAD starts doing its security thing).

    Before I resort to P/Invoke, any other ideas are welcome :-)

    --
    http://www.caddzone.com

    AcadXTabs: MDI Document Tabs for AutoCAD 2004/2005/2006
    http://www.acadxtabs.com
    Please use plain text.
    *Albert Szilvasy

    Re: DelayLoad in .NET ?

    06-15-2005 11:09 AM in reply to: *Tony Tanzillo
    If you don't want to do P/Invoke you could check what the current process is
    using the Process class.

    Albert

    "Tony Tanzillo" wrote in message
    news:4875206@discussion.autodesk.com...
    I was puzzled enough by this problem to run Visual Studio
    under Depends.exe's profiler, and it looks like the problem is
    that the DesignMode property is returning false, and the code
    is trying to call into the Interop to get the AutoCAD window
    handle. That also explains why it doesn't fail when I create
    an instance of that form class, but does when I try to derive
    a new form class from it.

    As it turns out, DesignMode returns true only when called from
    the class that is actually instantiated in the designer, but does
    not return true when called from a base class.

    That is supposedly because an instance of a base class is not
    what is being 'designed'.

    So, I have to find another queue to see if the form is actually
    running in AutoCAD or not. I can stick the call to get the Window
    handle inside of a try/catch block, and deal with it that way, but
    by the time the exception bubbles up, all kinds of stuff has
    already happened in the IDE (e.g., the AutoCAD interop DLLs are
    loaded; and AutoCAD starts doing its security thing).

    Before I resort to P/Invoke, any other ideas are welcome :-)

    --
    http://www.caddzone.com

    AcadXTabs: MDI Document Tabs for AutoCAD 2004/2005/2006
    http://www.acadxtabs.com
    Please use plain text.
    *Tony Tanzillo

    Re: DelayLoad in .NET ?

    06-15-2005 12:44 PM in reply to: *Tony Tanzillo
    Thanks. I'll have a look at that.

    --
    http://www.caddzone.com

    AcadXTabs: MDI Document Tabs for AutoCAD 2004/2005/2006
    http://www.acadxtabs.com

    "Albert Szilvasy" wrote in message news:4875885@discussion.autodesk.com...
    If you don't want to do P/Invoke you could check what the current process is
    using the Process class.

    Albert

    "Tony Tanzillo" wrote in message
    news:4875206@discussion.autodesk.com...
    I was puzzled enough by this problem to run Visual Studio
    under Depends.exe's profiler, and it looks like the problem is
    that the DesignMode property is returning false, and the code
    is trying to call into the Interop to get the AutoCAD window
    handle. That also explains why it doesn't fail when I create
    an instance of that form class, but does when I try to derive
    a new form class from it.

    As it turns out, DesignMode returns true only when called from
    the class that is actually instantiated in the designer, but does
    not return true when called from a base class.

    That is supposedly because an instance of a base class is not
    what is being 'designed'.

    So, I have to find another queue to see if the form is actually
    running in AutoCAD or not. I can stick the call to get the Window
    handle inside of a try/catch block, and deal with it that way, but
    by the time the exception bubbles up, all kinds of stuff has
    already happened in the IDE (e.g., the AutoCAD interop DLLs are
    loaded; and AutoCAD starts doing its security thing).

    Before I resort to P/Invoke, any other ideas are welcome :-)

    --
    http://www.caddzone.com

    AcadXTabs: MDI Document Tabs for AutoCAD 2004/2005/2006
    http://www.acadxtabs.com
    Please use plain text.
    *Tony Tanzillo

    Re: DelayLoad in .NET ?

    06-15-2005 01:31 PM in reply to: *Tony Tanzillo
    Thansk again. This is all that was needed:

    protected bool Designing
    {
    get
    {
    Process p = Process.GetCurrentProcess();
    return String.Compare(p.ProcessName, "devenv", true) == 0;
    }
    }

    --
    http://www.caddzone.com

    AcadXTabs: MDI Document Tabs for AutoCAD 2004/2005/2006
    http://www.acadxtabs.com

    "Albert Szilvasy" wrote in message news:4875885@discussion.autodesk.com...
    If you don't want to do P/Invoke you could check what the current process is
    using the Process class.

    Albert

    "Tony Tanzillo" wrote in message
    news:4875206@discussion.autodesk.com...
    I was puzzled enough by this problem to run Visual Studio
    under Depends.exe's profiler, and it looks like the problem is
    that the DesignMode property is returning false, and the code
    is trying to call into the Interop to get the AutoCAD window
    handle. That also explains why it doesn't fail when I create
    an instance of that form class, but does when I try to derive
    a new form class from it.

    As it turns out, DesignMode returns true only when called from
    the class that is actually instantiated in the designer, but does
    not return true when called from a base class.

    That is supposedly because an instance of a base class is not
    what is being 'designed'.

    So, I have to find another queue to see if the form is actually
    running in AutoCAD or not. I can stick the call to get the Window
    handle inside of a try/catch block, and deal with it that way, but
    by the time the exception bubbles up, all kinds of stuff has
    already happened in the IDE (e.g., the AutoCAD interop DLLs are
    loaded; and AutoCAD starts doing its security thing).

    Before I resort to P/Invoke, any other ideas are welcome :-)

    --
    http://www.caddzone.com

    AcadXTabs: MDI Document Tabs for AutoCAD 2004/2005/2006
    http://www.acadxtabs.com
    Please use plain text.