Travaillant actuellement pour un client désirant utiliser un composant .NET depuis Office 2000, il a donc fallu s'attaquer à COM Interop. Et, bien que la documentation le dise, il est possible d'avoir quelques surprises lorsqu'il s'agit de gérer les exceptions et les erreurs COM.
Prenons donc le code suivant :
[ComVisible(true)]
public int myMethod()
{
// snip
throw new myException("Description", 2500);
// snip
// Do something here
return 0;
}
public class myException : ApplicationException
{
// COM Error base number is 0x80040000
private const int kCOMErrorBaseNumber = unchecked((int) 0x80040000);
public myException() : base(){}
public myException(string strMessage, int ErrorCode) : base(strMessage)
{
// HResult = kCOMErrorBaseNumber + ErrorCode);
HResult = ErrorCode);
}
}
Du côté client, par exemple sous Excel, en ajoutant la bonne référence sur notre composant COM écrit en .NET, écrivons le code suivant :
Sub Test()
On Error GoTo err_handling
Dim a As New myDotNetComponent
Dim b As Long
b = a.myMethod()
' Do something
Exit Sub
err_handling:
MsgBox Err.Number
End Sub
Le seul moyen de fixer le numéro d'erreur qui sera retourné au client COM est de le faire dans le constructeur de l'exception, comme on peut le voir dans le code du composant COM. Si on exécute le code VB, aucune erreur ne sera retournée. Le code situé sous l'étiquette err_handling n'est pas exécuté. Surprise, non ?
L'explication se trouve dans la documentation Microsoft. On peut y lire qu'un code d'erreur HRESULT doit avoir son bit de poids fort mis à 1 pour que ce soit considéré pour une erreur. Ainsi donc, même si une exception est levée dans le composant COM .NET et qu'elle est propagée au-delà vers le code client, si le code HRESULT affecté n'est pas correct, on risque de passer à côté d'un tel problème. Il est possible de corriger cela en ajoutant la valeur 0x80000000 à notre code d'erreur pour que cela fonctionne. Pour être plus sûr de ne pas entrer en collision avec des code existants, j'ai ajouté la valeur 0x80040000.
Actually working for a customer which would like to use a .NET component from an Office 2000 application, I had to work with COM Interop. And, even the documentation understandable, it is possible to have some surprises when managing and handling exceptions and COM errors.
So, take the following code :
[ComVisible(true)]
public int myMethod()
{
// snip
throw new myException("Description", 2500);
// snip
// Do something here
return 0;
}
public class myException : ApplicationException
{
// COM Error base number is 0x80040000
private const int kCOMErrorBaseNumber = unchecked((int) 0x80040000);
public myException() : base(){}
public myException(string strMessage, int ErrorCode) : base(strMessage)
{
// HResult = kCOMErrorBaseNumber + ErrorCode);
HResult = ErrorCode);
}
}
On the client side, for exemple from Excel, adding the right reference to our .NET written COM component, write the following code :
Sub Test()
On Error GoTo err_handling
Dim a As New myDotNetComponent
Dim b As Long
b = a.myMethod()
' Do something
Exit Sub
err_handling:
MsgBox Err.Number
End Sub
The only way to set the error code that will be returned to the COM client is to do that in the exception constructor as we can see it in the COM component code. If we run the VB code, no error will be returned and the err_handling labelled code will not be run. Surprising isn't it ?
The explanation can be found in the Microsoft documentation. We can read that an HRESULT error code must have its high-order bit must be set to 1 to be considered as en error. So, even en exception is raised in the .NET COM component and bubbled to the client code, if the HRESULT code is not correctly set, we can miss an error. It is possible to change that adding a 0x80000000 value to our error code. But, to be sure that we are not colliding with existing error codes, I added the value 0x80040000.


