Problem:
Customer was attempting to do a PInvoke function call from a C# ASP.NET code-behind program to an unmanaged COBOL .DLL created with Net Express 3.1 preSP1.
Customer compiled the application using REENTRANT"2" but the application also uses embedded SQL Server ODBC statements and the SQL(Thread=Isolate) was not supported in 3.1 pre SP1.
Customer could successfully run the ASP.NET application and call the unmanaged COBOL .DLL if only one user was running the application. When they added multiple users making simultaneous requests for the Web application the Platform Invoke call started to fail and one client's working-storage would end up in another client's program.
Pgm1: 3rd party C# .NET? multithreaded exe calls/passes data Pgm2: COBOL dll calls/passes data Pgm3: COBOL dll
Resolution:
The following information is in regards to your attempts to create a C# based ASP.NET application which uses Platform Invoke to call an unmanaged Net Express pre-SP1 multi-threaded .DLL.
The short answer to this is "No" it will not work and this is due to the Thread Pooling environment of IIS. You cannot use Platform Invoke but you can use the .NET COM Interop technology known as RCW.
First off Net Express pre-SP1 and post-SP1 are stabilized products. Code corrections are no longer being made for these versions.
Applications that are developed using these compilers and run-time systems have never been (and never will be) tested in any of the newer Microsoft OS platforms, such as XP SP2 and Windows 2003, or environments such as .NET.
All of the following technical material and recommendations are based on using Net Express 4.0 with the currently available WrapPack installed, at this time ALL05N40.
The recommendations below are based on both internal testing of the Net Express 4.0 product as well as information that is provided directly by Microsoft regarding development in the ASP.NET environment.
When Microsoft first released their ASP (non-.NET) platform for running Internet applications under IIS, the ASP technology was designed to be used with COM Server and COM technologies as the backend solution for their multi-tiered Web application development platform. ASP provided the VBScript and JScript languages to create the front-end UI and handle the middle-tier communications to IIS while the bulk of the business application logic including database activity and transaction handling were done by COM Server applications which were instantiated using the ASP Server.CreateObject() functionality so that its methods could be invoked in order to handle the backend of the application.
ASP.NET technology was designed to be backward compatible with ASP and the Server.CreateObject() functionality still exists. Microsoft has added other methods for calling COM Servers from a .NET program which allows for early binding of the component instead of the late binding method used with Server.CreateObject().
Designing an internet application using ASP.NET can become a very complex task due to the number of variables that have to be taken into account by the application.
Some of these variables are;
1. How many end-users are expected to be simultaneously making requests to the Web site?
2. How large is the ASP.NET application that will be loaded into the ASP.NET worker process?
3. How much memory is available on the IIS Server computer?
4. What other components are required by the ASP.NET application, i.e., SQL Server, Oracle database, COM Servers etc.?
5. What OS and version of IIS is being used?
6. What method is being used for Session State management, InProc, ASPState manager, SQL Server?
7. Does the application require that program data be stateful across multiple requests?
8. If the application is being converted from an unmanaged application then how many programs (.DLLs, GNT's) need to be converted and did the old application have a single controlling entry point or a variable number of entry points into the backend programs?
7. How many processors are available on the Server machine?
ASP.NET handles incoming requests by using thread pooling technology.
This means that the same client can make multiple requests during the same application session and ASP.NET might assign a different thread from the thread pool than the thread on which the application was running during a previous request.
Likewise, the thread that was previously used by your application may have been assigned to another client request.
The ASP.NET application has no control over this thread pooling handling.
This is all handled by the ASP.NET worker process which is hosting the application.
By default ASP.NET applications create threads that are compatible with the COM Multi-threaded Apartment architecture.
This can be modified by adding an application attribute which will inform the ASP.NET run-time system that it should use the STA (Single Threaded Apartment model) architecture instead.
This is provided for compatibility with COM Servers that were created using VB 6.0 where only STA components could be created.
STA does not mean that the COM Server is not multi-threaded it simply means that every thread that runs within the COM application gets its own thread data storage and that multiple threads will not interfere with each other.
COBOL COM Servers have the same requirements as VB COM Servers.
They must also be created to use the Apartment Threaded model.
When a COBOL COM Server is instantiated, it's methods can then be invoked from an ASP.NET application and the code within the COM method can then use COBOL CALL statements to call COBOL .DLL's that have been created as unmanaged code.
All of the programs that are called as part of this unmanaged run-unit must be compiled with both the REENTRANT"2" and NODYNAM compiler directives.
Also all .DLLs including the COBOL COM Server must be linked to the shared multi-threaded run-time system.
If any of these applications make use of embedded SQL statements then they also need to be compiled with the SQL(THREAD=ISOLATE) directive so that each thread will get it's own SQL connection and it's own copy of any declared cursors within the application.
This is the recommended method of calling unmanaged COBOL code from within an ASP.NET application.
Net Express 4.0 has a tool called the Interface Mapping Toolkit which makes it easy to create a COM wrapper for a COBOL .DLL by mapping the linkage section data items onto COM data items and then the program can be registered as a COM Server and can be instantiated by ASP.NET.
The reason that the .NET Framework Platform Invoke technology cannot be used to call an unmanaged COBOL .DLL directly from an ASP.NET program is because of the thread pooling issue within ASP.NET.
Simply compiling a COBOL application with the REENTRANT"2" directive does not provide for the COM type apartment threading that is required by the ASP.NET environment.
It simply causes the compiler to generate the application so that all data in Working-Storage will be treated as if it were thread-local storage so that each new thread that enters the application will have its own data storage assigned.
But the thread data within these programs is identified as belonging to a particular thread by using the thread's ID as a key to the thread data.
In a thread pool environment the same application may be running using a different thread ID each time it is called.
This is all dependant on the application and the number of users, etc.
If you are using PInvoke from a C# class or a COBOL class in the ASP.NET environment to call an unmanaged COBOL .DLL without a COM wrapper then it may actually appear to work within a limited test environment.
But if you go into production or use a Stress testing tool to emulate a high volume usage environment then this is when the problems will begin to show up.
In our own in-house testing using the Microsoft Stress tool we have been able to reproduce disastrous results for which there is no fix.
This is due to technical issues imposed by the ASP.NET and IIS environments.
The unmanaged COBOL .DLL's cause a problem with the garbage collection facilities of .NET.
Unless you start the application by instantiating it as a COM Server then there is no way to finalize and unload the unmanaged .DLL and COBOL run-time system from memory.
Microsoft and Micro Focus recommend that developers take one of the following approaches:
1. Convert the unmanaged code to managed code. This is the best solution.
2. Call unmanaged code using a COM wrapper. Micro Focus provides tools for this and the Visual Studio .NET Framework provides tools to do this.
3. Create a COM wrapper for your unmanaged code and then call it as a serviced component which runs under COM . This requires that you use classes from the .NET System.EnterpriseServices namespace and that all database transactions are handled by using an Object Context that is created for the threads.
If you want to attempt to use PInvoke to COBOL in an ASP.NET application then you are on your own as far as the problems that you will encounter.
This application configuration is not supported by Micro Focus and to go a step further we actually expect problems to occur.
In any case, if you are using the OpenESQL embedded SQL technology within a multi-threaded application and the application is compiled with 3.1 pre-SP1 then you will have problems when allowing multiple threads to run within the SQL applications.
This is because there were issues relating to the use of the SQL(THREAD=ISOLATE) directive that were not fixed until the release of 3.1 SP1 and higher.
In order to keep threads from colliding with each other the SQL programs would have to be compiled using the SERIAL directive instead of REENTRANT"2".
This means that only one thread would be allowed to enter the application at a time.
All other threads would be placed on a queue until the active thread exited the application.
In an Internet application with a high volume of usage, this is sure to slow down the response time of the application until ASP.NET Session timeouts begin to occur and users will be kicked out of the application.



