It is currently April 27th, 2024, 8:04 pm

Resolving Rainmeter Paths

Get help with installing and using Rainmeter.
User avatar
MerlinTheRed
Rainmeter Sage
Posts: 889
Joined: September 6th, 2011, 6:34 am

Resolving Rainmeter Paths

Post by MerlinTheRed »

I'm looking into a functionality for the Sublime package that lets you open files that are included with the @include statement (and perhaps also other files, like images in imageName= etc.).

For this I ideally need to be able to resolve all the built-in path variables that rainmeter provides:

#PROGRAMDRIVE#
#PROGRAMPATH#
#SETTINGSPATH#
#SKINSPATH#
#PLUGINSPATH#
#ADDONSPATH#

PROGRAMDRIVE and PROGRAMPATH can be resolved from the path of the Rainmeter installation that the user has to set if it's not located in C:\Program Files\Rainmeter. Do you know a way of autodetecting this path (like a registry entry, environment variable etc.)?

SETTINGSPATH is more or less fixed, I suppose, since Rainmeter needs to know where this file is, as it contains the location of the skins path. Is this always in the same location, or is there a difference between Windows XP and later Windows versions? For a portable installation, I think the settings file is located in the Rainmeter folder itself, so I'm guessing a Rainmeter.ini that is in the same location as Rainmeter.exe takes precedence over the AppData one.
This path is not very important for include-locating purposes as people will seldom include a file from this location.

SKINSPATH could be read from Rainmeter.ini theoretically (if there is a very reliable way of locating it). Currently I'm offering the possibility of setting it manually (like the Rainmeter path). If it's not set manually, I'm trying the default locations, depending on the windows version and user name. This should work in the majority of cases, but if I can make it more reliable I'll be happy to do so.

PLUGINSPATH and ADDONSPATH: Are they always inside the Rainmeter program folder? Or have they been moved to "AppData"? I really don't know how to locate them properly, but again it's not that important as it will seldom be used for include paths.

#CURRENTPATH#
#ROOTCONFIGPATH#
#@#
#CURRENTFILE
#CURRENTCONFIG
All those can be derived from the path of the current skin file and SKINSPATH, so they are not a problem.

If somebody could help me with figuring those paths out, I'd be very grateful.
Have more fun creating skins with Sublime Text 2 and the Rainmeter Package!
User avatar
jsmorley
Developer
Posts: 22631
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Resolving Rainmeter Paths

Post by jsmorley »

#PROGRAMPATH#
This can't be reliably detected by any registry entry or environment variable. You are either going to have to have some way for the user to browse for Rainmeter.exe and set that value to some settings file, or use a program written in some language (C++, C#, AutoIt etc.) that can either detect the path of the Rainmeter.exe currently running or query Rainmeter itself for the location using window messages.

#PROGRAMDRIVE#
Can only be obtained once you have #PROGRAMPATH#, by simply parsing the path. I doubt you will ever need this but be careful how you parse it, as it can be a UNC server path like \\ComputerName\SharedVolume\Folder.

#SETTINGSPATH#
This is also tricky. Basically what you have to do is look for Rainmeter.ini in #PROGRAMPATH#. (portable install) If it is found there, that is the path. If not, it will be %APPDATA%\Rainmeter. It can also be obtained by querying Rainmeter with window messages.

#SKINSPATH#
Treacherous. If #PROGRAMPATH# AND #SETTINGSPATH# are the same, (portable install) then it is #PROGRAMPATH#Skins. If not, then it will be %USERPROFILE%\Documents\Rainmeter\Skins. However, this can be overridden by the user with a setting in Rainmeter.ini, so you would have to read that file and parse it to be really sure. The only reliable way to automatically get this in my opinion is to read Rainmeter.ini or use a program that can query Rainmeter with window messages.

#PLUGINSPATH#
#SETTINGSPATH#Plugins

#ADDONSPATH#
#SETTINGSPATH#Addons

I think in a practical sense you have two choices. 1) Have the user browse for / define these values in some dialog you create and save them. 2) Use some program like AutoIt that can query Rainmeter for these values using windows messages and save them. If you want to do 2), I can write you something in a few minutes that can return the values, you just need to tell me what you want to do with them.

Code: Select all

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseUpx=n
#AutoIt3Wrapper_Res_requestedExecutionLevel=asInvoker
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <SendMessage.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <Array.au3>

$RAINMETER_QUERY_WINDOW = WinGetHandle("[CLASS:RainmeterTrayClass]")
$WM_QUERY_RAINMETER = $WM_APP + 1000
$RAINMETER_QUERY_ID_SKINS_PATH = 4101
$RAINMETER_QUERY_ID_SETTINGS_PATH = 4102
$RAINMETER_QUERY_ID_PROGRAM_PATH = 4104
$RAINMETER_QUERY_ID_CONFIG_EDITOR = 4106
$WM_QUERY_RAINMETER_RETURN = ""

Dim $PathsArray[4]

$hGUI = GUICreate("", 1, 1, -1, -1)

If Not ProcessExists("Rainmeter.exe") Then
	MsgBox(16, "GetPath Error", "Rainmeter must be running to use GetPath.")
	Exit
EndIf

GUIRegisterMsg($WM_COPYDATA, "_ReadMessage")

 _SendMessage($RAINMETER_QUERY_WINDOW, $WM_QUERY_RAINMETER, $RAINMETER_QUERY_ID_PROGRAM_PATH, $hGUI)
 $PathsArray[0] = $WM_QUERY_RAINMETER_RETURN

 _SendMessage($RAINMETER_QUERY_WINDOW, $WM_QUERY_RAINMETER, $RAINMETER_QUERY_ID_SETTINGS_PATH, $hGUI)
$PathsArray[1] = $WM_QUERY_RAINMETER_RETURN

 _SendMessage($RAINMETER_QUERY_WINDOW, $WM_QUERY_RAINMETER, $RAINMETER_QUERY_ID_SKINS_PATH, $hGUI)
 $PathsArray[2] = $WM_QUERY_RAINMETER_RETURN

 _SendMessage($RAINMETER_QUERY_WINDOW, $WM_QUERY_RAINMETER, $RAINMETER_QUERY_ID_CONFIG_EDITOR, $hGUI)
 $PathsArray[3] = $WM_QUERY_RAINMETER_RETURN

_ArrayDisplay($PathsArray)


Func _ReadMessage($hWnd, $uiMsg, $wParam, $lParam)

	$pCds = DllStructCreate("dword;dword;ptr", $lParam)
	$pData = DllStructGetData($pCds, 3)
	$pMem = DllStructCreate("wchar[" & DllStructGetData($pCds, 2) & "]", DllStructGetData($pCds, 3))
	$WM_QUERY_RAINMETER_RETURN = DllStructGetData($pMem, 1)

EndFunc ;_ReadMessage
GetPath.zip
You do not have the required permissions to view the files attached to this post.
User avatar
MerlinTheRed
Rainmeter Sage
Posts: 889
Joined: September 6th, 2011, 6:34 am

Re: Resolving Rainmeter Paths

Post by MerlinTheRed »

Thanks for the input. If it isn't too much effort a program I can call with the variable name as command line input that writes the resulting string to stdout and exits would be nice. I can then try to invoke it from python. The problem with this is that Rainmeter would have to be running for this to work, which is not that ideal.

I intend to have a setting the user can set to the path where Rainmeter.exe is located (I think setting one variable after installing the package is not too much to ask of the user), so I don't need to try to detect where it is automatically. Ideally, all the other variables could be retrieved using this information.

Perhaps one could launch a Rainmeter process that doesn't display anything and is used only to query the variable values, then closed again? Probably too much overhead.

I guess it would be most convenient from a user perspective if these values didn't need a running Rainmeter instance if at all possible (perhaps a script crashed your Rainmeter and you want to open the script file by clicking on the path in the Script meter or something). So my approach to getting the values might look something like this:
  • #PROGRAMPATH#: Load the "rainmeter_path" setting, or assume "C:\Program Files\Rainmeter" if not present
  • #PROGRAMDRIVE#: Parse #PROGRAMPATH#
  • #SETTINGSPATH#: Look in '#PROGRAMPATH#. If not found, look in "%APPDATA%\Rainmeter\"
  • #SKINSPATH#: Read setting from "#SETTINGSPATH#Rainmeter.ini". If not specified, check if portable install and use "#PROGRAMPATH#Skins" or "%USERPROFILE%\Documents\Rainmeter\Skins" accordingly (is there a difference between Windows versions if I use this name?)
  • #PLUGINSPATH# and #ADDONSPATH#: derive from #SETTINGSPATH#
I'll have to research a bit about the UNC paths. How do I know how much of the path I need to extract?

EDIT: I wasn't even finished responding and you already uploaded a program ;) Thanks a lot. This might be useful to other people too.
Have more fun creating skins with Sublime Text 2 and the Rainmeter Package!
User avatar
jsmorley
Developer
Posts: 22631
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Resolving Rainmeter Paths

Post by jsmorley »

I would in fact insist that Rainmeter be running and use a program to get the values with window messages. I don't think any other approach is going to be reliable. Even asking the user to browse for / input the settings is open to user error. For instance, there is a "fake" Rainmeter.exe in %APPDATA%\Rainmeter, that is there for backwards compatibility for old addons. This must NOT be selected as %PROGRAMPATH# by the user.

In a practical sense, you only need to set these values one time per user, so asking that Rainmeter be running when you do so is not too much in my opinion.

I would personally go no other way.

On UNC paths, there really is no concept of "drive", so I would really try to stay away from getting / using #PROGRAMDRIVE# at all. For 99.9% of users you just parse up to the ":", but in a UNC environment I guess what you would call the "drive" is up to the first SINGLE "\".
User avatar
jsmorley
Developer
Posts: 22631
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Resolving Rainmeter Paths

Post by jsmorley »

P.S. Writing to STDOUT is tricky and I would not go that way if I could help it. The way AutoIt works is that it actually writes to STDIN of a "child" process (your python presumably) that AutoIt must execute. Then the child process must be written to sit watching STDIN for an incoming stream and capture it. This is not your daddy's DOS environment or a linux terminal session where a common STDOUT/STDIN is shared. Every app has it's own.

Also, only ANSI can be written to STDIN by AutoIt, so there can be issues with paths in other languages.

I would just have the AutoIt write the values to some settings file that you then read.
User avatar
jsmorley
Developer
Posts: 22631
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Resolving Rainmeter Paths

Post by jsmorley »

As to "Documents" in different versions of Windows, I think %USERPROFILE%\My Documents\ should work in all cases. Win7 has an alias to "Documents" that points to "My Documents", so that should be fine.

Windows XP: C:\Documents and Settings\YourName\My Documents\Rainmeter
Windows Vista & Win7: C:\Users\YourName\My Documents\Rainmeter
User avatar
MerlinTheRed
Rainmeter Sage
Posts: 889
Joined: September 6th, 2011, 6:34 am

Re: Resolving Rainmeter Paths

Post by MerlinTheRed »

This is really not an easy decision. Actually I really don't want to distribute any exe files with the package when I think about it. Having to run an exe outside of Sublime is even less elegant. Perhaps I can send WindowMessages with Python itself... Do you have some reference to what window messages Rainmeter supports? If only you could do something like:

Code: Select all

C:\Program Files\Rainmeter> Rainmeter.exe #SKINSPATH#
C:\Users\You\Documents\Rainmeter\Skins\
About the "My Documents" alias: Typing C:\Users\MyName\My Documents\Rainmeter into the explorer address bar doesn't work, so I guess I'll have to get the Windows version and derive it from there. Does Rainmeter correctly handle the fact that you can relocate the "My Documents" folder to somewhere else than the default location? If yes, I'll have to get some registry value to get it correctly.

UNC paths: I'll just ignore them and fail silently

EDIT: If I want to do WindowMessages in python, I'd need to include the pywin32 library, which is a little much. I've started to write a little c program that does what I want, however I've got problems with figuring out how to send window messages correctly. This is what I have right now:

Code: Select all

#include <windows.h>
#include <iostream>

using namespace std;

int main(int argc, char const *argv[])
{
	cout<<"Trying to get the Rainmeter window handle"<<endl;
	LPCTSTR windowClass = "RainmeterTrayClass";
	HWND hWin = FindWindow(windowClass, NULL);

	if(!hWin) return 1;

	cout<<"Handle found"<<endl;
	UINT msg = WM_APP + 1000;
	WPARAM wp = 4102; //settings path
	LPARAM lp = 0;

	LRESULT res = SendMessage(hWin, msg, wp, lp);

	if(!res) return 2;

	cout<<"Return value received"<<endl;
	cout<<"int value: "<<long(res)<<endl;
	cout<<"string value: "<<(LPTSTR*)res<<endl;

	return 0;
}
The Rainmeter Window is retrieved correctly, and the result of the SendMessage function is not 0. However, I don't really know what to do with the LRESULT, as it could be anything. Can I even get values this way or do I need to create a callback function that listens to the result being sent back via another WindowMessage from Rainmeter?
Have more fun creating skins with Sublime Text 2 and the Rainmeter Package!
User avatar
jsmorley
Developer
Posts: 22631
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Resolving Rainmeter Paths

Post by jsmorley »

I'm no C person, so I can't help too much, but yeah, you have to have your program listen for incoming WM_COPYDATA messages sent to to its window handle. When you use SendMessage to send to Rainmeter, you need to send a handle to a window created in your program along with it, and Rainmeter will send back the answer to that window handle. The answer is not a "string" as such, but a structure you will need to take apart.

http://rainmeter.net/cms/Developers-API-WindowMessage
User avatar
Mordasius
Posts: 1173
Joined: January 22nd, 2011, 4:23 pm
Location: GMT +8

Re: Resolving Rainmeter Paths

Post by Mordasius »

MerlinTheRed wrote:About the "My Documents" alias: Typing C:\Users\MyName\My Documents\Rainmeter into the explorer address bar doesn't work, so I guess I'll have to get the Windows version and derive it from there. Does Rainmeter correctly handle the fact that you can relocate the "My Documents" folder to somewhere else than the default location? If yes, I'll have to get some registry value to get it correctly.
%USERPROFILE%\My Documents\ doesn't work properly if you move frequently written to folders like My Documents from your SSD C: drive using a Hard link. Rainmeter doesn't seem to have any problems with a relocated 'My Documents'.

The Registry value at HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders\Personal points to the relocated 'My Documents' on my machine.
User avatar
jsmorley
Developer
Posts: 22631
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Resolving Rainmeter Paths

Post by jsmorley »

In general, trying to "figure out" these Rainmeter paths is an exercise in futility in my opinion. You can get it right probably 90% or more of the time, but I have no use at all for a program that works 90% of the time. It's crap.

You are going to have to either ask the user where these paths are, and hope they get it right, or query Rainmeter for it with some kind of program. Any language that can deal with window messages should work fine.