How to raise Exception from a C# .NET COM. In fact I'm inside a Win32 application build using Borland Delphi 7.0 and I'm try to consume a function in .NET. Everything is working fine except the fact raising exception in C# are not re-routed to Delphi, I guess I miss some kind of method decorator. Here a piece of code:
void iLinkV2.FindLinkedClientEng(string SourceApp, string SourceClientID,
ref object ClientID, ref object Engagement)
{
throw new COMException("errClientNotFound",
(int)ExceptionsType.errClientNotFound);
ClientID = SourceClientID;
}
[ComVisible(true),
Guid("D731C821-EEA2-446E-BC5A-441DCA0477F5"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface iLinkV2
{ ...
}
You don't need to. Exceptions are not part of COM Interop specification and should not cross module boundaries. You can use status codes to indicate any errors.
Catch the exception just before method exits (in each COM interface method). Turn the result into an error code. If you use an HRESULT then you can consume it as safecall and get the Delphi compiler to re-raise an exception on the other side of the fence.
Related
I have to register a callback function written in unmanaged code from c#. I have written the following codes:
//Declaration for c++ code
[DllImport("sdk.dll")]
public static extern void sdkSetDequeueCallback(DequeueCallback cbfunc);
//call back delegate
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DequeueCallback(OutData data);
GCHandle dequeueCallbackPinHandle;
Deque = new DequeueCallback(CallBackMethod);
{//try out
// Getting error following line (1 line) , Error : Object contains non-primitive or non-blittable data.
//dequeueCallbackPinHandle = GCHandle.Alloc(Deque, GCHandleType.Pinned);
//GC.KeepAlive(Deque);
}
SetDequeueCallback(Deque);
//Call back method in c#
public async void CallBackMethod(OutData data)
{}
The above code is working without the tryout block, but the issue is application is getting stopped after a random period of time( some times 30 mins, 1 Hr , 8 hr etc). No error detected in try..catch blocks, but getting following NullReferenceException error from the Windows Event Log(application):
Description: The process was terminated due to an unhandled exception.
Exception Info: System.NullReferenceException. I think
this
can be the issue, but tryout codes are not working. Please help me to resolve the issue
You need to insure your 'Deque' reference is alive and not overwritten as long as the callback is being used in C++. You are responsible for holding delegate C# object alive as long as the corresponding C++ callback is used.
Better yet, just use Scapix Language Bridge, it generates C++ to C# bindings (including callbacks), completely automatically. Disclaimer: I am the author of Scapix Language Bridge.
I have created a library wrapper which invokes FlexNet Publisher. It is written in C. I am trying to P/Invoke it:
[DllImport("lmgr11.dll")]
public static extern void free_job();
Its implementation is fairly simple:
void WINAPI free_job()
{
if (jobPtr != NULL)
lc_free_job(jobPtr);
jobPtr = NULL;
}
The documentation says that lc_free_job should free a job as well as all resources. Calling lc_free_job works just fine from native code (I made an ATL object expose a wrapper to it through a COM object and I can consume this method all day long from a Visual C++ console application, so I know it must work).
However, from C#, when I try to P/Invoke this method, I get the following error which crashes my application:
Unhandled exception at 0x00007FFA39358283 (ntdll.dll) in
CerberusTestHarness.exe: 0xC0000374: A heap has been corrupted
(parameters: 0x00007FFA393AF6B0).
Why is this? Can I catch it or circumvent it in any way? I can't seem to catch the exception if I wrap the call in a try-catch because its not throwing a .NET exception. Note that I don't have the source code for lc_free_job so I can't inspect it or view its source code, unfortunately. Its written in C.
The issue was due to my C# P/Invoke code from a previous invocation. Hopefully this helps anyone else who comes across the same issue.
I had defined this P/Invoke to get an error string back from a function which returns char *:
[DllImport("lmgr11.dll")]
public static extern string errstring();
I was calling it every time something failed in my 3rd party library as above. This is not the correct way to P/Invoke a method which returns char *. Otherwise, when freeing the error from the native side, it will cause a heap corruption due to the way this string was marshaled.
It needed to be defined like this:
[DllImport("lmgr11.dll")]
public static extern IntPtr errstring();
And called as follows:
var errorMessage = Marshal.PtrToStringAnsi(errstring());
I am in the process of replacing a COM+ hosted application with a .Net variant.
The steps I took are roughly:
Generate an Interop assembly of the old dll
Create a new class in C# .Net that derives from ServicedComponent and implements the IMyClass interface from the interop.
I have annotated the class with a [Guid("...")] and a [ProgId("...")] attribute to match the class in the old dll
The end result looks like this:
[ProgId("MyComponent")]
[Guid("...")]
public class MyClass : ServicedComponent, IMyClass
{
public MyClass()
{
}
public object Open(string arg1, string arg2)
{
/* trace arguments*/
}
public object Run()
{
/* implementation here */
}
public void Close()
{
return;
}
}
This assembly is installed on a remote machine using regsvcs.exe
Now most clients use code similar to this unittest code:
Type typeFromProgID = Type.GetTypeFromProgID("MyComponent", "remoteMachine", true);
dynamic comInstance = Activator.CreateInstance(typeFromProgID);
comInstance.Open(string.Empty, string.Empty);
comInstance.Run();
comInstance.Close();
This works perfectly, the .Net tracing on the remote machine tells me everything is working as it should. Other clients use code similar to this:
Type typeFromProgID = Type.GetTypeFromProgID("MyComponent", "remoteMachine", true);
MyClass comInstance = (MyClass)Activator.CreateInstance(typeFromProgID);
comInstance.Open(string.Empty, string.Empty);
comInstance.Run();
comInstance.Close();
The first line is the same and seems to work fine, the rest acts weird. The VS debugger shows the lines are being executed. The remoteMachine shows that no methods are being executed.
The last call to Close(), which actually just returns, throws an exception:
System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Result StackTrace:
at Interop.MyComponent.IMyClass.Close()
at UnitTestProject1.UnitTest1.temp()
What have I missed in my implementation to support this last (typed) scenario?
This sounds like mismatched metadata, e.g. marshaling is different between the one that happens with the original type library and the one that happens with your server registered .NET assembly.
My guess is that the untyped case runs well because it depends solely on IDispatch, which .NET implements quite well, but the typed test fails (and I'm surprised it doesn't fail earlier) because of this mismatch.
I'm consuming a com object in c#, using com-interop. I provide an event handler to the object, which it calls after doing its thing. Here's the problem. I have some sanity-check code in the handler that throws an exception if all is not well with the world:
_comObj.OnRequestCompleted += (int requestID, RequestStatus status) =>
{
if (<sanity check fails>)
throw new Exception("This is insane!!!");
...
}
The com object is apparently swallowing those exceptions, because I never see them in my app. What do I need to do to "surface" those exceptions so that I am notified when they occur?
This is not unexpected behavior if the event is raised by the COM server. Exceptions are not allowed to cross the COM divide, there is no binary interop standard for exceptions. The CLR installs a catch-all exception handler in its interop layer, the code that calls your event handler. The exception you raised is translated to an HRESULT, the standard way in which COM code reports failure. Which will be 0x80131500 for a generic Exception object.
What happens next is entirely up to the COM server, it needs to do something useful with the HRESULT it got. Not seeing it do anything it all is not terribly unexpected, function call return values are often ignored, especially so with notification style calls. You certainly won't get anything resembling a managed exception.
Work with the COM component vendor or author to work something out, if possible. Which isn't very often the case. Not much you can do beyond just logging the failure then. Call Environment.Exit() if this is a gross problem that cannot possibly be ignored. This isn't really any different from having your program terminated with an unhandled exception.
You could use the Application.Current.Dispatcher.Invoke() method, by supplying a delegate in which you throw your exception.
If you look at the standard C++ COM server implementation that's generated by all Visual Studio versions (what you get using the wizards like this: ATL Tutoral Step 5: Adding an Event), the C++ code for raising an event (named a "connection point" in COM terms) looks like this.
HRESULT Fire_MyCustomEvent()
{
...
for (int iConnection = 0; iConnection < cConnections; iConnection++)
{
CComVariant varResult;
DISPPARAMS params = { NULL, NULL, 0, 0 };
hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &varResult, NULL, NULL);
}
return hr;
}
So, hr does contain an error code but in general, the COM server implementer just does
Fire_MyCustomEvent()
Because in general, there can be more than one caller. What would you do if one caller fail? Fail everyone? In the general case, nothing. See this interesting discussion here on a similar subject: Observers Should Never Throw Exceptions
That said, if you own the COM server, you can catch Invoke error, and in this example, pass EXCEPINFO as the 7th argument to invoke. It will contain the .NET Exception text "This is insane!!!".
I am trying to get access to an running instance of an application, Rational Rhapsody, through COM. I am trying to use the C++/CLI COM calls.
From C++ calling:
rhapsody::RPApplication^ App = safe_cast<rhapsody::RPApplication^>( Marshal::GetActiveObject("Rhapsody.Application"));
Causes a COM Exception : 800401E3 (Operation Unavailable)
But, using Marshal::GetActiveObject("Word.Application") works just fine. Using gcnew rhapsody::RPApplication() works fine to make a new instance and the same code in C#:
rhapsody.RPApplication App = (rhapsody.RPApplication) Marshal.GetActiveObject("Rhapsody.Application")
works just fine.
Any ideas why it doesn't work from C++/CLI?
Is your main() routine in C++/CLI flagged with [STAThread]? It's commonly required, especially when dealing with COM objects.
[STAThread]
int main(array<System::String^>^args)
{
// code here...
}