SldWorks::Callback Not Working As Advertised

The API docs say this:

The CallBackArgs value contains the information to pass to the callback function. 
This value is untouched by the API and is passed through to the callback as a BSTR.
Therefore, the format of what is inside of the string can be whatever the caller wants it to be. 
Handling and use of it is the sole responsibility of the callback function.

I created a simple add-in that contains this method:

[DllExport]
public static int test(string args)
{
    MessageBox.Show("Hello " + args);
    return 0;
}

I then created a simple Windows Form app with a single button that when clicked runs this code:

private void runButton_Click(object sender, EventArgs e)
{
    var progId = "SldWorks.Application"; 
    var progType = System.Type.GetTypeFromProgID(progId);
    swApp = System.Activator.CreateInstance(progType) as SolidWorks.Interop.sldworks.ISldWorks;
    int loadResult = swApp.LoadAddIn(<my_addin_name>);
    swApp.Visible = true;
    int retval = swApp.CallBack("<my_addin_name>@test", -2, "Test!");
}

Everything works, except that somewhere along the way the String passed to the CallBack function is truncated to a single character:

This happens for both a Windows Forms app and for a SOLIDWORKS macro. I can’t try a C++ app since I don’t have the C++ bits installed in Visual Studio at the moment. Has anyone used SldWorks::CallBack successfully with the String argument?

C# returns LPWSTR, but it needs BSTR, as written in “Remarks”. It could be like this:

public static int test([MarshalAs(UnmanagedType.BStr)] string args)

I can’t check the functionality. :dashing_away:

2 Likes

It has been a while since i used it. this is what i found in my notes:

When SOLIDWORKS calls your add-in using CallBack, the final argument is passed as a BSTR (COM string). If your C# method just uses string args, .NET may misinterpret the string (it defaults to LPWSTR, that includes a hidden length prefix), which can cause truncation .

you should explicitly tell .NET to treat the argument as a BSTR like this:

public static int test([MarshalAs(UnmanagedType.BStr)] string args)
1 Like

i see that mikhov is faster than me… and we are saying the same :smiley:

1 Like

MarshalAs(UnmanagedType.Bstr) works, almost. Rather than a a one character string, I now get a string consisting of the correct value followed by the null terminator \0 followed by a slew of random characters. So for my purposes I can extract the correct value by finding the index of the null terminator and then using Substring to chop out the bit that I need.

Seems like SW may not be properly initializing the BSTR (as described here) before passing it along.

So, my BSTR issue is resolved ,but this leads to the next problem. To make sure this is reliable, I created a little test. I call the SldWorks::Callback method in a loop, passing in the loop index and keeping a running total on the add-in side. So for a loop from 1 to 10, the total should be 55. It is 55, every now and then. Most of the time it is something less than 55. So I increased the loop from 10 to 100 and added a little bit of code to catch exceptions and keep a count of retry attempts. A typical run gives results like this:

So nearly 40% of the calls throw an exception with some of the calls failing as many as 5 times before succeeding. Every subsequent run is different, but the failure percentage is typically 30-40%. Adding a Thread.sleep() to slow things down made no difference. The failure rate was still about 40%.

The exception seems to always be the same, RPC_E_SERVERFAULT, which points to a Win32 exception being thrown on the SOLIDWORKS side. No real way to determine what’s going on.

Seems like I’m just going to have to brute force retry SldWorks::Callback for a period of time or a number of attempts and hope that SW does what it is supposed to do. This is not good programming practice, but my hands are tied.

Perhaps the MessageBox GUI function is too fast or in a different thread (there are no problems with it in VBA :face_with_monocle: ). Try writing to a file :speech_balloon:.
Maybe try another technology for transferring between processes?
CallBack, as you can see, is 40% unreliable - a complete failure. Even if

args?.Split('\0')[0]

works, it is still creepy and slow and leaves a bad feeling somewhere in the soul.
Maybe global variables? Or other technologies that I don’t know at all.

According to my VAR, reports are that SldWorks::CallBack is extremely dodgy and should be avoided for performance-sensitive or repetitive tasks.

On top of the 40% failure due to Exception rate, I’ve also noticed that a large percentage of the time the argument string doesn’t make it through the transition from outside the SW process to inside the SW process. So once again, I’ll have to repeatedly call the CallBack method until it succeeds or until the number of retries exceeds some value and then punt and inform the user.

I’ve come to the conclusion that the SOLIDWORKS API has had zero testing. No respectable programmer would test something, have it fail 40% of the time and say “Looks good! Ship it!”.

5 Likes

Well, that leaves options other than no testing…

Just say’n.

Fun new wrinkle. More often than not, when I make the call to SldWorks::CallBack with a valid string (verified in Visual Studio debugger), when SW subsequently calls the add-in method, the passed in string is empty. Not garbage, just completely empty. Sometimes after a few attempts in a loop it will appear as expected. Other times it never shows up in 10 tries.

My next idea is to create a C++ add-in to see if it behaves better.