Putting the screen working area into environment variables

I’ve found that if I type these two commands into powershell:

Add-Type -AssemblyName System.Windows.Forms

The response I get on my system is as follows:

BitsPerPixel : 32
Bounds       : {X=0,Y=0,Width=3840,Height=1080}
DeviceName   : \\.\DISPLAY1
Primary      : True
WorkingArea  : {X=0,Y=46,Width=3840,Height=1034}

BitsPerPixel : 32
Bounds       : {X=3840,Y=-158,Width=1920,Height=1200}
DeviceName   : \\.\DISPLAY2
Primary      : False
WorkingArea  : {X=3840,Y=-158,Width=1920,Height=1200}

What I would like to do is to write a script which puts the four numbers representing the working area of the primary display (0,46,3840,1080) in my example into 4 different environment variables that I could query from another application. Can anyone give me a suggestion about how this can be done. (I have no experience writting powershell scripts). Thanks ~Paul

Oops, I meant to say the four numbers (0,46,3840,1034) representing the working area. (I typed the last number incorrectly).

A simple approach would like below,

Add-Type -AssemblyName System.Windows.Forms
$DeviceName = 'DISPLAY2'
$ScreenDetail = [System.Windows.Forms.Screen]::AllScreens
$RequiredDisplay = $ScreenDetail | Where-Object -FilterScript {$_.DeviceName -match $DeviceName}
[System.Environment]::SetEnvironmentVariable('WorkingAreaX',$RequireDisplay.WorkingArea.X, [System.EnvironmentVariableTarget]::Machine)

# similar code for Y, Width and Height

Since the Environment variable scope is set to Machine, these variable wont be available in the same process and child processes. For that Process scope has to be used. If both are required, then Environment variable for each will have to be created per scope.

1 Like

I think you meant to say $DisplayName = ‘DISPLAY1’ since I wanted the primary display.
Also I think you have an extra single quote between Machine and the closing parenthesis. (It gave me an error message on that.)
With those changes, I executed the script in the powershell by typing .\WorkingArea.ps1
Then I typed “dir env:” and it displayed all the environment variables but not the 4 new ones that I had defined in my script. I also didn’t see them using the system applet in the windows control panel. I also tried changing Machine, to Process or User but in all those cases I did not see the environment variables when I typed “dir env:”. Do you have any guesses of what I am doing wrong?

My full script is shown below:

Add-Type -AssemblyName System.Windows.Forms
$DeviceName = 'DISPLAY1'
$ScreenDetail = [System.Windows.Forms.Screen]::AllScreens
$RequiredDisplay = $ScreenDetail | Where-Object -FilterScript {$_.DeviceName -match $DeviceName}
[System.Environment]::SetEnvironmentVariable('screenX',$RequireDisplay.WorkingArea.X, [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('screenY',$RequireDisplay.WorkingArea.Y, [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('screenW',$RequireDisplay.WorkingArea.Width, [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('screenH',$RequireDisplay.WorkingArea.Height, [System.EnvironmentVariableTarget]::Machine)

Corrected by removing unwanted single quote.

If Machine scope is used, then You should be opening new PowerShell process to see them.
And the DeviceName variable value can be changed according to your preference.

If you need the environment variable in the same shell, then use Process scope.

This script simply did nothing. No environment variables were created. No errors, warnings, or anything. After much head scratching I found the problem. You defined the variable $RequiredDisplay but then when you referenced it to create the environment variable you spelled it differently ($RequreDisplay). Unfortunately I copied your script exactly. Once I fixed that problem, the script worked correctly. Despite that, I’m sure this would have taken me even longer without your help, so I thank you for that! ~Paul

1 Like

Now that I got the script working I find that it is useless for my intended purpose. I was planning on calling the script from another programming environment that doesn’t have the ability to get this display information on its own. I thought I could run a powershell script from this environment just like I can run batch scripts. But this won’t work because Windows doesn’t allow user powershell scripts to run by default like it does for batch scripts. I found I had to enter a command such as:

set-executionpolicy unrestricted -Scope CurrentUser

in a powershell administrator window before it would allow the script to run. I can’t really expect the users of my program to enter such an arcane command. Is there any workaround to this problem, or will I have to write a program in a language such as C# that would have access to the information I want?

I recommend to run

PowerShell /?

on a command line to learn how to run a PowerShell session. I recommend to take special notice of the parameter -ExecutionPolicy !!! :wink:

I was calling powershell using a command such as:

powershell -inputformat none -file scriptName.ps1

I tried changing this to:

powershell -inputformat none -file scriptName.ps1 -ExecutionPolicy Unrestricted

However the error message I got was the same:

File E:\mcode\misc\workingArea.ps1 cannot be loaded because running scripts is disabled on this system. For more  
information, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170. 
    + CategoryInfo          : SecurityError: (:) [], ParentContainsErrorRecordException 
    + FullyQualifiedErrorId : UnauthorizedAccess 

Am I using this parameter correctly?
This error went away when I changed the execution policy for a powershell window (as administrator).


If you read the error carefully, it says that to understand a lil more, its better to read Get-Help about_Execution_Policies. This will give you more help for sure.

I have read all that, but it doesn’t seem to suggest that the powershell will be useful to me. I’m not sure why the command I mentioned above that specified the ExecutionPolicy as unrestricted won’t run, but the help text suggests that this may have been overridden by a group policy. In any case I can’t expect my users to modify some arcane setting in the group policy editor or to run powershell with an equally arcane command to allow the script to run. It’s frustrating though, because you seem to know something that I don’t that may mean that the powershell isn’t as useless as it seems. But I don’t know what it is.

How about trying -ExecutionPolicy Bypass or you have already tried it ?

Are you sure there is no group policy? If you have a group policy users without administrative rights cannot overwrite this setting.

A bit off topic but you can try a batch file:

for /F "skip=1" %%a in ('wmic desktopmonitor get ScreenHeight /value') do set %%a
for /F "skip=1" %%a in ('wmic desktopmonitor get ScreenWidth /value') do set %%a

Olaf - you are probably correct in that the group policy is not allowing unsigned powershell scripts from running. But I didn’t set such a policy which implies that the policy is there by default or at least that it is very easy to create such a policy accidently perhaps when other software is installed. But if it happened to me, then it would probably happen to some of my users … which makes the powershell script a non-starter. There is no reason to force my users to use the group policy editor (most of whom won’t even know what that is) for something as benign as writing a text file to a folder known to have write permission. (I gave up on the environment variable idea, and I’m now using a simple text file.). It sounds like I might have to create a .exe file using C# or other language to create this text file. (I’ve never used C# so this isn’t going to be trivial for me.)

tonyd - no that is not off topic. A batch file would be a lot easier for me because having an exe file in a distributation that normally only contains source code will be problematic. Batch is a lot better because it is a text file and I know there are no default group policies preventing batch files from running. But your batch file just gives me the screen dimensions which are already easily available in my programming environment. What I need are the the coordinates of the working area … i.e. the portion of the screen that is not occupied by the taskbar. Is there any way to get this information using a batch file? It would be wonderful if there was a way to do that.

They are not there by default and they won’t be created accidently. Is your environment an enterprise environment? Are you the only administrator in this environment? What is it actually what you’re trying to do - not the environment variable thing … the big picture? I’d suspect we are having an

They aren’t allowed to do that anyway. Well … they shouldn’t be allowed.

My first thought on this topic was to get what you needed from WMI, hence the batch and WMIC. You may want to enter a blank “wmic /?” to see if there is anything you can query to get what you want. Sorry for not being any help.

Can you share details about your application that might help get an answer, programming language etc?

I got sidetracked from this issue, but I’m back to it now and I hope olaf and kvparson haven’t lost interest. BTW I tried “Bypass” instead of “Unrestricted” as kvparson suggested but I got the same error. Perhaps this is an “XY problem” as olaf suggests so I will give you the wider scope if the issue:
I’m using a public domain scientific programming environment called GNU Octave (basically a clone of the proprietary Matlab language). From Octave, one can find the screen resolution but not the screen working area (i.e. where you can put stuff avoiding the task bar), so I wrote a function to return the screen working area. The function first looks for a power shell script called workingArea.ps1 and if it doesn’t find it, it creates the workingArea.ps1 file with the following contents:

Add-Type -AssemblyName System.Windows.Forms
$Scr = [System.Windows.Forms.Screen]::AllScreens
$Di = $Scr | Where-Object -FilterScript {$_.DeviceName -match 'DISPLAY1'}
$Di.WorkingArea.X, $Di.WorkingArea.Y,
$Di.WorkingArea.Width, $Di.WorkingArea.Height, $Di.Bounds.Height |
Out-File -FilePath e:\Octave\workingArea.txt -Encoding ASCII

(Actually the file path of the workingArea.txt will change depending on the location of the Octave script).

Then it executes the powershell script, reads the results from the workingArea.txt file and returns that result. The full Octave code (workingArea.m) follows:

function p = workingArea()
% returns [x y width height] of the primary display useable are not including the taskbar
% where: x is the distance in pixels from the left edge
%        y is the distance in pixels from the bottom edge

  f2 = which(mfilename);  % path and file name of this function
  f2(end)='';             % remove .m extension
  f1 = [f2 'ps1'];        % name of powershell script
  f2 = [f2 'txt'];        % name of powershell script results file
  if ~exist(f1)           % create powershell script file if it doesn't already exist
    f = fopen(f1,'w');
    fprintf(f,'Add-Type -AssemblyName System.Windows.Forms\n');
    fprintf(f,'$Scr = [System.Windows.Forms.Screen]::AllScreens\n');
    fprintf(f,'$Di = $Scr | Where-Object -FilterScript {$_.DeviceName -match ''DISPLAY1''}\n');
    fprintf(f,'$Di.WorkingArea.X, $Di.WorkingArea.Y,\n');
    fprintf(f,'$Di.WorkingArea.Width, $Di.WorkingArea.Height, $Di.Bounds.Height |\n');
    fprintf(f,'Out-File -FilePath %s -Encoding ASCII\n',f2);
  system(['powershell -inputformat none -file ' f1 ' -ExecutionPolicy Unrestricted']); % execute powershell script
  p = textread(f2,'%n');      % read script results
  p(2) = p(5) - p(2) - p(4);  % convert top edge coordinates to bottom edge
  p = p(1:4)';                % return first four elements as row vector

When I run this script from Octave, it returns the following error message:

File E:\Octave\workingArea.ps1 cannot be loaded because running scripts is disabled on this system. For more
information, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170.
    + CategoryInfo          : SecurityError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : UnauthorizedAccess

Although the error message seems to suggest that windows is not allowing the powershell script to run, I also thought it possible that somehow Octave was not allowing it. So I tried running the script in Matlab and I found that it worked! I know I am not imagining this because I tested it with several different taskbar sizes and positions and the script always returned the correct working area. But the frustrating thing is I tried it the next day and I found that it no longer worked, and in fact returned the exact same error message I received from Octave. (I’m not sure if I had done a reboot between those sessions.) Despite the initial success, I never got it to work again. I also tried it on another computer and also got the same error message that running scripts is disabled.

So to summarize … I know this script has potential because I have seen it work, but I have no idea what is usually preventing the powershell script from executing.

Do you think this is a windows issue or merely an Octave/Matlab issue? If it’s the later, perhaps I could get help on the Matlab or Octave forums, but I doubt I would find someone there with this specialized knowledge.

Thanks for your help and patience.

Thanks for letting us know the detailed flow.

Just to know if its Octave related, can you open one command prompt as admin and run the powershell.exe ... and see if its throwing the same error, if so it won’t be specific to Octave.

I tried running the powershell as administrator, cd’d to the folder with my script and typed “./workingArea.ps1” and it gave me the same error message I reported before that the script can’t load because running scripts are disabled. I also tried powershell ISE (both regular and as administrator) which looks like a fancier version of the command window, but in both instances it gave the same error message. So I guess my problem is with windows and not with Octave. I don’t know if that is good news or not.