Stuck adding a .NET *.dll reference in GhPython

Hi!,

I’m migrating the tools I coded some time ago to connect SAP2000 V16 with GH to V17 version. CSi has changed the way the API is used and I cannot figure out how to make it work.

In the help file (with a VB example) the say:

The first step in using the CSi API from an external application is to reference SAP2000v17.DLL or SAP2000v17.TLB from your application. If using Excel VBA, reference SAP2000v17.TLB by opening the VBA editor, clicking the Tools menu > References command and selecting SAP2000v17.TLB from the program installation folder.

Next, within your application, you will create a variable of interface type cOAPI, and an instance of the Sap2000 object which implements cOAPI. In VBA this could be accomplished as:

Dim mySapObject As Sap2000v17.cOAPI
Set mySapObject = CreateObject(“CSI.SAP2000.API.SapObject”)

The first line creates the interface variable and the second line creates the instance of the Sap2000 object which implements the interface. Now that an instance of the Sap2000 object has been created in your application, start SAP2000 using the following VBA command:

SapObject.ApplicationStart

My translation to Python:

import clr

clr.AddReference(‘SAP2000v17.dll’)

import SAP2000v17 as sap

sapObj = sap.cOAPI()

SapObj.ApplicationStart(6)

Python says:

Runtime error (TypeErrorException): Cannot create instances of cOAPI because it is abstract
Traceback:
line 7, in script

This error refers to line 7: “sapObj = sap.cOAPI()”

Anyone knows where is my mistake? I’m noticing that I’m making something wrong creating the variable interface and the sap2000 object instance.

I’ve tried with:

sapObj = sap.cOAPI
setattr(sapObj,‘SetAsActiveObject’,True)

but I get:

Runtime error (MissingMemberException): attribute ‘SetAsActiveObject’ of ‘cOAPI’ object is read-only

It seams that I cannot create a proper instance of the Sap2000 object.

Thanks in advance.

Based on a very quick search of the internet, I think creation would be

sapObj = Sap2000v17.SapObject()

This is based on
http://docs.csiamerica.com/help-files/sap2000-oapi/Example_Code/Example_3_(Visual_C_2005).htm

1 Like

And ghpython rises:

Runtime error (MissingMemberException): attribute ‘SapObject’ of ‘namespace#’ object is read-only

I’ve tried it :frowning: And, that refers to the old API…they try to explain it like this:

In version 17 of SAP2000, the API has been separated from the main SAP2000 executable into a dynamic link library (DLL). This will change how API client applications connect to CSI software. You will no longer directly reference the SAP2000 executable assembly. Instead, you will reference the API DLL. In addition, you will no longer be able to declare a variable of type SapObject. Instead, create a variable of type SAP2000v17 cOAPI, which is an interface type. Then instantiate an object that implements the cOAPI interface. This process is detailed below.

And below they detail the process as I copied in the first post.

Thanks for the fast answer.

And here is where I’m lost…

Probably the only people that can really help are the SAP2000 developers. I can only guess at what to try. If I had a C# or VB.NET sample, I could easily craft up a python equivalent

I copied it in the first post:

Dim mySapObject As Sap2000v17.cOAPI
Set mySapObject = CreateObject(“CSI.SAP2000.API.SapObject”)

The first line creates the interface variable and the second line creates the instance of the Sap2000 object which implements the interface. Now that an instance of the Sap2000 object has been created in your application, start SAP2000 using the following VBA command:

SapObject.ApplicationStart

Thanks in advance @stevebaer . I will try to contact them too.

That is VBA code, not VB.NET.

Hi!,

Sorry, you are right :smile:

Here we go using vb.net:

Imports SAP2000v17
Dim ret As Integer
Dim myAssembly As System.Reflection.Assembly
myAssembly = System.Reflection.Assembly.LoadFrom(pathToEXE)
Dim mySapObject As cOAPI
mySapObject = DirectCast(myAssembly.CreateInstance(“CSI.SAP2000.API.SapObject”), cOAPI)
ret = mySapObject.ApplicationStart()

And using C#:

using SAP2000v17;
myAssembly = System.Reflection.Assembly.LoadFrom(pathToEXE);
cOAPI mySapObject;
mySapObject = (cOAPI)myAssembly.CreateInstance(“CSI.SAP2000.API.SapObject”);

Ok, I’ve copy the structure in Python using System module but still getting an error.

import System

myAssembly = System.Reflection.Assembly.LoadFrom(“myPathHere”)
sapObject = myAssembly.CreateInstance(“CSI.SAP2000.API.SapObject”)

sapObject.ApplicationStart()
SapMod = SapObj.SapModel()

Error: Runtime error (MissingMemberException): ‘NoneType’ object has no attribute ‘ApplicationStart’

It seams that I’m not using CreateInstance properly and an instance of SapObject is not assigned to sapObject…mmmm Printing it python returns “None”

P.S.: I’ve check the “myPathHere” to point to my *.dll and if I print myAssembly it returns the SAP2000V17 info.

Last test for today (I just saw that “CSI.SAP2000.API.SapObject” is in the exe file, not in the *.dll):

import System

myAssembly = System.Reflection.Assembly.Load(“SAP2000.exe”)
print myAssembly
sapObj = myAssembly.CreateInstance(“CSI.SAP2000.API.SapObject”)

print sapObj
sapObj.ApplicationStart()
SapMod = SapObj.SapModel()

It prints to console:

SAP2000, Version=17.0.0.0, Culture=neutral, PublicKeyToken=null
Runtime error (TargetInvocationException): Se produjo una excepción en el destino de la invocación.
Traceback:
line 6, in script

So it seams that the assembly is loaded into myAssembly but it generates an error (“An exception occurred in the target of an invocation.”) when we call its “CreateInstance” method.

I’ve been investigating the API and the files that are used to reference it.

The SAP2000.exe files is the one containing the “CSI.SAP2000.API.SapObject” class but something is going wrong trying to cast it to the python variable.

New try using some hints found here:

It basically creates a a variable with the type and try to create a instance using System.Activator. But we got the same error: “An exception occurred in the target of an invocation.”

import System

myAssembly = System.Reflection.Assembly.Load(“SAP2000.exe”)
print myAssembly

type = myAssembly.GetType(“CSI.SAP2000.API.SapObject”)
print type

sapObj = System.Activator.CreateInstance(type)

Output:

SAP2000, Version=17.0.0.0, Culture=neutral, PublicKeyToken=null
CSI.SAP2000.API.SapObject
Runtime error (TargetInvocationException): Se produjo una excepción en el destino de la invocación.
Traceback:
line 8, in script

And this doesn’t work either.

Trying to specify assembly and constructor:
sapObj = System.Activator.CreateInstance(str(myAssembly),str(type))

Hi @Angel
can you try using clr.LoadAssemblyFromFileWithPath(path) #> assembly or a similar call from the clr module? Then, use the resulting assembly with myAssembly.CreateInstance(typeName).

If that does now work, try catching the exception and reading the ex.InnerException. Catching is done by using

try:
    lineThatDoesNotWork()
except Exception, ex:
    print ex.InnerException

Alternatively, I suggest you use the debugger from _EditPythonScript. If you get the correct code there, it will work in GhPython, too. Some more info about caching are here:

Hi @piac ,

First of all, thanks for the answer. Any help will be welcome.
I’ve tried the two things that you told me.

  1. Using clr.LoadAssemblyFromFileWithPath(path) and then myAssembly.CreateInstance(typeName) returns the same error than before.

import System
import clr

myAssembly = clr.LoadAssemblyFromFileWithPath(“C:\Program Files (x86)\Computers and Structures\SAP2000 17\SAP2000.exe”)
print myAssembly
sapObj = myAssembly.CreateInstance(“CSI.SAP2000.API.SapObject”)

Returns:

SAP2000, Version=17.0.0.0, Culture=neutral, PublicKeyToken=null
Runtime error (TargetInvocationException): Se produjo una excepción en el destino de la invocación.

  1. Trying to catch the error with:

import System
import clr

myAssembly = clr.LoadAssemblyFromFileWithPath(“C:\Program Files (x86)\Computers and Structures\SAP2000 17\SAP2000.exe”)
print myAssembly

try:
sapObj = myAssembly.CreateInstance(“CSI.SAP2000.API.SapObject”)
except Exception, ex:
print ex.InnerException

Returns:

SAP2000, Version=17.0.0.0, Culture=neutral, PublicKeyToken=null
Runtime error (MissingMemberException): ‘StandardError’ object has no attribute ‘InnerException’

Then reading the stackoverflow post that you linked here I tried with:

import System 
import clr

myAssembly =  clr.LoadAssemblyFromFileWithPath("C:\\Program Files (x86)\\Computers and Structures\\SAP2000 17\\SAP2000.exe")
print myAssembly

try:
    sapObj = myAssembly.CreateInstance("CSI.SAP2000.API.SapObject")
except Exception, ex:
     print type(ex).__name__
     print type(ex.clsException).__name__

And it returns:

SAP2000, Version=17.0.0.0, Culture=neutral, PublicKeyToken=null
Runtime error (ArgumentTypeException): SapObject() takes no arguments (1 given)

I’m going to try running the code in the Rhino Python editor and see how could I catch the error.

Again, we are trying to help but we do not produce or have either Sap or their bindings at our disposal, so it is hard for us to tell. That being said, what happens if you replace the last lines with a simple:

sap = CSI.SAP2000.API.SapObject()```

?
Also, check that `print type(ex).__name__ ` really prints `Runtime error (ArgumentTypeException): SapObject() takes no arguments (1 given)`. It seems something is odd.

About the debugger, you can place a breakpoint where the ex variable is available and just inspect it with its attributes.

I’m perfectly aware of that and I will never be able to express my gratitude to the Mcneel team to be so helpful in every post in the forum. I’ve written to CSi team and I’m waiting for the answer.

A was checking that and it seams that the innerException is:

It seams that there is a dependency with one module that is not in scope or something. I’ve located that CSICommon.dll and it’s in SAP2000 root folder.

And finally it seams that I’ve found the error: there is something wrong with the CSICommon.dll library. To be precise, it seams that there is a problem with its x86/x64 compatibility.

If I try to import the CSICommon.dll with “clr.AddReferenceToFileAndPath()”, the system throws this error:

Runtime error (BadImageFormatException): No se puede cargar el archivo o ensamblado 'CSICommon.DLL' ni una de sus dependencias.  no es una aplicación Win32 válida. (Excepción de HRESULT: 0x800700C1)

Thanks @piac and @stevebaer for the support in this painful quest to find the problem with the API. I’ve attached this conversation in the email to CSi.

With this we directly find the error that we have been searching for:

Runtime error (FileNotFoundException): No se puede cargar el archivo o ensamblado 'CSICommon, Version=13.0.0.0, Culture=neutral, PublicKeyToken=null' ni una de sus dependencias. El sistema no puede encontrar el archivo especificado

Arrggg! Hating that error localization thing. It says that the system cannot find the specified file. I’ll try to register de DLL.

P.S.: I tried…but the the RegSvr32 rises (bad direct translation): "The modulo was loaded ‘PathHere’, but it didn’t find the entrance point ‘DllRegisterServer’.

Have you tried running this script in the 32bit version of Rhino?

1 Like

and…BINGO! The script works perfectly in Rhino 32Bits…o.O

And the question is, is there any way to get it working in x64 without needing a different *.dll? (Adding this to the CSi request email).

I can switch to 32bits RH but it’s not the ideal workaround.

Thanks!

1 Like