document doc causes Common L. runtime error when using in UC

document doc causes Common L. runtime error when using in UC

5thSth
Advocate Advocate
1,152 Views
9 Replies
Message 1 of 10

document doc causes Common L. runtime error when using in UC

5thSth
Advocate
Advocate

I create a few custom UserControls modules that call on .net api. the point of those custom UCs was to populate a parent UC.

That way, I'd manage my code easier, and create objects that could be reused later on. the problems appear when I try using Visual Studio Designer Toolbox to add the said modules to a parent UC. I get a "common language runtime error" that always originates from

 

"Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;"

 

to illustrate that its not my coding error, I've tried testing it in a stand alone app, and behold, same error appears again.

so far I've had no problems running api commands from User Controls, but this is the first time I try having a childUC with api commands,

and I'm unsure if thats the cause of the problem.

 

I've attached a screenshot of my test app: the error appears as soon as I try to add the child control to the parent UC. (myCommands, and myPlugin are unaltered).

 

code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Autodesk.AutoCAD.ApplicationServices;

namespace why_no_work
{
    public partial class child : UserControl
    {
        public child()
        {
            InitializeComponent();
        }

        Document doc = 
            Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
    }
}

 

I tried cleaning the solution, rebuilding the solution, tried disableing optimize code,...

 

using Acad2017/VS2015/Win10/x64

0 Likes
1,153 Views
9 Replies
Replies (9)
Message 2 of 10

5thSth
Advocate
Advocate

Im sorry to see I gotno responses, but I've made some minimum progress, and maybe it gives you a clue how to solve the problem.

 

unlike the code above where document doc is declared in class body, or in class declaration, I've declared doc in buton_clicked event method, and the UI properly implements in the Designer.

 

I'm interested as to why doc cant be loaded in the initialization part, and if/how its possible to make it happen

 

 

 

namespace why_no_work
{
    public partial class child : UserControl
    {

        public child()
        {
            InitializeComponent();
            //if I initialize doc here, I get the error
} private void child_Load(object sender, EventArgs e) {
//if I initialize doc here, I get the error } private void button1_Click(object sender, EventArgs e) {
//it works here, please help... ...sadface Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; } }

 

0 Likes
Message 3 of 10

norman.yuan
Mentor
Mentor

So, you are saying that the VS itself raises CLR exception, right?

 

When you set your project to AutoCAD .NET assemblies (accoremgd/acdbmgd/acmgd.dll), where you get this DLL files? Do you use the DLLs downloaded from ObjectARX SDK, or you use the ones comes with AutoCAD installation (in AutoCAD installation folder)?

 

If it is the later, then that is likely the reason. Because the AutoCAD installed DLLs has runtime dependencies that are only available when AutoCAD is running, while when you design a Form, the form designed tries to find the dependencies when AutoCAD is not running, thus the error. This was a well-known issue when AutoCAD .NET API was introduced more than 10 years ago. Autodesk later included the DLLs in ObjectARX SDK, which are "strip-down" version, where those unnecessary dependencies (unnecessary for the form development) were removed. If you compare the size of the DLL files from SDK and the Acad installed ones, you would find the ones from SDK is much smaller.

 

So, you should ALWAYS use the DLLs from SDK for the development, BUT DO NOT DEPLOY them (that is why the "COPY LOCAL" property must be set to "False" for these references).

 

Also, event using DLLs from SDK would solve your issue, tie AutoCAD operation to your UI, especailly the AutoCAD event handling, is not so good programming practice. You should do it in a separate class/module and have your UI to use that class/module.

 

Norman Yuan

Drive CAD With Code

EESignature

Message 4 of 10

5thSth
Advocate
Advocate

Thank you for your reply, you told me some valuable infos,and some new things to read about/learn!

 

I'm using the ObjectARX SDK dlls, and all are set to False.

 

 

Also, event using DLLs from SDK would solve your issue, tie AutoCAD operation to your UI, 
especailly the AutoCAD event handling, is not so good programming practice.
You should do it in a separate class/module and have your UI to use that class/module.

 

^could you please clarify this paragraph? What do you mean by "event using DLLs from SDK would solve your issue"?

 

as for using SDK through separate classes, its making the code cumbersome and hard to organise, especially when I'm only trying to call on few lines of code in the form/UC.

 

 

also, Im confused as to why the acad functions are fully functional when applied directly in User Control (PS>UC>document doc =....) ...

but  NOT when I try creating a chain of user controls ( PS> Main UC> Child UC>document doc =....)  

 

-Im not sure if its ObjectARX or VS causing the problem.

 

-also, what comes to mind as a possible problem is that I'm forgetting to disable/enable some (child) UC property to make parent UC ignore non standard/renderable parts of the code

 

- or there's some sort of a rule that Im unfamiliar that prevents non Framework commands  not being renderable if not on the top most element of the UC.

 

 

I hope you can clarify some of my dillemas/doubts. Thank you

 

p.s. once again, thanks for your help, and your blog!

 

0 Likes
Message 5 of 10

norman.yuan
Mentor
Mentor

Sorry for my typo in 

 

Also, event using DLLs from SDK would solve your issue, tie AutoCAD operation to your UI, 
especailly the AutoCAD event handling, is not so good programming practice.
You should do it in a separate class/module and have your U

"event" should be "even".

 

It is rule of thumb to separate UI with complicated data processing logic, especially when the UI is an UserControl that is often meant to be reused in different UI (forms, mostly). The UI should only accept user interaction (clicking...) and pass it on to businesss object/component that does the real job, or show the data passed in by business object/component. So, the UI does not care where the data is from (AutoCAd drawing, or database somewhere), and does not care what happens when user interaction is passed on. The said business object/component should work on its own regardless UI exists or not, or the UIs are different.

 

A quick, simplized business object could be like

 

using Autodesk.AutoCAd.ApplicationServices...

using Autodesk.AutoCAD.Database....

public class DoAcadWork

{

    public int UpdateBlocks()

    {

       //Place your AutoCAD related code here,

       //for example, you update a few block and return the count of blocks being updated

    }

}

 

And in a UI (form), and you create the business object at form level

 

private DoAcadWork _tool=ull;

 

Then you can instanciate it when the form is loaded, or you can instanciates it outside the form and pass it into the UI/form (via UI/Form's constructor, or property).

 

Then assume the form/UI has a button abd a label:

 

private void Button1_Click(...)

{

    int updateCount=tool.UpdateLocks();

    label1.Text=updateCount + " blocks updated!";

}

 

As you can see, in this overly simplified example, you do not need to introduce AutoCAD related references to the UI/form with "using.../Import...". The UI simply does not know AutoCAD at all. It only pass user interaction (clicking) to the business object, and show the resulting data of the business object's action.

 

You could further explore some welknown code pattern, such as MVC, MVVM... or concepts like "separate concerns"...

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 6 of 10

5thSth
Advocate
Advocate

thanks for taking the time to answer

.I'm reading into MVC and MVVM, and as a newby I appreciate such directions, as its hard to develope a good learning curve on your own (if you have any more reading material, please share; I'm about a year into coding, and could use some theory)

 

 

as for the code, I tried your suggestion, and I get the same error. 
Only difference is, that the function can be called from "button and similar events classes), but still, if I try using the method from UC load event, or constructor, I get the error.

 

   public class classB
    {
        public void wtf()
        {

            Document docx = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
        }
    }
   public partial class child : UserControl
    {
        private classB myClass = null;

        public child()
        {
            InitializeComponent();
            //DOESNT WORK HERE
            myClass = new classB();
            myClass.wtf();
        }
private void child_Load(object sender, EventArgs e) { //DOESNT WORK HERE }
private void button1_Click(object sender, EventArgs e) { //WORKS HERE } }

 

I've got the same results when using UC and CustomControl as the child object.

 

in attachment: screenshot, and test project

 

even if I cant get it to work, I'd appreciate if someone could explain or direct me why the behaviour is different for "on call events", and "UC startup" events, at least for future reference

 

 

0 Likes
Message 7 of 10

norman.yuan
Mentor
Mentor

Without knowing the whole details of your UserControl/UI that uses the UserControl, here is my suggestion that you can try:

 

Do not instantiate the classB in the UserControl's default constructor, which is called in VisualStudio's form designer when you add the UserControl to a Form, that is when VisualStudio's form designed tries to figure out what the MdiDocument is when reference "myClass" is created.

 

If the classB is only meant for doing something according to user interaction (like in the button's click event handler) then you can instantiate there, which only happened at run time (i.e AutoCAD is running).

 

Or, you can create a public property so that the classB can be instantiated outside the UserControl and being assigned to the UserControl when needed. That is:

 

in the UserControl class, add code

 

public classB MyClass

{

    set { myClass=value; }

    get { return myClass; }

}

 

 

Then in somewhere of you app, when the UI that contains the UserControl is created and user, you may have code like

 

var myClass=new classB();

MyUserControl.MyClass=myClass;

...

 

 

Norman Yuan

Drive CAD With Code

EESignature

Message 8 of 10

5thSth
Advocate
Advocate

the point of UC/UC structure was simply for code management. 
Everything worked/works file while in one UC (UI+Biz part), I've also seen few examples at spiderinet and through the interface,  where they use acad logic in a single UC, and packs it in a paletteSet, and worked fine 

 

only thing I did differently now is since Ive actually made a bigger script, tried to clean up the code so I could have few smaller UCs, instead of one big one.

-create my "gruped button/combo box sets" so that instead of 10 buttons+10 text boxes, I have 10 x(button+TB) controls. 

 

 

-my UC was supposed to load all printers in one combo box, and populate another combo box with available media. Except for printer settings it was ment to contain few other objects and classes. Since the code became cumbersome, I tried separating the printer part into a separate logic UC. 

 

thats why I was trying to put the code at load event. Since I needed to get the printer/media names as soon as the form starts up

-there are other ways, of preloading it, and creating/sending a string list... but this seemed at the time, like the fastest way

 

next time I'll know better, but I'd still like to know why doc wont work in constructor/on_load, and works on button_click. that seems like arbitrary mechanics, and I'd like to know why (now that I've wasted a week of bug proofing, and loosing my sleep over this ).

 

as for the error type, I've came across few places on net where the error originates because of 32/64 bit compatibility, and I've turned on compatibility mode in vs2015 since I'm @ acad2017 + netFrameworks 4.5(in target framework), so can someone do me a favour and see if the error is maybe version related?

0 Likes
Message 9 of 10

norman.yuan
Mentor
Mentor

You mentioned spiderinet/Kean (Through-the-interface). They are excel in terms of their programming knowledge. However the short blogs they wrote, specific on AutoCAD, mostly do not focus on promoting "best practice" on application structure as whole; the articles in my blogs are the same. Rather, they are meant to show a logic solution to a specific AutoCAD programming task.

 

So, placing AutoCAD specific code inside a UI, especially the UI component's default contructor is not, in most cases, a good practice, even it works.

 

Also, your issue may be even caused specifically by VS2015, or the combination of VS2015/Acad2017, which I do not have access to currently. But as I said, even it works, I'd never place AutoCAD specific code dependent class inside UI component's default constructor and instantiate it there.

Norman Yuan

Drive CAD With Code

EESignature

Message 10 of 10

5thSth
Advocate
Advocate

thank you for your help. I understand the need for practicing consientious coding,  I just didnt expect it to matter as much at my educational/hobbist level, and low sphistication level of my apps 
basically, Im just developing programs for my personal use, and trying to learn coding in the process

 

still, I believe that this concept should have worked, tidy or not. Accessing the .net method from a Child UC shouldt be that big of a deal. 

 

 

attm, I've managed to create a bypass for the error I get when using it in a constructor/elsewhere like this:

 

        public child()
        {
            InitializeComponent();

            if (this.DesignMode)
            { return; }
            else
            {
                Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            }
        }

and I dont see why this is not default behaviour of the .MdiActiveDocument to begin with. 

 

edit: I do understand the possible errors that could arise, had someone for example used document count to procedurally add buttons in the child UC, and it would be impossible for the designer to preview/render it before the code executes. but still, I'd expect the debugger to have a better grasp of the consequences... but then again, lot of things for me to learn. 

P.S. could you please recommend me some good reading material, concepts that someone at my lvl should read? I've gone through c# books, but now, I think I should get a better understanding of the general programing practices and theory... and any pointer would shave off considerable time.. since attm, I'm basically just stumbling on this forum and stackoverflow. TY

0 Likes