Problems calling IConnectionPointImpl interface from C ++ caused by modal WinForms

We have our own C ++ application that supports some VBA macros of various types over COM. One of these VBAExtension types VBAExtension registered using the main C ++ application, which results in an instance (derived from the class) IConnectionPointImpl<Extension, &DIID_IExtensionEvents, CComDynamicUnkArray> . This works great; both kernels and other VBA macros can access the methods in IExtensionEvents, given the corresponding VBAExtension object.

We also have a .NET assembly (written in C #) that also loads into the main application at runtime. For historical reasons, the assembly is loaded with a VBA macro with automatic start; then, when the user clicks a specific button, another VBA macro launches the main entry point of the assembly, which calls the System.Windows.Forms dialog for further interaction.

This is a setting. I see some strange behavior when accessing the VBAExtension methods from the .NET assembly. In particular, I run the following code from different places in the assembly:

 foreach (VBAExtension ve in app.Extensions) { System.Diagnostics.Debug.Print("Ext: " + ve.Name); } 

If I run it from the constructor of the main assembly object; or from the main entry point of the assembly (before the dialog box VBAExtension ), everything is fine - I get the VBAExtension names printed.

However, if I run the same code from the command launched by the button in assemblies ( modal - we call form.ShowDialog() )) WinForm, all ve.Name empty. The pDispatch->Invoke call created by the IConnectionPointImpl subclass succeeds (returns S_OK), but does not set any return vars.

If I changed the dialog to non-modal (called using form.Show() ), the names will work again. Modality (modality?) Forms, apparently, affect the success of IConnectionPointImpl calls.

Does anyone know what is going on?

Edit:. After the first publication, I demonstrated that this is not a calling call stack, which matters; instead, whether a call is made from a modal dialog. I updated the main text.

Edit 2: Answer the Hans question, answer his diagnostic questions:

  • As expected, there is no error in the good (weak) case if I rename the VBA event handler. A call simply does not return data.
  • I put the MsgBox call in the VBA handler; it is displayed in an incompatible case, but not in a modal case. Ergo, the handler does not execute in the modal case.
  • Using Err , I can say that if we remove the exception in the VBA handler, we get a VBA error dialog. After clearing this call, C ++ Invoke has 0x80020009 ("An exception occurred") as the return code, and pExcepInfo is populated with general failure values ​​(VBA swallowed the actual data).
  • The event does not fire on the second display of the modal dialog, either immediately after the first dialog, or during the second call to the C # add-in.

I will try to dig our message loops as the next step.

+6
source share
1 answer

There are too many facts in this question to build an answer. There may be something very simple, there may be an unpleasant problem with memory corruption, or an unclear dependency within the VBA interpreter on the state of the stream. A rough diagnosis is that the VBA event handler just didn't start. This is not an accident in general, the declarative style used in Basic to declare event handlers leaves very little good way to diagnose subscription problems. Many VBA programmers have lost shreds of hair trying to fix the β€œwhy the event handler problem didn't start” problem, like this one.

Gather some hard facts and add them to your question:

  • First, make sure your C ++ code can really see that there is no event handler at all. Use a good version, rename the event handler. The expectation that you will not do this when raising an event that the shell does not sign is not a mistake.
  • Make sure that the event handler is indeed executed in a bad version. Ask him to do something else than assign a BSTR argument, some of which can easily be seen as a file on disk.
  • Make sure you can correctly diagnose the exception in the event handler. Assign an Err object and make sure your C ++ code creates the correct diagnostics. Note that your call to IDispatch :: Invoke () skips NULL for pExcepInfo, not a good way to generate diagnostics.
  • Check if the event is being executed the second time you show the window, if this happens, then you have a problem with the execution.

Focusing on ShowDialog (). This method has many side effects. The first thing that doesn't work anymore is the message loop in your C ++ code. The .NET message loop is currently sending messages. Look at your side effects by doing more work than just GetMessage / DispatchMessage (). This work is no longer done. Also look for your code base for PostThreadMessage (), these messages get to the floor when .NET code is running.

And keep in mind that your own C ++ code loses control when C # code calls ShowDialog (). It does not regain control until you close the window. This can cause a simple order fulfillment problem, your C ++ code should not do anything important after doing what it does to run the C # code.

+4
source

All Articles