Hace you considered a SheetCAM plugin API?

Hi Les,

I have done much more reading about DDE. Nonbody loves it, even Microsoft. Have you thought about a Plugin API if you don’t want to expose a COM object model? You could just do a LoadLibrary and GetProcAddress of the main plugin DLL entry points and allow the plugin to register for services. These services could be the LoadJob(), LoadDrawing(), etc. The plugin could register a notification window where you could send process completion notices, etc…

The plugin could then expose whatver API was approriate for it’s clients, or just do local processing to enhance SheetCAM.

-James

That would work but you would have to write a plugin that talks to your
macro. As the current problem is finding a good way to talk to your
macro, I don’t think it would be any real advantage. Would you be
interested in writing a plugin if I provided the interface?

I am currently investigating COM and I may implement it if it isn’t too
difficult. The problem is that I intend to rewrite the GUI at some point
in the not too distant future using the wxWidgets cross platform
toolkit. This will enable me to release both Linux and Windows versions
of SheetCam. wxWidgets does not directly support COM as there is no
Linux equivalent.

Les

I have done much more reading about DDE. Nonbody loves it, even Microsoft. Have you thought about a Plugin API if you don’t want to expose a COM object model? You could just do a LoadLibrary and GetProcAddress of the main plugin DLL entry points and allow the plugin to register for services. These

Hi Les,

Yes, I will write the first plugin and collaborate on the interface definitions if you wish. Since the plugin will be running in-process with SheetCAM it is the best place to implement platform specific interfaces like COM (IDispatch for VB). Plugins will also enhance SHeetCAM greatly if you let us hook the processing, allowing third party import routines, etc.

This does need to be carefully defined.

-James

Sounds good to me. Hooking the import routines shouldn’t be a problem. I
had originally intended the import filters to be plugins so a lot of the
infrastructure is already in place. Export/post processing is also
modular and based on C style callbacks so it would be easy to make that
available to the plugin.

Process definitions are a bit more tricky. Exposing Borland C++ classes
would probably be a bad idea because that would force the plugins to be
built with the Borland compiler. I think we need to keep to a pure C
interface which is compiler independent.

I’ll have a think about a spec. If you have any ideas then let me know.

Les


jemmyell wrote:

Hi Les,

Yes, I will write the first plugin and collaborate on the interface definitions if you wish. Since the plugin will be running in-process with SheetCAM it is the best place to implement platform specific interfaces like COM (IDispatch for VB). Plugins will also enhance SHeetCAM greatly if you let us hook the processing, allowing third party import routines, etc.

Hi Les,

I spent the weekend exploring ideas and ended up writing a prototype system in VC++ and VB.

Basically, I have an MFC app that calls ‘PluginStartup’ in InitInstance and ‘PluginShutDown’ in ExitInstance. In PluginStartup I pass a pointer to a data structure that has two arrays, one of process IDs (ID_LOADJOB for example) and one of function callback pointers. The MFC app (emulating the SheetCAM process) does all interfacing to C functions using LoadLibrary and GetProcAddress.

The prototype plugin is a MFC appwizard DLL that is a straight C function library AND a COM inprocess server to talk to VB.

I am using a thread instantiated in the ‘SheetCAM’ (my prototype process) process space to issue the callbacks to the initiating process. I have a named event in that thread that can be signaled from the COM object attached to VB to cause the callbacks to be made

I have good results on all fronts, but still some difficulties passing parameters from VB to the callback thread. I do have shared memory in the DLL but it does not seem to be part of the COM objects process space, so I must write a parameter passing function for that.

Does all of this sound reasonable?

Do you want me to post the prototype here, or email it to you?

-James

Hi James,

I have been working on this. SheetCam will look for all dlls in the
SheetCam directory. If they export the function scInit() then they are
loaded and scInit() is called. SheetCam will look for a number of other
callbacks. If they exist they will be called at the appropriate time,
otherwise they will be ignored. You should be able to link directly to
SheetCam by creating a .lib file from SheetCam.exe using implib. Note
that the functions must be linked by name, not by ordinal. Ordinals
could change, breaking your plugin. I can’t provide a Visual C++ library
file as I am using Borland C++builder.

You should be able to attach your source files but you will have to zip
them up first as there are a limited number of file extensions that the
forum will accept.

Have a look at the attached a header file for a list of the functions
that are currently being implemented. Hopefully they should be ready for
the next release

#ifndef PLUGINAPI_H_INCLUDED
#define PLUGINAPI_H_INCLUDED

#ifdef SHEETCAM_APP
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT __declspec(dllimport)
#endif




extern “C”{

/*Functions that SheetCam can call.
Apart from scInit(), all other functions are optional. If they exist
they will be called. If not they will be ignored
*/

#ifndef SHEETCAM_APP

/* this function must be implemented. It is called immediately after the
dll is loaded. If it returns false then the dll is ignored

Use PluginID in all calls to SheetCam where it is needed
*/
DLLIMPORT bool WINAPI scInit(int PluginID);

/* called when a plugin’s menu is accessed*/
DLLIMPORT void WINAPI scOnMenu(int MenuIndex);

#endif


/* Functions available to the plugin*/


/* Load a drawing into the current part
If FileName == NULL then the user will be prompted for a file and
FileType will be ignored.
returns:
true if file loaded successfully
*/
enum scFILETYPES{scFILETYPE_DXF =0,scFILETYPE_HPGL,scFILETYPE_EXCELLON,
scFILETYPE_EMF};

DLLEXPORT bool WINAPI scLoadDrawing(LPSTR FileName, scFILETYPES FileType);

/* Load a job. If FileName == NULL then the user will be prompted for a file
returns:
true if file loaded successfully
*/
DLLEXPORT bool WINAPI scLoadJob(LPSTR FileName);

/* Check if SheetCam is processing paths. You can still safely call most
functions while SheetCam is processing paths.
returns:
true if SheetCam is processing paths
*/
DLLEXPORT bool WINAPI scIsProcessing(void);

/* Run the currently selected post processor. This function will wait for
SheetCam to finish processing paths before returning.
If OutFile == NULL then the user will be prompted for a file name.
returns:
true if processing complete with no errors
false if file not opened or there were any errors
*/
DLLEXPORT bool WINAPI scRunPost(LPSTR OutFile);


/*Menu functions. All menus are referred to by name using the English
translation and excluding the & underline symbol. For example “View”
refers to
the View menu even if it is displayed as ‘Anzeige’(German translation).
*/

/*Add a main menu. It will appear before ‘Before’. For instance if
Before ==“Tools”, the menu will appear between ‘View’ and ‘Tools’.
If Before == NULL or the name does not exist then the menu will appear
before
the ‘Help’ menu. If ‘Name’ already exists then no new menu will be
created and
the index of the existing menu will be returned.

Returns: -1 to indicate an error or a unique index number that will be used
by scPluginMenu to refer to the menu.
*/
DLLEXPORT int WINAPI scAddMainMenu(int PluginID, LPSTR Before, LPSTR Name);

/*Add a sub menu. Path is a fully qualified path to the menu. For instance
Options->Complexity would be referred to as as /Options/Complexity.
Note the unix style forward slash!
It is an error for Path to be NULL or to refer to a nonexistent main menu.

Returns: -1 to indicate an error or a unique index number that will be used
by scPluginMenu to refer to the menu.
*/
DLLEXPORT int WINAPI scAddMenu(int PluginID, LPSTR Path, LPSTR Name);

}; //extern"C"
#endif

Hi Les,

This looks good too, but I would encourage the use of ‘soft’ linkage since that would make the versioning of SheetCAM features easier in the future. I.E. under the plan I was working on, SheetCAM would pass me the full set of callbacks currently available matched by callback ID. I could then call the functions that my version of the plugin was written for without concern for any new features / functions and the associated export .lib associated with that particular compilation. That way everybody that wanted to write a plugin could use only the functions they wanted to, and they would not have to be concerned with changes in the layout caused by recompilation of SheetCAM. Otherwise, I think each plugin will have to relink for each new feature set, since the layout of the function entry points would change in the .lib.

What do you think?

I will post my work in process when I get home so you can see what I am thinking in terms of actual code.

-James

As long as you link via name rather than ordinal you will be OK. If new
functions are added an older dll simply won’t see them. After doing some
experiments I have produced a .def file. You simply link with the .def
file and everything should work. As far as I know .def files are
universal so it should work with any compiler.

This method is a lot simpler for the plugin programmer and (slightly)
more typesafe.

Les

Hi Les,

Ok, sounds good. When do you think I can get a test version and .DEF file to work up a plugin prototype?

-James

The attached zip file contains a test version of SheetCam with a demo
dll file. The demo dll adds a new main menu with ‘load drawing’ and
‘load job’ sub-menus. It demonstrates most of the currently available
features.

Unzip SheetCam.exe and demo.dll into your SheetCam directory. The other
files are the source files for demo.dll.

Les

PS email subscribers can click on the link below:

Les,

This is really great. I will work with this right away.

Thanks so much!

-James

Hi Les,

It looks like the VC++ tools don’t support an IMPORTS statement for .EXE level.

Here is the failure message from VC++

Compiling resources…
Linking…
.\VBDemo.def : warning LNK4017: IMPORTS statement not supported for the target platform; ignored
.\VBDemo.def : warning LNK4017: “SHEETCAM.EXE”.scAddMainMenu statement not supported for the target platform; ignored
.\VBDemo.def : warning LNK4017: “SHEETCAM.EXE”.scAddMenu statement not supported for the target platform; ignored
.\VBDemo.def : warning LNK4017: “SHEETCAM.EXE”.scIsProcessing statement not supported for the target platform; ignored
.\VBDemo.def : warning LNK4017: “SHEETCAM.EXE”.scLoadDrawing statement not supported for the target platform; ignored
.\VBDemo.def : warning LNK4017: “SHEETCAM.EXE”.scLoadJob statement not supported for the target platform; ignored
.\VBDemo.def : warning LNK4017: “SHEETCAM.EXE”.scRunPost statement not supported for the target platform; ignored
Creating library Debug/VBDemo.lib and object Debug/VBDemo.exp

VBDemo.dll - 0 error(s), 7 warning(s)

Here is what my .DEF file looks like:

; VBDemo.def : Declares the module parameters for the DLL.

LIBRARY “VBDemo”
DESCRIPTION ‘VBDemo Windows Dynamic Link Library’

EXPORTS
DllCanUnloadNow PRIVATE
DllGetClassObject PRIVATE
DllRegisterServer PRIVATE

IMPORTS
“SHEETCAM.EXE”.scAddMainMenu
“SHEETCAM.EXE”.scAddMenu
“SHEETCAM.EXE”.scIsProcessing
“SHEETCAM.EXE”.scLoadDrawing
“SHEETCAM.EXE”.scLoadJob
“SHEETCAM.EXE”.scRunPost

If you have an import .LIB I would like to give that a try, if I may.

-James

Hi James,

I can’t provide you with an library because Borland C++ Builder libs
aren’t compatible with VC++ libs. However you should be able to use
implib to create a .lib. With Borland the command would be implib
sheetcam.lib sheetcam.exe. I assume the VC++ version of implib will
work in a similar manner.

My only worry is that it may link by ordinal. If it does we will have
severe compatibility problems with different releases.

Somewhere I have an old version of VC++. I’ll dig it out and see if I
can convince it to work with a .def file.

Les

Hmm, I’ve done some more research and it looks like VC++ doesn’t have
implib but it looks like you can generate a .lib from a .def file. Here
is an MSDN article http://support.microsoft.com/kb/q131313/. From what
I can see you need to create a file called SheetCam.def, containing the
following text:

EXPORTS
scAddMainMenu
scAddMenu
scIsProcessing
scLoadDrawing
scLoadJob
scRunPost

Now on the command line use LIB /DEF:SheetCam.def
If all goes to plan you should end up with SheetCam.lib which you can
then link to your dll. I would suggest trying my demo first as it is
about as basic as you can get.

Use a dll exports viewer such as
http://www.heaventools.com/PE_Explorer_Exports_Viewer.htm to check
your dll is exporting scInit and scOnMenu without prepended underscores.

Les

Hi Les,

Yes, I found some references to this on CodeProject also. I will read the MS article now. I did create a .DEF with the library name and an EXPORTS section, but I’m still getting unresolved externals at link time

enerating Code…
Linking…
Creating library Debug/VBDemo.lib and object Debug/VBDemo.exp
Demo.obj : error LNK2001: unresolved external symbol __imp__scAddMenu@12
Demo.obj : error LNK2001: unresolved external symbol __imp__scAddMainMenu@12
Demo.obj : error LNK2001: unresolved external symbol __imp__scLoadJob@4
Demo.obj : error LNK2001: unresolved external symbol __imp__scLoadDrawing@8
Debug/VBDemo.dll : fatal error LNK1120: 4 unresolved externals
Error executing link.exe.

VBDemo.dll - 5 error(s), 0 warning(s)

I am definitely implementing your demo until all other issues have been resolved.

-James

Hi again,

Here is the .DEF file I made to generate the .LIB from

-James

sorry, my attachment did not go. here it is:

LIBRARY “SheetCAM”

EXPORTS
scAddMainMenu
scAddMenu
scIsProcessing
scLoadDrawing
scLoadJob
scRunPost

-James

the LINK /DUMP options DOES definitely see all your exports from SheetCAM BTW

-James

Hi Les,

here is my .DEF file modified to add silent function signature junk that Microsoft is putting there. It allows my DLL to link, BUT sheetCAM cannot use it. I am attaching a ZIP file with the .DEF and the DLL for you.

I am afraid that MS deliberately is making binary linkage with borland tools (and probably others) not work.

-James

Hi Les,

Just to give you a taste of my ‘soft’ linkage I was developing:


This is what the SheetCAM side would look like:


#define ID_LOADJOB 0x01

void CALLBACK LoadJob(char *);

WORD numInterface;

typedef struct {
WORD *SCInterfaceID;
FARPROC *SCInterface;
} PLUGIN_DATA;

PLUGIN_DATA SCData;

HINSTANCE hPlugin = NULL;

typedef VOID (CALLBACK *PLUGINSTARTUP)(WORD, PLUGIN_DATA *);
typedef VOID (CALLBACK *PLUGINSHUTDOWN)();

PLUGINSTARTUP PluginStartup = NULL;
PLUGINSHUTDOWN PluginShutdown = NULL;


// Build Callback / capabilities table

numInterface = 1;

SCData.SCInterfaceID = (WORD *)calloc(numInterface, sizeof(WORD));

SCData.SCInterface = (FARPROC *)calloc(numInterface, sizeof(FARPROC));

SCData.SCInterfaceID[0] = ID_LOADJOB;

SCData.SCInterface[0] = (FARPROC)(LoadJob);

// Load the plugin DLL

hPlugin = LoadLibrary(“SheetCAMPlugin.dll”);

if (NULL != hPlugin) {

// Call PluginStartup()

PluginStartup = (PLUGINSTARTUP)GetProcAddress(hPlugin,“PluginStartup”);

if (NULL != PluginStartup) {

(*PluginStartup)(numInterface, &SCData);
}
}


And this is what the startup in the DLL would look like:

STDAPI PluginStartup(WORD numInterface, PLUGIN_DATA *scData)

{
int idx;

AFX_MANAGE_STATE(AfxGetStaticModuleState());

DbgMsg(“PluginStartup entry”);

// Setup the SheetCAM callback table

pluginTable.SCInterfaceID = (WORD *)calloc(numInterface, sizeof(WORD));

pluginTable.SCInterface = (FARPROC *)calloc(numInterface, sizeof(FARPROC));

for (idx = 0; idx < numInterface; idx++) {

pluginTable.SCInterfaceID[idx] = scData->SCInterfaceID[idx];

pluginTable.SCInterface[idx] = scData->SCInterface[idx];
}

// Start the callback thread

CreateThread(NULL,0L,CallbackThread,&threadData,0,NULL);

// Since we were called, SheetCAM is running

IsRunning = TRUE;

DbgMsg(“PluginStartup exit”);

return S_OK;
}


Just my two cents. I hope we can make the hard linkage work, it is simpler.

-James

I downloaded Visual Studio 2005 and did some experiments. VC++ really
insists on name mangling, no matter what you do. In the end I have had
to resort to referencing functions by ordinal. This arrangement will
still make SheetCam backwards compatible with older plugins. Newer
plugins trying to use functions that are not available will load but
when they try to access unavailable functions they will crash. In the
same situation, BCB based plugins (using names) will simply fail to load.

The next release of SheetCam (due out in a few hours) will include an
SDK directory containing both VC++ and BCB demo projects.

Les