TypeLoadException and its error message

I hope that gives you a bit more insight;

Why the class not registered message ?

I have temporary enabled the flag for COM visibility for Toolbox.pas but it makes absolute no difference.

Are we missing some registration if we deploy it by the web site deployer to the web server?

EXTRACTING:
I think what you would need to have the same effect as we have is
an IIS 7.5 web server in remote location
and then best you tell me which DLL’s I shall pass to you or which DL’s source code

Given we pass the full solution you will have a lot work stubbing out or commenting out calls.

Here’s the servers component diagram, for now, all except a few is 3GL spaghetti code.

It looks like anything that calls Toolbox’s MAXI will cause this?

That is the case. But calling other functions in DLL’s made from .PAS has the same effect. That is the reason why we just inserted a simple call to a global available Toolbox function MAXI which stands for return me the bigger integer of the two integers passed. Toolbox is just to see as an example. With other components we have the same effect as soon as we call a procedure or function.

Again: do not forget that; this components run fine (as far as we see it for now, as we are not finished with testing) when i.e. being called from the Visual Studio 2012 Ultimate Test Framework’s Unit Test capabilities, where all the Unit Test are written in Oxygene.

But as soon as that code has to execute at a Web Server we have the problem.

Hrmm, so this should be easy to extract so I can see it here? It could be a codegen bug or something else but it’s hard to know without having a look.

verified all dll’s using peverify and found a problem with P2CSMOD.dll

How shall I proceed, it is just a ordinary Oxygen project as many others.

somehow very interesting:

  .method /*060001A2*/ assembly hidebysig static 
          int32  Main(string[] A_0) cil managed
  // SIG: 00 01 08 1D 0E
  {
    .entrypoint
    // Method begins at RVA 0xa52c
    // Code size       2 (0x2)
    .maxstack  1
    .locals /*11000006*/ init ([0] int32 Result)
    IL_0000:  /* 06   |                  */ ldloc.0
    IL_0001:  /* 2A   |                  */ ret
  } // end of method __Global::Main

} // end of class P2CSMOD.LTBATCH.__Global

why does the main entry point here have only 1 argument ? why only in P2CSMOD this time.
what happens if only one of the dependent DLL’s can not be loaded/initialized due to the fact that only one DLL has such a problem as we found out just now. I did not expect that. But have to leave now.

Thanks and regards
Josef

It shouldn’t emit .entrypoint for library projects. Fixed.

Demonstrator under which cases a TypeInitializationException happens, found at
http://www.dotnetperls.com/typeinitializationexception

Converted to

namespace TypeInitializerExceptionDemo;
interface
uses
  System;
type
  Program = class
  private
    class constructor;
    class method Main(args: array of String);
  end;
implementation
class constructor Program;
begin
  //
  // Static constructor for the program class.
  // ... Also called a type initializer.
  // ... It throws an exception in runtime.
  //
  //try
    var number: Integer := 100;
    var denominator: Integer := Integer.Parse('0');
    var &result: Integer := number / denominator;
    Console.WriteLine(&result)
  
  //except    on ex: Exception do begin
  //    Console.WriteLine(ex.Message)
  //  end;
  //end
end;

class method Program.Main(args: array of String);
begin // Entry point
end;
end.

It might be worth and very helpful in this situation if we can force the byte code generator to emit a try catch block around the class constructor to know where and why it triggers an error.

Also it still does not answer the question why our code runs in one environment, i.e. when we test it in a unit test environment, and why it fails when called by code which is called from w3wp.exe of the IIS 7.5 on a remote host.

the idea is that you can use a debugger to find where this happens (enabling exceptions); obviously above will fail with a divide by zero exception. The compiler shouldn’t add a try/catch for this as it wouldn’t know what to do in the catch.

Did you get my mail btw?

Thanks Carlo,

The demonstrator should only show, “ONE OF the circumstances” under which the static constructor raises a TypeInitializationException. (no more no less)

In our case, we talk about a call to a DLL named Toolbox.dll.
This DLL is implemented in Oxygene.
It has a __Global class and I think it must have a static constructor because

… we see a TypeInitializationException when we call a Function inside this DLL but only when we call it from an IIS 7.5 w3wp.exe -> FKTmapManager.DLL 's Method Authenticate() which has a call to Toolbox.__Global.MAXI(5,0);

That is to say, when w3wp.exe calls it’s FKTmapMAnager.DLL (which was compiled on the fly and is C# implemented) it does not raise any exception.

But when this FKTmapManager DLL then calls a Function in Toolbox, which has to initialize it’s __Global Object first, an Exception is raised of which we like to see the true cause.
In other words: what goes wrong when the constructor of the __Global class in Toolbox.dll gets called ?

how can we debug how that compiler generated constructor works ?

It is running now! No more TypeInitializationException when running as a Web Site Web service DLL from a remote host IIS 7.5 w3wp.exe. For that I went back to simple Pascal DLL.

namespace ToolboxPAS;
interface
uses
  System;
VAR
  Stop_Time: String := "1949.Mar.19";
implementation
end.

because I found out, that what ever I have coded, the effect is always the same; TypeInitializationException when run from a Web Server but not in Unit Test Environments from VS 2012 Ultimate. Or when used local being called by an EXE or any other DLL.

The code builds; then you take ILSpy and look at the emitted C# code.
It looks like that for ToolboxPAS.pas when looking at the __Global class

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ToolboxPAS
{
    [CompilerGenerated]
    [StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)]
    public static class __Global
    {
        public static string Stop_Time = "1949.Mar.19";
    }
}

and when looking at the variable emitted it looks as

// ToolboxPAS.__Global
public static string Stop_Time = "1949.Mar.19";

So I am creating a ToolboxCS.cs project, coming as close as possible to the code emitted by ILSpy as C#

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ToolboxCS
{
    [CompilerGenerated]
    [StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)]
    public static class __Global
    {
        public static string Stop_Time = "1949.Mar.19";
    }
}

I tested both, doing local VS Unit Test, they behave the same. I build my large Web Site Web Service Project and deployed it to IIS 7.5 SpezplaWebService. Under .\bin I watched the two new small DLL’s ToolboxPAS.DLL and ToolboxCS.DLL. Then I started the code and it succeeds in the try catch block for the first line of code but fails with the known exception for the second line of code.

try
{
    payout.wscol.get_Item("wsout").Add("stCS",ToolboxCS.__Global.Stop_Time);
    payout.wscol.get_Item("wsout").Add("stPAS", ToolboxPAS.__Global.Stop_Time);
} catch (Exception ex) 
{
   payout.wscol.get_Item("wsout").Add("Exception", ex.Message);
   payout.wscol.get_Item("wsout").Add("InnerExce", ex.InnerException.Message);
   return false;
}

The InnerException.Message was a

Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG))

which was wrapped into a TypeInitializationException and sent back to the client.

So what is the difference in code ? NONE

I went for the assembly files in ILSpy and compared the two AssemblyInfos files emitted and found differences.
I made the content of the ToolboxPAS AssemblyInfos the same as the one for ToolboxCS.
I generated a new GUID because there was none, but that had no effect.

Now:
the AssemblyInfos for ToolboxPAS looks

namespace ToolboxPAS;

interface

uses
  System,
  System.Diagnostics,
  System.Reflection,
  System.Runtime.CompilerServices,
  System.Runtime.InteropServices,
  System.Runtime.Versioning,
  System.Resources;

[assembly: AssemblyTitle("ToolboxPAS")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("AXA Technology Services GmbH")]
[assembly: AssemblyProduct("ToolboxPAS")]
[assembly: AssemblyCopyright("Copyright © AXA Technology Services GmbH 2015")]
[assembly: AssemblyTrademark("")]
//[assembly: AssemblyCulture("")]
[assembly: CompilationRelaxations(8)]

[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default or 
DebuggableAttribute.DebuggingModes.DisableOptimizations or 
DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints or 
DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: TargetFramework(".NETFramework,Version=v4.5", FrameworkDisplayName = ".NET Framework 4.5")]

// Setting ComVisible to false makes the types in this assembly not visible 
// to COM components.  If you need to access a type in this assembly from 
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("D290EB86-1975-48F3-A3A5-DD2A96ECB700")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version 
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers 
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]


//
// In order to sign your assembly you must specify a key to use. Refer to the 
// Microsoft .NET Framework documentation for more information on assembly signing.
//
// Use the attributes below to control which key is used for signing. 
//
// Notes: 
//   (*) If no key is specified, the assembly is not signed.
//   (*) KeyName refers to a key that has been installed in the Crypto Service
//       Provider (CSP) on your machine. KeyFile refers to a file which contains
//       a key.
//   (*) If the KeyFile and the KeyName values are both specified, the 
//       following processing occurs:
//       (1) If the KeyName can be found in the CSP, that key is used.
//       (2) If the KeyName does not exist and the KeyFile does exist, the key 
//           in the KeyFile is installed into the CSP and used.
//   (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
//       When specifying the KeyFile, the location of the KeyFile should be
//       relative to the project output directory, which in Oxygene by default is the
//       same as the project directory. For example, if your KeyFile is
//       located in the project directory, you would specify the AssemblyKeyFile 
//       attribute as [assembly: AssemblyKeyFile('mykey.snk')]
//   (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
//       documentation for more information on this.
//
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile('')]
//[assembly: AssemblyKeyName('')]

implementation

end.

In short;
I commented out all line not also emitted by the CS compiler, and added all the ones missing !

And since I made all that changes it runs perfect !

I still need to track down, which of this assembly statements needs to be emitted mandatory or dropped mandatory to make a DLL run under an IIS 75 without raising an TypeInitializationException. I have to do this because my other DLL’s written in Oxygene show the same TypeInitializationException when called the first time for what ever reason.

your feedback is very welcome.
Josef

And more insight:
as soon as I drop this from AssemblyInfos I get the TypeInitializationException

{
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default or 
DebuggableAttribute.DebuggingModes.DisableOptimizations or 
DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints or 
DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
}

and I can not drop it (I don’t know how to drop it) from the ToolboxCS projects AssemblyInfos file because it’s not there but somehow emitted by the CS compiler and seen only if you look at it using ILSpy.

I will add manually this to all my remaining DLL projects and report here.

Oke. So that fixes it for you? That’s pretty weird.

The real wired thing is that Visual Studio when creating a C# project does not add all assembly attributes to the file but emits some of them only to the binaries and those can only bee seen in code emitted by ILSpy (or the like);

Otherwise, Oxygene integrated into Visual Studio does not emit / add this important assembly attributes when one changes in configuration manager from RELEASE to DEBUG. (this as opposite to C# made assemblies) .

The very unpleasant thing however is that this causes troubles only when one deploys the DLL to a web service based on IIS or so and runs perfect in local applications under unit testing or by local call from a application.

The TypeInitializationException however is raised then regardless of DEBUG or RELEASE configuration.

The C# compiler just emits that attribute for you then. But it makes no sense that its’ required in the first place. It’s a hint at the debugger after all. I’ll investigate why this happens, but I’m glad you’ve found a workaround.

I just changed all my Oxygene pas projects to inlude this Debugg stuff into the AssemblyInfo File and now I can debugg down to the lowest module/dll which comes from us.

But maybe important to know for you, please look at my picture above where you can see all the DLL’s / components, that as soon as our code intends to call into this Oracle.DataAccess.DLL we get a TypeInitializationException.

That is the end of the story for today. we have no control over this DLL and it’s make. So what is wrong with our configuration that this Oracle.DataAccess.DLL raise an exception when we call it, and when it’s own cctor is activated.

This is a bit I do not yet understand. What is the proper setup for the solution. For the deployment? All our shown DLL’s must run under a IIS 7.5 and the main DLL written in C# is called from w3wp.exe, and all works in x64 mode. All is compiled with Any_CPU.

and BTW; much thanks for your help
Regards
Josef

This must be a bug in the w3wp process really, can’t be anything else. I don’t know why it crashes in the first palce because it shouldn’t.

The cause why it crashes in the Oracle.DataAccess DLL is that deep in the DLL an attempt is made to register something. And because that happens while the cctor executes, this exception is just wrapped by a TypeInitializationException. So we have to seek what Oracle likes to register and why it does not succeed.

Could it just be 32 vs 64bits oracle libraries?

Found it using ProcessMonitor, filtering out all w3wp.exe activity: It shows that the impersonated user is X036713, the user calling from a Lab-Top, my own users-ID. But this users is unknown in the active directory of the server where for what ever Policy only V-Users are created. This is for me V036713. And attempting to RegOpenKey with that X036713 Users retruns a BAD_IMPERSONATION for a RegOpenKey to the Registry Key HKLM\SOFTWARE\Oracle\ODP.NET.

The Permissions of the Key shows that READ access is allowed for all users but as this X-User is not known on that Server we get BAD_IMPERSONATION. If that does not happen any-more then we do not get a TypeInitializationException from the Oracle.DataAccess.DLL.

End of Story.

Good to hear!