COMException HResults not Transferred to MFC Client

D

dynastar

I'm trying to send custom COMExceptions from my C# server. My test client in
C# has no problem reading the HRESULT I send (say, 0x80040002). Needless to
say, the MFC client in C++ allows this HRESULT only to the .tli file, which I
assume is part of the client. The next thing that happens, it is converted to
an SEHException with a value of 0x80004005. This happens everytime, and does
it for any derivative of SEHException.

Is there some attribute of decoration I need to make?

I can post the code, it is pretty simple.
 
D

Dmytro Lapshyn [MVP]

Hi,

First of all, how to you throw custom COMExceptions? I'd recommend that you
did it with Marshal.ThrowExceptionFromHR.

Also, the exception code itself looks suspicious as it can easily interfer
with system error codes. As far as I remember, it is safe to use codes from
0x80040201 and above for custom errors (VB6 has a dedicated constant for
that called vbObjectError).

Finally, as I am not an MFC programmer, can you please elaborate on what the
..tli file is?
 
D

dynastar via DotNetMonster.com

Hi Dmytro,
This is the entire server class (ServerTest assembly):

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Lifetime; // to set the lease to infinite
using System.Runtime.InteropServices;

namespace ServerTest
{
/// <summary>
/// Summary description for IOComponent.
/// </summary>
public class Class1 : MarshalByRefObject, ServerData
{
static int m_EC;
#region Marshal By Reference Methods
public override Object InitializeLifetimeService()
{
return null;
}
#endregion
public Class1()
{
m_EC = 0;
}
public int MAKE_HRESULT(int sev, int fac, int code)
{
return (((sev)<<31) | ((fac)<<16) | ((code)));
}

#region ServerData Members

public void ThrowException()
{
int hRes = MAKE_HRESULT(1,4,(int)m_EC++);
throw new COMException("My new exception",hRes);
// Creates an
HRESULT of 0x80040000
}

#endregion
}
}

The ServerData interface is this:
using System;

namespace ServerTest
{
/// <summary>
/// Summary description for serverInterface.
/// </summary>
public interface ServerData
{
// methods
void ThrowException();
}
}


To use this, the #import of the server code creates a secondary header file (.
tli) from the .tlb.
Here's my code for the client proxy, also written in C#:
// ServerProxy.cs

using System;
using ServerTest;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Lifetime; // to set the lease to infinite
using System.Runtime.InteropServices;

namespace COMServerPS
{
/// <summary>
/// Summary description for ServerProxy.
/// </summary>
public class ServerProxy : MarshalByRefObject, ServerData
{
public Class1 _Server; // object

#region Marshal By Reference Methods
public override Object InitializeLifetimeService()
{
return null;
}
#endregion

public ServerProxy()
{
_Server = new Class1();
}
#region ServerData Members

[PreserveSig]
public void ThrowException()
{
try
{
_Server.ThrowException();
}
catch (Exception ex)
{
int hr = Marshal.GetHRForException(ex);
throw ex;
}
}

#endregion
}
}

This is the MFC header file:
// WindowsMFCApplicationDlg.h : header file
//

#pragma once
#import "COMServerProxy.tlb" named_guids
#import "ServerTest.tlb" named_guids


// CWindowsMFCApplicationDlg dialog
class CWindowsMFCApplicationDlg : public CDialog
{
// Construction
public:
CWindowsMFCApplicationDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data
....
// Namespace(from .tlb)::InterfacePtr
ServerTest::ServerDataPtr m_ioServer;

// Implementation
protected:
....

public:
afx_msg void OnBnClickedButton1();
};

And the implementation file:
// WindowsMFCApplicationDlg.cpp : implementation file
//

#include "stdafx.h"
#include "WindowsMFCApplication.h"
#include "WindowsMFCApplicationDlg.h"
#include ".\windowsmfcapplicationdlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

using namespace System;
using namespace System::Runtime::InteropServices;

....
BOOL CWindowsMFCApplicationDlg::OnInitDialog()
{
CDialog::OnInitDialog();
....
// must call CoInitialize() at least once prior to calling CreateInstance()
HRESULT hRes = CoInitialize(NULL);

// CreateInstance(Assembly Name::Class Name)
hRes = m_ioServer.CreateInstance(__uuidof(COMServerProxy::ServerProxy));

return TRUE; // return TRUE unless you set the focus to a control
}

void CWindowsMFCApplicationDlg::OnBnClickedButton1()
{
HRESULT hr = S_OK;
try
{
hr = m_ioServer->ThrowException();
}
catch(COMException* ex )
{
int ec = ex->get_ErrorCode();
Exception *exc = ex->GetBaseException();

hr = Marshal::GetHRForException(ex);
}
catch(Exception* ex )
{
hr = Marshal::GetHRForException(ex);
// HRESULT is 0x80004005
}
}

Tha's about it. It works between the managed types, but not unmanged C++.

Thanks,
Steve

Hi,

First of all, how to you throw custom COMExceptions? I'd recommend that you
did it with Marshal.ThrowExceptionFromHR.

Also, the exception code itself looks suspicious as it can easily interfer
with system error codes. As far as I remember, it is safe to use codes from
0x80040201 and above for custom errors (VB6 has a dedicated constant for
that called vbObjectError).

Finally, as I am not an MFC programmer, can you please elaborate on what the
.tli file is?
I'm trying to send custom COMExceptions from my C# server. My test client
in
[quoted text clipped - 11 lines]
I can post the code, it is pretty simple.
 
D

Dmytro Lapshyn [MVP]

Hi,

Please try using Marshal.ThrowExceptionForHR and correct HR codes first. If
this does not help, please follow up in this thread.

I am also curious why do you inherit your server class from
MarshalByRefObject. Is your server an out-of-proc (DCOM) server?

dynastar via DotNetMonster.com said:
Hi Dmytro,
This is the entire server class (ServerTest assembly):

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Lifetime; // to set the lease to infinite
using System.Runtime.InteropServices;

namespace ServerTest
{
/// <summary>
/// Summary description for IOComponent.
/// </summary>
public class Class1 : MarshalByRefObject, ServerData
{
static int m_EC;
#region Marshal By Reference Methods
public override Object InitializeLifetimeService()
{
return null;
}
#endregion
public Class1()
{
m_EC = 0;
}
public int MAKE_HRESULT(int sev, int fac, int code)
{
return (((sev)<<31) | ((fac)<<16) | ((code)));
}

#region ServerData Members

public void ThrowException()
{
int hRes = MAKE_HRESULT(1,4,(int)m_EC++);
throw new COMException("My new exception",hRes);
// Creates
an
HRESULT of 0x80040000
}

#endregion
}
}

The ServerData interface is this:
using System;

namespace ServerTest
{
/// <summary>
/// Summary description for serverInterface.
/// </summary>
public interface ServerData
{
// methods
void ThrowException();
}
}


To use this, the #import of the server code creates a secondary header
file (.
tli) from the .tlb.
Here's my code for the client proxy, also written in C#:
// ServerProxy.cs

using System;
using ServerTest;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Lifetime; // to set the lease to infinite
using System.Runtime.InteropServices;

namespace COMServerPS
{
/// <summary>
/// Summary description for ServerProxy.
/// </summary>
public class ServerProxy : MarshalByRefObject, ServerData
{
public Class1 _Server; // object

#region Marshal By Reference Methods
public override Object InitializeLifetimeService()
{
return null;
}
#endregion

public ServerProxy()
{
_Server = new Class1();
}
#region ServerData Members

[PreserveSig]
public void ThrowException()
{
try
{
_Server.ThrowException();
}
catch (Exception ex)
{
int hr = Marshal.GetHRForException(ex);
throw ex;
}
}

#endregion
}
}

This is the MFC header file:
// WindowsMFCApplicationDlg.h : header file
//

#pragma once
#import "COMServerProxy.tlb" named_guids
#import "ServerTest.tlb" named_guids


// CWindowsMFCApplicationDlg dialog
class CWindowsMFCApplicationDlg : public CDialog
{
// Construction
public:
CWindowsMFCApplicationDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data
...
// Namespace(from .tlb)::InterfacePtr
ServerTest::ServerDataPtr m_ioServer;

// Implementation
protected:
...

public:
afx_msg void OnBnClickedButton1();
};

And the implementation file:
// WindowsMFCApplicationDlg.cpp : implementation file
//

#include "stdafx.h"
#include "WindowsMFCApplication.h"
#include "WindowsMFCApplicationDlg.h"
#include ".\windowsmfcapplicationdlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

using namespace System;
using namespace System::Runtime::InteropServices;

...
BOOL CWindowsMFCApplicationDlg::OnInitDialog()
{
CDialog::OnInitDialog();
...
// must call CoInitialize() at least once prior to calling
CreateInstance()
HRESULT hRes = CoInitialize(NULL);

// CreateInstance(Assembly Name::Class Name)
hRes = m_ioServer.CreateInstance(__uuidof(COMServerProxy::ServerProxy));

return TRUE; // return TRUE unless you set the focus to a control
}

void CWindowsMFCApplicationDlg::OnBnClickedButton1()
{
HRESULT hr = S_OK;
try
{
hr = m_ioServer->ThrowException();
}
catch(COMException* ex )
{
int ec = ex->get_ErrorCode();
Exception *exc = ex->GetBaseException();

hr = Marshal::GetHRForException(ex);
}
catch(Exception* ex )
{
hr = Marshal::GetHRForException(ex);
// HRESULT is 0x80004005
}
}

Tha's about it. It works between the managed types, but not unmanged C++.

Thanks,
Steve

Hi,

First of all, how to you throw custom COMExceptions? I'd recommend that
you
did it with Marshal.ThrowExceptionFromHR.

Also, the exception code itself looks suspicious as it can easily interfer
with system error codes. As far as I remember, it is safe to use codes
from
0x80040201 and above for custom errors (VB6 has a dedicated constant for
that called vbObjectError).

Finally, as I am not an MFC programmer, can you please elaborate on what
the
.tli file is?
I'm trying to send custom COMExceptions from my C# server. My test
client
in
[quoted text clipped - 11 lines]
I can post the code, it is pretty simple.
 
D

dynastar via DotNetMonster.com

Hi Dmytro,
This is a remoted server which is why I use the MarshalByRefObject. I have
been working with C# servers for over a year now, and ran in to this problem
early on. Normally, COM objects return HRESULTs that can simply be assigned
as the STDMETHODIMP return value to the call. I simply want to maintain this
functionality because I have to deal with legacy code.

I have now tried your suggestion but get the same result: the 0x80040001
HRESULT becomes 0x80004005 and I must use try..catch blocks. There is
something in the layer between the c# agent and the MFC proxy that is
modifying the HRESULT to the base class (SEHException) HRESULT.

Thanks,
Steve
Hi,

Please try using Marshal.ThrowExceptionForHR and correct HR codes first. If
this does not help, please follow up in this thread.

I am also curious why do you inherit your server class from
MarshalByRefObject. Is your server an out-of-proc (DCOM) server?
Hi Dmytro,
This is the entire server class (ServerTest assembly):
[quoted text clipped - 216 lines]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top