Pelles C forum

C language => Work in progress => Topic started by: sergey on May 27, 2016, 03:29:06 PM

Title: Pelles-C + OLE for access to AutoCAD
Post by: sergey on May 27, 2016, 03:29:06 PM
Continuing the theme:
http://forum.pellesc.de/index.php?topic=6936.0

OLE-Automation allows you to connect to such "heavy" server as AutoCAD.
There is no need to use C ++.
There is no need to connect to the project AutoCAD library such as a *.tlb or *.dll
To this end it is necessary and sufficient to know AutoCAD objects available from the Windows registry.
It is enough to know the names of the objects and the names of the methods of these objects.
Console application written in Pelles-C, is connected to the AutoCAD and is further AutoCAD dialog box - Drawing-templates.
This means that AutoCAD objects actually available for programming.

Important features:
1. The absence of complex (and often obscure) programming constructs.
2. assume that the program does not depend on the version of AutoCAD (almost every following post-2000-year version of AutoDesk that any changes in the program access to your product).

Of course there is not the program, but only a 'template', have to learn the basics of OLE-Automation.

There is another interesting tool for OLE-Automation - 'disphelper'.
But it is poorly documented.
Here is an example of using 'disphelper' for access to MS-Excel from C-lang.
Unfortunately I was not able to compile this example in Pelles-C.
In MS-VC2005 environment it works.

References:
https://support.microsoft.com/en-us/kb/181473
https://en.wikipedia.org/wiki/IDispatch
http://disphelper.sourceforge.net/
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL on May 28, 2016, 10:55:57 AM
If you like wrappers download this (http://www.pellesc.de/source_start.php?file=autoword.zip) for Word automation.
It use AutoWrap (https://support.microsoft.com/en-us/kb/238393) example.

https://support.microsoft.com/en-us/kb/216686

https://support.microsoft.com/en-us/kb/216388
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on May 28, 2016, 10:15:41 PM
Good examples.
However, Word and Excel Microsoft products.
Therefore, these examples demonstrate quite well themselves and Microsoft.

I am interested in a different problem: how to get to the object model of AutoCAD?
And along the way there is an insurmountable obstacle: the initialization of the object 'AutoCAD.Document'.
Because almost all of the objects needed to work/programming in AutoCAD, only available as a 'AutoCAD.Document.<ALL_OBJECTS_OF_THE_AutoCAD>'.

I rewrote your example for MS-WORD eg for AutoCAD.
But as in the previous examples, the object initialization 'AutoCAD.Document' NOT occurs.
It does not occur even when the project has acax17enu.h file (in which almost the entire object model AutoCAD).
But I have not found a way to connect to the file.

From this I conclude: all of these examples to work exclusively through Windows registry.
And there is no object 'AutoCAD.Document'.

If anyone knows the solution to this problem (?).
With the example of a few lines of code, not blah-blah-blah ...
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL on May 29, 2016, 12:40:12 PM
DispHelper test:
Code: [Select]
#define WIN32_LEAN_AND_MEAN
#include "disphelper.h"
#include <stdio.h>
#pragma comment(lib, "user32")

#define HR_TRY(func) if (FAILED(func)) { printf("\n## Fatal error on line %d.\n", __LINE__); goto cleanup; }

int ACadSample1(void)
{
DISPATCH_OBJ(pApp);
dhInitialize(TRUE);
dhToggleExceptions(TRUE);

HR_TRY( dhCreateObject(L"AutoCAD.Application", NULL, &pApp) );
dhPutValue(pApp, L".Visible = %b", TRUE);
HR_TRY( dhCallMethod(pApp, L".Documents.Add") );
DISPATCH_OBJ(pDoc);
HR_TRY( dhGetValue(L"%o", &pDoc, pApp, L".ActiveDocument") );

cleanup:
dhToggleExceptions(FALSE);
SAFE_RELEASE(pApp);
dhUninitialize(TRUE);
return 0;
}

int main(void)
{
ACadSample1();
return 0;
}
Look acadauto.chm for objects.

For old IntelliCAD 6.x engine (progeCAD Smart 2009) AutoWrap example too:
Code: [Select]
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <ole2.h>
#include <stdio.h>

#pragma lib "user32.lib"
#pragma lib "ole32.lib"
#pragma lib "oleaut32.lib"
#pragma lib "uuid.lib"

HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp,
      WCHAR* ptName, int cArgs, ...);

//int main(int argc, char* argv[])
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
// Initialize COM for this thread...
   CoInitialize(NULL);

   // Get CLSID for our server...
   CLSID clsid;
   HRESULT hr = CLSIDFromProgID(L"ICad.Application", &clsid);

   if(FAILED(hr)) {

      MessageBox(NULL, "CLSIDFromProgID() failed", "Error", 0x10010);
      return -1;
   }

   // Start server and get IDispatch...
   IDispatch *pApp;
   hr = CoCreateInstance(&clsid, NULL, CLSCTX_LOCAL_SERVER, &IID_IDispatch, (void **)&pApp);
   if(FAILED(hr)) {
      MessageBox(NULL, "ICad not registered properly", "Error", 0x10010);
      return -2;
   }

   // Make it visible (i.e. app.visible = 1)
   {
      VARIANT x;
      x.vt = VT_I4;
      x.lVal = 1;
      AutoWrap(DISPATCH_PROPERTYPUT, NULL, pApp, L"Visible", 1, x);
   }
   IDispatch *pLib = NULL;
   {
      VARIANT result;
      VariantInit(&result);
      AutoWrap(DISPATCH_PROPERTYGET, &result, pApp, L"Library", 0);
      pLib = result.pdispVal;
   }
   IDispatch *pDoc = NULL;
   {
      VARIANT result;
      VariantInit(&result);
      AutoWrap(DISPATCH_PROPERTYGET, &result, pApp, L"ActiveDocument", 0);
      pDoc = result.pdispVal;
   }
   IDispatch *pModelSpace = NULL;
   {
      VARIANT result;
      VariantInit(&result);
      AutoWrap(DISPATCH_PROPERTYGET, &result, pDoc, L"ModelSpace", 0);
      pModelSpace = result.pdispVal;
   }

   IDispatch *pPoint1 = NULL;
   {
      VARIANT result;
      VariantInit(&result);
      VARIANT parm1, parm2, parm3;
      parm1.vt = VT_R8; parm1.dblVal = 0.0; // Z
      parm2.vt = VT_R8; parm2.dblVal = 0.0; // Y
      parm3.vt = VT_R8; parm3.dblVal = 0.0; // X
      AutoWrap(DISPATCH_PROPERTYGET | DISPATCH_METHOD, &result, pLib, L"CreatePoint", 3, parm1, parm2, parm3);
      pPoint1 = result.pdispVal;
   }
   IDispatch *pPoint2 = NULL;
   {
      VARIANT result;
      VariantInit(&result);
      VARIANT parm1, parm2, parm3;
      parm1.vt = VT_R8; parm1.dblVal =   0.0; // Z
      parm2.vt = VT_R8; parm2.dblVal = 100.0; // Y
      parm3.vt = VT_R8; parm3.dblVal = 100.0; // X
      AutoWrap(DISPATCH_PROPERTYGET | DISPATCH_METHOD, &result, pLib, L"CreatePoint", 3, parm1, parm2, parm3);
      pPoint2 = result.pdispVal;
   }
   IDispatch *pLine1 = NULL;
   {
      VARIANT result;
      VariantInit(&result);
      VARIANT parm1, parm2;
      parm1.vt = VT_DISPATCH; parm1.pdispVal = pPoint2;
      parm2.vt = VT_DISPATCH; parm2.pdispVal = pPoint1;
      AutoWrap(DISPATCH_PROPERTYGET | DISPATCH_METHOD, &result, pModelSpace, L"AddLine", 2, parm1, parm2);
      pLine1 = result.pdispVal;
   }
   {
      VARIANT x;
      x.vt = VT_I4;
      x.lVal = 1;
      AutoWrap(DISPATCH_PROPERTYPUT | DISPATCH_METHOD, NULL, pDoc, L"Regen", 1, x);
   }


   // Release references...
   if (pModelSpace) pApp->lpVtbl->Release(pModelSpace);
   if (pDoc) pApp->lpVtbl->Release(pDoc);
   if (pLib) pApp->lpVtbl->Release(pLib);
   if (pApp) pApp->lpVtbl->Release(pApp);
   // Uninitialize COM for this thread...
   CoUninitialize();
      return 0;
}

//
// AutoWrap() - Automation helper function...
//
HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp,
      WCHAR* ptName, int cArgs, ...)
{
      // Begin variable-argument list...
      va_list marker;
      va_start(marker, cArgs);

      if(!pDisp) {
            MessageBox(NULL, "NULL IDispatch passed to AutoWrap()",
                       "Error", 0x10010);
            exit(0);
      }

      // Variables used...
      DISPPARAMS dp = { NULL, NULL, 0, 0 };
      DISPID dispidNamed = DISPID_PROPERTYPUT;
      DISPID dispID;
      HRESULT hr;
      char buf[200];
      char szName[200];

      // Convert down to ANSI
      WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL);

      // Get DISPID for name passed...
      hr = pDisp->lpVtbl->GetIDsOfNames(pDisp, &IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT,
                                &dispID);
      if(FAILED(hr)) {
            sprintf(buf,
                    "IDispatchGetIDsOfNames(\"%s\") failed w/err0x%08lx",
                    szName, hr);
            MessageBox(NULL, buf, "AutoWrap()", 0x10010);
            return hr;
      }

      // Allocate memory for arguments...
      VARIANT *pArgs = malloc((cArgs+1) * sizeof(VARIANT));

      // Extract arguments...
      for(int i=0; i<cArgs; i++) {
            pArgs[i] = va_arg(marker, VARIANT);
      }

      // Build DISPPARAMS
      dp.cArgs = cArgs;
      dp.rgvarg = pArgs;

      // Handle special-case for property-puts!
      if(autoType & DISPATCH_PROPERTYPUT) {
            dp.cNamedArgs = 1;
            dp.rgdispidNamedArgs = &dispidNamed;
      }

      // Make the call!
      hr = pDisp->lpVtbl->Invoke(pDisp, dispID, &IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType,
                         &dp, pvResult, NULL, NULL);
      if(FAILED(hr)) {
            free(pArgs);
            sprintf(buf,
                    "IDispatchInvoke(\"%s\"=%08lx) failed w/err 0x%08lx",
                    szName, dispID, hr);
            MessageBox(NULL, buf, "AutoWrap()", 0x10010);
            return hr;
      }
      // End variable-argument section...
      va_end(marker);

      free(pArgs);

      return hr;
}
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on May 30, 2016, 08:21:51 PM
Another great example! Thank you for your attention.

I want to note: 'ProgeCAD' draws the 'Line' but does not draw 'Poin1' and 'Poin2'.
It would be good to add a strings for connection to the existing 'ICad' object (i.e. check out 'ICad' that is already running on your computer).

'ACADAUTO.CHM' documents and other reading since 2005 (I was something written in VBA and VB6.

All my attempts to adapt your code to AutoCAD failed.
Chain:
'Application' -> 'ActiveDocument' -> 'ModelSpace'
does not work, 'Point' and 'Line' are NOT created.
I have added the 'Documents' and 'Document' that through the chain:
'Application' -> 'Documents' -> 'Document' -> 'ModelSpace'
create a 'Point' and 'Line' - it did not happen.
Formally all of the objects 'AutoCAD' up to 'ModelSpace' are created.

Of course I can throw this venture.
Object Model 'IntelliCad' more accessible than the 'AutoCAD'.
I can write the right things on VisualBasic-6 for the 'AutoCAD'.
But I'm haunted by a complete lack of information about what 'AutoCAD' is available for the programming of 'Plain-C'.
Even not-free 'Object-ARX VC ++' from AutoDesk work as VBA within 'AutoCAD' and change (obsolete) every three years.
Some success in this business reached only Delphi-programmers. But it 'OOP'.

If you have the opportunity and have a desire to look my code in the 'AutoCAD'.
Maybe things are not so hopeless and you can fix the code to make it work.

Thanks again for your attention and good advice.

Here my example:
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on May 31, 2016, 01:39:06 AM
Yes, but ...  so:
To create a point, use the AddPoint method.
-------------------------------------------
Creates a Point object at a given location.
Signature
---------
RetVal = object.AddPoint(Point)
Object
------
ModelSpace Collection, PaperSpace Collection, Block
The object or objects this method applies to.
Point
-----
Variant (three-element array of doubles); input-only
The coordinates of the point to be created.
RetVal
------
Point object - The newly created Point object.

Sub Example_AddPoint()
    ' This example creates a point in model space.
    Dim pointObj As AcadPoint
    Dim location(0 To 2) As Double
   
    ' Define the location of the point
    location(0) = 5#: location(1) = 5#: location(2) = 0#
   
    ' Create the point
    Set pointObj = ThisDrawing.ModelSpace.AddPoint(location)
    ZoomAll
   
End Sub
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on June 01, 2016, 12:51:00 AM
Add some code ProgeCAD:
Code: [Select]
   IDispatch *pAddPnts = NULL;
   {
VARIANT result;
VariantInit(&result);
VARIANT parm1, parm2;
parm1.vt = VT_DISPATCH; parm1.pdispVal = pPoint1;
parm2.vt = VT_DISPATCH; parm2.pdispVal = pPoint2;
AutoWrap(DISPATCH_PROPERTYGET | DISPATCH_METHOD, &result, pModelSpace, L"AddPointEntity", 1, parm1);
AutoWrap(DISPATCH_PROPERTYGET | DISPATCH_METHOD, &result, pModelSpace, L"AddPointEntity", 1, parm2);
pAddPnts = result.pdispVal;
   }

2 small 'Points' on ends of the 'Line':
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL on June 01, 2016, 09:40:07 AM
I think in older IntelliCAD (<7)/progeCAD uses Library.CreatePoint result Point only as location for other functions.
AutoCAD use Utility.CreateTypedArray Point1, vbDouble, 0,0,0 for creating array of 3 doubles for same purpose.
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on June 06, 2016, 10:45:58 AM
1. AutoCAD plases in Windows-registry ONLY 'CLSID_AcadApplication'
   but to programming AutoCAD you need also (as minimum) 2 objects:
   CLSID_AcadDocument
   CLSID_AcadModelSpace
   wich missing (not presents) in Windows-registry.

2. Script-lang Python has module named 'comtypes-1.1.2',
   wich helpfully connect to AutoCAD and programmin it.
   https://github.com/enthought/comtypes/releases

3. sample-code Python (ver.2.7):
Code: [Select]
   c:\cmd.exe python
   import array
   import comtypes.client
   #Get running instance of the AutoCAD application
   app = comtypes.client.GetActiveObject("AutoCAD.Application")
   #Get the ModelSpace object
   ms = app.ActiveDocument.ModelSpace
   #Add a POINT in ModelSpace
   pt = array.array('d', [0,0,0])
   point = ms.AddPoint(pt)
   #Add a LINE in ModelSpace
   pt1 = array.array('d', [1.0,1.0,0])
   pt2 = array.array('d', [2.0,2.0,0])
   line = ms.AddLine(pt1, pt2)
4. Roman Haritonov wrote Python-application
   https://github.com/reclosedev/pyautocad
   http://readthedocs.org/docs/pyautocad
   to 'manage' AutoCAD by 'comtypes-1.1.2':
   pyautocad.api: api.py types.py utils.py compat.py cache.py tables.py
   
5. Thomas Heller wrote by MS-VisuaStudio 'comtypes-1.1.2' (in 2005--2015)
   https://github.com/enthought/comtypes
   https://pypi.python.org/pypi/comtypes
   comtypes-1.1.2.zip\comtypes-1.1.2\source\ has 2 files:
   AvmcIfc.tlb and AvmcIfc.dll
   to enable write C/C++ applications for COM-OLE Automation,
   that enable also programming AutoCAD.
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL on June 06, 2016, 05:55:13 PM
Quite awful with C >:(
Code: [Select]
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define INITGUID
#include <ole2.h>

#define COBJMACROS
#define __AutoCAD_INCLUDEALIAS
typedef DWORD OLE_COLOR;
//line 3197: //typedef INT64 LONG_PTR ; /// allready in basetsd.h
//#include "acax17enu.h" // from acax18enu.tlb
#include "acad.h"

#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "oleaut32.lib")

char *szAppName = "AutoCadTest";

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
HRESULT hr;
CLSID clsid;
IUnknown *pUnk = NULL;
IAcadApplication *pACad = NULL;
IAcadDocument *pACadDoc = NULL;
IAcadUtility* pUtility = NULL;
IAcadDatabase *pDataBase = NULL;
IAcadModelSpace *pModelSpace = NULL;

CoInitialize(NULL);
hr = CLSIDFromProgID(L"AutoCAD.Application", &clsid);
hr = GetActiveObject(&clsid, NULL, (IUnknown **)&pUnk);
if (pUnk) hr = pUnk->lpVtbl->QueryInterface(pUnk, &IID_IAcadApplication, (void **)&pACad);
if (!pACad) hr = CoCreateInstance(&clsid, NULL, CLSCTX_ALL, &IID_IAcadApplication, (void **)&pACad);
if (pACad) {
IAcadApplication_put_Visible(pACad, -1);
IAcadApplication_get_ActiveDocument(pACad, &pACadDoc);
if (pACadDoc) {
IAcadDocument_get_Utility(pACadDoc, &pUtility);
IAcadDocument_get_Database(pACadDoc, &pDataBase);
if (pDataBase) {
IAcadDatabase_get_ModelSpace(pDataBase, &pModelSpace);
if (pModelSpace) {
IAcadLine *pLine;
VARIANT vpt1, vpt2; // for points
SAFEARRAY sarray1, sarray2; // for arrays
SAFEARRAYBOUND sab1[1], sab2[1];
sab1[0].lLbound = 0; sab1[0].cElements = 3;
sab2[0].lLbound = 0; sab2[0].cElements = 3;

VARIANT parm1[3], parm2[3];
parm1[0].vt = VT_R8; parm1[0].dblVal = 0.0; // X
parm1[1].vt = VT_R8; parm1[1].dblVal = 0.0; // Y
parm1[2].vt = VT_R8; parm1[2].dblVal = 0.0; // Z
parm2[0].vt = VT_R8; parm2[0].dblVal = 100.0; // X
parm2[1].vt = VT_R8; parm2[1].dblVal = 100.0; // Y
parm2[2].vt = VT_R8; parm2[2].dblVal = 0.0; // Z

sarray1.cDims = 1;
sarray1.fFeatures = FADF_VARIANT | FADF_HAVEVARTYPE | FADF_FIXEDSIZE | FADF_STATIC;
sarray1.cbElements = sizeof(VARIANT);
sarray1.cLocks = 0;
sarray1.pvData = &parm1;
sarray1.rgsabound[0].lLbound = 0;
sarray1.rgsabound[0].cElements = 3;

sarray2.cDims = 1;
sarray2.fFeatures = FADF_VARIANT | FADF_HAVEVARTYPE | FADF_FIXEDSIZE | FADF_STATIC;
sarray2.cbElements = sizeof(VARIANT);
sarray2.cLocks = 0;
sarray2.pvData = &parm2;
sarray2.rgsabound[0].lLbound = 0;
sarray2.rgsabound[0].cElements = 3;

IAcadUtility_CreateTypedArray(pUtility, &vpt1, VT_R8, &sarray1);
IAcadUtility_CreateTypedArray(pUtility, &vpt2, VT_R8, &sarray2);

IAcadModelSpace_AddLine(pModelSpace, vpt1, vpt2, &pLine);
if (pLine) {
MessageBox(0,"Line",0,0);
} else MessageBox(0,"No Line",0,0);
}
}
}
IAcadApplication_Release(pACad);
} else
MessageBox(0, "No AutoCAD.Application ?", szAppName, MB_OK);
CoUninitialize();
return 0;
}
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on June 06, 2016, 10:23:03 PM
I see:
#include "acad.h"
It is from acad.exe ?
But can't compile acad.exe by 'FtypeLib.exe' - ERROR!
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL on June 06, 2016, 11:01:46 PM
No, it's from ACAD.TLB (v. 2000) created with FTypeLib.
Use your version of header, as example still needs IID_IAcadApplication from there.
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on June 06, 2016, 11:58:45 PM
Paste from the previous 'acax17enu.h' ...
It really works!
And while AutoCadTest.exe = 22.5 kB

I did not know which way to look.
Even I started to do something in Python and VC ++.
But it did not work.

Now, for the expansion of this program. I will write functions.

I do not know how to thank you for your help.
THANK YOU! THANK YOU! THANK YOU!
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL on June 10, 2016, 08:15:57 PM
If someone don't want to include needlessly GUIDs to exe, here is some ideas :
Code: [Select]
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

const IID IID_ACAD_15 = {0x8e75d910,0x3d21,0x11d2,{0x85,0xc4,0x08,0x00,0x09,0xa0,0xc6,0x26}};
const IID IID_ACAD_16 = {0x93bc4e71,0xafe7,0x4aa7,{0xbc,0x07,0xf8,0x0a,0xcd,0xb6,0x72,0xd5}};
const IID IID_ACAD_17 = {0x8f17437c,0x2efb,0x4fc4,{0x81,0x88,0xee,0xfa,0x50,0xfe,0x71,0x47}};
const IID IID_ACAD_18 = {0x84f323fc,0xc179,0x4704,{0x87,0xe7,0xe3,0xd5,0x76,0xc2,0x59,0x9e}};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
HKEY hk;
TCHAR szTmp[260];
//ReadRegStr(HKEY_CLASSES_ROOT, "AutoCAD.Application\\CurVer", NULL, szTmp, sizeof(szTmp));
//if (RegOpenKey(HKEY_CLASSES_ROOT, TEXT("AutoCAD.Application\\CurVer"), &hk) == ERROR_SUCCESS) {
if (RegOpenKey(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Classes\\AutoCAD.Application\\CurVer"), &hk) == ERROR_SUCCESS) {
DWORD dwType, dwSize;
dwSize = sizeof(szTmp);
RegQueryValueEx(hk, NULL, 0, &dwType, (BYTE *)szTmp, &dwSize);
RegCloseKey(hk);
}
//int nVer = *(WORD*)&szTmp[20];
IID *pIID = NULL;
if (*(WORD*)&szTmp[20] == 0x3531) pIID = (IID*)&IID_ACAD_15;
else if (*(WORD*)&szTmp[20] == 0x3631) pIID = (IID*)&IID_ACAD_16;
else if (*(WORD*)&szTmp[20] == 0x3731) pIID = (IID*)&IID_ACAD_17;
else if (*(WORD*)&szTmp[20] == 0x3831) pIID = (IID*)&IID_ACAD_18;
pIID = NULL;
if (szTmp[21] == '5') pIID = (IID*)&IID_ACAD_15;
else if (szTmp[21] == '6') pIID = (IID*)&IID_ACAD_16;
else if (szTmp[21] == '7') pIID = (IID*)&IID_ACAD_17;
else if (szTmp[21] == '8') pIID = (IID*)&IID_ACAD_18;
if (pIID) MessageBox(0,szTmp,0,MB_OK);

return 0;
}
BOOL ReadRegStr(HKEY hKey, const TCHAR *szKey, const TCHAR *szSubKey, TCHAR *szValue, DWORD nSize)
{
HKEY hk;
DWORD dwType, dwSize;
if (RegOpenKey(hKey, szKey, &hk) == ERROR_SUCCESS)
{
dwSize = nSize;
RegQueryValueEx(hk, szSubKey, 0, &dwType, (BYTE *)szValue, &dwSize);
RegCloseKey(hk);
return TRUE;
}
return FALSE;
}
 
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on June 11, 2016, 08:44:38 PM
Interesting idea, I will read and think.
In the meantime, here's what happens.

Practical work in AutoCAD often associated with any drawing, which already has some of the objects.
The program that loads AutoCAD with a blank drawing less in demand.
But I could not do so at the start of the program checked already downloaded copy AutoCAD (with the right to work drawing) and connect to the copy.

Therefore, I chose another way.
After loading AutoCAD with a blank drawing, the program calls the built-in in AutoCAD dialogue <FileOpen>.
And loads selected for the work a drawing.
After that will work functions that perform something in the user-selected drawing.

I left in a line of code, a program that creates 2 points and the line.
They do not interfere with the future work program, but it's nice please me - reminding help prfessionalny programmer on the most difficult stage of the creation of this program.
Thanks to him.

For some unknown reason to me the macro-command only works when you double call.
That line of code:
Code: [Select]
if (pACadDoc) {
IAcadDocument_SendCommand(pACadDoc, L"_open ");
IAcadDocument_SendCommand(pACadDoc, L"_OPEN");
}
And here are the options that do not work:
Code: [Select]
/*
if (pUtility) {
IAcadUtility_Prompt(pUtility, L"_open ");
IAcadUtility_Prompt(pUtility, L"_OPEN ");
// variants of:
//(pUtility)->lpVtbl->Prompt(pUtility, L"_open ");
//IAcadUtility_Prompt(pUtility, L"_open \\n");
}
*/
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL 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 )
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on June 13, 2016, 02:24:04 AM
All miraculous works!
Thanks again!
Now I will deal with these awkward VARIANTS ...
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey 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;
}
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL 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.

Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey 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;
}
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL 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);
}
...
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey 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.
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey 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;
}
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey 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:
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL 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.
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey 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.
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL 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;
}
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey 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:
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey 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 (?).
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL 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;
}
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on June 28, 2016, 01:08:44 AM
Fixed code.
Thank you, I would not have found.
Now polyline performed.
But the joy was short-lived.
Even after changing Sarray AutoCAD Text is not executed.
Also not working Rotate Polyline.
But in arguments of this function missing Variant-type.
Code: [Select]
/* create arrow as Polyline & put it on Point-1 & rotate it to calculated Angle */
void Arrow12 (double distance, double angle, double dArrowScale)
{
// add a polyline object into Model-Space
pLWP = drawLWP (pUtility, pModelSpace, dArrowScale);
rotPoint = rotpnt (pUtility,  pModelSpace);
// add a text object into Model-Space
txtPoint = rottxt (pUtility, pModelSpace, pText, distance, dArrowScale);

//rotate LWPolyline around BasePoint vp1
if (IAcadLWPolyline_Rotate(pLWP, rotPoint, angle))
IAcadLWPolyline_Update(pLWP);
//rotate Text around BasePoint vpT
if (IAcadText_Rotate(pText, txtPoint, angle))
IAcadText_Update(pText);

return;
}

IAcadLWPolyline* drawLWP (IAcadUtility* pUtility, IAcadModelSpace* pModelSpace, double dArrowScale)
{
// add a polyline object into Model-Space
HRESULT hr;
VARIANT vpnt; // TypedArray for LWPolyLine
SAFEARRAY* psarrayL = SafeArrayCreateVector(VT_VARIANT, 0, 10);

// LWPolyline
((VARIANT*)psarrayL->pvData)[0].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[0].dblVal = dblPnt1[0];
((VARIANT*)psarrayL->pvData)[1].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[1].dblVal = dblPnt1[1];
((VARIANT*)psarrayL->pvData)[2].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[2].dblVal = dblPnt1[0] + 0.5 * dArrowScale;
((VARIANT*)psarrayL->pvData)[3].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[3].dblVal = dblPnt1[1];
((VARIANT*)psarrayL->pvData)[4].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[4].dblVal = dblPnt1[0] + 0.25 * dArrowScale;
((VARIANT*)psarrayL->pvData)[5].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[5].dblVal = dblPnt1[1] + 0.05 * dArrowScale;
((VARIANT*)psarrayL->pvData)[6].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[6].dblVal = dblPnt1[0] + 0.25 * dArrowScale;
((VARIANT*)psarrayL->pvData)[7].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[7].dblVal = dblPnt1[1] - 0.05 * dArrowScale;
((VARIANT*)psarrayL->pvData)[8].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[8].dblVal = dblPnt1[0] + 0.5 * dArrowScale;
((VARIANT*)psarrayL->pvData)[9].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[9].dblVal = dblPnt1[1];

// sarray for LWPoly inserted in vpnt
hr = IAcadUtility_CreateTypedArray(pUtility, &vpnt, VT_R8, psarrayL);
SafeArrayDestroy(psarrayL);  // free array
// draw LWPolyline in ModelSpace
if (!hr) {
hr = IAcadModelSpace_AddLightWeightPolyline(pModelSpace, vpnt, &pLWP);
IAcadLWPolyline_Update(pLWP);
}

return pLWP;
}

VARIANT rotpnt (IAcadUtility* pUtility, IAcadModelSpace* pModelSpace)
{
HRESULT hr;
VARIANT vp1; // TypedArray for Text insertion-point
SAFEARRAY* psarrayP = SafeArrayCreateVector(VT_VARIANT, 0, 2);

// rotation point BasePoint
((VARIANT*)psarrayP->pvData)[0].vt = VT_R8;
((VARIANT*)psarrayP->pvData)[0].dblVal = dblPnt1[0];
((VARIANT*)psarrayP->pvData)[0].vt = VT_R8;
((VARIANT*)psarrayP->pvData)[0].dblVal = dblPnt1[1];

// sarray for LWPoly inserted in vpnt
hr = IAcadUtility_CreateTypedArray(pUtility, &vp1, VT_R8, psarrayP);
SafeArrayDestroy(psarrayP);  // free array

if (IAcadLWPolyline_Rotate(pLWP, vp1, 0.75))
IAcadLWPolyline_Update(pLWP);

return vp1;
}

VARIANT rottxt (IAcadUtility* pUtility, IAcadModelSpace* pModelSpace, IAcadText* pText, double distance, double dArrowScale)
{
HRESULT hr;
VARIANT vpT; // TypedArray for Text insertion-point
SAFEARRAY* psarrayT = SafeArrayCreateVector(VT_VARIANT, 0, 2);
wchar_t sDistance[8];

// Text Insert point
((VARIANT*)psarrayT->pvData)[0].vt = VT_R8;
((VARIANT*)psarrayT->pvData)[0].dblVal = dblPnt1[0]+0.05*dArrowScale;
((VARIANT*)psarrayT->pvData)[1].vt = VT_R8;
((VARIANT*)psarrayT->pvData)[1].dblVal = dblPnt1[1]+0.05*dArrowScale;

// sarray for BasePoint inserted in vp1
hr = IAcadUtility_CreateTypedArray(pUtility, &vpT, VT_R8, psarrayT);
SafeArrayDestroy(psarrayT);  // free array
// draw LWPolyline in ModelSpace
if (!hr) {
// Architect drawing units mm, Geodetic drawing units m
// draw text-label (in centimeters) nearest of arrow
_ltow( (long)(distance * iUserUnits), sDistance, 10 );
hr = IAcadModelSpace_AddText(pModelSpace, sDistance, vpT, 0.15*dArrowScale, &pText);
IAcadText_Update(pText);
}

return vpT;
}
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL on June 28, 2016, 04:08:39 PM
Code: [Select]
HRESULT (STDMETHODCALLTYPE *AddText)(IAcadModelSpace *This, BSTR TextString, VARIANT InsertionPoint, DOUBLE Height, IAcadText* *pResult); //!< Creates a single line of textSo use BSTR.
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on June 29, 2016, 06:01:25 PM
Arrows (polylines) are now created (!!!).
The problem was in the third coordinate point: dblPnt1[2].

I changed the text of the declaration, but that nothing has changed.
The text is not created in any versions of the code, even if the test is introduced as:
Code: [Select]
IAcadModelSpace_AddText(pModelSpace, L"Text-test", vpT, 2.0, ppText);
Similarly, not created, and multi-line text.

Code: [Select]
double dblPnt[3], dblPnt1[3], dblPnt2[3]; // X,Y,Z arrays

VARIANT rotpnt (IAcadUtility* pUtility, IAcadModelSpace* pModelSpace)
{
HRESULT hr;
VARIANT vp1; // TypedArray for Text insertion-point
SAFEARRAY* psarrayP = SafeArrayCreateVector(VT_VARIANT, 0, 3);

// rotation point BasePoint
((VARIANT*)psarrayP->pvData)[0].vt = VT_R8;
((VARIANT*)psarrayP->pvData)[0].dblVal = dblPnt1[0];
((VARIANT*)psarrayP->pvData)[1].vt = VT_R8;
((VARIANT*)psarrayP->pvData)[1].dblVal = dblPnt1[1];
((VARIANT*)psarrayP->pvData)[2].vt = VT_R8;
((VARIANT*)psarrayP->pvData)[2].dblVal = dblPnt1[2];

// sarray for LWPoly inserted in vpnt
hr = IAcadUtility_CreateTypedArray(pUtility, &vp1, VT_R8, psarrayP);
SafeArrayDestroy(psarrayP);  // free array

return vp1;
}


IAcadText* *ppText = NULL;

VARIANT rottxt (IAcadUtility* pUtility, IAcadModelSpace* pModelSpace, double distance, double dArrowScale)
{
HRESULT hr;
VARIANT vpT; // TypedArray for Text insertion-point
SAFEARRAY* psarrayT = SafeArrayCreateVector(VT_VARIANT, 0, 3);
wchar_t sDistance[8];
//BSTR bstr = NULL;
// Text Insert point
((VARIANT*)psarrayT->pvData)[0].vt = VT_R8;
((VARIANT*)psarrayT->pvData)[0].dblVal = dblPnt1[0]+0.05*dArrowScale;
((VARIANT*)psarrayT->pvData)[1].vt = VT_R8;
((VARIANT*)psarrayT->pvData)[1].dblVal = dblPnt1[1]+0.05*dArrowScale;
((VARIANT*)psarrayT->pvData)[2].vt = VT_R8;
((VARIANT*)psarrayT->pvData)[2].dblVal = dblPnt1[2];

// sarray for BasePoint inserted in vp1
hr = IAcadUtility_CreateTypedArray(pUtility, &vpT, VT_R8, psarrayT);
SafeArrayDestroy(psarrayT);  // free array
// draw single-line Text in ModelSpace
if (!hr) {
// Architect drawing units mm, Geodetic drawing units m
// draw text-label (in centimeters) nearest of arrow
_ltow( (long)(distance * iUserUnits), sDistance, 10 );
BSTR bstr = NULL;
bstr = SysAllocString (sDistance);
hr = IAcadModelSpace_AddText(pModelSpace, bstr, vpT, 0.15*dArrowScale, ppText);
SysFreeString (bstr);
IAcadText_Update(*ppText);
}

return vpT;
}
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL on June 29, 2016, 07:56:19 PM
I think this works:
Code: [Select]
...
VARIANT vpt1; // for points
BSTR bstr;
IAcadText *pText;
hr = CadCreateUtilityPoint(pUtility, &vpt1, 0.0, 0.0, 0.0);
if (!hr) {
bstr = SysAllocString(L"Test text");
hr = IAcadModelSpace_AddText(pModelSpace, bstr, vpt1, 10.0, &pText);
if (pText) IAcadEntity_Rotate((IAcadEntity*)pText, vpt1, (45*M_PI/180));
SysFreeString(bstr);
VariantClear(&vpt1);
}
...
Code: [Select]
HRESULT CadCreateUtilityPoint(IAcadUtility *pUtility, VARIANT *vpt1, double x, double y, double z)
{
HRESULT hr;
SAFEARRAY* psarr = SafeArrayCreateVector(VT_VARIANT, 0, 3);
((VARIANT*)psarr->pvData)[0].vt = VT_R8;
((VARIANT*)psarr->pvData)[0].dblVal = x;
((VARIANT*)psarr->pvData)[1].vt = VT_R8;
((VARIANT*)psarr->pvData)[1].dblVal = y;
((VARIANT*)psarr->pvData)[2].vt = VT_R8;
((VARIANT*)psarr->pvData)[2].dblVal = z;
hr = IAcadUtility_CreateTypedArray(pUtility, vpt1, VT_R8, psarr);
SafeArrayDestroy(psarr); // free array
if (hr) ErrorString(hr);
return hr;
}

HRESULT CadCreateUtilityPoints(IAcadUtility *pUtility, VARIANT *vpt1, int nCount, double  dblPoints[])
{
HRESULT hr;
SAFEARRAY* psarr = SafeArrayCreateVector(VT_VARIANT, 0, nCount);
for (int i = 0; i < nCount; i++) {
((VARIANT*)psarr->pvData)[i].vt = VT_R8;
((VARIANT*)psarr->pvData)[i].dblVal = dblPoints[i];
}
hr = IAcadUtility_CreateTypedArray(pUtility, vpt1, VT_R8, psarr);
SafeArrayDestroy(psarr); // free array
if (hr) ErrorString(hr);
return hr;
}
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on June 29, 2016, 11:58:05 PM
Еще раз спасибо!
Danke nochmals!
Thanks again!


I still think that without VARIANT-type in the library (and as a whole in AutoCAD) creation of a working code would be much easier and more correct.

Here's how it looks now.
A little more work and we can forget about this monster called "Computer drawing assistant".
There are others more worthy and understandable programs.
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on June 30, 2016, 11:10:25 PM
He remembered that no management layers and color, little programs for practical work. Therefore, I decided to slightly improve the program:
1.
For the arrows to create a new layer called 'Arrows'
For inscriptions deviation creates a new layer called 'OrtoDiff'
But here I waited for surprises from AutoDesk.
The second layer is not created and the program crashes.
Quote from AutoDesk:
"Layers Collection: To add a new member to the collection, use the Add method."
Code: [Select]
IAcadLayers* pLayers = NULL;
IAcadLayer* pLayer1;
IAcadLayer* pLayer2;

void SetLayer1 (void)
{
VARIANT_BOOL *pbRes = NULL;

// add new layer for 'Arrows' (Strelka)
IAcadDatabase_get_Layers(pDataBase, &pLayers);
// add new layer  for 'Arrows' (Strelka)
IAcadLayers_Add(pLayers, L"Arrows", &pLayer1);
IAcadLayer_put_color(pLayer1, acGreen);
IAcadLayer_get_LayerOn(pLayer1, pbRes);
// and make layer active
IAcadDocument_put_ActiveLayer(pACadDoc, pLayer1);
}

void SetLayer2 (void)
{
VARIANT_BOOL *pbRes = NULL;

// add new layer for 'OrtoDiff' text-labels
IAcadDatabase_get_Layers(pDataBase, &pLayers);
// add new layer  for 'Arrows' (Strelka)
IAcadLayers_Add(pLayers, L"OrtoDiff", &pLayer2);
IAcadLayer_put_color(pLayer2, acRed);
IAcadLayer_get_LayerOn(pLayer2, pbRes);
// and make layer active
IAcadDocument_put_ActiveLayer(pACadDoc, pLayer2);
}

2.
And again, the value is often the result of deviations from the project are normalized (eg construction).
Therefore, he added combo box to set the limit of the normalized deviations.
For values above the limit, the text color of the deviation varies from green to red.
Then these inscriptions are clearly visible.
It turned out that to change the text color is not easy.
These two options do not work:
Code: [Select]
IAcadAcCmColor* pAcCmColor;
// IAcadAcCmColor_put_ColorIndex(pAcCmColor, 1);
// IAcadEntity_put_TrueColor(pText, pAcCmColor);

// IAcadAcCmColor_SetRGB(pAcCmColor, (LONG)1024, (LONG)64, (LONG)64);
// IAcadEntity_put_TrueColor((IAcadEntity*)pText, pAcCmColor);
However, more complex than the text of the object is done as follows:
Code: [Select]
//!< Specifies the color of the dimension text (DIMCLRT system variable)
hr=put_TextColor(IAcadDimAligned *This, ACAD_COLOR TextColor);
3.
Before exiting the program is executed drawing regeneration:
Code: [Select]
bstr = SysAllocString (L"_REGENALL ");
IAcadDocument_SendCommand(pACadDoc, bstr);
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL on July 01, 2016, 08:50:23 AM
1:
Code: [Select]
HRESULT (STDMETHODCALLTYPE *Add)(IAcadLayers *This, BSTR Name, IAcadLayer* *pResult);
Code: [Select]
...
IAcadLayers* pLayers = NULL;
IAcadDatabase_get_Layers(pDataBase, &pLayers);
if (pLayers) {
IAcadLayer* pLayer1;
IAcadLayer* pLayer2;
BSTR bstr;
bstr = SysAllocString(L"Layer1");
IAcadLayers_Add(pLayers, bstr, &pLayer1);
SysFreeString(bstr);
if (pLayer1) {
IAcadLayer_put_color(pLayer1, acGreen);
IAcadLayer_Release(pLayer1);
}
bstr = SysAllocString(L"Layer2");
IAcadLayers_Add(pLayers, bstr, &pLayer2);
SysFreeString(bstr);
if (pLayer2) {
IAcadLayer_put_color(pLayer2, acRed);
IAcadLayer_Release(pLayer2);
}
IAcadLayers_Release(pLayers);
}
...
2:
Code: [Select]
...
if (pText) {
IAcadAcCmColor* pAcCmColor;
IAcadText_get_TrueColor(pText, &pAcCmColor);
if (pAcCmColor) {
IAcadAcCmColor_SetRGB(pAcCmColor, 255, 64, 64);
IAcadText_put_TrueColor(pText, pAcCmColor);
IAcadAcCmColor_Release(pAcCmColor);
}
//IAcadText_put_color(pText, acBlue);
IAcadText_Rotate(pText, vpt1, (45*M_PI/180));
IAcadText_Release(pText);
}
...
3:
Code: [Select]
...
IAcadApplication_Update(pCad);
...
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on July 02, 2016, 07:53:30 PM
1.
Fixed code for 'Layers' and 'Colors' (from TimoVJL).
Code: [Select]
void SetLayers (void)
{
IAcadLayers* pLayers = NULL;
IAcadDatabase_get_Layers(pDataBase, &pLayers);
if (pLayers) {
BSTR bstr;
bstr = SysAllocString(L"OrtoDiff");
IAcadLayers_Add(pLayers, bstr, &pLayer2);
SysFreeString(bstr);
if (pLayer2) {
IAcadLayer_put_color(pLayer2, acGreen);
}
bstr = SysAllocString(L"Arrows");
IAcadLayers_Add(pLayers, bstr, &pLayer1);
SysFreeString(bstr);
if (pLayer1) {
IAcadLayer_put_color(pLayer1, acGreen);
IAcadDocument_put_ActiveLayer(pACadDoc, pLayer1);
}
IAcadLayers_Release(pLayers);
}
}
Code: [Select]
VARIANT rottxt ( ... )
...
if (pText && distance>(dTolerance*iUserUnits)) {
IAcadText_get_TrueColor(pText, &pAcCmColor);
if (pAcCmColor) {
IAcadAcCmColor_SetRGB(pAcCmColor, 255, 64, 64);
IAcadText_put_TrueColor(pText, pAcCmColor);
IAcadAcCmColor_Release(pAcCmColor);
}
//IAcadText_put_color(pText, acBlue);
IAcadText_Release(pText);
}
...
2.
Add calculate OrtoDiffs with Tolerance deviations.
3.
LogInFile() - ad function to write in logfile.txt session with Date-Time indication.
Code: [Select]
Log a meassured orto-diffs:
Date 02-07-16 Time 20-12-39
OrtoDiff=19.9797 TO BIG!!
OrtoDiff=20.0202 TO BIG!!
OrtoDiff=3.30772 accepted
OrtoDiff=5.82749 accepted
OrtoDiff=29.0576 TO BIG!!
OrtoDiff=25.3733 TO BIG!!
OrtoDiff=8.27729 accepted
OrtoDiff=34.4855 TO BIG!!
OrtoDiff=8.27729 accepted
OrtoDiff=34.4855 TO BIG!!
End of session: Time 20-13-30
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on July 11, 2016, 01:53:37 PM
Delaunay Triangulation - one more attractive for solving the problem in AutoCAD.
There are old, but the quality of C-source:
http://www.cs.cmu.edu/~quake/triangle.html (http://www.cs.cmu.edu/~quake/triangle.html)
http://www.netlib.org/voronoi/triangle.zip (http://www.netlib.org/voronoi/triangle.zip)
http://www.codeproject.com/KB/recipes/587629/triangulation-source.zip (http://www.codeproject.com/KB/recipes/587629/triangulation-source.zip)

For this we need to get from the drawing all '3dPoints' with their coordinates.
Function:
Code: [Select]
IAcadUtility_GetPoint (pUtility, Point, Prompt, & vpnt);gives the coordinates of points, but it works in the interactive mode, you have to manually Pick each point, so it is not applicable.

Function:
Code: [Select]
QueryInterface (IAcadUtility * This, GUID * riid, VOID ** ppvObj);in the cycle to give all the objects in the drawing, of which the object name 'AcDbPoint' need to create a list of coordinates.
This list is then passed to the triangulation functions.
But there is no function that identifies the coordinates of the object 'Entity'.
Code: [Select]
IAcadModelSpace_get_Count (pModelSpace, & nCount);
for (...) {
IAcadModelSpace_Item (pModelSpace, var, & pEntity);
IAcadUtility_QueryInterface (pUtility, (GUID *) & IID_IAcadEntity, (void **) pEntity);
IAcadEntity_get_EntityName (pEntity, & pResult);
// Call function:
getentcoords (pUtility, pEntity, dblPnt);
// ...
}
Here, there is a program crashing.

The structure 'Entity' as well as the structure 'Point' present its coordinates.
But how to get them?

After receiving the 'Entity -> Name==Point'
it is necessary to "tell the program", that:
pPoint == pEntity
or may be as follows:
ppPoint == ppEntity
to then get the coordinates, but it is impossible.

I suppose that such things exist functions:
Code: [Select]
get_Handle (IAcadModelSpace * This, BSTR * pResult);or:
Code: [Select]
get_ObjectID (IAcadModelSpace * This, LONG * pResult);but I found no indications for their use.

Got any bright ideas?
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL on July 11, 2016, 04:31:00 PM
Not so bright:
Code: [Select]
...
if (pEntity) {
IAcadPoint *pPoint = NULL;
hr = IAcadEntity_get_EntityName(pEntity, &bstr);
if (bstr) {
printf("\t%ls", bstr);
SysFreeString(bstr);
}
hr = ((IDispatch*)pEntity)->lpVtbl->QueryInterface((IDispatch*)pEntity, &IID_IAcadPoint, (void**)&pPoint);
if (!hr) {
VARIANT var1;
IAcadPoint_get_Coordinates(pPoint, &var1);
if (var1.vt == (VT_ARRAY | VT_R8)) {
printf("\t%f\t%f\t%f",
((double*)var1.parray->pvData)[0],
((double*)var1.parray->pvData)[1],
((double*)var1.parray->pvData)[2]
);
SafeArrayDestroy(var1.parray);
}
IAcadPoint_Release(pPoint);
}
printf("\n");
IAcadEntity_Release(pEntity);
}
...
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on July 11, 2016, 05:32:05 PM
Thank you. Let me see.

I have worked here such variant:
Code: [Select]
int getentcoords(IAcadEntity** pEntity)
{
HRESULT hr;
IDispatch *pDisp = NULL;
VARIANT var;
VariantInit(&var);

hr = IAcadEntity_Copy(*pEntity, &pDisp);
if (!hr) {
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);
}
if (var.vt == (VT_ARRAY | VT_R8)) {
dblPnt[0] = ((double*)var.parray->pvData)[0];
dblPnt[1] = ((double*)var.parray->pvData)[1];
dblPnt[2] = ((double*)var.parray->pvData)[2];
SafeArrayDestroy(var.parray);
if (dblPnt[2] == 0) return hr;
//else: add dblPnt[] intolist-array
lPointsCount++;
}
}
if (!pDisp) {
*pEntity = NULL;
SafeArrayDestroy(var.parray);
return hr;
}
return hr;
}

I was wrong.
In my code, the coordinates are not available.
Your code works correctly - the coordinates there!
Thank you.
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on August 12, 2016, 12:16:30 AM
First result to create 'Surface' from AutoCAD 3dPoints --> 3dFaces.
It's Delaunay-Triangulation.
To find 'Holes' in created 'Surface' need AutoCAD 'Hatch', but ...
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL on August 12, 2016, 05:07:23 PM
But IAcadModelSpace_AddHatch needs BSTR.
Code: [Select]
...
VARIANT vhot; vhot.vt = VT_I4; vhot.lVal = 0;
IAcadLWPolyline *pLWPL;
hr = CadCreateUtilityPoints(pUtility, &vpt1, 8, pts);
hr = IAcadModelSpace_AddLightWeightPolyline(pModelSpace, vpt1, &pLWPL);
bstr = SysAllocString(L"ANSI31");
hr = IAcadModelSpace_AddHatch(pModelSpace, 1, bstr, 1, vhot, &pHatch);
SysFreeString(bstr);
if (!hr) {
vobj.vt = VT_DISPATCH;
vobj.ppdispVal = (IDispatch **)pLWPL;
hr = IAcadHatch_AppendOuterLoop(pHatch, vobj);
if (hr) ErrorString(hr);
hr = IAcadHatch_Evaluate(pHatch);
} else ErrorString(hr);
VariantClear(&vpt1);
...
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on August 12, 2016, 11:30:20 PM
Thank you for acax17enu.tlb.txt file - very comfortable to use.

Fixed function.
Here's how it looks now:
Code: [Select]
// check 3dFace-mesh: if Surface is created - Hatch all 3dFaces to view Holes
void FindHoles (IAcadUtility* pUtility, IAcadModelSpace* pModelSpace, VARIANT* varpntlist)
{
//acHatch = 17;
VARIANT vHatchType;
vHatchType.vt = VT_BOOL;
vHatchType.boolVal = 0;
BSTR bstr = NULL;
VARIANT vpnt; // TypedArray for LWPolyLine
SAFEARRAY* psarrayL = SafeArrayCreateVector(VT_VARIANT, 0, 8);
long i = 0;
char sVal[48];
long p0, p1, p2;

pFile2 = stdin;
freopen("triangles.txt","r",stdin);  // reopen file as stdin for read

while (scanf("%d %d %d", &p0, &p1, &p2) !=EOF) {
// make LWPolyline from varpoints of 3dFace
((VARIANT*)psarrayL->pvData)[0].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[0].dblVal = ((double*)varpntlist[p0].parray->pvData)[0];
((VARIANT*)psarrayL->pvData)[1].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[1].dblVal = ((double*)varpntlist[p0].parray->pvData)[1];
((VARIANT*)psarrayL->pvData)[2].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[2].dblVal = ((double*)varpntlist[p1].parray->pvData)[0];
((VARIANT*)psarrayL->pvData)[3].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[3].dblVal = ((double*)varpntlist[p1].parray->pvData)[1];
((VARIANT*)psarrayL->pvData)[4].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[4].dblVal = ((double*)varpntlist[p2].parray->pvData)[0];
((VARIANT*)psarrayL->pvData)[5].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[5].dblVal = ((double*)varpntlist[p2].parray->pvData)[1];
((VARIANT*)psarrayL->pvData)[6].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[6].dblVal = ((double*)varpntlist[p0].parray->pvData)[0];
((VARIANT*)psarrayL->pvData)[7].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[7].dblVal = ((double*)varpntlist[p0].parray->pvData)[1];
// sarray for LWPoly inserted in vpnt
hr = IAcadUtility_CreateTypedArray(pUtility, &vpnt, VT_R8, psarrayL);
SafeArrayDestroy(psarrayL);  // free array
// draw LWPolyline in ModelSpace
if (!hr) {
hr = IAcadModelSpace_AddLightWeightPolyline(pModelSpace, vpnt, &pLWP);
IAcadLWPolyline_Update(pLWP);
}
bstr = SysAllocString(L"ANSI31");
hr = IAcadModelSpace_AddHatch(pModelSpace, 1, bstr, 1, vHatchType, &pHatch);
SysFreeString(bstr);
if (!hr) {
vpnt.vt = VT_DISPATCH;
vpnt.ppdispVal = (IDispatch **)pLWP;
hr = IAcadHatch_AppendOuterLoop(pHatch, vpnt);
if (hr) MessageBox(0, "hr error", "IAcadHatch_AppendOuterLoop", 0);
hr = IAcadHatch_Evaluate(pHatch);
MessageBox(0, "Hatch added", "AddHatch", 0);
i++;
}
else MessageBox(0, "hr error", "IAcadModelSpace_AddHatch", 0);
VariantClear(&vpnt);
}
sprintf(sVal, "End of Hatch 3dFaces: %d triangles", i);
MessageBox(0, sVal, "FindHoles", 0);
freopen("CON","r", stdin);
}
Not satisfied string:
Quote
hr = IAcadModelSpace_AddHatch (pModelSpace, 1, bstr, 1, vHatchType, & pHatch);

At the same time built a 1 polyline.
Then the following message:
Quote
else MessageBox (0, "hr error", "IAcadModelSpace_AddHatch", 0);
The program then falls.

Apparently the problem is ad declare of VARIANT 'vHatchType'

Here's my test drawing:
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on August 22, 2016, 09:52:36 PM
'Hatch' in AutoCAD for programming not an easy thing.
In the C-lang (unlike AutoLISP and VBA) function Hatch (AddHatch) does not work.
This is how it is written in the acax17enu.h (acax17enu.tlb):
hr = AddHatch (IAcadModelSpace * This, INT PatternType, BSTR PatternName, VARIANT_BOOL Associativity, VARIANT HatchObjectType, IAcadHatch * * pResult);
or through a macro:
IAcadModelSpace_AddHatch (This, PatternType, PatternName, Associativity, HatchObjectType, pResult);

Argument 'VARIANT HatchObjectType' not 'typedef enum acHatchObjectType'.
And if in AutoLISP or in VBA it's the optional argument (i.e., can be omitted), the C function without this argument does not work.
Nowhere except AddHatch function argument 'HatchObjectType' not found.
And so his "appearance" in the structure VARIANT unknown.

So, after many unsuccessful trials (and errors), after reading the forums on the theme of 'AutoCAD HATCH', had to use the function 'command' of AutoLISP.
This AutoLISP 'command' hatch successfully 3dFaces (according ACAD-Help it is impossible).
Also, the command-line, you can directly insert the various properties of the hatch.
In short: old AutoLISP rescued.

The attached archive example of creating a surface (Surface as TIN) from '3dFaces' on irregularly spaced points:
1 of the points existing in the current Drawing - button 'SURFACE',
2. the points X, Y, Z in the file 'points.txt' - Button 'Read File',

Same (1 and 2) can be performed using an external LISP program (DTM.lsp + DTM.dsl) Romanian programmer Nikolae Manda - button 'DTM LISP'.
These two files must be in the AutoCAD system folder, such as C:\Program Files\AutoCAD 20xx\Support\

The code that performs a triangulation:
vdefs.h
voron_main.c (main.c)
voronoi.c
geometry.c
heap.c
memory.c
edgelist.c
output.c
its author Steve J. Fortune ( (c)1987-1992 years).
Despite the age of the code, it took quite a bit fixes for inclusion in the project.

There are other available sources, such as Clarkson-Delaunay - Ken Clarkson (c) 1995

Almost everything that is in the public domain triangulate the so-called 'Convex Hull'.
For serious work requires the implementation of "Delaunay triangulation to the conditions," and like all serious in the public domain is not available.

It is for the overall development:
http://algolist.manual.ru/maths/geom/ (http://algolist.manual.ru/maths/geom/)
http://gts.sourceforge.net (http://gts.sourceforge.net)
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on August 22, 2016, 10:44:39 PM
Addition:
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL on August 23, 2016, 09:44:07 AM
Code: [Select]
Argument 'VARIANT HatchObjectType' not 'typedef enum acHatchObjectType'.
And if in AutoLISP or in VBA it's the optional argument (i.e., can be omitted), the C function without this argument does not work.
for your old code default value for unused parameter should be 0 in C
Code: [Select]
...
VARIANT HatchType;
HatchType.vt = VT_I4;
HatchType.lVal = 0;
...
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on August 23, 2016, 06:22:59 PM
Hello, Timo!
I have thought that you have lost interest in the subject.

I've already tried Type VT_I4
Nevertheless, I repeat the experiment.
On the other hand experience with LISP code is also useful.

AutoCAD Hach for programming is difficult and in other CAD's.

This and previous project successfully compiled for AutoCAD 2012
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on August 23, 2016, 07:57:13 PM
Hatch objects are now created.
But the function
IAcadHatch_AppendOuterLoop(pHatch, vpnt);
is not executed.
Code: [Select]
// check 3dFace-mesh: if Surface is created - Hatch all 3dFaces to view Holes
void FindHoles (IAcadUtility* pUtility, IAcadModelSpace* pModelSpace, VARIANT* varpntlist)
{
//acHatch = 17;
VARIANT vHatchType;
//vHatchType.vt = VT_BOOL;
//vHatchType.boolVal = 0;
vHatchType.vt = VT_I4;
vHatchType.lVal = 0;

BSTR bstr = NULL;
VARIANT vpnt; // TypedArray for LWPolyLine
SAFEARRAY* psarrayL = SafeArrayCreateVector(VT_VARIANT, 0, 8);
long i = 0;
char sVal[48];
long p0, p1, p2;

pFile2 = stdin;
freopen("triangles.txt","r",stdin);  // reopen file as stdin for read

while (scanf("%d %d %d", &p0, &p1, &p2) !=EOF) {
// make LWPolyline from varpoints of 3dFace
((VARIANT*)psarrayL->pvData)[0].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[0].dblVal = ((double*)varpntlist[p0].parray->pvData)[0];
((VARIANT*)psarrayL->pvData)[1].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[1].dblVal = ((double*)varpntlist[p0].parray->pvData)[1];
((VARIANT*)psarrayL->pvData)[2].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[2].dblVal = ((double*)varpntlist[p1].parray->pvData)[0];
((VARIANT*)psarrayL->pvData)[3].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[3].dblVal = ((double*)varpntlist[p1].parray->pvData)[1];
((VARIANT*)psarrayL->pvData)[4].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[4].dblVal = ((double*)varpntlist[p2].parray->pvData)[0];
((VARIANT*)psarrayL->pvData)[5].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[5].dblVal = ((double*)varpntlist[p2].parray->pvData)[1];
((VARIANT*)psarrayL->pvData)[6].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[6].dblVal = ((double*)varpntlist[p0].parray->pvData)[0];
((VARIANT*)psarrayL->pvData)[7].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[7].dblVal = ((double*)varpntlist[p0].parray->pvData)[1];
// sarray for LWPoly inserted in vpnt
hr = IAcadUtility_CreateTypedArray(pUtility, &vpnt, VT_R8, psarrayL);
SafeArrayDestroy(psarrayL);  // free array
// draw LWPolyline in ModelSpace
if (!hr) {
hr = IAcadModelSpace_AddLightWeightPolyline(pModelSpace, vpnt, &pLWP);
IAcadLWPolyline_Update(pLWP);
}
// hr = IAcadModelSpace_AddHatch(pModelSpace, 1, L"SOLID", TRUE, HatchType, &pHatch);
bstr = SysAllocString(L"SOLID");
//bstr = SysAllocString(L"ANSI31");
hr = IAcadModelSpace_AddHatch(pModelSpace, 1, bstr, 1, vHatchType, &pHatch);
SysFreeString(bstr);
if (!hr) {
vpnt.vt = VT_DISPATCH;
vpnt.ppdispVal = (IDispatch **)pLWP;
hr = IAcadHatch_AppendOuterLoop(pHatch, vpnt); // OuterLoop = LWPolyline = 3dFace
if (hr) {
MessageBox(0, "hr error", "IAcadHatch_AppendOuterLoop", 0);
return;
}
hr = IAcadHatch_Evaluate(pHatch);
MessageBox(0, "Hatch added", "AddHatch", 0);
i++;
}
else MessageBox(0, "hr error", "IAcadModelSpace_AddHatch", 0);
VariantClear(&vpnt);
}
sprintf(sVal, "End of Hatch 3dFaces: %d triangles", i);
MessageBox(0, sVal, "FindHoles", 0);
freopen("CON","r", stdin);
}
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL on August 23, 2016, 10:12:33 PM
What error code is?
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on August 24, 2016, 03:48:53 PM
hr=8021007B
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: TimoVJL on August 24, 2016, 06:47:34 PM
OK, only object array is accepted.
COMException (0x8021007B) "Incorrect array of objects"

This works with nanoCAD
Code: [Select]
VARIANT voa; // object array
SAFEARRAYBOUND rgsaBound;
rgsaBound.lLbound = 0L; rgsaBound.cElements = 1;
voa.vt = VT_ARRAY | VT_DISPATCH;
voa.parray = SafeArrayCreate(VT_DISPATCH, 1, &rgsaBound);
long int i = 0;
hr = SafeArrayPutElement(voa.parray,&i,(void*)pLWPL);
hr = IAcadHatch_AppendOuterLoop(pHatch, voa);
or
Code: [Select]
VARIANT voa; // object array
SAFEARRAY* psarr = SafeArrayCreateVector(VT_DISPATCH, 0, 1);
IDispatch* parms[1];  // = {(IDispatch *)pLWPL};
parms[0] = (IDispatch *)pLWPL;
psarr->pvData = parms;
voa.vt = VT_ARRAY | VT_DISPATCH;
voa.parray = psarr;
hr = IAcadHatch_AppendOuterLoop(pHatch, voa);
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on August 24, 2016, 08:00:49 PM
Code fragment:
Code: [Select]
// draw LWPolyline in ModelSpace
if (!hr) {
hr = IAcadModelSpace_AddLightWeightPolyline(pModelSpace, vpnt, &pLWP);
IAcadLWPolyline_Update(pLWP);
}
//bstr = SysAllocString(L"SOLID");
bstr = SysAllocString(L"ANSI31");
hr = IAcadModelSpace_AddHatch(pModelSpace, 1, bstr, 1, vHatchType, &pHatch);
SysFreeString(bstr);
if (!hr) {
//new code start
VARIANT voa; // object array
SAFEARRAYBOUND rgsaBound;
rgsaBound.lLbound = 0L; rgsaBound.cElements = 1;
voa.vt = VT_ARRAY | VT_DISPATCH;
voa.parray = SafeArrayCreate(VT_DISPATCH, 1, &rgsaBound);
long int k = 0;
hr = SafeArrayPutElement(voa.parray,&k,(void*)pLWP);
hr = IAcadHatch_AppendOuterLoop(pHatch, voa);
//new code end
hr = IAcadHatch_Evaluate(pHatch);
VariantClear(&voa);
i++;
}
else {
sprintf(sVal, "hr=%x", hr);
MessageBox(0, sVal, "AddHatch", 0);
}
VariantClear(&vpnt);
}

Fixed, there is an improvement.

Code changes between:
// New code start
...
and:
// New code end

Lines of code "if (hr)" I removed after inspection - everything is fine.
Now Hatch performed, but only one time, then the program crashes.

I changed the type of Hatch on:
Code: [Select]
bstr = SysAllocString (L "ANSI31");Hatch type changes.

It is not clear where the program falls, she should go to the second line of the file with the points of indices:
Code: [Select]
while (scanf ( "%d %d %d", &p0, &p1, &p2)! = EOF)and to report at the end of the cycle of the number of processed triangles.
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on August 24, 2016, 09:58:19 PM
Move the line:
Code: [Select]
SafeArrayDestroy (psarrayL); // Free arrayto the end of the function and all healed.
Code: [Select]
// check 3dFace-mesh: if Surface is created - Hatch all 3dFaces to view Holes
void FindHoles (IAcadUtility* pUtility, IAcadModelSpace* pModelSpace, VARIANT* varpntlist)
{
//acHatch = 17;
VARIANT vHatchType;
vHatchType.vt = VT_I4;
vHatchType.lVal = 0;

BSTR bstr = NULL;
VARIANT vpnt; // TypedArray for LWPolyLine
SAFEARRAY* psarrayL = SafeArrayCreateVector(VT_VARIANT, 0, 8);
HRESULT hr;
long i = 0;
char sVal[48];
long p0, p1, p2;

pFile2 = stdin;
freopen("triangles.txt","r",stdin);  // reopen file as stdin for read
while (scanf("%d %d %d", &p0, &p1, &p2) !=EOF) {
// make LWPolyline from varpoints of 3dFace
((VARIANT*)psarrayL->pvData)[0].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[0].dblVal = ((double*)varpntlist[p0].parray->pvData)[0];
((VARIANT*)psarrayL->pvData)[1].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[1].dblVal = ((double*)varpntlist[p0].parray->pvData)[1];
((VARIANT*)psarrayL->pvData)[2].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[2].dblVal = ((double*)varpntlist[p1].parray->pvData)[0];
((VARIANT*)psarrayL->pvData)[3].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[3].dblVal = ((double*)varpntlist[p1].parray->pvData)[1];
((VARIANT*)psarrayL->pvData)[4].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[4].dblVal = ((double*)varpntlist[p2].parray->pvData)[0];
((VARIANT*)psarrayL->pvData)[5].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[5].dblVal = ((double*)varpntlist[p2].parray->pvData)[1];
((VARIANT*)psarrayL->pvData)[6].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[6].dblVal = ((double*)varpntlist[p0].parray->pvData)[0];
((VARIANT*)psarrayL->pvData)[7].vt = VT_R8;
((VARIANT*)psarrayL->pvData)[7].dblVal = ((double*)varpntlist[p0].parray->pvData)[1];
// sarray for LWPoly inserted in vpnt
hr = IAcadUtility_CreateTypedArray(pUtility, &vpnt, VT_R8, psarrayL);
// draw LWPolyline in ModelSpace
if (!hr) {
hr = IAcadModelSpace_AddLightWeightPolyline(pModelSpace, vpnt, &pLWP);
IAcadLWPolyline_Update(pLWP);
}
//bstr = SysAllocString(L"SOLID");
bstr = SysAllocString(L"ANSI31");
hr = IAcadModelSpace_AddHatch(pModelSpace, 1, bstr, 1, vHatchType, &pHatch);
SysFreeString(bstr);
if (!hr) {
VARIANT voa; // object array
SAFEARRAYBOUND rgsaBound;
rgsaBound.lLbound = 0L; rgsaBound.cElements = 1;
voa.vt = VT_ARRAY | VT_DISPATCH;
voa.parray = SafeArrayCreate(VT_DISPATCH, 1, &rgsaBound);
long int k = 0;
hr = SafeArrayPutElement(voa.parray,&k,(void*)pLWP);
hr = IAcadHatch_AppendOuterLoop(pHatch, voa);
hr = IAcadHatch_Evaluate(pHatch);
VariantClear(&voa);
i++;
}
else {
sprintf(sVal, "hr=%x", hr);
MessageBox(0, sVal, "AddHatch", 0);
}
VariantClear(&vpnt);
}
SafeArrayDestroy(psarrayL);  // free array
sprintf(sVal, "End of Hatch 3dFaces: %d triangles", i);
MessageBox(0, sVal, "FindHoles", 0);
freopen("CON","r", stdin);
if(pFile2 != 0) fclose(pFile2);  // close file 2
}
Title: Re: Pelles-C + OLE for access to AutoCAD
Post by: sergey on August 25, 2016, 05:19:16 PM
I am very pleased to know that in a fabulously beautiful northern country living person, capable to spend their time and share their knowledge to help an unknown neighbor.

With TIMO and under his guidance, has turned yet another example of AutoCAD software Pelles-C.
The program code is far from perfect, but resolved such humble tasks as:
1. The construction of three-dimensional points on the surface in the form of irregular triangles (3dFaces),
2. 'Hatch' (brushing) triangles to see the missing islands (Holes),
3. The construction of triangles means AutoCAD-library acax[XX]enu.tlb and using LISP-program DTM.lsp,
4. brushing means triangles AutoCAD-library acax[XX]enu.tlb, or by means of LISP-commands,
5. Use for building points existing in the current AutoCAD drawing,
6. Use for the construction of points from a text file 'points.txt' (strings as: X Y Z).

The surface is constructed on the assumption that the set of points is 'Convex-Hull'
It certainly does not always correspond to the real data in the topography, hydrography and other applications.
But nothing and no one bothers to improve computing algorithms, which laid out in free access by Steve J. Fortune ((c) 1987-1992 years).

I would suggest to Mr. DMac place project 'acad_sirface' on one popular site, but remember that the headquarters of a large "SoftXxxx" company is located very close to him and abandoned the idea.

In this archive included project, which was compiled for AutoCAD 2010--2012
In the archive is FtypeLib.exe Converter - XXXX.TLB  --> XXXX.H (by author 'frankie' - an active member of this forum).
In the archive is also LISP-program 'DTM.lsp + DTM.dcl' by Romanian programmer Nicolae Manda.