C language > Work in progress

Pelles-C + OLE for access to AutoCAD

(1/11) > >>

sergey:
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/

TimoVJL:
If you like wrappers download this for Word automation.
It use AutoWrap example.

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

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

sergey:
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 ...

TimoVJL:
DispHelper test:

--- Code: ---#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;
}
--- End code ---
Look acadauto.chm for objects.

For old IntelliCAD 6.x engine (progeCAD Smart 2009) AutoWrap example too:
--- Code: ---#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;
}
--- End code ---

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

Navigation

[0] Message Index

[#] Next page

Go to full version