------------------------------
Kavitha Natarajan
Rocket Software Forum Member
------------------------------
Hi Kavitha,
We used to have a sample program in the old Net Express product that demonstrated how to create a Windows service using the native COBOL OO-support and it was quite complex, but this is not available in Visual COBOL. In Visual COBOL there is a project template for creating a Windows Service using the .NET Framework that can be used instead.
I am attaching a small demo that I wrote by converting the C# tutorial found here to Visual COBOL.
I developed this using Visual COBOL V10 for Visual Studio 2022 so you probably cannot load it into your product version, but you could look at the source code used. If you need this instead using V8.0 and VS2019 let me know and I will try to create this for you.
Hi Kavitha,
We used to have a sample program in the old Net Express product that demonstrated how to create a Windows service using the native COBOL OO-support and it was quite complex, but this is not available in Visual COBOL. In Visual COBOL there is a project template for creating a Windows Service using the .NET Framework that can be used instead.
I am attaching a small demo that I wrote by converting the C# tutorial found here to Visual COBOL.
I developed this using Visual COBOL V10 for Visual Studio 2022 so you probably cannot load it into your product version, but you could look at the source code used. If you need this instead using V8.0 and VS2019 let me know and I will try to create this for you.
Thank you so much for the response and the code. I tried the Windows Service (.NET Framework) project in Visual COBOL 8.0 and Visual Studio 2019. Included the code you had provided but facing few issues, trying to work through it. If you can provide the code for V8.0 and VS2019, it would be of great help. Thanks once again!
I believe installUtil.exe is the command to install this executable as service, can you please confirm?
------------------------------
Kavitha Natarajan
Rocket Software Forum Member
------------------------------
                
Thank you so much for the response and the code. I tried the Windows Service (.NET Framework) project in Visual COBOL 8.0 and Visual Studio 2019. Included the code you had provided but facing few issues, trying to work through it. If you can provide the code for V8.0 and VS2019, it would be of great help. Thanks once again!
I believe installUtil.exe is the command to install this executable as service, can you please confirm?
------------------------------
Kavitha Natarajan
Rocket Software Forum Member
------------------------------

Thanks!

Thanks!
Here is a sample of getting the user name 3 ways in a .NET program:
      $set ilusing"System.Runtime.InteropServices"
       class-id. myclass as "testNETwinapi.myclass".
       method-id Main public static.
       procedure division.
           *> 1. Use platform invoke
           declare user-name as type StringBuilder = new StringBuilder(256)
           declare user-len as binary-long = 256
           declare mybool = GetUserNameA(by value user-name, by reference user-len)
           *> 2. Get logged in user
           declare user-name2 as string = type Environment::UserName
           *> 3. Get Domain also
           declare user-name3 as string = type System.Security.Principal.WindowsIdentity::GetCurrent::Name
           goback.
       
       end method.
       method-id GetUserNameA static private
       attribute DllImport("advapi32.dll").
       procedure division using by value user-name as type StringBuilder
                                         reference user-len as binary-long
                           returning mybool as condition-value.
       
       end method.
       end class myclass.Here is a sample of getting the user name 3 ways in a .NET program:
      $set ilusing"System.Runtime.InteropServices"
       class-id. myclass as "testNETwinapi.myclass".
       method-id Main public static.
       procedure division.
           *> 1. Use platform invoke
           declare user-name as type StringBuilder = new StringBuilder(256)
           declare user-len as binary-long = 256
           declare mybool = GetUserNameA(by value user-name, by reference user-len)
           *> 2. Get logged in user
           declare user-name2 as string = type Environment::UserName
           *> 3. Get Domain also
           declare user-name3 as string = type System.Security.Principal.WindowsIdentity::GetCurrent::Name
           goback.
       
       end method.
       method-id GetUserNameA static private
       attribute DllImport("advapi32.dll").
       procedure division using by value user-name as type StringBuilder
                                         reference user-len as binary-long
                           returning mybool as condition-value.
       
       end method.
       end class myclass.Actually, the way that you are doing it in native code can also be done in managed code. You just have to add the following directive to the top of the program:
$set ilpinvoke"advapi32.dll"
Actually, the way that you are doing it in native code can also be done in managed code. You just have to add the following directive to the top of the program:
$set ilpinvoke"advapi32.dll"


The same program that I gave to you works correctly on my system and all messages are written to the event log.

The same program that I gave to you works correctly on my system and all messages are written to the event log.

Thanks Chris.
Thanks Chris.
Under which user account are you running the service?
The tutorial has the following warning:
--------------------------
This setting installs the service and runs it by using the local system account.
Important
The LocalSystem account has broad permissions, including the ability to write to the event log. Use this account with caution, because it might increase your risk of attacks from malicious software. For other tasks, consider using the LocalService account, which acts as a non-privileged user on the local computer and presents anonymous credentials to any remote server. This example fails if you try to use the LocalService account, because it needs permission to write to the event log.
Under which user account are you running the service?
The tutorial has the following warning:
--------------------------
This setting installs the service and runs it by using the local system account.
Important
The LocalSystem account has broad permissions, including the ability to write to the event log. Use this account with caution, because it might increase your risk of attacks from malicious software. For other tasks, consider using the LocalService account, which acts as a non-privileged user on the local computer and presents anonymous credentials to any remote server. This example fails if you try to use the LocalService account, because it needs permission to write to the event log.
Hi Chris,
I believe I am logged in as LocalSystem account as I was able to write to event log using the code attached. Attached code is a pure Visual COBOL code (not Windows service program) when run using start command wrote the instructed messages to event log which was verified in the Event Viewer. But if I use the same Call WINAPI ReportEventA or WriteEntry() function in Windows service program, specified logs in the code do not reflect in Event Viewer.
Do you suspect that event log writing through a Windows service program (that is run by starting the service manually) might require a different kind of user permission as against the event log write using the pure Visual COBOL code (that is run by start command in command line) ?
Hi Chris,
I believe I am logged in as LocalSystem account as I was able to write to event log using the code attached. Attached code is a pure Visual COBOL code (not Windows service program) when run using start command wrote the instructed messages to event log which was verified in the Event Viewer. But if I use the same Call WINAPI ReportEventA or WriteEntry() function in Windows service program, specified logs in the code do not reflect in Event Viewer.
Do you suspect that event log writing through a Windows service program (that is run by starting the service manually) might require a different kind of user permission as against the event log write using the pure Visual COBOL code (that is run by start command in command line) ?
When you are not running as a service the program will be run with the credentials of the logged in user by default.
When run as a service you can select the account under which the service will be run. Bring up the Services window where you are starting the service, and the Log On As field will show you what this is.
In this tutorial there is the following section that details setting the account to LocalSystem so that it will have the proper rights to write to the registry.
--------
In the Design view for ProjectInstaller, choose serviceProcessInstaller1 for a C# project, or ServiceProcessInstaller1 for a Visual Basic project, then choose Properties from the shortcut menu. Set the Account property to LocalSystem from the drop-down list.
This setting installs the service and runs it by using the local system account.

When you are not running as a service the program will be run with the credentials of the logged in user by default.
When run as a service you can select the account under which the service will be run. Bring up the Services window where you are starting the service, and the Log On As field will show you what this is.
In this tutorial there is the following section that details setting the account to LocalSystem so that it will have the proper rights to write to the registry.
--------
In the Design view for ProjectInstaller, choose serviceProcessInstaller1 for a C# project, or ServiceProcessInstaller1 for a Visual Basic project, then choose Properties from the shortcut menu. Set the Account property to LocalSystem from the drop-down list.
This setting installs the service and runs it by using the local system account.

Thank you for your guidance, Chris. My service program was writing to event logs with both User and LocalSystem account. I missed to check the right place where program logs were written. Until now, I was checking only 'Application' under 'Windows Logs' in Event Viewer. Only after going through each item thoroughly, I found that the program written logs appear under 'Applications and Service Logs'. I believe I got the basic service program functioning with user enabled event logging. I will proceed to add more functionality to this structure as my project develops.
I had query on below code. I believe SetServiceStatus method and below timer related statements are optional and are not a necessity for event log write functionality. Can you please confirm?
      method-id SetServiceStatus private static
          attribute DllImport("advapi32.dll").
      procedure division using handle as type System.IntPtr,
                               by reference serviceStatus as type ServiceStatus
                     returning mybool as condition-value.
end method.
          declare timer = new type Timer
          set timer::Interval = 60000 *> 60 seconds
          attach new type ElapsedEventHandler(self::OnTimer) to timer::Elapsed
          invoke timer::Start
          declare serviceStatus = new type ServiceStatus
          set serviceStatus::dwCurrentState = type ServiceState::SERVICE_START_PENDING
          set serviceStatus::dwWaitHint = 100000
          declare mybool as condition-value
            = type Service1::SetServiceStatus(self::ServiceHandle, by reference serviceStatus)
          set serviceStatus::dwCurrentState = type ServiceState::SERVICE_RUNNING
          set mybool = type Service1::SetServiceStatus(self::ServiceHandle, by reference serviceStatus)
Thank you for your guidance, Chris. My service program was writing to event logs with both User and LocalSystem account. I missed to check the right place where program logs were written. Until now, I was checking only 'Application' under 'Windows Logs' in Event Viewer. Only after going through each item thoroughly, I found that the program written logs appear under 'Applications and Service Logs'. I believe I got the basic service program functioning with user enabled event logging. I will proceed to add more functionality to this structure as my project develops.
I had query on below code. I believe SetServiceStatus method and below timer related statements are optional and are not a necessity for event log write functionality. Can you please confirm?
      method-id SetServiceStatus private static
          attribute DllImport("advapi32.dll").
      procedure division using handle as type System.IntPtr,
                               by reference serviceStatus as type ServiceStatus
                     returning mybool as condition-value.
end method.
          declare timer = new type Timer
          set timer::Interval = 60000 *> 60 seconds
          attach new type ElapsedEventHandler(self::OnTimer) to timer::Elapsed
          invoke timer::Start
          declare serviceStatus = new type ServiceStatus
          set serviceStatus::dwCurrentState = type ServiceState::SERVICE_START_PENDING
          set serviceStatus::dwWaitHint = 100000
          declare mybool as condition-value
            = type Service1::SetServiceStatus(self::ServiceHandle, by reference serviceStatus)
          set serviceStatus::dwCurrentState = type ServiceState::SERVICE_RUNNING
          set mybool = type Service1::SetServiceStatus(self::ServiceHandle, by reference serviceStatus)
Hi Kavitha,
That's great that you are up and running!
Yes, you are correct that the SetServiceStatus function code is optional and is just included in the example, for the sake of completeness. It is however recommended that if your service takes a long time starting up or shutting down that it is a good idea to set the SERVICE_START_PENDING and SERVICE_STOP_PENDING statuses so that the user is informed as to what is going on. The timer routine is simply to emulate a long startup process for the example.
Hi Kavitha,
That's great that you are up and running!
Yes, you are correct that the SetServiceStatus function code is optional and is just included in the example, for the sake of completeness. It is however recommended that if your service takes a long time starting up or shutting down that it is a good idea to set the SERVICE_START_PENDING and SERVICE_STOP_PENDING statuses so that the user is informed as to what is going on. The timer routine is simply to emulate a long startup process for the example.
Thank you Chris for that clear explanation.
I have been progressing on including more project related code to the service program. When I began including a bit more complex logic into the program, build is giving below error:
error COBCH1501 : Insufficient memory
It is the same code logic which I tested standalone and worked good, but when included in service program, gives above build error. Do you have suggestions to overcome this build error?
Thanks!
Thank you Chris for that clear explanation.
I have been progressing on including more project related code to the service program. When I began including a bit more complex logic into the program, build is giving below error:
error COBCH1501 : Insufficient memory
It is the same code logic which I tested standalone and worked good, but when included in service program, gives above build error. Do you have suggestions to overcome this build error?
Thanks!
Hi Kavitha,
I suggest opening a support case to report the issue you are encountering during build. You may do so via https://my.rocketsoftware.com or by contacting your local Rocket Software customer care (see https://my.rocketsoftware.com/RocketCommunity/s/contact-us" target="_blank" rel="noopener">Contact Us).
Regards,
Hi Kavitha,
That's great that you are up and running!
Yes, you are correct that the SetServiceStatus function code is optional and is just included in the example, for the sake of completeness. It is however recommended that if your service takes a long time starting up or shutting down that it is a good idea to set the SERVICE_START_PENDING and SERVICE_STOP_PENDING statuses so that the user is informed as to what is going on. The timer routine is simply to emulate a long startup process for the example.
Hi Chris,
Fano has been helping me with the build error. Meanwhile, I had a clarification while including more COBOL logic with tags in columns 73 to 80. This works fine in a standalone COBOL application. However, when the same logic is included inside the service program, compile error is thrown for each statement that have tags in 73rd column. Sample error is shown in below picture. Is there a workaround for such compile error?

------------------------------
Kavitha Natarajan
Fidelity Information Svcs Inc
------------------------------
                
Hi Chris,
Fano has been helping me with the build error. Meanwhile, I had a clarification while including more COBOL logic with tags in columns 73 to 80. This works fine in a standalone COBOL application. However, when the same logic is included inside the service program, compile error is thrown for each statement that have tags in 73rd column. Sample error is shown in below picture. Is there a workaround for such compile error?

------------------------------
Kavitha Natarajan
Fidelity Information Svcs Inc
------------------------------
Hi Kavitha,
The default sourceformat directive for native COBOL projects in Visual Studio is Fixed Format in which Area B stops at column 72 but for managed .NET projects (which is what you are now using), the default is Variable Format, in which Area B is extended to 250 characters. You are getting the error because the compiler assumes that these comments are part of the source code and not comments as they would be in Fixed Format.
To solve this problem you can change the sourceformat directive to Fixed Format on the COBOL tab of the Project Properties page or you could make your comments in-line comments by prefacing them with an *> 
e.g.         *> tag 1
Hi Kavitha,
The default sourceformat directive for native COBOL projects in Visual Studio is Fixed Format in which Area B stops at column 72 but for managed .NET projects (which is what you are now using), the default is Variable Format, in which Area B is extended to 250 characters. You are getting the error because the compiler assumes that these comments are part of the source code and not comments as they would be in Fixed Format.
To solve this problem you can change the sourceformat directive to Fixed Format on the COBOL tab of the Project Properties page or you could make your comments in-line comments by prefacing them with an *> 
e.g.         *> tag 1
Thank you Chris! Project properties change to Fixed has resolved the compiler issue. Currently, I have a query on this - I am including a logic that calls standalone native COBOL program - ProgramA (say) that is already compiled, built and .exe is present in a different location other than /bin/debug. Is there anyway that our service program will be able to pick this .dll or .exe from this location (other than /bin/debug) when a CALL statement is included?
Thank you Chris! Project properties change to Fixed has resolved the compiler issue. Currently, I have a query on this - I am including a logic that calls standalone native COBOL program - ProgramA (say) that is already compiled, built and .exe is present in a different location other than /bin/debug. Is there anyway that our service program will be able to pick this .dll or .exe from this location (other than /bin/debug) when a CALL statement is included?
In order to call a native program from a managed application the native program would have to be a .dll, not an .exe.
If you set the system PATH to include the folder in which the native .dll resides it will be searched during the call.
You could also set the path in an app.config file that you add to your managed project that could look like the following:
In order to call a native program from a managed application the native program would have to be a .dll, not an .exe.
If you set the system PATH to include the folder in which the native .dll resides it will be searched during the call.
You could also set the path in an app.config file that you add to your managed project that could look like the following:
Hi Chris,
Thank you for the response. I have been doing changes and test runs myself to see if I can overcome 'Insufficient memory' build issue that I have been facing. So far, no luck.
This is my program scenario - In my .cbl program of the Windows service program, I have a copybook which calls two programs - A, B. Programs A, B are .dlls which are already compiled and built. Programs A, B source .cbl and their corresponding .dlls are present in a different location other than the Windows service project. Now, when I build my Windows service program, I get 'Insufficient memory' error. If I add the programs A, B to my Windows service project and compile, build, build completes without error. I want to understand why this is so. Please let me know your thoughts on this.
Additional location info -
Windows service project source location - D:\\Kavitha\\WindowsService\\WindowsService8
Windows service project .exe location - D:\\Kavitha\\WindowsService\\WindowsService8\\bin\\Debug
Program A,B source location - D:\\VCSOURCE
Program A,B .dll location - D:\\VCRUN
Thanks & Regards,
Kavitha.
Hi Chris,
Thank you for the response. I have been doing changes and test runs myself to see if I can overcome 'Insufficient memory' build issue that I have been facing. So far, no luck.
This is my program scenario - In my .cbl program of the Windows service program, I have a copybook which calls two programs - A, B. Programs A, B are .dlls which are already compiled and built. Programs A, B source .cbl and their corresponding .dlls are present in a different location other than the Windows service project. Now, when I build my Windows service program, I get 'Insufficient memory' error. If I add the programs A, B to my Windows service project and compile, build, build completes without error. I want to understand why this is so. Please let me know your thoughts on this.
Additional location info -
Windows service project source location - D:\\Kavitha\\WindowsService\\WindowsService8
Windows service project .exe location - D:\\Kavitha\\WindowsService\\WindowsService8\\bin\\Debug
Program A,B source location - D:\\VCSOURCE
Program A,B .dll location - D:\\VCRUN
Thanks & Regards,
Kavitha.
Hi Kavitha,
We tested this yesterday with the example that you sent in through the support case and we discovered what is causing the memory error during compilation. It appears to be a combination of using the ilpinvoke"advapi32.dll" and the call to "GETVARS". It appears to be trying to resolve the reference to the call "GETVARS" within the native .dll. If I change the name of GETVARS to something else the error doesn't occur.
I find that if you comment out the ilpinvoke directive and instead add a reference to your project by right-clicking on the references folder and project name and selecting Add Reference and then using the Browse button to navigate and select c:\\Windows\\system32\\advapi32.dll then the compiler error goes away.
We are still investigating but if you could give this a try and see if it fixes the issue for you, that would be helpful.
Thanks.
Hi Kavitha,
We tested this yesterday with the example that you sent in through the support case and we discovered what is causing the memory error during compilation. It appears to be a combination of using the ilpinvoke"advapi32.dll" and the call to "GETVARS". It appears to be trying to resolve the reference to the call "GETVARS" within the native .dll. If I change the name of GETVARS to something else the error doesn't occur.
I find that if you comment out the ilpinvoke directive and instead add a reference to your project by right-clicking on the references folder and project name and selecting Add Reference and then using the Browse button to navigate and select c:\\Windows\\system32\\advapi32.dll then the compiler error goes away.
We are still investigating but if you could give this a try and see if it fixes the issue for you, that would be helpful.
Thanks.

------------------------------
Kavitha Natarajan
Fidelity Information Svcs Inc
------------------------------
                

------------------------------
Kavitha Natarajan
Fidelity Information Svcs Inc
------------------------------
Actually, you shouldn't have any console screen I-O in a Windows Service as, by default they run in the background without any user interaction.
You can get rid of the error by changing the output type to Console Application on the Application project properties tab.
Actually, you shouldn't have any console screen I-O in a Windows Service as, by default they run in the background without any user interaction.
You can get rid of the error by changing the output type to Console Application on the Application project properties tab.
Thank you Chris. Yes, in the present requirement outcome, there is no console operation to be done. These code/subprograms I have been working on to include have been developed more than 15+ years. ACCEPT/DISPLAY and few more console related statements are present. Unfortunately, budget/time constraint to alter all of them now.
I was hesitant in the beginning to change the Output type in properties to Console application thinking if the windows service execution will be affected in anyway. I tested with a basic service program and the change in properties worked good with service execution too.
Now, with present code that I have shared in the support case, compile and build came out clean, but facing below error during service start.
----

------------------------------
Kavitha Natarajan
Fidelity Information Svcs Inc
------------------------------
                
Thank you Chris. Yes, in the present requirement outcome, there is no console operation to be done. These code/subprograms I have been working on to include have been developed more than 15+ years. ACCEPT/DISPLAY and few more console related statements are present. Unfortunately, budget/time constraint to alter all of them now.
I was hesitant in the beginning to change the Output type in properties to Console application thinking if the windows service execution will be affected in anyway. I tested with a basic service program and the change in properties worked good with service execution too.
Now, with present code that I have shared in the support case, compile and build came out clean, but facing below error during service start.
----

------------------------------
Kavitha Natarajan
Fidelity Information Svcs Inc
------------------------------
On the project properties Application tab, change the Startup object to be Main:
Already have an account? Login
Enter your E-mail address. We'll send you an e-mail with instructions to reset your password.