1. Thomas Rolseth
  2. PowerServer
  3. Wednesday, 28 December 2022 04:16 AM UTC

I recently migrated and deployed an application to PowerServer 2022 for a client as a proof-of-concept.  The application uses the java runtime via calls to a dll to retrieve and display image files.  When the app is launched in client/server mode via a shortcut, it executes a small batch file that that adds two folders to the PATH environment variable.  These folders contain the java runtime and supporting files needed to handle the image file processing.  In PowerServer mode, a JVM error is thrown because the app can no longer find the runtime.  I tried to call the batch file in the PowerServer project's preload event but it didn't work.  I think this is because setting the path in this manner in the PowerServer environment is only temporary and by the time the app starts, the cmd instance running the batch script has shut down and the path reverts to what it was originally.

I've tried setting the path in code using RegistrySet but 1) it somethings did weird things to the path and 2) it requires that you run the app as an administrator.  I've also tried using the SETX command in the batch file but in my research, came across a lot of posts saying that this was dangerous.  And it didn't seem to work.

Is there another way to set the path permanently?  Manually going into the environment variables and adding the path solves the problem but this is a last resort.  We would prefer to not have end users do this on their computers unless there is a way to automate it.

Has anyone dealt with an issue like this?

Miguel Leeuwe Accepted Answer Pending Moderation
  1. Thursday, 5 January 2023 16:41 PM UTC
  2. PowerServer
  3. # 1

Hi Thomas,

 

This is what I do:

External function declarations:

function boolean SetEnvironmentVariable(ref string lpName, ref string lpBuffer) library "KERNEL32.DLL"  Alias For "SetEnvironmentVariableW"
function boolean GetEnvironmentVariable(ref string lpName, ref string lpBuffer, ref ulong nSize) library "KERNEL32.DLL" Alias For "GetEnvironmentVariableW"

In the code below, there's a lot more than you need, like logging the set path to a txt file and other things, but just search on "SetEnvironmentVariable" and "GetEnvironmentVariable"

If you need more help, I can try to make a small sample app to achieve what you want, but I'm pretty busy right now, so it'll take me some time (like the weekend maybe).

// u6, v1, mjl, 12/11/19: have to set the path to the runtime SHARED in earlier stage:
// For checks on path's:
string ls_pathInsertList[]
int li_pathInsert
string ls_check_setPath, ls_what = 'PATH'
ulong lul_size = 2048

boolean	lb_rc
string ls_temp, ls_value, ls_path2Shared, ls_path, ls_tempPath, ls_folder
//n_cst_platformunicode lnv_platform
boolean lb_valid_path = false

//f_SetPlatform(lnv_platform, TRUE)
//ls_path = lnv_platform.of_getPath()
ls_path = is_appDir // if not running the EXE, this won't work well, path is different

// see if we have a valid shared directory:
ls_path2Shared = mid(ls_path, 1, lastpos(ls_path, '\')) + "SHARED"

int li_rc, li_filewriterc, li_filewriterc2	
n_cst_filesrv 	lnv_filesrv
li_rc = f_SetFilesrv_no_gnv_app(this, lnv_filesrv, True)

// u1, mjl, 16/01/17: get windows temp dir.
is_win_temp_dir = lnv_filesrv.of_getTempPath()
// u3, mjl, 17/10/17: string returned already ends on '\', but let's make sure
if len(is_win_temp_dir) > 0 then
	if right(is_win_temp_dir, 1) <> '\' then
		is_win_temp_dir += '\'
	end if
end if

try
	li_filewriterc = lnv_filesrv.of_FileWrite (ls_path + "\_setPathLog.txt", 'SETTING THE PATH:~r~n', FALSE)
catch ( throwable e)
	// nothing
end try

// v1, mjl, 03/10/19: write to temp folder too, since might not have permissions to write in application's folder:
string lsWinTempDir
lsWinTempDir = this.is_win_temp_dir
if right(lsWinTempDir, 1 )  = '\' then
	lsWinTempDir = Mid(lsWinTempDir, 1, len(lsWinTempDir) - 1)
end if
try
	li_filewriterc2 = lnv_filesrv.of_FileWrite (lsWinTempDir + "\_setPathLog.txt", 'SETTING THE PATH:~r~n================', FALSE)
catch ( throwable e2)
	// nothing
end try	

// add ls_path2Shared to the userPath:
string ls_pathInsert = ""

// first we try to add the missing path:
ls_pathInsert = (ls_path2Shared + "\JRE\BIN;")
li_pathInsert ++
ls_pathInsertList[li_pathInsert] = ls_pathInsert
// first we try to add the missing path:
ls_pathInsert = ls_path2Shared + "\JRE;"
li_pathInsert ++
ls_pathInsertList[li_pathInsert] = ls_pathInsert

// first we try to add the missing path:
ls_pathInsert = (ls_path2Shared + ";" )
li_pathInsert ++
ls_pathInsertList[li_pathInsert] = ls_pathInsert

//ls_path = (ls_pathInsert + ls_path) // u2, mjl, 29/06/18: commented because I want \shared to be the first path added, then the rest:
int li_i
for li_i = 1 TO li_pathInsert
	// v1, mjl, 31/07/19: only add the path if it's valid, if not could break the validity of following folders in the rest of the path:
	if DirectoryExists( mid(ls_pathInsertList[li_i], 1, len(ls_pathInsertList[li_i]) - 1) ) then
		ls_path = (ls_pathInsertList[li_i] + ls_path)
	else
		if li_rc > 0 then
			try
				li_filewriterc = lnv_filesrv.of_FileWrite (is_appdir + "\_setPathLog.txt", '~r~nTHE PARTIAL PATH:~r~n"' + mid(ls_pathInsertList[li_i], 1, len(ls_pathInsertList[li_i]) - 1) + '"~r~n, COULD NOT BE ACCESSED OR DOES NOT EXIST!', true)
			catch ( throwable e3)
				// nothing
			end try
			try
				li_filewriterc2 = lnv_filesrv.of_FileWrite (is_win_temp_dir + "\_setPathLog.txt", '~r~nTHE PARTIAL PATH:~r~n"' + mid(ls_pathInsertList[li_i], 1, len(ls_pathInsertList[li_i]) - 1) + '"~r~n, COULD NOT BE ACCESSED OR DOES NOT EXIST!', true)
			catch ( throwable e4)
				// nothing
			end try
		end if
	end if
next

if isnull(ls_path) then ls_path = ""
// u6, v1, mjl, 12/11/19: append the current path to new path:
string ls_oldPath
boolean lb_success
ls_oldPath = space(lul_size)
lb_success = GetEnvironmentVariable( ref ls_what, ref ls_oldPath, ref lul_size)
ls_oldPath = trim(ls_oldPath)
if NOT lb_success then
	ls_oldPath = ""
end if
ls_path += ";" + ls_oldPath

if li_rc > 0 then
	try
		li_filewriterc = lnv_filesrv.of_FileWrite (is_appdir + "\_setPathLog.txt", "~r~nSETTING THE PATH TO:~r~n" + ls_path, true)
	catch ( throwable e5)
		// nothing
	end try
	try
		li_filewriterc2 = lnv_filesrv.of_FileWrite (is_win_temp_dir + "\_setPathLog.txt", "~r~nSETTING THE PATH TO:~r~n" + ls_path, true)
	catch ( throwable e6)
		// nothing
	end try
end if

lb_success = SetEnvironmentVariable(ref ls_what, ref ls_path)
if NOT lb_success then
	if li_rc > 0 then
		try
			li_filewriterc = lnv_filesrv.of_FileWrite (is_appdir + "\_setPathLog.txt", "~r~nFAILED TO SET THE PATH!" + ls_path, true)
		catch ( throwable e7)
			// nothing
		end try
		try
			li_filewriterc2 = lnv_filesrv.of_FileWrite (is_win_temp_dir + "\_setPathLog.txt", "~r~nFAILED TO SET THE PATH!" + ls_path, true)
		catch ( throwable e8)
			// nothing
		end try
	end if
	Messagebox("Connection Error", "Failed to include the Path to the SHARED folder: ~r~n" + &
					"( " + ls_path2Shared + " ).~r~n~r~nPlease contact Credica.", StopSign!)
	lb_valid_path = false
else
	// v1, mjl, 31/07/19: double check if it really worked:
	ls_check_setPath = space(lul_size)
	lb_rc = GetEnvironmentVariable( ref ls_what, ref ls_check_setPath, ref lul_size)
	
	ls_check_setPath = trim(ls_check_setPath)
	if NOT lb_rc then
		ls_check_setPath = ""
	end if

	if pos(ls_check_setPath, ls_path) > 0 then
		lb_valid_path = true // in case it wasn't
		if li_rc > 0 then
			try
				li_filewriterc = lnv_filesrv.of_FileWrite (is_appdir + "\_setPathLog.txt", '~r~n~r~n"OF_SETPATH_RUNTIME()" HAS SUCCEEDED TO SET THE PATH.', true)
			catch ( throwable e9)
				// nothing
			end try
			try
				li_filewriterc2 = lnv_filesrv.of_FileWrite (is_win_temp_dir + "\_setPathLog.txt", '~r~n~r~n"OF_SETPATH_RUNTIME()" HAS SUCCEEDED TO SET THE PATH.', true)
			catch ( throwable e10)
				// nothing
			end try
		end if
	else
		if li_rc > 0 then
			try
				li_filewriterc = lnv_filesrv.of_FileWrite (is_appdir + "\_setPathLog.txt", '~r~n~r~nSOMEHOW "OF_SETPATH_RUNTIME()" HAST FAILED TO SET THE PATH AND THE CURRENT PATH IS:~r~n' + ls_path, true)
			catch ( throwable e11)
				// nothing
			end try
			try
				li_filewriterc2 = lnv_filesrv.of_FileWrite (is_win_temp_dir + "\_setPathLog.txt", '~r~n~r~nSOMEHOW "OF_SETPATH_RUNTIME()" HAST FAILED TO SET THE PATH AND THE CURRENT PATH IS:~r~n' + ls_path, true)
			catch ( throwable e12)
				// nothing
			end try
		end if
		lb_valid_path = false
	end if
end if

if li_rc > 0 then
	li_rc = f_SetFilesrv_no_gnv_app(this, lnv_filesrv, false)
end if

if NOT lb_valid_path then
	messagebox("Error Shared Directory", "Your environment's PATH variable does not contain a valid SHARED directory")
end if
Comment
  1. Miguel Leeuwe
  2. Friday, 6 January 2023 15:06 PM UTC
In the SHARED folder we have the Powerbuilder runtime DLLs. This allows us to be able to run different versions of Powerbuilder on the same PC of our customers, without having to modify the PATH manually on each PC and without having to install any runtime MSI. I know that now we have an XML that could be used to indicate the runtime folder, but in older versions of powerbuilder that's not the case. It's working very well.
  1. Helpful
  1. Miguel Leeuwe
  2. Friday, 6 January 2023 15:12 PM UTC
We use PFCs and when doing things in the constructor of n_cst_appmanager, things work. If at a later stage, we got an "accessibility" dll error when on RDP or Citrix, (so not just Citrix but also Remote Desktop connections).
  1. Helpful
  1. Thomas Rolseth
  2. Thursday, 26 January 2023 17:21 PM UTC
Miguel and Chris, thanks for help with this. Client decided to manually add the java path to their environment variables so I will mark this as resolved. I will keep your suggestions and code samples for reference in case this comes up again.



Tom
  1. Helpful 1
There are no comments made yet.
Thomas Rolseth Accepted Answer Pending Moderation
  1. Wednesday, 4 January 2023 20:19 PM UTC
  2. PowerServer
  3. # 2

Attached is a screenshot of the shortcut.  It kicks off an syc script.  That script copies a bunch of files and at the very end, it calls cms.bat.  I've also attached the batch file (as a text file).  Note that it 1) sets the path to the java runtime, assigns the exe to a variable and then at the end uses the 'start' command to launch the app.  Given this, what's the best approach?

Tom

Attachments (2)
Comment
  1. Miguel Leeuwe
  2. Thursday, 5 January 2023 04:30 AM UTC
Also, adding (now you are overwriting!), "." (current directory) to the path doesn't make any sense, since standard windows behaviour already will always look in the current directory first, before looking at the PATH settings.

Correct me if I'm wrong, which is not impossible!
  1. Helpful
  1. Thomas Rolseth
  2. Thursday, 5 January 2023 15:44 PM UTC
Miguel,

The bat file that I attached is what the client is using in their current PB 2019 version of the app (i.e. not PowerServer) and has been in use for a long time. For the migration to PB 2022 and PowerServer, I modified it by removing the elements you mention (i.e. SET PATH=.) but that didn't help. I'm hoping that running my modified version using Chris's or Logan's suggestions will work. I tried to use RegistrySet() in the app open event but while it worked for me on my workstation, once I deployed it to users RegistrySet() would return -1. I'm assuming it is due to lack of permissions.
  1. Helpful
  1. Miguel Leeuwe
  2. Thursday, 5 January 2023 16:32 PM UTC
Hi Thomas,

I promised to look at my code (whilst on my holiday) and it turns out I'm using windows API functions. Please see my latest answer (within a couple of minutes).
  1. Helpful
There are no comments made yet.
Chris Pollach @Appeon Accepted Answer Pending Moderation
  1. Wednesday, 4 January 2023 03:05 AM UTC
  2. PowerServer
  3. # 3

Hi Tomas;

  I just updated my STD framework to allow any PB App (Native, PowerClient, or PowerServer) so that the PB App can have a .BAT file name passed into it. When the App starts and sees that a .BAT was passed in the command line, it then starts the BAT file and fully exits. This has the effect - especially for PowerClient and PowerServer based Apps - to actually run the full PB App from a BAT file and thus, you can set whatever MS-Windows environment settings that you need for the PB App's execution right from the .BAT file!

Test Case ...

  The current test code is very simple ...

// CIPTEST                                                     // Run from BAT file  ?
IF  as_commandline =  THIS.is_app_batch_file_name     THEN
    RUN ( as_commandline )
    HALT
END IF

  I still need to harden the above prototype code but this test proves that my idea is sound and would allow BAT files to be launched by the PB App itself. Which in turn, would relaunch the PB App again passing in or setting up further runtime settings as required. This especially solves the need for any PC or PS application to have a BAT startup.

    I will have this as a new feature in the next STD Framework for it's first 2023 release. Food for thought!   :-)

HTH

Regards .... Chris

Comment
  1. Chris Pollach @Appeon
  2. Thursday, 5 January 2023 19:28 PM UTC
Hi Everyone;

I hardened the above code last night and even made it more flexible so that any PB Native, PC and / or PS PB App can relaunch itself using a BAT, PowerShell, Short-cut .. or whatever mechanism to setup the PB runtime App's environment for whatever external software support it requires (ie: Java). I am just QA'ing the final code now and then I hope to have a beta version of the STD Framework (first 2023 version) uploaded to the Source Forge website later this week. I will keep you posted when uploaded.

Regards ... Chris
  1. Helpful
  1. Chris Pollach @Appeon
  2. Friday, 6 January 2023 21:40 PM UTC
Hi Everyone;

I have now completed a 2023 Release #1 "beta" version of the STD Framework that now includes the ability to run automatically *any* PB Native, PowerClient or PowerServer based App via a PowerShell, BAT to O/S Short-cut as defined in the "Application Controller" object class - just by filling out only the external name to use to launch the PB App start-up. This also includes support for any M-Code, P-Code, 32 or 64 bit PB Apps.

I tested this new feature by setting up Tesseract, Java, and ImageMagic environments prior to launching the PB Test Apps and these all worked as expected. You can see the new framework code and also see it in action in the latest OrderEntry Demo App "Beta". You can download this version from here: https://sourceforge.net/projects/stdfndclass/files/Applications/PowerBuilder/OrderEntry/Beta/

Enjoy!

Regards ... Chris
  1. Helpful
There are no comments made yet.
Chris Pollach @Appeon Accepted Answer Pending Moderation
  1. Wednesday, 28 December 2022 13:53 PM UTC
  2. PowerServer
  3. # 4

Hi Tomas;

  I know exactly what you're doing via running PB Apps via a .BAT file. It's also necessary for other environmental settings besides just supporting Java. I've used this approach since PB 2.0 to run a plethora of PB apps.

  Unfortunately, both PowerClient & PowerServer are not designed to tag a .BAT file as the app startup. That would require a design change to those features.

  As a workaround though, I wonder if you could try starting a "stub" PS based PB App EXE in which it's only purpose is to Run() the .bat file & then terminate?

 Food for thought.  HTH 

Regards... Chris 

Comment
There are no comments made yet.
Logan Liu @Appeon Accepted Answer Pending Moderation
  1. Wednesday, 28 December 2022 09:45 AM UTC
  2. PowerServer
  3. # 5

Hi Thomas,

Does it work if adjusting your previous shortcut to start your Cloud App EXE (e..g: %appdata%\PBApps\Applications\localhost_salesdemo_cloud\salesdemo_cloud.exe)?

Could you help to provide a small case that shows how did you make this shortcut and adds two folders to the PATH environment variable in the native PB app? 

Regards, Logan

Comment
  1. Logan Liu @Appeon
  2. Thursday, 29 December 2022 09:25 AM UTC
Hi Thomas,

I can confirm that the preload event can not solve this issue. It's by design.

If you don't want to create a shortcut to run the bat to start your app. There is another workaround you can try:

1) Write your start.bat file to set the path and start the app with a specified command line parameter (E.g.: -bat).

2) Upload the start.bat file to external files so it will be downloaded.

3) In the application, run the .bat file to restart your app.

If IsPowerServerApp( ) And Trim(commandline) <> '-bat' Then

  Run('Start.bat')

  Halt Close

End If
  1. Helpful 2
  1. Chris Pollach @Appeon
  2. Thursday, 29 December 2022 13:26 PM UTC
Hi Logan ;

I would even just use the same PB App by using the command line. So if (for example) the command line is empty, the PS app runs the .Bat file & terminates. In the .Bat file, the same PS App is started (for example) "PBApp.exe BAT". If the PS App sees the BAT in the command line, it continues to run normally as it does today. Food for thought. ;-)

Regards ... Chris
  1. Helpful
  1. Chris Pollach @Appeon
  2. Wednesday, 4 January 2023 02:51 AM UTC
Hi Everyone ... see my verified solution in a larger reply.
  1. Helpful
There are no comments made yet.
Miguel Leeuwe Accepted Answer Pending Moderation
  1. Wednesday, 28 December 2022 06:37 AM UTC
  2. PowerServer
  3. # 6

[EDIT] I now realize that you using powerServer and not classic like I do. Maybe that makes some difference. Sorry ...

I use RegistrySet() when opening our applications and it works great. Corrected (see my latest answer. I wasn't using RegistrySet() but a windows API. Sorry for the confusion). I take whatever is in the PATH, and append that to the values I want to add. That way, I'm sure thinks work. In the past(?), I believe the PATH folders would not work if there would be an invalid folder somewhere in it.

When you close the application that setting is gone which is exactly what we want. The set is only for your current instance of the application running.

Also, no need to run as administrator at least not for me. (in any case you could try setting the path for the user and not the local machine).

Later, I'll try to access my code in the office 6:36 AM here.

Comment
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.