PLS-Recovery Directory has "Monitor" subfolders - no files recovered at startup

James B

Member²
This was mentioned in another topic here, but that question was focused on a different cause for the issue.

PL/SQL Developer suddenly stopped restoring previously opened files at startup - as if it had lost the recovery files in the PLS-Recovery directory. When I investigated, I found that the files were there, but were under sub-folders named for monitor configurations - e.g. PLS-Recovery\Monitors-2560-1920 and PLS-Recovery\Monitors-1680-1920-1680 (matching my home and work environments, respectively). There were also restore files in the root PLS-Recovery directory, last modified 3/24/2016 (which matches a first, odd startup, where only one of my instance's recovery files were available).

When did these "monitor" subfolders start appearing? I haven't run any updates in some time (current version is 11.0.5.1775). Why are they there in the first place? I can't think of any reason why I would want different session recoveries betweeen home and work... The work I'm doing is the same, regardless of what my current monitor configurations are (if I take my laptop to a conference room and have no monitor, will I have no recovery session because I now only have one monitor??).

Why is this happening? And how do I stop it?

 
You can copy recovery files to the appropriate Monitor configuration folder and continue work, but this is awkward.

I support this - "monitor folder" is bad implementation!
 
Last edited:
There's a set of config files in each of the folders (including the root PL/SQL Developer temp directory) called desktop.... they are completely different in each folder, so I can't just merge the folders :(
 
I'd like to bump this topic, as I am also very frustrated by this new recovery file implementation. I'm puzzled by the design choice and can't see the intended benefit of storing recovery files specific to monitor resolution.

The downside (failure to restore) feels like a pretty serious bug. I am usually running multiple PL/SQL Developer instances on my main development desktop, but I frequently use a combination of Citrix and Remote Desktop to log into it from elsewhere. I remote into that desktop from a variety of computers, from home and from work, and the monitor resolutions are constantly changing. I haven't crashed too many times since I upgraded to v11, but already it seems like every time I launch a new instance from a new configuration, I am presented with a different set of recovery files to choose from.

So crash recovery has effectively stopped working for me with version 11, at least not without some manual manipulation of the location of those files.

I haven't seen Marco weigh in; I'm very curious about what benefits this design might be providing that I'm not seeing, and specifically I'd like to know whether Allround officially considers this a bug and plans a fix.

Thanks!
 
So I just noticed today that there's a checkbox to disable this in Preferences -> User Interface -> Options: "Save Layout Per Multi Monitor Setup". Testing that now to see if it behaves as expected.

If that's the case, it would've been nice if someone at AA would've pointed it out by now :(
 
Last edited:
So here's how I got around this :mad:

1. Download Hermann Schinagl's Link Shell Extension: http://schinagl.priv.at/nt/hardlinkshellext/linkshellextension.html#download
2. Follow the installation instructions, including the VC redistributables (mind whether installing 64- or 32-bit)
3. Close all instances of PL/SQL Developer
4. Go to your Roaming PLS-Recovery directory: C:\Users\\AppData\Roaming\PLSQL Developer\PLS-Recovery
5. Create a folder called Monitors-All (name not important)
6. For each Monitor-nnnn-nnnn folder you have, move all the files in that folder to your new Monitors-All folder.
IMPORTANT: If prompted to overwrite files, DO NOT overwrite them... follow the steps below before moving them
7. Copy the name of the folder for each monitor - e.g. Monitors-2560-1920
8. Delete that folder
9. Right-drag the Monitors-All folder and "drop" it in the same folder
10. In the popup menu, choose Drop Here -> Junction
11. Rename the new junction folder to the name of the monitors folder you deleted (e.g. Monitors-2560-1920)
12. Repeat for each Monitors sub-folder


A junction is basically a pointer to a folder... this essentially causes all monitor sub-folders to point to the same location for storing crash recovery files.

IF YOU ARE WARNED TO OVERWRITE FILES IN STEP 6:

1. Open a windows command prompt to the folder you're copying from
2. Rename all files using the following command:
ren *.* 123456.*
where 123456 is whatever name you choose (PL/SQL Developer uses all-number titles by default)
3. Open the .cfg file in that directory for edit
4. Find all instances of the original name and replace with the new name. For example, in my files, the lines:
RecoveryFile=C:\Users\\AppData\Roaming\PLSQL Developer\PLS-Recovery\Monitors-1680-1920-1680\desktop.001
RecoveryFile=C:\Users\\AppData\Roaming\PLSQL Developer\PLS-Recovery\Monitors-1680-1920-1680\desktop.002

became
RecoveryFile=C:\Users\\AppData\Roaming\PLSQL Developer\PLS-Recovery\Monitors-1680-1920-1680\123456.001
RecoveryFile=C:\Users\\AppData\Roaming\PLSQL Developer\PLS-Recovery\Monitors-1680-1920-1680\123456.002

5. Return to step 6, above, and retry the file move


Now restart PL/SQL Developer... you should see all of your crashed sessions. And you should continue to see all of them regardless of which monitor configuration you're working on. Let me know if that's not the case for anyone.

Sheesh. :mad:

 
Wow, that's one of the uglier end-user workarounds I've seen, and it's crazy that it's necessary, but I admire your effort and appreciate you documenting it.

However, I'm looking at my own system and, in addition to recovery files in a "Monitors-1280-1280" subfolder (I have only one), I also have recovery files in the root of PLS-Recovery. Both locations have files which are currently being used -- their mod dates show they are being touched every couple minutes.

I don't see how your process accounts for recovery files which are not in a Monitors subfolder. Is my setup unusual? Apparently there is some condition under which the root is still used -- if only we knew how to force this to happen in all cases!

Incidentally, the only conflicting .cfg between the two locations is the one in your example: "desktop". Any idea of the significance of this name, versus all the others which are numbered? Is that just the first?

FYI I'm running version 11.0.6.1796 64-bit, my main desktop configuration is dual 1280x1024 monitors (which I assume maps to the "Monitors-1280-1280" folder?), but I also Remote into this machine several times a day from a laptop which has a single 1600x900 display, and also on occasion from home using a couple of other resolutions.

So unfortunately it looks like your fix won't work for me. I would need to spend some time launching and killing sessions and logging in at various monitor configurations in order to divine which folders are used when, but even if I understood it, I can't imagine it works to create a junction between a folder and its' parent -- wouldn't that set up a dangerous loop condition?

Ugh...
 
I had configurations in the root folder, as well... trying to remember what I did with those. It may have just worked because I moved everything to the Monitors-All subfolder. It may have also had something perversely to do with the fact that I set "save layout per multi monitor setup" to "false" (in Preferences->User Interface->Options)... those are the only things I've done beyond what's listed below.

I did a search in all of my preference files and the registry to see if I set something to point to that folder explicitly, but I can't find anything. But I absolutely did have files in the root by default. If I had to guess, I'd say they're related to you logging in remotely and only having one monitor being displayed.

Take a look at the following registry keys... in each of them, the Instance.Monitors:nnnn:nnnn subfolder is an entry for my monitor configurations. See if there's anything promising in the parent/root.

Most likely candidate: HKEY_CURRENT_USER\Software\Allround Automations\PL/SQL Developer\Desktop\Instance.Monitors:2560:1920... look under the root Desktop key, as well

Second Runner Up: HKEY_CURRENT_USER\Software\Allround Automations\PL/SQL Developer\Position\Instance.Monitors:2560:1920

Let me know if you find anything - sorry for the slow response.

PS: The junction wouldn't have to be under the root folder - I just did that for convenience. But you could put the junction under, say, C:\Users\\AppData\Roaming\PLSQL Developer\AADrivesMeNuts and drop both the monitor subfolders and their parent into THAT folder as junctions ;) Make sense?

And yes, this is definitely one of the ugliest workarounds I've seen, as well :(

.
 
Damn. You got me... I unplugged my second monitor and opened up PLSD, and the session wrote to the root directory :cry: And you're right, junctioning subdirectories like that puts the entire universe in danger :( This will still work, but we're now officially into HPIMA territory :crazy:

So the link shell extension has a limitation - you can't junction in a way that creates a recursive folder structure. It's an artificial limitation though - doing so is perfectly legit from the filesystem's perspective (albeit a really, really bad idea). I had to pull out Mark Russinovich's junction tool. It's really just a front-end for fsutil, but I've long forgotten the commands to create junctions.

https://technet.microsoft.com/en-us/sysinternals/bb896768.aspx

So. Here's what you have to do:
1. Back up everything in the PLS-Recovery folder
2. Rename PLS-Recovery to Monitors-All
3. Create a junction: PLSQL Developer\PLS-Recovery should point to PLSQL Developer\Monitors-All
junction.exe "PLS-Recovery" "C:\Users\\AppData\Roaming\PLSQL Developer\Monitors-All"
4. cd PLS-Recovery
5. Create a junction in the PLS-Recovery, pointing again to Monitors-All. Name it for your monitor configuration
junction.exe "Monitors-2560-1920" "C:\Users\\AppData\Roaming\PLSQL Developer\Monitors-All"
6. Repeat for every monitor configuration folder you have.


Now you should be able to expand the PLS_Recovery file and see not only the recovery files, but also a subdirectory for your monitor config with the recovery files.

Do be warned: This isn't harmful per se, but you'll notice you can keep drilling down into monitor folders until windows explorer has a thermal runaway incident in a positive feedback loop. But other than that, everything will be fine :D

I tested, and PLSD shows and writes to the same set of recovery files whether I'm on one monitor or two.

Sheesh.

.
 
Wow, you said creating a recursive folder structure is a "really, really bad idea" and then you went ahead and documented it for me! :eek:

When you tried it yourself, did you leave it that way? How long?

Any application, utility or system service which tries to scan the folder hierarchy (for instance to do a virus scan or recursive copy) is likely to get stuck in an infinite loop, which sounds like a great way to cause myself all kinds of future pain.

My company uses Roaming User profiles, and my understanding is that the \AppData\Roaming directory gets copied up to the server and then down to any machine I log into. This could potentially break my ability to log in, to my machine or any others -- NO THANKS!

(Why isn't Allround addressing this???)
 
Believe it or not, I haven't had any issues - and it's been six months, now? Most apps (e.g. our antivirus software) are smart enough not to follow junctions, as they know the underlying directory is controlled/monitored elsewhere. I don't know if my company keeps a roaming profile or not, so I can't say for sure it wouldn't break, but if this directory had been left in its original location in \AppData\Local, the question would be moot **hem**Marco**hem**. While I'm dreaming, I'd also like a pony.

But in any case, it's worked fine for me. I still hate it :mad:
 
I have been encountering this issue for years, and am finally beginning to understand exactly what creates it. Thank you, all. I have been able to witness how starting a remote desktop suddenly causes my sessions to start saving in a different folder. I can see, from people describing the same problem, how any access with a different monitor configuration must be doing the same.

The Link Shell solution made me nervous, so I have created an AHK script as an alternate solution. (AHK, or AutoHotKey, is a Windows scripting language. If you have it on your system, you can double-click any AHK script to run it.) What this one does is keep the root and all "Monitors" folders in sync. It copies the newest .cfg files, overwriting old ones. It also attempts to detect when "window" files (e.g. desktop.common.001 if you have an unsaved window in your desktop=common session) are no longer in use, and delete them.

This script needs to be located in your PLS-Recovery directly, and I HIGHLY recommend backing up the directory before running it. You also have to run multiple times until you get no output, because files are moving around as it operates. I can simplify that, but I am eager for the possibility of having other testers, so here is version 1.0. (At the moment it also does a poor job of handing "crash" files because it cannot detect when they are consumed (recovered or ignored) versus not yet having been backed up, so it just keeps copying them around forever. I'm sure there are other things I could be doing better, too. I will probably put a new version up within a month.)

Code:
#NoEnv                       ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn                      ; Enable warnings to assist with detecting common errors.
SendMode Input               ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
#SingleInstance force

; ==================== IMPORTANT NOTES =============================================================

; PL/SQL Developer has an amazing "desktop=" feature which allows you to autosave your workspace(s) and re-load all open windows on startup.
; However, this feature breaks when you have multiple monitor configurations, e.g. when remoting in from a laptop, or possibly saving the workspaces on a shared drive.
; In this situation, PL/SQL Developer puts single-monitor setups in the root folder, and multi-monitor setups in a folder named for the resolution of those desktops.
; So if you have a workspace open on one machine and then remote in, suddenly the autosave is happening to a different folder.

; Given that this is happening, but that I would prefer my workspaces to operate seamlessly regardless of how I am accessing them,
; I want something that can automatically detect when any ".cfg" file is created or updated in any of the folders and keep the others at the newest version.

; Also, PL/SQL Developer, when working in a single directory, will delete temporary files that are no longer in use.  This script *would* keep those deleted files alive
; by continually copying them into other directories and back again.  This won't really harm anything, since they won't be connected to any workspaces.  So, to reduce
; clutter I have also added a check to detect when they are not in use.  So for the root directory and each Monitors directory, this script will loop through non-cfg
; files, then parse the corresponding "desktop.*.cfg" file to see whether the filename appears in any of them.  If not, it will delete it.  Note that this means ANY FILE
; in this folder starting with "desktop." (and not ending in ".cfg" or ".ini") or ending in ".0xx" will be deleted if it does not belong to a PL/SQL Developer workspace.

; Sometimes we get "crash" files which are handled poorly by this script.  I don't know how to detect when these ones are no longer necessary.

; EXTRA-IMPORTANT NOTE: This script is not very thoroughly vetted yet, and it deletes/overwrites files.  Thus, *back up* the entire PLS-Recovery directory before *each* use.
; At some point, I'd love to have this run automatically, maybe several times per hour.  As it stands, it should be run prior to opening workspaces and probably only then.
; Also, it needs to be run several times until there is no output, since it's moving and deleting files in a very specific order. Probably best to do this while no
; PL/SQL Developer windows are open so that files aren't being modified by both at the same time.

; ==================== GLOBAL VARIABLES ============================================================

; Turn on or off the copy/delete functions (this was useful during testing, but I might as well leave it in)
global do_copy = true
global do_delete = true

; Turn on or off detailed debug messages for the copy/delete functions
global msg_copy = false
global msg_delete = false

; ==================== MAIN PROGRAM ================================================================

file1 =
file2 =
file2dir =
cfgfile =
parsestr =
FileList = ; List files first to prevent errors as we operate on the files we're checking
FileList2 =
DirList =

Loop, Files, *, D
DirList = %DirList%%A_LoopFileName%`n
Loop, Parse, DirList, `n
{
	if (InStr(A_LoopField, "Monitors") = 1)
	{
		file2dir = %A_LoopField%
		if msg_copy or msg_delete
			MsgBox, Found directory: %file2dir%
	}
}

; For each .cfg in the root directory, check each folder and check if the file exists.  If it doesn't, copy it.  If it does, compare the dates.
Loop, Files, *
FileList = %FileList%%A_LoopFileName%`n
Loop, Parse, FileList, `n
{
	file1 = %A_LoopField%
	if (InStr(A_LoopField, ".cfg") >= 1)
	{
		if msg_copy
			MsgBox Found config file: %file1%
		Loop, Parse, DirList, `n
		{
			FileList2 =
			if (InStr(A_LoopField, "Monitors") = 1)
			{
				if msg_copy
					MsgBox Checking folder: %A_LoopField%
				file2dir = %A_LoopField%
				file2 = %file2dir%/%file1%
				CompareFiles(file1, file2)
			}
		}
		
	} else {
		if (InStr(A_LoopField, "desktop.") = 1 and InStr(A_LoopField, ".ini") = 0)
		{
			if msg_delete
				MsgBox, Found non-config %file1%
			cfgfile := Substr(file1, 1, Instr(file1, ".", false, -1)) . "cfg"
			ParseFile(cfgfile, file1, file1)
		}
	}
	
}

; Then, for each file in each of the folders, check if it exists in the root.
Loop, Parse, DirList, `n
{
	FileList2 =
	if (InStr(A_LoopField, "Monitors") = 1)
	{
		file2dir = %A_LoopField%
		;MsgBox, Found dir %file2dir%
		Loop, Files, %A_LoopField%\*
		FileList2 = %FileList2%%A_LoopFileName%`n
		Loop, Parse, FileList2, `n
		{
			FileList =
			file2 = %A_LoopField%
			if (InStr(A_LoopField, ".cfg") >= 1)
			{
				if msg_copy
					MsgBox Found config file: %file2dir%/%file2%.  Checking root.
				CompareFiles(file2dir . "/" . file2, file2)
			} else {
				if (InStr(A_LoopField, "desktop.") = 1 and InStr(A_LoopField, ".ini") = 0) or (RegExMatch(A_LoopField, "[.][0-9][0-9][0-9]$") > 0)
				{
					if msg_delete
						MsgBox, Found non-config %file2%
					cfgfile := file2dir . "/" . Substr(file2, 1, Instr(file2, ".", false, -1)) . "cfg"
					did_delete := ParseFile(cfgfile, file2, file2dir . "/" . file2)
					if not %did_delete%
					{
						if msg_delete
							MsgBox, Kept %file2%; comparing to root
						CompareFiles(file2dir . "/" . file2, file2)
					}
					
				}
			}
		}
	}
}
return

; ==================== FUNCTIONS ===================================================================

ParseFile(file, str, delfile)
{
	did_delete = false
	FileRead, filestr, %A_ScriptDir%/%file%
	if ErrorLevel
		MsgBox, Could not open file %file%: %A_LastError%
	else
	{
		if msg_delete
			MsgBox, Parsing %file% for string "%str%"
		if (InStr(filestr, str) < 1)
		{
			MsgBox, Deleting %delfile%
			if do_delete
			{
				FileDelete, %delfile%
				did_delete = true
			}
		}
		else
		{
			if msg_delete
				MsgBox, File is in use by a workspace.  Keeping.
		}
	}
	return did_delete
}

OverwriteFile(srcFile, destFile)
{
	MsgBox, Copying %srcFile% to %destFile%
	if do_copy
		FileCopy, %srcFile%, %destFile%, 1
}

CompareFiles(file1, file2)
{
	; Compares the system date on file1 and file2 and updates the older with the most recent version
	if msg_copy
		MsgBox, Comparing %file1% with %file2%
	IfNotExist, %file2%
		OverwriteFile(file1, file2)
	else
	{
		FileGetTime, time1, %file1%
		FileGetTime, time2, %file2%
		if time1 - time2 > 0
			OverwriteFile(file1, file2)
		if time2 - time1
			OverwriteFile(file2, file1)
	}
}
 
Last edited:
Back
Top