Hi Nick and Jörgen,
It *is* possible to create an add-in using Borland tools, but it isn't easy!
After fighting the MS Visual C++ compiler (not even close to ANSI compliant)
I finally went back to Borland C++ and figured out how to make it work.
The issues will be the same, but you will have to translate to Delphi
Pascal, I'm not familiar enough with it to tell you exactly how to do it.
Now here is the main problem:
You want to implement one of two interfaces which Inventor will attempt to
call on startup:
- "ApplicationAddInServer" (a dispatch interface, primarily for Visual
Basic) or
- "IRxApplicationAddInServer" (a custom interface).
Note that "_IRxApplicationAddInServer" is an internal interface which you
are not supposed to use, and it WILL NOT BE CALLED by Inventor on startup,
so your code will never run. (In general, you should not use any interfaces
that begin with an underscore, as Autodesk makes no commitment to their
continuity.)
Unfortunately, the Borland inport tool will not offer to let you implement
either of those interfaces! (IRxApplicationAddInServer is a custom
interface, so I don't know why it doesn't work, maybe because the
"OLEAutomation" flag isn't set.)
The work-around is to select "_IRxApplicationAddInServer" as the interface
you are implementing, and then manually patch up the code to implement
"IRxApplicationAddInServer" instead.
- Create an ActiveX Library Project
- Import the "Autodesk Inventor Object Type Library"
- Now create a new COM Object and choose in the dialog under "implementing
interface" the interface "_IRxApplicationAddInServer"
To patch up the code, you must do three things:
1) The implementation class must inherit from IRxApplicationAddInServer, not
_IRxApplicationAddInServer: I used a global search and replace in the
editor.
2) The interfaces of the methods Activate, Deactivate, ExecuteCommand, and
get_Automation must be changed to match the signatures for the
IRxApplicationAddInServer class instead of those for
_IRxApplicationAddInServer class. A file which defines the interfaces
should have been created during the import of the type library, in C++ it is
named "Inventor_TLB.h"
3) Add implementation code for the methods Activate, Deactivate,
ExecuteCommand, and get_Automation.
This is what it looks like in C++ before and after modification. I've
deleted blocks of code that are not changed. Also note that Borland C++
Builder renames some interfaces during the import to avoid conflicts with
Borland VCL classes, in particular the Application class is renamed to
"App".
Also note (to give credit where it's due) some of the code is copied from
the Inventor Application Wizard for Visual C++.
******************** LrTestAddInImpl.h -- Before Modification
**********************************************
// LRTESTADDINIMPL.H : Declaration of the TLrTestAddInImpl
. . . . . .
////////////////////////////////////////////////////////////////////////////
/
// TLrTestAddInImpl Implements _IRxApplicationAddInServer, default
interface of LrTestAddIn
// ThreadingModel : Apartment
// Dual Interface : FALSE
// Event Support : FALSE
// Default ProgID : TestInventorAddIn.LrTestAddIn
// Description : Example Code to make an Inventor Add In
////////////////////////////////////////////////////////////////////////////
/
class ATL_NO_VTABLE TLrTestAddInImpl :
public CComObjectRootEx
,
public CComCoClass,
public _IRxApplicationAddInServer
{
public:
TLrTestAddInImpl() {}
. . . . . .
BEGIN_COM_MAP(TLrTestAddInImpl)
COM_INTERFACE_ENTRY(_IRxApplicationAddInServer)
END_COM_MAP()
// _IRxApplicationAddInServer
public:
STDMETHOD(Activate(ApplicationAddInSitePtr AddInSiteObject,
TOLEBOOL FirstTime));
STDMETHOD(Deactivate());
STDMETHOD(ExecuteCommand(long CommandID));
STDMETHOD(get_Automation(AddInAutomationPtr* AutomationPtr));
};
#endif //LrTestAddInImplH
******************** LrTestAddInImpl.cpp -- Before Modification
**********************************************
// LRTESTADDINIMPL : Implementation of TLrTestAddInImpl (CoClass:
LrTestAddIn, Interface: _IRxApplicationAddInServer)
. . . . . .
STDMETHODIMP TLrTestAddInImpl::Activate(
ApplicationAddInSitePtr AddInSiteObject, TOLEBOOL FirstTime)
{
try
{
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID__IRxApplicationAddInServer);
}
return S_OK;
};
STDMETHODIMP TLrTestAddInImpl::Deactivate()
{
try
{
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID__IRxApplicationAddInServer);
}
return S_OK;
};
STDMETHODIMP TLrTestAddInImpl::ExecuteCommand(long CommandID)
{
try
{
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID__IRxApplicationAddInServer);
}
return S_OK;
};
STDMETHODIMP TLrTestAddInImpl::get_Automation(
AddInAutomationPtr* AutomationPtr)
{
try
{
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID__IRxApplicationAddInServer);
}
return S_OK;
};
******************** LrTestAddInImpl.h -- After Modification
**********************************************
// LRTESTADDINIMPL.H : Declaration of the TLrTestAddInImpl
. . . . . .
////////////////////////////////////////////////////////////////////////////
/
// TLrTestAddInImpl Implements IRxApplicationAddInServer, default
interface of LrTestAddIn
// ThreadingModel : Apartment
// Dual Interface : FALSE
// Event Support : FALSE
// Default ProgID : TestInventorAddIn.LrTestAddIn
// Description : Example Code to make an Inventor Add In
////////////////////////////////////////////////////////////////////////////
/
class ATL_NO_VTABLE TLrTestAddInImpl :
public CComObjectRootEx,
public CComCoClass,
public IRxApplicationAddInServer
// ^^^^^^^^^^^^^^^^^^^^^^^^^ Inherit from IRxApplicationAddInServer,
not _IRxApplicationAddInServer
{
public:
TLrTestAddInImpl() {}
. . . . . .
BEGIN_COM_MAP(TLrTestAddInImpl)
COM_INTERFACE_ENTRY(IRxApplicationAddInServer)
END_COM_MAP()
private:
CComPtr m_pSite;
CComPtr m_pApplication;
// Definitions of commands here.
CComPtr m_pCmdTestCommand;
// IRxApplicationAddInServer
public:
// **** Note changed signature in Activate and get_Automation. Signature
copied from Inventor_TLB.h
STDMETHOD(Activate(Inventor_tlb::IRxApplicationAddInSitePtr pAddInSite,
Inventor_tlb::BooleanType FirstTime) );
STDMETHOD(Deactivate());
STDMETHOD(ExecuteCommand(long CommandID));
STDMETHOD(get_Automation(LPUNKNOWN* ppUnk));
// Return the Rubicon Add-In Site of this object's attachment. (Some useful
inline functions)
const CComPtr Site() const
{ return m_pSite; }
// Return the Rubicon Application.
const CComPtr & Application() const
{ return m_pApplication; }
};
#endif //LrTestAddInImplH
******************** LrTestAddInImpl.cpp -- After Modification
**********************************************
// LRTESTADDINIMPL : Implementation of TLrTestAddInImpl (CoClass:
LrTestAddIn, Interface: IRxApplicationAddInServer)
. . . . . .
STDMETHODIMP TLrTestAddInImpl::Activate(
Inventor_tlb::IRxApplicationAddInSitePtr pAddInSite,
Inventor_tlb::BooleanType FirstTime)
{
try
{
HRESULT Result=NOERROR;
CComPtr pAppUnk, pCmdUnk;
TVariant vInTools(true);
// Add display names of commands here.
WideString bstrTestCommand("Test Command");
// High-level interfaces supplied directly and implicitly by Inventor to
the
// AddIn. These may be held right up until the directive to shutdown
(Deactivate) is received.
m_pSite = pAddInSite;
Result = pAddInSite->get_Application(&pAppUnk);
if( FAILED( Result ) ) goto wrapup;
Result = pAppUnk->QueryInterface (DIID_App, (void **) &m_pApplication);
if( FAILED( Result ) ) goto wrapup;
// TODO:
// This is where you would place any additional startup code.
// Create commands here.
//
Result = pAddInSite->CreateCommand(bstrTestCommand, 0, vInTools,
&pCmdUnk);
if( FAILED( Result ) ) goto wrapup;
Result = pCmdUnk->QueryInterface (DIID_Command, (void **)
&m_pCmdTestCommand);
if( FAILED( Result ) ) goto wrapup;
MessageBox( 0, "Test Command Activated", "Test Add-In", MB_OK );
return Result;
wrapup:
MessageBox( 0, "LrTestAddIn Activated With Errors", "Test Add-In",
MB_OK );
return Result;
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IRxApplicationAddInServer);
}
};
STDMETHODIMP TLrTestAddInImpl::Deactivate()
{
try
{
// Addin wizard destroys commands here
if (m_pCmdTestCommand.p)
{
m_pCmdTestCommand->Delete();
m_pCmdTestCommand.Release();
}
// Cleanup up of interfaces supplied by Inventor and held on by this
AddIn
// at initialization or load.
m_pApplication.Release();
m_pSite.Release();
return NOERROR;
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IRxApplicationAddInServer);
}
return S_OK;
};
STDMETHODIMP TLrTestAddInImpl::ExecuteCommand(long CommandID)
{
try
{
MessageBox( 0, "LrTestAddIn Execute", "Test Add-In", MB_OK );
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IRxApplicationAddInServer);
}
return S_OK;
};
STDMETHODIMP TLrTestAddInImpl::get_Automation(LPUNKNOWN* ppUnk)
{
try
{
if (!ppUnk)
return E_INVALIDARG;
*ppUnk = NULL;
return E_NOINTERFACE;
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IRxApplicationAddInServer);
}
};
I hope this is enough to get you going. Good Luck!
"jpisarz" wrote in message
news:F5733EC568C0924A777F92CF64F91B03@in.WebX.maYIadrTaRb...
> Hi Nick,
> I am now trying to create an addin since one week but its a hard job and I
> have the feeling that I will give up soon and programm with VB6.
>
> Here the points for Delphi 6 SP1 using IV 5.3 SP1 (all german versions,
but
> it schould work international):
>
> - Create an ActiveX Library Projekt
> - Import the "Autodesk Inventor Object Type Library"
> and have the first problem: the import is not correct. Look for the
> Interface:
>
> ApplicationAddInSite = dispinterface
> ['{E3571299-DB40-11D2-B783-0060B0F159EF}']
> property Type_: ObjectTypeEnum readonly dispid 0;
> property Application: Application readonly dispid 50336769;
> function CreateCommand(const CommandName: WideString; CommandID:
> Integer;
> InstallInTools: OleVariant):Command; dispid
> 1610678274;
> end;
>
> and add the missing line "function CreateCommand.....dispid 1610678274;"
>
> I don't know if it is the only mistake, but it is an important.
>
> - Now create a new Com-Object and choose in the dialog under "implementing
> interface" the Interface
>
> "_IRxApplicationAddInServer"
>
>
> Here is the code you have to Add to the new Object:
>
> //this is added by the com object wizard
> type
> TTAppAddInServ = class(TAutoObject, _IRxApplicationAddInServer)
> protected
> function Activate(const AddInSiteObject: ApplicationAddInSite;
> FirstTime: WordBool): HResult; stdcall;
> function Deactivate: HResult; stdcall;
> function ExecuteCommand(CommandID: Integer): HResult; stdcall;
> function Get_Automation(out AutomationPtr: AddInAutomation): HResult;
> stdcall;
> { Protected-Deklarationen }
> end;
>
>
>
> var
> oInventor : Inventor_TLB.Application;
> oCmd1 : Inventor_TLB.Command;
>
> implementation
>
> uses ComServ;
>
>
> function TTAppAddInServ.Activate(const AddInSiteObject:
> ApplicationAddInSite; FirstTime: WordBool): HResult;
> begin
> //init
> AppDefault := InitApplication;
> //get the InventorObject
> oInventor := AddInSiteObject.Application;
>
> //Create commands
> oCmd1 := AddInSiteObject.CreateCommand('My Add In Command',1,True);
>
> Result := S_OK;
> end;
>
> function TTAppAddInServ.Deactivate: HResult;
> begin
> //CleanUp
> oInventor := nil;
> oCmd1 := nil;
> Result := S_OK;
> end;
>
> function TTAppAddInServ.ExecuteCommand(CommandID: Integer): HResult;
>
> begin
> Case CommandID of
> 1: begin
> //here your Code for your command
> end;
> end;
> Result := S_OK;
> end;
>
> function TTAppAddInServ.Get_Automation(
> out AutomationPtr: AddInAutomation): HResult;
> begin
> Result := E_NOINTERFACE;
> end;
>
> //this is added by the com object wizard
> initialization
> TAutoObjectFactory.Create(ComServer, TTAppAddInServ,
Class_TAppAddInServ,
> ciMultiInstance, tmApartment);
> end.
>
>
>
> That's the base for an addin. Registering the addin is the same as for VB,
> shown in the SDK documentation. Use the GUID from the CoClass created by
the
> ActiveX Library Projekt Wizard.
>
>
> All other stuff is "normal" COM-programming. To avoid trouble getting the
> events done, you should use the Event Sink Importer from Binh Ly:
> http://www.techvanguards.com/ There is also a "TypeExport" which creates
a
> more correct Version of "invetor_TLB.pas".
>
> But, as i said, its hard to code. Till now, I don't get the interactive
> selection part working. Also there is trouble with dynamic string arrays
> getting via OLEVariant.
>
> greetings from berlin, germany
> Jörgen
>
>
> "Nick Hall" schrieb im Newsbeitrag
> news:Xns91E74087BFE4Bnickhallaltasystems@64.124.46.110...
> > Has anyone implemented an addin using Delphi ?
> >
> > Could anyone point me at some sample code ?
> >
> > Thanks
> > Nick
>
>