NO

Author Topic: Pelles-C + OLE for access to AutoCAD  (Read 5652 times)

Online TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 1641
Re: Pelles-C + OLE for access to AutoCAD
« Reply #15 on: June 12, 2016, 11:46:58 am »
Code: [Select]
...
hr = CLSIDFromProgID(L"AutoCAD.Application", &clsid);
hr = GetActiveObject(&clsid, NULL, &pUnk);
if (pUnk) hr = pUnk->lpVtbl->QueryInterface(pUnk, &IID_IAcadApplication, (void **)&pACad);
if (!pACad) hr = CoCreateInstance(&clsid, NULL, CLSCTX_ALL, &IID_IAcadApplication, (void **)&pACad);
...
parameter is BSTR
Code: [Select]
BSTR bstrCmd = SysAllocString(L"_OPEN ");
IAcadDocument_SendCommand(pACadDoc, bstrCmd );
SysAllocFree(bstrCmd )
« Last Edit: June 13, 2016, 09:15:13 am by TimoVJL »
May the source be with you

Offline sergey

  • Member
  • *
  • Posts: 97
Re: Pelles-C + OLE for access to AutoCAD
« Reply #16 on: June 13, 2016, 02:24:04 am »
All miraculous works!
Thanks again!
Now I will deal with these awkward VARIANTS ...

Offline sergey

  • Member
  • *
  • Posts: 97
Re: Pelles-C + OLE for access to AutoCAD
« Reply #17 on: June 14, 2016, 10:48:17 pm »
I thought that my questions have been exhausted, but I was wrong. Unfortunately.

Getting coordinates of any point in the drawing is one of the fundamental elements in the programming AutoCAD.
Even more important than getting any other primitives AutoCAD.
There is a function that should return in program the coordinates of the selected point from a drawing.
IAcadUtility_GetPoint: VARIANT Point, VARIANT *pResult

But it is (for me) does not work. And I do not understand why a point is present twice as argument this function.

The same in function for return of any entity AutoCAD.
IAcadUtility_GetEntity: IDispatch** Object, VARIANT* PickedPoint

But the function sends a command to the command-line AutoCAD works fine.
Here is a function:
Code: [Select]
int sendcommand(IAcadDocument *pACadDoc)
{
// This method processes any AutoCAD command-line function, including LISP expressions
// VBA: ThisDocument.SendCommand(Command)
//!< Sends a command string from a VB or VBA application to the document for processing
// hr = SendCommand(IAcadDocument *This, BSTR Command);
// IAcadDocument_SendCommand(This, Command);

BSTR bstrC = NULL;
bstrC = SysAllocString ( L"_CIRCLE 90.0,57.0,0.0 30.0 " );
//bstr = SysAllocString ( L"_CIRCLE " );
IAcadDocument_SendCommand(pACadDoc, bstrC);
SysFreeString ( bstrC );

BSTR bstrPL = NULL;
bstrPL = SysAllocString ( L"_3DPOLY " );
IAcadDocument_SendCommand(pACadDoc, bstrPL);
SysFreeString ( bstrPL );

BSTR bstrP = NULL;
bstrP = SysAllocString ( L"_POINT " );
IAcadDocument_SendCommand(pACadDoc, bstrP);
SysFreeString ( bstrP );

return 0;
}

Online TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 1641
Re: Pelles-C + OLE for access to AutoCAD
« Reply #18 on: June 15, 2016, 12:46:10 pm »
The first is relative base point.
Code: [Select]
    HRESULT (STDMETHODCALLTYPE *GetPoint)(IAcadUtility *This, VARIANT Point, VARIANT Prompt, VARIANT *pResult);
#define IAcadUtility_GetPoint(This, Point, Prompt, pResult) (This)->lpVtbl->GetPoint(This, Point, Prompt, pResult)
Quote
Point
Variant (three-element array of doubles); input-only; optional
The 3D WCS coordinates specifying the relative base point.

Prompt
Variant (string); input-only; optional
The text used to prompt the user for input.

RetVal
Variant (three-element array of doubles)
The 3D WCS coordinates of the point the AutoCAD user has selected.

May the source be with you

Offline sergey

  • Member
  • *
  • Posts: 97
Re: Pelles-C + OLE for access to AutoCAD
« Reply #19 on: June 16, 2016, 11:59:44 am »
This function is performed, but does not return:
Code: [Select]
int getpnt(IAcadUtility* pUtility)
{
//!< Gets the point selected in AutoCAD
//hr = GetPoint(IAcadUtility *This, VARIANT Point, VARIANT Prompt, VARIANT *pResult);
//IAcadUtility_GetPoint(This, Point, Prompt, pResult);

VARIANT* ppnt; // returned to pointer Point as variant
VARIANT vpnt[3]; // var-struct for one 3d-point
vpnt[0].vt = VT_R8;
vpnt[0].dblVal = 10.0;
vpnt[1].vt = VT_R8;
vpnt[1].dblVal = 20.0;
vpnt[2].vt = VT_R8;
vpnt[2].dblVal = 30.0;

BSTR bstr = NULL;
bstr = SysAllocString ( L"pick the existing point--> " );
   
VARIANT Prompt;  // struct tagVARIANT as BSTR
Prompt.vt = VT_BSTR;
Prompt.bstrVal = bstr;
SysFreeString ( bstr );

char sVal[64];
IAcadUtility_GetPoint(pUtility, *vpnt, Prompt, ppnt);
sprintf(&sVal[0], "X=%f\nY=%f\nZ=%f", vpnt[0].dblVal, vpnt[1].dblVal, vpnt[2].dblVal);
MessageBox(0, sVal, "Returned Point", 0);

and also this function:
Code: [Select]
int getent(IAcadUtility* pUtility)
{
// VBA .Utility.GetEntity returnObj, basePnt, "Select an object"
//!< Gets an object interactively
//hr = GetEntity(IAcadUtility *This, IDispatch** Object, VARIANT* PickedPoint, VARIANT Prompt);
//IAcadUtility_GetEntity(This, Object, PickedPoint, Prompt);

//IAcadPoint *pPoint;
IDispatch** Object;

BSTR bstr = NULL;
bstr = SysAllocString ( L"pick any entity: " );
   
VARIANT Prompt;  // struct tagVARIANT as BSTR
Prompt.vt = VT_BSTR;
Prompt.bstrVal = bstr;
SysFreeString ( bstr );

VARIANT vpnt[3]; // struct for one 3d-point
vpnt[0].vt = VT_R8;
vpnt[0].dblVal = 10.0;
vpnt[1].vt = VT_R8;
vpnt[1].dblVal = 20.0;
vpnt[2].vt = VT_R8;
vpnt[2].dblVal = 30.0;

char sVal[48];

IAcadUtility_GetEntity(pUtility, Object, vpnt, Prompt);
sprintf(&sVal[0], "X=%f\nY=%f\nZ=%f", vpnt[0].dblVal, vpnt[1].dblVal, vpnt[2].dblVal);
MessageBox(0, sVal, 0, 0);

return 0;
}

Online TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 1641
Re: Pelles-C + OLE for access to AutoCAD
« Reply #20 on: June 16, 2016, 01:36:45 pm »
nanoCAD
Code: [Select]
...
if (pUtility) {
VARIANT var1, var2, var3;
VariantInit(&var1);  // EMPTY
var2.vt = VT_BSTR;
var2.bstrVal = SysAllocString(L"Pick a point");  // PROMPT
hr = InanoCADUtility_GetPoint(pUtility, var1, var2, &var3);
if (!hr) {
TCHAR szTmp[64];
if (var3.vt == (VT_ARRAY | VT_R8)) {
double x,y,z;
x = ((double*)var3.parray->pvData)[0];
y = ((double*)var3.parray->pvData)[1];
z = ((double*)var3.parray->pvData)[2];
SafeArrayDestroy(var3.parray);  // free
sprintf(szTmp, TEXT("%X, %f, %f, %f"), var3.vt, x, y, z);
MessageBox(0, szTmp, "Pick a point", MB_OK);
}
}
SysFreeString(var2.bstrVal);
/// InanoCADUtility_Release(pUtility);
}
...
« Last Edit: June 16, 2016, 01:59:03 pm by TimoVJL »
May the source be with you

Offline sergey

  • Member
  • *
  • Posts: 97
Re: Pelles-C + OLE for access to AutoCAD
« Reply #21 on: June 16, 2016, 03:48:26 pm »
I was in 2005 concluded that AutoCAD is not the best in the implementation of CAD family.
I recently looked ProgeCAD and saw that he was no worse, and in some ways even better than AutoCAD.
There are Chinese ZWCAD and Russian NanoCAD.
Both tend to "catch up" AutoCAD, I do not see in them something good and superior to its prototype.

My strange interest in AutoCAD because:
1. in Russia has a very strong position of the product,
2. all my friends work there and do not want to switch to anything else.
3. In a variety of forums for 15 years (or even longer) there is a question:
"How to program AutoCAD from the outside."
4. AutoDesk-ObjectARX programming tool works "inside" of AutoCAD in this siysle no different from VBA.
5. VB-6 successfully working "outside" of AutoCAD, but MicroSoft long been his "forbidden" and at any moment can throw out the OS Windows VB6 support library.
6. VB.Net can program AutoCAD, but it has a number of significant drawbacks.

Offline sergey

  • Member
  • *
  • Posts: 97
Re: Pelles-C + OLE for access to AutoCAD
« Reply #22 on: June 17, 2016, 05:52:15 pm »
Using 'VARIANT' in AutoCAD is puzzling.
But even worse is that the different functions of the same subject is represented by different types of data.
For example AcadPoint:
Quote
1. as AutoCAD Object
2. as VARIANT with 3 Variant x, y, z
3. as VARIANT with 3 Double x, y, z

IAcadUtility_GetPoint () absolutely does not work in a separate function, but works in the 'main' function.
Here the lines of code for IAcadUtility_GetPoint () inserted in the very first working example and specially shifted to the left:
Code: [Select]
//... ... ...
IAcadUtility_CreateTypedArray(pUtility, &vpt1, VT_R8, &sarray1);
IAcadUtility_CreateTypedArray(pUtility, &vpt2, VT_R8, &sarray2);
// draw Points and Line in ModelSpace
IAcadModelSpace_AddPoint(pModelSpace, vpt1, &pPoint);
IAcadModelSpace_AddPoint(pModelSpace, vpt2, &pPoint);
IAcadModelSpace_AddLine(pModelSpace, vpt1, vpt2, &pLine);
if (pLine) {
MessageBox(0,"Line is drawn", szAppName, MB_OK);
} else MessageBox(0,"No Line",0,0);

VARIANT vpnt; // returned to pointer Point as variant

BSTR bstr = NULL;
bstr = SysAllocString ( L"pick the existing point--> " );
  VARIANT Prompt;  // struct tagVARIANT as BSTR
Prompt.vt = VT_BSTR;
Prompt.bstrVal = bstr;
SysFreeString ( bstr );

HRESULT hr = IAcadUtility_GetPoint(pUtility, vpt1, Prompt, &vpnt);
if (!hr) {
char sVal[64];
if (vpnt.vt == (VT_ARRAY | VT_R8)) {
double x,y,z;
x = ((double*)vpnt.parray->pvData)[0];
y = ((double*)vpnt.parray->pvData)[1];
z = ((double*)vpnt.parray->pvData)[2];
SafeArrayDestroy(vpnt.parray);  // free
sprintf(sVal, "%X\nX=%f\nY=%f\nZ=%f", vpnt.vt, x, y, z);
MessageBox(0, sVal, "Returned Point", 0);
}
}

}
}
}
IAcadApplication_Release(pACad); // free object 'AutoCAD.Application'
} else
MessageBox(0, "No AutoCAD.Application ?", szAppName, MB_OK);
}
CoUninitialize();
return 0;
}

Offline sergey

  • Member
  • *
  • Posts: 97
Re: Pelles-C + OLE for access to AutoCAD
« Reply #23 on: June 17, 2016, 09:00:45 pm »
Some changes.
GetPoint and CetCoordinates now working.
GetObject not.
Some working SendCommand (natyve for AutoCAD and LISP).

Thanks Timo!

Source project (for acad-2007),
adscreeshot:

Online TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 1641
Re: Pelles-C + OLE for access to AutoCAD
« Reply #24 on: June 18, 2016, 03:47:43 pm »
Thank's Sergey
You can use Utility.Prompt for to show some messages to AutoCAD as MessageBox hangs in debugger sometimes in nanoCAD.
Then i have to from menu  Debug -> Break and press  F5 couple times.

You can send PM to me if issue don't belong to this forum at first time.
May the source be with you

Offline sergey

  • Member
  • *
  • Posts: 97
Re: Pelles-C + OLE for access to AutoCAD
« Reply #25 on: June 19, 2016, 08:27:30 pm »
Here is a list of functions that have been added to the program and called from main:
Code: [Select]
// set 3d-point as reference only (base-point x=0, y=0, z=0)
VARIANT setpnt0(IAcadUtility* pUtility, IAcadModelSpace* pModelSpace);
// draw 3d-point by predefined coordinates
IAcadPoint* setpnt(IAcadUtility* pUtility, IAcadModelSpace* pModelSpace, double* dblPnt, IAcadPoint* pPoint);
// draw 3d-line by predefined coordinates of 2 Points
IAcadLine* setline(IAcadUtility* pUtility, IAcadModelSpace* pModelSpace, double* dblPnt1, double* dblPnt2);
// get coordinates of 3d-point picked by user
int getpnt(IAcadUtility* pUtility, VARIANT Point);
// get integer, string entered by user
int getint(IAcadUtility* pUtility);
int getstr(IAcadUtility* pUtility);
// get any Acad entity picked by user
int getent(IAcadUtility* pUtility, IAcadEntity* pEntity);
// send some AutoCAD commands in drawing
int sendcommand(IAcadDocument *pACadDoc);
// get coordinates of any point picked by user
int getcoords(IAcadModelSpace* pModelSpace, IAcadPoint *pPoint);
// get info (EntityName) about entityes in drawing
int getentinfo(IAcadModelSpace *pModelSpace, IAcadEntity* pEntity);
Of these, only one
Code: [Select]
getent(IAcadUtility* pUtility, IAcadEntity* pEntity)still does not work as expected.

It is the function call:
Code: [Select]
IAcadUtility_GetEntity(pUtility, (IDispatch**)&pEntity, &vpnt, Prompt);this function must return a reference to any object in a drawing - 'pEntity'.
But this does not happen, it returns only 'vpnt' - the point picked by mouse on the drawing - and require real object AcadEntity.
This function opens the door to the drawing database and gives the programmer greater powers, such as copy, move, transformation and removal of objects.

And small change in function:
getstr()-->IAcadUtility_GetString() had to use a MessageBoxW() instead MessageBox()
because the cast wctomb() gives a mixture of the previous string variables.

In function sendcommand() some commands AutoCAD shows that it sent to the its own command line.
Unfortunately, not all commands are executed one after another if placed in this one function. The reasons are not clear.

Online TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 1641
Re: Pelles-C + OLE for access to AutoCAD
« Reply #26 on: June 20, 2016, 11:15:36 am »
Try this:
Code: [Select]
int CadGetEntity(IAcadUtility* pUtility, IAcadEntity **pEntity, double pt[3], WCHAR *wcPromt)
{ // (GetEntity)(IAcadUtility *This, IDispatch** Object, VARIANT* PickedPoint, VARIANT Prompt);
HRESULT hr;
IDispatch *pDisp = NULL;
VARIANT var2, var3;
VariantInit(&var2);
var3.vt = VT_BSTR;
var3.bstrVal = SysAllocString(wcPromt);
hr = IAcadUtility_GetEntity(pUtility, &pDisp, &var2, var3);
if (!hr) {
if (pDisp) {
hr = pDisp->lpVtbl->QueryInterface(pDisp, &IID_IAcadEntity, (void**)pEntity);
}
if (var2.vt == (VT_ARRAY | VT_R8)) {
pt[0] = ((double*)var2.parray->pvData)[0];
pt[1] = ((double*)var2.parray->pvData)[1];
pt[2] = ((double*)var2.parray->pvData)[2];
SafeArrayDestroy(var2.parray);
}
}
if (!pDisp) *pEntity = NULL;
SysFreeString(var3.bstrVal);
return hr;
}
« Last Edit: June 20, 2016, 12:28:48 pm by TimoVJL »
May the source be with you

Offline sergey

  • Member
  • *
  • Posts: 97
Re: Pelles-C + OLE for access to AutoCAD
« Reply #27 on: June 20, 2016, 09:47:01 pm »
After adding 5 lines:
Code: [Select]
if (pDisp) {
hr = pDisp->lpVtbl->QueryInterface(pDisp, &IID_IAcadEntity, (void**)pEntity);
char sVal[64];
wchar_t* pResult;
IAcadEntity_get_EntityName(*pEntity, &pResult);
sprintf(sVal, TEXT("EntType=%p EntName %ls"), *pEntity, pResult);
MessageBox(0, sVal, "Returned Entity Info", 0);
}
I saw that the 'Entity' returns to the program your type and name.
And I'm over it unsuccessfully puffed 4 days.
I know exactly what that decision I would not have found it - I do not understand COM-OLE.

I do not know how to thank you.

That's what the program draws in AutoCAD - quite a bit, but this set of features from which it is already possible to build useful things:
« Last Edit: June 20, 2016, 10:08:54 pm by sergey »

Offline sergey

  • Member
  • *
  • Posts: 97
Re: Pelles-C + OLE for access to AutoCAD
« Reply #28 on: June 26, 2016, 08:40:44 pm »
Strange things happen when you call AutoCAD functions (see userutils1.c file).
Quote
1.
startAutoCAD() -->
   GetArrow () -->
      getpnt() --> dblPnt1[0], dblPnt1[1]; (X, Y)
      Arrow12) -->
         drawLWP() --> IAcadModelSpace_AddLightWeightPolyline() not executed
[[Crash program here]]
         rotpnt() --> IAcadUtility_CreateTypedArray()
         rottxt() --> IAcadModelSpace_AddText()
         IAcadLWPolyline_Rotate(pLWP, rotPoint, angle)
         IAcadText_Rotate(pText, txtPoint, angle)

2. preinitialized dblPnt1[0], dblPnt1[1]
startAutoCAD() --> dblPnt1[0] = 53.000; dblPnt1[1] = 64.000;
   //getpnt() --> commented out as no needed
   drawLWP() --> IAcadModelSpace_AddLightWeightPolyline()
   rotpnt() -->
   double angle = 0.35;
   IAcadLWPolyline_Rotate() --> not executed
[[Crash program here]]
   rottxt() -->
   IAcadModelSpace_AddText() -->
   IAcadText_Rotate() -->
In the first example in function getpnt() - arrays get their values dblPnt1[0], dblPnt1[1] from picked point.
These values are then accessed from other functions (I tested it).
But AutoCAD functions as if they do not see and don't create PolyLine.

In the second example function getpnt() commented out, and the preset values of X and Y in function drawLWP() AutoCAD accepts and creates a PolyLine.

Other AutoCAD functions to work with objects already created do not work. For example:
IAcadLWPolyline_Move()

It may be required some other data structure VARIANT (not SAFEARRAY) to supply these functions (?).

Online TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 1641
Re: Pelles-C + OLE for access to AutoCAD
« Reply #29 on: June 27, 2016, 07:18:04 pm »
This may help:
Code: [Select]
HRESULT CadTestLWPL(IAcadUtility *pUtility, IAcadModelSpace *pModelSpace)
{
HRESULT hr;
SAFEARRAY* psarr = SafeArrayCreateVector(VT_VARIANT, 0, 6);
((VARIANT*)psarr->pvData)[0].vt = VT_R8;
((VARIANT*)psarr->pvData)[0].dblVal = 0.0;
((VARIANT*)psarr->pvData)[1].vt = VT_R8;
((VARIANT*)psarr->pvData)[1].dblVal = 0.0;
((VARIANT*)psarr->pvData)[2].vt = VT_R8;
((VARIANT*)psarr->pvData)[2].dblVal = 100.0;
((VARIANT*)psarr->pvData)[3].vt = VT_R8;
((VARIANT*)psarr->pvData)[3].dblVal = 100.0;
((VARIANT*)psarr->pvData)[4].vt = VT_R8;
((VARIANT*)psarr->pvData)[4].dblVal = 0.0;
((VARIANT*)psarr->pvData)[5].vt = VT_R8;
((VARIANT*)psarr->pvData)[5].dblVal = 100.0;

VARIANT vpt1;
hr = IAcadUtility_CreateTypedArray(pUtility, &vpt1, VT_R8, psarr);
SafeArrayDestroy(psarr); // free array

if (!hr) {
IAcadLWPolyline *pLWPL;
hr = IAcadModelSpace_AddLightWeightPolyline(pModelSpace, vpt1, &pLWPL);
}
return hr;
}
« Last Edit: June 27, 2016, 07:41:32 pm by TimoVJL »
May the source be with you