Multisim and Ultiboard

cancel
Showing results for 
Search instead for 
Did you mean: 

Automation API C++ Builder

Can anybody give an example of how to use the Multisim Automation API with C++ Builder?

 

Thanks,

Steve.

0 Kudos
Message 1 of 15
(7,109 Views)

Does anybody have an example of accessing the Multisim Automation API using C++ from any vendor?  GNU C++?  Visual C++?

 

Thanks,

Steve.

0 Kudos
Message 2 of 15
(7,093 Views)

Hello Steve,

 

I have used the Multisim API with LabVIEW but never with C++. In your development environment look for functions for ActiveX, they should list all the ActiveX servers registered on your system and Multisim will be one of them. From here you will be able to retrieve all the objects, methods and properties exposed by the server. Use them to open/close a connection to Multisim, open/close files, etc.

 

I hope this helps.

Fernando D.
National Instruments

0 Kudos
Message 3 of 15
(7,036 Views)

Fernando, thank you so much for replying!

 

Builder has an "Import Component" feature where I can view the complete list of registered ActiveX controls.  Nowhere in the list do I see Multisim.  There are multiple listings for National Instruments that I believe are related to NI VISA installations.  In the event the "Import Component" is missing some registered ActiveX controls, is there an alternative method I should try to look for additional registered ActiveX controls?  For example, should I dig through the registry using regedit.exe?  If so, for what would I look?

 

Now then, the "Import Component" feature will also import Registered Type Libraries of which Multisim has one registered as "NI Circuit Design Suite Multisim 11.0", Version 1.0, which uses "msinterface.dll".  By clicking a series of buttons I end up with two *.cpp files with corresponding *.h files; "MultisimInterface_OCX.cpp" and "MultisimInterface_TLB.cpp".  I have found I can compile and attempt to run a program by including both the files and accessing some of the classes in the "MultisimInterface_OCX.cpp" file.  The two classes/objects that are exposed by the "*_OCX.cpp" file are TMultisimCircuit and TMultisimApp, which according to the Multisim help that is, frankly, geared entirely toward VB, are the two classes/objects I should be using.  Here is the simple code I have been trying:

		TMultisimApp *mcMSimApp = new TMultisimApp(Application);
		TMultisimCircuit *mcSimulationCircuit/* = new TMultisimCircuit(mcMSimApp)*/;

		mcMSimApp->Connect();

		mcMSimApp->OpenFile((wchar_t *)"C:\\Documents and Settings\\stemay\\My Documents\\Projects\\B454PUNV-E\\Simulations\\CompleteInverterWXFormerWI_LSerMeas.ms11"); // Claims Access is Denied
		mcMSimApp->Disconnect();

		mcSimulationCircuit->AutoConnect = true;
		delete mcSimulationCircuit;
		delete mcMSimApp;

 

 

 

Am I supposed to do something special?  I am trying to follow the confusing VB code:

 

 

Dim MSApp As New MultisimInterface.MultisimApp
MSApp.Connect 

Dim strFileName As String
strFileName = "C:\circuits\ClassBPushPullAmp.ms11" 

Dim Circuit As MultisimInterface.MultisimCircuit
Set Circuit = MSApp.OpenFile(strFileName) 

 I really don't understand VB that well.  I would guess a pointer to a class is DIMensioned and set equal to the address of a new instance of MultisimApp(?).  I just don't understand the MultisimInterface.MultisimApp part, though.

 

 

 

I sincerely appreciate any information you can give me regarding this interface.

 

Thanks

Steve.

0 Kudos
Message 4 of 15
(7,027 Views)

Hi Steve,

 

In the list of ActiveX servers look for 'NI Circuit Design Suite Multisim 11.0 (Ver 1.0)'. Two objects are exposed: MultisimApp (which represents the connection to Multisim) and MultisimCircuit (circuit currently loaded in Multisim). Each object has its own methods that you can use to open/close a connection to Multisim, open/close files, and so on.

 

I found a guide that will be helpful for you. Open Multisim and go to Help>>Multisim Help. Select the Contents tab and browse the Automation API 'book'. You will find more details about the Multisim API. You will also find a simple example that was written in Visual Basic, but it should be similar to C++.

 

I will try to find more information for you, but in the meantime I hope this helps.

Fernando D.
National Instruments

0 Kudos
Message 5 of 15
(7,003 Views)

Hi Steve -

 

Unfortunately, using COM inside of C++ isn't always trivial, and how you use it depends a lot on what exact tool you're using to import the COM objects into your C++ environment.  I'm familiar with the way that Microsoft Visual C++ imports COM interfaces, but I'm not familiar at all with the tool you're using.  COM is actually a pretty complicated technology.  VB wraps up a lot of the underlying mechanisms and hides them from view; some types of C++ imports similar amounts of wrapping up, and some other imports leave things very bare and make you do a lot of type-casting and error checking yourself.

 

I'd be interested to see what the function definition looks like that was created in the *.h file for the "OpenFile" member function of TMultisimApp, or even in seeing the entire *.h file.  Also, you might have to check some C++ Builder documentation on how to use imported COM objects.

 

Alex.

0 Kudos
Message 6 of 15
(6,933 Views)

Alex, thanks for replying!

 

I would be glad to supply the .h file.  I could also supply the .cpp file, but it is huge.  *** I attached the .h files, but forums.ni.com claimed "The file does not have a valid extension for an attachment", so I changed the extension to txt.  The attached files really are genuine .h files. ***  As I said above, the wrapper was generated by importing the type library.  I could not find a registered ActiveX control to import.  Is there something I need to do extra to register the ActiveX control.

 

I have attached two .h files.  At the 'highest' level where I write my code, I use the _OCX.h file, which is, in turn, supported by the _TLB.h file.  Both .h files have to be included in the project.

 

Alex, do you have an example showing how to use VC++ with Multisim?  I would be curious to compare.

 

I am confident Builder can work with COM interfaces.  I have a 1100 line wrapper I wrote to work with Excel, which accesses the COM interface directly.  Most of the keywords I access via the COM interface came from studying VB script generated during a Macro capture.  For that matter, I have countless programs used daily at the benches that access NI VISA.  I will see if I can dig up more stuff to read regarding COM for Builder.  Most of what I have read compares the calling conventions between what Borland uses and what Microsoft uses for DLLs.

 

 

Thanks!

Steve.

 

 

Download All
0 Kudos
Message 7 of 15
(6,924 Views)

Hi Steve -

 

There is no ActiveX component in the Multisim COM library.  COM is a way of sharing objects between programs that includes a number of different protocols, one of which is ActiveX, but not every COM library necessarily has any ActiveX controls.  You do seem to be importing the correct type library.

 

There are several different ways of importing COM objects into Visual C++ and the code looks different in each case, which is why we don't supply an example: we'd have to supply three or four different examples for the different import methods.

 

If I do the import using MFC wrapper classes (very similar to what you're doing I think - it generates some header files that define objects called "CMultisimApp" and "CMultisimCircuit"), then I would use the API like this:

 

 

 

// Initialize COM
CoInitializeEx(NULL, NULL);

// These wrapper objects are declared, but they're just empty shells with 
// no COM objects inside yet
CMultisimApp app;
CMultisimCircuit circuit;

// Create the MultisimApp COM object from the MultisimInterface library 
// and store it in the wrapper object.
app.CreateDispatch(L"MultisimInterface.MultisimApp");

// Connect to a new running instance of Multisim
app.Connect();

// Open a file.  The OpenFile function returns a IDispatch pointer that 
// can be attached to a MultisimCircuit object.
circuit.AttachDispatch(app.OpenFile(L"C:\\ewbtests\\Bandpass.ms11"));

// Do stuff with the circuit here...

// Release the circuit object when we're done.
circuit.ReleaseDispatch();

// The Disconnect function shuts down Multisim, so any MultisimCircuit 
// objects will become unusable after calling Disconnect
app.Disconnect();

// Release the application object
app.ReleaseDispatch();

// Uninitialize COM
CoUninitialize();

 

Note that I never use the C++ "new" and "delete" operators.  COM manages the lifetimes of its own objects, all of which are "dispatch pointers" under the hood (the wrapper classes each have a private LPDISPATCH member variable that holds the dispatch pointer).  Therefore, I use the functions "CreateDispatch", "AttachDispatch", and "ReleaseDispatch" to manipulate the actual COM objects and attach them to their appropriate C++ wrapper classes (COM objects are reference-counted, so if I attach the same dispatch pointer to two different wrapper classes, it won't be destroyed until after both of them release it).  These dispatch functions are provided by the auto-generated wrapper classes.  

 

The "Connect", "OpenFile" and "Disconnect" functions are defined by the MultisimInterface COM library and end up getting invoked on the underlying dispatch object.  

 

Finally, to keep things safe, I really should be using a "try" block and catching any thrown exception of type COleException, which is what happens when COM calls experience an error in this type of wrapper class (under the hood, all dispatch object functions return a simple error code of type HRESULT, the wrapper class encapsulates this in a thrown exception object).

 

Alex.

 

0 Kudos
Message 8 of 15
(6,893 Views)

Hi again Steve -

 

After looking at your *.h files, it looks to me like the C++ Builder equivalents of "CreateDispatch" and "ReleaseDispatch" are "Connect" and "Disconnect".  This is unfortunate because we use "Connect" and "Disconnect" as functions to launch and quit Multisim, but it looks like the wrapper classes were clever enough to rename the Multisim functions to "Connect1" and "Disconnect1".  

 

This means that you'll need to call "Connect" first to connect to the COM server (the equivalent of my "CreateDispatch") and then immediately call "Connect1" to launch Multisim.  That should prevent the access denied error on the OpenFile call (it failed because Multisim wasn't running).  

Note that you have to capture the return value of the OpenFile call and store it in a TMultisimCircuit pointer - you can't use "Connect" with the circuit object because it is "non-creatable" in COM terminology.  The circuit object will also die if you Disconnect or Disconnect1 the TMultisimApp pointer, so don't disconnect the application object until you are finished using the circuit object.

 

Alex.

 

Message 9 of 15
(6,874 Views)

Alex,

 

Once again, thanks so much for replying.

 

I have read both of your posts and have been working with them as best I can.  Through trial and error I managed to install the Multisim type libraries into the CodeGear C++ Builder IDE so I can place non-visual components on the Form, which makes it easier to access the interface.  The beauty of this setup is the generated wrapper classes are precompiled, so you can make sure the wrapper classes work before attempting to use them. 

 

Anyway, I have had a little more success today.  Your last suggestion really made a huge help!  Connect() followed by Connect1() was right on.  Below is the code with which I have been experimenting:

 

Immediately below is the "Main.cpp" ("PlayWTMultiSimAppUnit.cpp") code (odCircuitFile is an OpenDialog box to find the *.ms11 file, and btnOpenSchematic is the TButton set up to fire off the open schematic event):

 

 

void __fastcall TForm1::btnOpenSchematicClick(TObject *Sender)
{
if (odCircuitFile->Execute())
{
mappMultisim->Connect();
mappMultisim->Connect1();

mcirSimulationCircuit->ConnectTo(mappMultisim->OpenFile(odCircuitFile->FileName.c_str()));
}
}
void __fastcall TForm1::btnGitComponentsClick(TObject *Sender)
{
VARIANT varComponents;

varComponents = mcirSimulationCircuit->EnumComponents(ctComponentTypeFilter);
if (VarType(varComponents) != varNull)
{
strcat(cComponents, varComponents.pcVal);
usComponents = varComponents.bstrVal;
}
}

 Immediately below is the "Main.h" ("PlayWTMultiSimAppUnit.h") code:

class TForm1 : public TForm
{
__published:	// IDE-managed Components
	TMultisimApp *mappMultisim;
	TMultisimCircuit *mcirSimulationCircuit;
	TButton *btnOpenSchematic;
	TOpenDialog *odCircuitFile;
	TButton *btnGitComponents;
	void __fastcall btnOpenSchematicClick(TObject *Sender);
	void __fastcall btnGitComponentsClick(TObject *Sender);
private:	// User declarations
	Multisiminterface_tlb::ComponentType ctComponentTypeFilter;
	UnicodeString usComponents;
	char cComponents[256];
public:		// User declarations
	__fastcall TForm1(TComponent* Owner);
};

 

 

The above code compiles - AND RUNS!!!  Unfortunately, I can't seem to access the components or the reference designators or whatever EnumComponents() is supposed to do.  When I run the program and click btnOpenSchematic, multisim.exe shows up in Windows processes using about 120MBytes of RAM - about right.  After the schematic opens, the amount of RAM allocated to the multisim.exe increases slightly - about right.  When I stop the program (click the red "X" button at the top left-hand corner of the program), multisim.exe 'disappears' from the list of Windows processes - once again, about right.  This is all very encouraging, so far.

 

Now I need to move forward.  I decided as a confidence booster, I would attempt to load all the reference designators, hence the EnumComponents() function call.  Unfortunately, what I find in usComponents and cComponents (yes, redundant; I was merely trying to figure out what kind of data would be returned) was garbage data.  cComponents = 0x01 (?) and usComponents = 0x0001, 0x0100, 0x0004, 0x0000 (?) (I will never get used to UnicodeStrings)  I am confident I am doing something quite wrong.  In case you are wondering, the schematic I am using is a relatively simple schematic from the Multisim Basic training, "Up_down.ms11".  There are 10 or so components with simple reference designators, so I decided it would be a good candidate for experimentation.

 

I appreciate your help and I will continue to experiment as I wait for your next response!

 

Thanks,

Steve.

0 Kudos
Message 10 of 15
(6,863 Views)