It is currently April 16th, 2024, 4:58 pm

Why does this ini crash Rainmeter on startup?

Get help with creating, editing & fixing problems with skins
User avatar
balala
Rainmeter Sage
Posts: 16142
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: Why does this ini crash Rainmeter on startup?

Post by balala »

Cariboudjan wrote: December 3rd, 2020, 7:28 pm It's alright. I modified it. I have it almost going now. I just need to find a way to remove everything after the last backslash without crashing Rainmeter again.

eg. Change C:\Windows\Explorer.exe to C:\Windows\

I used this before, but this crashes Rainmeter for a reason I still don't fully understand. I assume because when the skin first loads, it contains no value, and therefor there is no "\" to find or anything after it. Still though. Why does this crash Rainmeter? Why doesn't it just fail?
Screenshot 2020-12-03 123130.png



Tried changing it to this. It doesn't crash Rainmeter, but it also doesn't do anything in Rainmeter, even though it works in the regex101 test.
Screenshot 2020-12-03 124409.png
To be honest working with regular expressions in many cases is, well, let's say, weird. I'm not always sure what to do, this time for instance. Sorry, but probably someone else will have to help you here.
User avatar
Yincognito
Rainmeter Sage
Posts: 7118
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Why does this ini crash Rainmeter on startup?

Post by Yincognito »

Cariboudjan wrote: December 3rd, 2020, 7:28 pm It's alright. I modified it. I have it almost going now. I just need to find a way to remove everything after the last backslash without crashing Rainmeter again.

eg. Change C:\Windows\Explorer.exe to C:\Windows\

I used this before, but this crashes Rainmeter for a reason I still don't fully understand. I assume because when the skin first loads, it contains no value, and therefor there is no "\" to find or anything after it. Still though. Why does this crash Rainmeter? Why doesn't it just fail?
Screenshot 2020-12-03 123130.png



Tried changing it to this. It doesn't crash Rainmeter, but it also doesn't do anything in Rainmeter, even though it works in the regex101 test.
Screenshot 2020-12-03 124409.png
I encountered this substitution crash a LOT of times in my and other's skins, due to the fact that I extensively work with regex. What you need to know is that you can ALWAYS avoid the crash, by either:
- avoiding the capture - technically, ANY round brackets enclosed pattern, not just "captures" - from returning an empty string, like jsmorley suggested
OR
- if the first workaround isn't suited, use a "marker string" - basically, a character or characters highly unlikely to appear in the original string, used to "delimit" your pattern - after the colon in your substitution, so that the outcome will never be empty, followed by easily removing the marker string from the result in the final substitution once you're done with your process.

Example of the latter method (disregard the unknown quantities below, what matters is the approach):

Code: Select all

[Variables]
Cleaner="(?:^[^[\x200B]].*|.*[^[\x200B]]$)":"","(?:^[\x200B]|[\x200B]$)":"","(?:^\\\d+|\\\d+$)":""
...
...
[SomeMeasure]
...
Substitute="^((?:\d+,){#Level#}).*$":"[\x200B]\1[\x200B]","#Cleaner#"
So, above I used the [\x200B], aka the Zero Width Space as a "marker" to "enclose" the \1 result in order to prevent the outcome from ever being "empty", followed by removing both the "not found" occurences of the capture (i.e. the ones missing the beginning or ending markers) as well as the markers themselves (and the possible \1 leftovers) in the Cleaner substitution variable.

Personally, I prefer the 2nd variant in my regexes, as I don't have to ever bother with an empty result from a "capture", but although the approach is quite simple in nature, I realize that it might not suit every scenario or might not be for everyone. I already have the variable that I just copy paste in the skins I need, so it's just a matter of adding the markers in the particular substitution and a few clicks.

P.S. In the example above, the marker is a single character, but it can be two different ones, if you want to differentiate the beginning and ending of the match (e.g. < and >, etc.)
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
Cariboudjan
Posts: 268
Joined: May 12th, 2019, 8:55 am

Re: Why does this ini crash Rainmeter on startup?

Post by Cariboudjan »

And that's why you're in the credits, Yin.

Thanks for the tip.

In your example, what does #Level# represent? Could you show me in my Substitute below how your delimiter would work?

I did manage to find a simple albeit limited solution by using "#LastFocusedIM#":"" and the end of the regex.

So the entire line would be:
Substitute="ExecutablePath=(.*)\n.*":"\1","\n":"","(?<=exe)(?s)(.*$)":"","#LastFocusedIM#":""

Since I already have the image name "Photoshop.exe", "Notepad++.exe", etc. ready to use.

The problem with this solution is that an application name like Notepad++.exe registers the "++" in the regular expression, and confuses the substitution.

So instead of getting
ProcessLoc=C:\Program Files (x86)\Notepad++\
ProcessPath=C:\Program Files (x86)\Notepad++\notepad++.exe

I end up with
ProcessLoc=C:\Program Files (x86)\Notepad++\notepad++.exe
ProcessPath=C:\Program Files (x86)\Notepad++\notepad++.exenotepad++.exe

But since #LastFocusedIM# is a variable, I can't just escape those characters. Unless there's some way to have a single substitution ignore regular expression when RegExpSubstitute=1.

Which is why I used the method I used, but can't use, because it causes a crash. :-(

So here's what I have: (This doesn't crash Rainmeter)

In the actual script, LastFocusedIM is the currently focused app. Here it is set to "notepad++.exe". If you do not have notepad++ open/installed, change it to another app like "Photoshop.exe", open it, then refresh the skin for it to run. (though it would need to be notepad++ to see the bug I'm talking about)

Should run. Haven't tested.

Code: Select all

[Variables]
LastFocusedIM=notepad++.exe
ProcessPath=
ProcessLoc=

[Meter]
Meter=String
StringAlign=CenterCenter
SolidColor=255,55,55
W=500
H=100
Text=Click here
LeftMouseUpAction=[!CommandMeasure RunGetPath "Run"]
DynamicVariables=1

[RunGetPath]
Measure=Plugin
Plugin=RunCommand
Parameter=wmic process where "name='#LastFocusedIM#'" get ProcessID, ExecutablePath /FORMAT:LIST
State=Hide
OutputType=ANSI
OutputFile=
RegExpSubstitute=1
Substitute="ExecutablePath=(.*)\n.*":"\1","\n":"","(?<=exe)(?s)(.*$)":"","#LastFocusedIM#":""
FinishAction=[!WriteKeyValue Variables ProcessPath "[#CURRENTSECTION#]#LastFocusedIM#"][!WriteKeyValue Variables ProcessLoc "[#CURRENTSECTION#]"][!SetVariable ProcessPath "[#CURRENTSECTION#]#LastFocusedIM#"][!SetVariable ProcessLoc "[#CURRENTSECTION#]"][!SetOption Meter Text "#*ProcessPath*#"][!UpdateMeter Meter][!Redraw]
DynamicVariables=1
User avatar
Yincognito
Rainmeter Sage
Posts: 7118
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Why does this ini crash Rainmeter on startup?

Post by Yincognito »

Cariboudjan wrote: December 5th, 2020, 12:54 amIn your example, what does #Level# represent? Could you show me in my Substitute below how your delimiter would work?
Well, I just pasted things from one of my skins, that's why I said you should "disregard the unknown quantities". Instead of "^((?:\d+,){#Level#}).*$" it could be anything containing a "capture" pattern. #Level# is just a variable in the skin I posted the substitute from, it doesn't mean anything in this context, and neither does the "capture". I should have simplified things and just put a ^foo(.*)bar$ or something along those lines instead, but I assumed you'd get the idea.

That being said, I believe you're overcomplicating things. Unless your actual scenario involves precisely your posted code, I think this would do (no delimiters or anything "fancy" needed):

Code: Select all

[Variables]
RunGetPathOutput="#CRLF##CRLF#ExecutablePath=C:\Program Files\Notepad++\notepad++.exe#CRLF#Name=notepad++.exe#CRLF#ProcessId=5400#CRLF##CRLF##CRLF#"
LastFocusedIM=notepad++.exe
ProcessPath=
ProcessLoc=

[Meter]
Meter=String
SolidColor=255,55,55
W=500
H=100
Text=Click here
LeftMouseUpAction=[!CommandMeasure RunGetPath "Run"]
DynamicVariables=1

[RunGetPath]
Measure=Plugin
Plugin=RunCommand
Parameter=wmic process where "name='#LastFocusedIM#'" get Name,ExecutablePath,ProcessID /FORMAT:LIST
State=Hide
OutputType=ANSI
OutputFile=
RegExpSubstitute=1
Substitute="(?siU)^.*$":"#RunGetPathOutput#","^(?siU).*\RExecutablePath=(.*\R).*$":"\1","[^\\]*\R$":""
FinishAction=[!WriteKeyValue Variables ProcessPath "[#CURRENTSECTION#]#LastFocusedIM#"][!WriteKeyValue Variables ProcessLoc "[#CURRENTSECTION#]"][!SetVariable ProcessPath "[#CURRENTSECTION#]#LastFocusedIM#"][!SetVariable ProcessLoc "[#CURRENTSECTION#]"][!SetOption Meter Text "#*ProcessPath*#"][!UpdateMeter Meter][!Redraw]
DynamicVariables=1
Bear in mind that for me, WMIC returns an empty string for the ExecutablePath in the case of Notepad++, I have no idea why (but you should keep the scenario in mind as a possibility if you want to distribute your work). Thus, I had to "simulate" a proper WMIC output, in the #RunGetPathOutput# variable. Feel free to remove the variable and/or the "(?siU)^.*$":"#RunGetPathOutput#", part of the substitution, if for you the WMIC output contains a non-empty ExecutablePath.

This code returns the expected ...Loc and ...Path variables. \R means any new line "pattern" (i.e. \n, \r\n, etc.) and the keys of the approach are:
- include the \R new line character(s) when capturing the ExecutablePath output, so that the capture is never empty (that's why we won't need any other tricks to avoid Rainmeter's crash)
- no need to use lookbehind structures (which are subject to possible crashes if not properly avoiding empty results) like you did, just look for the last \ character in the full path (since we match NOT the \ characters) and delete stuff from there till the end (\R is not really necessary in the last substitution part, but I left it there so that it's clearer how the string looks like - feel free to remove it if you like)

P.S. If I think about it, I could consider the \R used in this way as a "delimiter", or a "marker" as well, since apart from capturing just until the line ends, its only other purpose is to "fill" the potentially empty capture with "something", to prevent the crash.
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth