Issues calling DLL (C#) from Thin Client

I’m having trouble with a small test program I’ve written to call a DLL on the client over thin client. Using C# in Visual Studio I made a DLL with a class to display a Popup message box.  I then wrote a small windows form application to test and I have no problem calling the DLL. I was unable to get NETDEFGEN to see the class until I noticed the documentation saying it needed to be .NET 3.5 or older, although it seems the thin client syntax doesn’t need that step anyway.

 

I then wrote a small COBOL program in Acubench to test the DLL. Using examples from the forum I created a couple push-buttons to test various functions and I was able to successfully call different kernel32.dll functions that were posted here in the past.

 

I then added a button to test the DLL I wrote. Below is the relevant push-button code.

 

       Main-Pb-1-Ex-Cmd-Clicked.              

           call "@[DISPLAY]:C:\acuthin\DLLtest1.dll@WINAPI".

                               

           call "@[DISPLAY]:DLLtest1.Class1".

      *     call "@[DISPLAY]:Class1".      

      *     call "@[DISPLAY]:@DLLtest1.DLLtest1.Class1".

                                   

           cancel "@[DISPLAY]:DLLtest1.dll".  

           .

 

My problem is with the second call to the actual function. I receive a “Program missing or inaccessible” error, and I receive this error regardless of the syntax of the 3 calls I tested above. I removed the @[DISPLAY] syntax, ran locally on my test system through Acubench, and received the same errors.

 

Here is the source for the DLL that I created (DLLtest1.cs)

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows.Forms;

 

namespace DLLtest1

{

    public class Class1

    {

        const string message = "Message Called in DLL";

        const string caption = "Caption";

        DialogResult result = MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Information);

    }

}

 

And here is the .def that NETDEFGEN made from it.

 

      *  ACUCOBOL-GT Version 9.0.0 .NET Copy Book - Generated On 1/4/2015 11:05:41 PM

 

           OBJECT @ASSEMBLY

           NAME    "@DLLtest1"

           VERSION "1.0.0.0"

           CULTURE "neutral"

           STRONG  "null"

 

 

      * FULLY-QUALIFIED-NAME DLLtest1.Class1, DLLtest1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

 

      * DLLtest1.Class1

           NAMESPACE "DLLtest1"

           CLASS     "Class1"

           MODULE    "DLLtest1.dll"

 

           CONSTRUCTOR, 0, @CONSTRUCTOR1

 

I’m running thin client from a Linux server, and acurcl and Linux runtime are both 9.1.2.1. Compiler, Acubench, and windows runtime are 9.0.

 

I’ve created a small zip and included the .cbl of my test program, the .cs for the DLL, and the .def that NETDEFGEN generated from it.

Any help or thoughts would be much appreciated. 

source_code.zip
  • The zip file did not include your dll, so I could not fully test. I would change the call "@[DISPLAY]:C:\acuthin\DLLtest1.dll@WINAPI". to call "@[DISPLAY]:C:\acuthin\DLLtest1.dllI". the @WINAPI really does apply to a dll that you are making. I would test on Windows first, and the calls would need to change from call "@[DISPLAY]:C:\acuthin\DLLtest1.dll". to call "DLLtest1.dll". I would test and get it working on Windows and then go with the @(DISPLAY) syntax and deploy on Linux via acurcl.

  • As far as the second call ... change call "@[DISPLAY]:DLLtest1.Class1". to    call "Class1"

    The first call loads the dll, subsequent calls call the functions in the dll.

  • Thank you for the feedback, I’ve removed the “WINAPI” reference. I went ahead and made a new program to run locally as well and included a few changes to make it easier to specify where the DLL is kept, although I still receive the same error. The initial call works, but the actual function/method call fails.

     

    If it’s helpful I made another zip that includes DLLTEST2.CBL, DLLtest1.dll, and the full Visual Studio project of the program I wrote to test the DLL outside of COBOL.

     

           IDENTIFICATION DIVISION.

           PROGRAM-ID. DLLTEST2.  

           WORKING-STORAGE SECTION.    

           78  DLL-LOCATION    value "C:\acuthin\".

           78  DLL-NAME        value "DLLtest1.dll".

           77  call-string     pic x(120).

           LINKAGE SECTION.         

           PROCEDURE DIVISION.

           Program-Begin.

               perform Call-DLL. 

           Program-End.    

               stop run. 

               .     

           Call-DLL. 

               string DLL-LOCATION DLL-NAME delimited by size

                 into call-string.

                                            

               call call-string.          

               DISPLAY MESSAGE "1: Calling Method: Class1".                                         

               call "Class1".                                                         

               DISPLAY MESSAGE "2: Call Successful".        

               cancel "DLLtest1.dll".  

               .      

    source_code2.zip
  • Apologies if I sent you in the wrong direction. Let's take a look at why the call to KERNEL32.DLL works, whereas the call to Class1 does not. In call KERNEL32.DLL, you call the dll and then call a function that returns a parameter :GetVersion"     GIVING myDWord.. In call DLLtest1, you are doing something very different, you are calling a dll and then asking for the dll to display a response  DialogResult result = MessageBox.Show. There are a couple of different ways a COBOL program can interact with .Net. If the dll provides a UI (as in this case) the AcuGT\sample\dotnet\NETOBJECTS  is a good example of this. The netdefgen copy book provides the runtime with the methods that can be called, but we use the Display, Modify, and Inquire verbs within COBOL to interact with the .Net assembly. If the .Net assembly does not have a UI, then we use the Create, modify and inquire verbs within COBOL to interact with the .Net assembly. The AcuGT\sample\dotnet\compositecontrol andAcuGT\sample\dotnet\amortontrol are other examples of the runtime interacting with a .Net assembly.

  • That makes more sense. I wasn’t actually thinking of the message box as being a UI and thought it would be the simplest example to create.

     

    Also reading a bit more I see that the “call dll” syntax is only for native code DLLs, not the MSIL kind I would be creating in C#. Using the sample compositecontrol as an example I restarted from scratch writing something without a UI to test.

     

    I made a small DLL to add 2 passed in integers, a form program to test, and then a COBOL program to test as well.

     

    After placing the DLL in the same directory as wrun32.exe I was able to call it from my COBOL program using “create” and everything worked great.

     

    However it doesn’t work when run through thin client. The program fails on “create” but returns no error. It simply says “extend thin client has stopped working”.

     

    I rebuilt everything in a standalone Acubench project to make sure existing compile switches weren’t an issue. I’ve attached a zip that includes the complete project including the .NET projects and source.

     

           IDENTIFICATION DIVISION.

           PROGRAM-ID. RUNDLL.  

           ENVIRONMENT DIVISION.

           CONFIGURATION SECTION.

           SPECIAL-NAMES.

               copy "dotnet35_math.def".

               .

           WORKING-STORAGE SECTION.       

           77  int1            signed-int value 10. 

           77  int2            signed-int value 25.  

           77  int3            signed-int value 0. 

     

           77  dotnet-math-add-handle usage handle of object.     

                            

           PROCEDURE DIVISION.

     

           Program-Begin.

               perform Call-DLL-Add.     

     

           Program-End.        

               exit program.

               stop run. 

               .

          *

           Call-DLL-Add.       

               create "dotnet35_add"

                 namespace is "dotnet35_math"

                 class-name is "AddIntClass"              

                 constructor is constructor1()

                 handle is dotnet-math-add-handle. 

     

               modify dotnet-math-add-handle "@Add" (int1, int2)

                      giving int3.

     

               display message int1 " " int2 " = " int3

                         title "Result". 

               .  

    rundll.zip
  • Excellent work. Now,on the machine where you are running thin client,have you registered your C# assembly? regsvr32 <ocx or dll name>

  • I had not run regsvr32. Checking the documentation it seems that it is used to register an ActiveX control as a COM object? In my case I’ve created a .NET Assembly. Today I attempted to run regsvr32 just in case that is necessary for both and I receive the error “DLL was loaded… DllRegister Server entry point was not found. “

    Reading up on that and finding another post on the forum it seems that regasm.exe is what I need to run to register the .NET assembly as a COM component? So I’ve also now done the following.

    • Signed the assembly with a strong name.

    • run regasm.exe on the dll.

    • Rerun NETDEFGEN on the dll creating a new .def which now references the “STRONG” value.

    • Run Marshal [dll] – I’m not sure what this step is for, I couldn't find any reference in the documentation, but I saw it recommended in another forum post.

    Tested after all the above and it still works local with wrun32.exe but no change to behavior when run with acuthin.exe.  

  • I thought the regsvr32 would do this. For .Net you need to have the assembly avaible in the Global Assembly Cache (GAC) ... here's an article about that

    msdn.microsoft.com/.../dkkx7f79(v=vs.110).aspx

  • I did this using the command >gacutil -i c:\acuthin\9.1.2.1\dotnet35_add.dll. I was able to verify that it was there with >gacutil /lr dotnet35_add. This returns the below.

    Microsoft (R) .NET Global Assembly Cache Utility. Version 4.0.30319.33440
    Copyright (c) Microsoft Corporation. All rights reserved.

    The Global Assembly Cache contains the following assemblies:
    dotnet35_add, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9fd5883bc1343728, processorArchitecture=MSIL

    Number of items = 1

    I still have the same issue with not working in thin client with no actual error message. One thing I noticed in reading on the GAC it seems that this would actually only be necessary if I wanted to use the DLL without having it in the same directory as the executable no? 

  • I believe that is true, it should work without GAC if your dll is in the same directory as thin client (acuthin.exe).