My objective was to use JNI to access functions from kernel32.dll. As you can see below, I was doing pretty bad. I wrote down the whole procedure in the answer.
Kernel32.java :
package tn.kernel;
public final class Kernel32 {
public static boolean loadKernel32(){
System.loadLibrary("kernel32");
return true;
}
public static native boolean K32EnumProcesses(int[] pProcessIds, int cb, int[] pBytesReturned);
}
MainClass.java :
package tn.kernel;
public class MainClass {
public static void main(String[] args) {
System.out.println("Program started.");
if(Kernel32.loadKernel32())
System.out.println("Kernel32.dll loaded.");
int n = 2000;
int[] procs = new int[n];
int ls = Integer.SIZE;
int[] rs = new int[1];
if(Kernel32.K32EnumProcesses(procs, ls * n, rs)){
System.out.println("Success");
}
System.out.println("Done.");
}
}
OUTPUT :
Program started.
Kernel32.dll loaded.
Exception in thread "main" java.lang.UnsatisfiedLinkError: tn.kernel.Kernel32.K32EnumProcesses([II[I)Z
at tn.kernel.Kernel32.K32EnumProcesses(Native Method)
at tn.kernel.MainClass.main(MainClass.java:15)
This the syntax for EnumProcesses :
BOOL WINAPI EnumProcesses(
_Out_ DWORD *pProcessIds,
_In_ DWORD cb,
_Out_ DWORD *pBytesReturned
);
If PSAPI_VERSION is 2 or greater, this function is defined as K32EnumProcesses in Psapi.h and exported in Kernel32.lib and Kernel32.dll. If PSAPI_VERSION is 1, this function is defined as EnumProcesses in Psapi.h and exported in Psapi.lib and Psapi.dll as a wrapper that calls K32EnumProcesses. Source : msnd.microsoft.com
I tried with both K32EnumProcesses and EnumProcesses. Same results.
Creating a 64 bits Dynamic-Link Library for Windows
Prerequisites: Visual Studio
1/ Create a Visual Studio C++ project (ex: dllexample)
• Select “Win32 Consol Application”
Select “DLL”
Select “Empty project”
2/ In “Solution explorer” right-click on “Header Files” > “Add” > “New Item…” > choose a name (ex: dllexample.h) > “Add”
• Define the headers of your functions in “dllexample.h” this way:
__declspec(dllexport) <type> funcName(parameters…);
…
3/ In “Solution explorer” right-click on “Source Files” > “Add” > “New Item…” > choose a name (ex: dllexample.cpp) > “Add”
• Use:
#include “dllexample.h”
• Define the body of your functions (from the “dllexample.h” header file) in the “dllexample.cpp” source file:
<type> funcName(parameters…){
//body instructions
}
• In the upper toolbar select “x64”
• Select “Build” > “Build Solution”
4/ Done
• You can find “dllexample.dll” and “dllexample.lib” in “projects/dllexample/x64/Debug”
• You can find “dllexample.h” in “projects/dllexample/dllexample”
Calling a 64 bits DLL file (ex: dllexample.dll) from another 64 bits DLL or executable file on Windows
Prerequisites: “dllexample.dll”, “dllexample.lib” and “dllexample.h” or a precise functions description or guide and Visual Studio
1/ Create a Visual Studio C++ project (ex: dllcall)
• Select “Win32 Consol Application”
Select “DLL” to create a DLL file, “Consol Application” to create an executable file
Select “Empty project”
2/ Copy “dllexample.dll”, “dllexample.lib” and “dllexample.h” to “projects/dllcall/dllcall”
3/ In “Solution explorer” right-click on “Header Files” > “Add” > “Existing Item…” > select ”dllexample.h” > “Add”
• If you’re making a DLL file, create a new header file (ex: dllcall.h) in which you define the headers of your functions this way:
__declspec(dllexport) <type> funcName(parameters…);
…
4/ In “Solution explorer” right-click on “Source Files” > “Add” > “New Item…” > choose a name (ex: dllcall.cpp) > “Add”
• Use:
#include “dllexample.h”
• If you’re creating a DLL file, use:
#include “dllcall.h”
And then define the body of the functions (from the “dllcall.h” header file) in the “dllcall.cpp” source file. At the same time you can call functions from “dllexample.h”:
<type> funcName(parameters…){
//body instructions
}
• In the upper toolbar select “x64”
• In “Solution explorer”, right-click on “dllcall” > “Properties” > “Linker” > “Input” > “Additional Dependencies” > “Edit” > add “dllexample.lib” (this option will be set only for the x64 debugger of the current Visual Studio project)
• Select “Build” > “Build Solution” to generate the DLL and the Import Library (.lib) files, “Run” to generate the executable file and test it
5/ Done
• You can find “dllcall.dll” and “dllcall.lib” or “dllcall.exe” in “projects/dllcall/x64/Debug”
• You can find “dllcall.h” in “projects/dllcall/dllcall”
Calling a 64 bits DLL file (ex: dllexample.dll) from a 64 bits Java program through JNI
Prerequisites: “dllexample.dll”, “dllexample.lib” and “dllexample.h” or a precise functions description or guide. Visual Studio, Eclipse with JNI plugin
1/ Create an Eclipse Java project (ex: dlltest)
2/ Create a class (ex: my.package.JNIClass)
• Define methods headers as close as the functions definitions in “dllexample.h” using keywords:
public static final native <type> funcName(parameters..);
…
• Run the project to generate “JNIClass.class”
3/ Open Command Line in folder “workspace/dllcall/src”
• Generate “my_package_JNIClass.h” header file by running command:
javah my.package.JNIClass
4/ Create a 64 bits DLL file (ex: dllcall.dll) that calls “dllexample.dll” and includes “my_package_JNIClass.h”
• In the “dllcall.cpp” source file, define the bodies of functions that are defined in the “my_package_JNIClass.h” header file
• “my_package_JNIClass.h” includes “jni.h”, to make it work, you must go to “Solution explorer” in Visual Studio and right-click on “Properties” > “Configuration Properties” > “C/C++” > “General” > “Additional Include Directories” > add the 64 bits “java/include” and “java/include/win32” paths (this option will be set only for the x64 debugger of the current Visual Studio project)
5/ Copy “dllcall.dll” and “dllexample.dll” to “workspace/dllcall/src”
• In “Package explorer”, right-click on “dlltest” > “Properties” > “Java Build Path” > “Source” > expand “my/package/src” > select “Native Library Location” > “Edit” > add “my/package/src” as location path
• Import the DLL files in “JNIClass.java” using:
static {
System.loadLibrary(“dllexample”);
System.loadLibrary(“dllcall”);
}
6/ If the 64 bits JRE is not selected, then go to “Run” > “Run Configurations” > “JRE” > “Alternate JREs” > “Installed JREs” > put the 64 bits Java JDK directory (Should be something like: “C:\Program Files\Java \jdk”, while 32 bits Java JDK can be found in the “Program Files (x86)” folder
7/ Done
• Now you can use the methods you defined in step 2
Related
Followings are my requirement:
Check if the Java is installed
Check if it's installed in a custom directory
if it's, then save the directory path in a variable
Otherwise detect the version and save the standard path in a variable
Below is the code that detects the version and save the standard path to a variable
Problems with my code:
If both 32 and 64 bit is installed it detects the both ..My aim is to detect only 64 bit in case both is installed.
if DirExists(ExpandConstant('{pf32}\java\')) then Is this what i can use to detect custom directory?
I don't think the above code is the right way to find custom directory of java. if the user installed in a different folder other than Java. the other problem is if we uninstall java it doesn't delete the folder java/JRE.
I'm using #TLama's code from Need help on Inno Setup script - issue in check the jre install
[Code]
#define MinJRE "1.7.0"
#define WebJRE "http://www.oracle.com/technetwork/java/javase/downloads/jre6downloads-1902815.html"
function IsJREInstalled: Boolean;
var
JREVersion: string;
JREPath:string
begin
{ read JRE version }
Result := RegQueryStringValue(HKLM32, 'Software\JavaSoft\Java Runtime Environment',
'CurrentVersion', JREVersion);
MsgBox('JAVA 32 bit detected.', mbInformation, MB_OK);
JREPath := 'C:\Program Files (x86)\Java'
{ if the previous reading failed and we're on 64-bit Windows, try to read }
{ the JRE version from WOW node }
if not Result and IsWin64 then
Result := RegQueryStringValue(HKLM64, 'Software\JavaSoft\Java Runtime Environment',
'CurrentVersion', JREVersion);
MsgBox('JAVA 64 bit detected.', mbInformation, MB_OK);
JREPath := 'C:\Program Files\Java'
{ if the JRE version was read, check if it's at least the minimum one }
if Result then
Result := CompareStr(JREVersion, '{#MinJRE}') >= 0;
end;
function InitializeSetup: Boolean;
var
ErrorCode: Integer;
begin
Result := True;
{ check if JRE is installed; if not, then... }
if not IsJREInstalled then
begin
{ show a message box and let user to choose if they want to download JRE; }
{ if so, go to its download site and exit setup; continue otherwise }
if MsgBox('Java is required. Do you want to download it now ?',
mbConfirmation, MB_YESNO) = IDYES then
begin
Result := False;
ShellExec('', '{#WebJRE}', '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode);
end;
end;
end;
JRE installation path is stored in registry like this:
[HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment]
"CurrentVersion"="1.8"
[HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.8]
"JavaHome"="C:\\Program Files\\Java\\jre1.8.0_172"
You can retrieve the installation path of the latest version (with 64-bit preference) using a code like this:
const
JavaKey = 'SOFTWARE\JavaSoft\Java Runtime Environment';
function GetJavaVersionAndPath(
RootKey: Integer; var JavaVersion: string; var JavaPath: string): Boolean;
var
JREVersion: string;
begin
Result :=
RegQueryStringValue(RootKey, JavaKey, 'CurrentVersion', JavaVersion) and
RegQueryStringValue(RootKey, JavaKey + '\' + JavaVersion, 'JavaHome', JavaPath);
end;
{ ... }
var
JavaVersion: string;
JavaPath: string;
begin
if GetJavaVersionAndPath(HKLM64, JavaVersion, JavaPath) then
begin
Log(Format('Java %s 64-bit found in "%s"', [JavaVersion, JavaPath]));
end
else
if GetJavaVersionAndPath(HKLM32, JavaVersion, JavaPath) then
begin
Log(Format('Java %s 32-bit found in "%s"', [JavaVersion, JavaPath]));
end
else
begin
Log('No Java found');
end;
end;
Depending on which JRE you have installed, that registry data may not be there. A more generic solution is probably preferable.
I wanted something I could use across multiple Inno Setup projects, so I wrote a DLL for detecting Java details (home directory, etc.):
https://github.com/Bill-Stewart/JavaInfo
Download from here: https://github.com/Bill-Stewart/JavaInfo/releases
The download includes a sample Inno Setup .iss script that demonstrates how to use the DLL functions.
I'm programming a mod.
Here's the code:
package net.minecraft.client.gui;
import java.io.IOException;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.audio.SoundHandler;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.util.ResourceLocation;
public class GuiButton extends Gui {
public static final ResourceLocation buttonTextures = new ResourceLocation("textures/gui/widgets.png");
As you can see, there's the variable buttonTextures and its resource location is "textures/gui/widgets.png". And I also have a second one and what I want to do is to change the buttonTextures with this second Java file when I execute it.
I just need to change the resource location of buttonTextures to my own path, but by an other Java file. Maybe it can be done by re-writing the code with the second script, I really have no clue.
What I'm making:
A PAYDAY 2 Mod for Minecraft. And of course, I'm starting with the menu. So I have two main menus. To switch to my menu, there's a button which you have to press. When you do that, it changes its texture. But the problem is, that it can't change the button textures to textures/gui/PAYDAY2widgets.png, because the variable is final.
As you can see, the first file is called GuiButton.
I made a copy of that file, called it GuiPAYDAY2Button.java, and set the path to my path to the texture (textures/gui/PAYDAY2widgets.png).
Now if I add that to the | (I changed the variables from GuiButton to GuiPAYDAY2Button, but then the game crashes after switching to the PAYDAY2 menu) | GuiPAYDAY2MainMenu.java file (the copy of the GuiMainMenu.javafile).
I also changed GuiButton to GuiPAYDAY2Button(in the GuiPAYDAY2MainMenu.java file), so it's directed to that file.
As I was talking about the crashing, this is the crash-report:
---- Minecraft Crash Report ---- // I feel sad now :(
Time: 8.4.15 21:58 Description: Rendering screen
java.lang.ClassCastException: net.minecraft.client.gui.GuiPAYDAY2Button cannot be cast to net.minecraft.client.gui.GuiButton
at net.minecraft.client.gui.GuiScreen.drawScreen(GuiScreen.java:99)
at net.minecraft.client.gui.GuiPAYDAY2MainMenu.drawScreen(GuiPAYDAY2MainMenu.java:453)
at net.minecraft.client.renderer.EntityRenderer.updateCameraAndRender(EntityRenderer.java:1167)
at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:1127)
at net.minecraft.client.Minecraft.run(Minecraft.java:410)
at net.minecraft.client.main.Main.main(Main.java:114)
at Start.main(Start.java:11)
A detailed walkthrough of the error, its code path and all known details is as follows:
-- Head --
Stacktrace:
at net.minecraft.client.gui.GuiScreen.drawScreen(GuiScreen.java:99)
at net.minecraft.client.gui.GuiPAYDAY2MainMenu.drawScreen(GuiPAYDAY2MainMenu.java:453)
-- Screen render details --
Details:
Screen name: net.minecraft.client.gui.GuiPAYDAY2MainMenu
Mouse location: Scaled: (202, 11). Absolute: (405, 456)
Screen size: Scaled: (427, 240). Absolute: (854, 480). Scale factor of 2
Stacktrace:
at net.minecraft.client.renderer.EntityRenderer.updateCameraAndRender(EntityRenderer.java:1167)
at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:1127)
at net.minecraft.client.Minecraft.run(Minecraft.java:410)
at net.minecraft.client.main.Main.main(Main.java:114)
at Start.main(Start.java:11)
-- System Details --
Details:
Minecraft Version: 1.8
Operating System: Windows 7 (amd64) version 6.1
Java Version: 1.8.0_31, Oracle Corporation
Java VM Version: Java HotSpot(TM) 64-Bit Server VM (mixed mode), Oracle Corporation
Memory: 707532608 bytes (674 MB) / 1038876672 bytes (990 MB) up to 1038876672 bytes (990 MB)
JVM Flags: 3 total; -Xincgc -Xmx1024M -Xms1024M
IntCache: cache: 0, tcache: 0, allocated: 0, tallocated: 0
Launched Version: mcp
LWJGL: 2.9.1
OpenGL: GeForce GTX 460 v2/PCIe/SSE2 GL version 4.5.0 NVIDIA 347.25, NVIDIA Corporation
GL Caps: Using GL 1.3 multitexturing. Using GL 1.3 texture combiners. Using framebuffer objects because OpenGL 3.0 is supported and separate blending is supported. Shaders are available because OpenGL 2.1 is supported. VBOs are available because OpenGL 1.5 is supported.
Using VBOs: No
Is Modded: Very likely; Jar signature invalidated
Type: Client (map_client.txt)
Resource Packs: []
Current Language: English (US)
Profiler Position: N/A (disabled)
UPDATE
I made a video some hours ago and it's a showcase to this mod. Maybe you'll understand what I want..: https://www.youtube.com/watch?v=ocHT7LdNBYY
Ok, I made the extending script look like this:
package net.minecraft.client.gui;
import net.minecraft.util.ResourceLocation;
public class GuiButtonTexureChange extends GuiButton {
public static ResourceLocation buttonTextures = new ResourceLocation("textures/gui/PAYDAY2widgets.png");
// Now this part is needed to be here (otherwise it throws errors):
public GuiButtonTexureChange(int buttonId, int x, int y, int widthIn, int heightIn, String buttonText) {
super(buttonId, x, y, widthIn, heightIn, buttonText); // TODO Auto-generated constructor stub
}
}
If you cannot modify some Java code (for instance it is part of a library), you can still overwrite any Java class you like.
Create another source folder (speaking in terms of Eclipse here), and inside that the exact package and file as the class you want to edit. In your case it should be package net.minecraft.client.gui and file GuiButton.java.
Now, if your own file comes first in classpath, you have successfully overwritten a class. All the other code of the library still works as it used to.
To ensure that your own code comes first in classpath when working with Eclipse:
Project properties -> Java Build Path -> Order and Export
It is probably wise to copy the existing code (of GuiButton) into your own file to have the same starting point, and then do your modifications on the code.
Depending on your security manager settings, you could change the path string at runtime via reflection.
Example :
static void setFinalStatic(final Field field, final Object instance, final Object newValue) throws Exception {
field.setAccessible(true);
final Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL & ~Modifier.PRIVATE);
field.set(instance, newValue);
}
public final static void main(String[] args) throws Exception {
String path = "initialPath";
final char[] after = "sneakyPath".toCharArray();
setFinalStatic(path.getClass().getDeclaredField("value"), path, after);
setFinalStatic(path.getClass().getDeclaredField("offset"), path, 0);
setFinalStatic(path.getClass().getDeclaredField("count"), path, after.length);
System.out.println(path);
}
I am trying to reduce the number of methods that are generated by Google proto-buf and one of the alternatives is to use proto-buf nano. However I found no documentation on how to use it. Except the package link, I can't find anything on how to generate java files from proto files using nano.
So the question is straight-forward: how to use google proto nano in order to generate java classes from proto files and how to use them in a project?
Looking at the main protobuf compiler source code:
#include <google/protobuf/compiler/javanano/javanano_generator.h>
....
int main(int argc, char* argv[]) {
google::protobuf::compiler::CommandLineInterface cli;
cli.AllowPlugins("protoc-");
...
// Proto2 JavaNano
google::protobuf::compiler::javanano::JavaNanoGenerator javanano_generator;
cli.RegisterGenerator("--javanano_out", &javanano_generator,
"Generate Java source file nano runtime.");
return cli.Run(argc, argv);
}
looking at protobuf's Readme
Nano Generator options
java_package -> <file-name>|<package-name>
java_outer_classname -> <file-name>|<package-name>
java_multiple_files -> true or false
java_nano_generate_has -> true or false [DEPRECATED]
optional_field_style -> default or accessors
enum_style -> c or java
To use nano protobufs outside of Android repo:
Link with the generated jar file <protobuf-root>java/target/protobuf-java-2.3.0-nano.jar.
Invoke with --javanano_out, e.g.:
./protoc '--javanano_out=java_package=src/proto/simple-data.proto|my_package,java_outer_classname=src/proto/simple-data.proto|OuterName:.' src/proto/simple-data.proto
To use nano protobufs within the Android repo:
Set 'LOCAL_PROTOC_OPTIMIZE_TYPE := nano' in your local .mk file. When building a Java library or an app (package) target, the build
system will add the Java nano runtime library to the
LOCAL_STATIC_JAVA_LIBRARIES variable, so you don't need to.
Set 'LOCAL_PROTO_JAVA_OUTPUT_PARAMS := ...' in your local .mk file for any command-line options you need. Use commas to join multiple
options. In the nano flavor only, whitespace surrounding the option
names and values are ignored, so you can use backslash-newline or
'+=' to structure your make files nicely.
The options will be applied to all proto files in LOCAL_SRC_FILES when you build a Java library or package. In case different options
are needed for different proto files, build separate Java libraries
and reference them in your main target. Note: you should make sure
that, for each separate target, all proto files imported from any
proto file in LOCAL_SRC_FILES are included in LOCAL_SRC_FILES. This
is because the generator has to assume that the imported files are
built using the same options, and will generate code that reference
the fields and enums from the imported files using the same code
style.
Hint: 'include $(CLEAR_VARS)' resets all LOCAL_ variables, including the two above.
Simple nano example from https://android.googlesource.com/platform/external/protobuf/+/master/src/google/protobuf/.
unittest_simple_nano.proto
package protobuf_unittest_import;
option java_package = "com.google.protobuf.nano";
// Explicit outer classname to suppress legacy info.
option java_outer_classname = "UnittestSimpleNano";
message SimpleMessageNano {
message NestedMessage {
optional int32 bb = 1;
}
enum NestedEnum {
FOO = 1;
BAR = 2;
BAZ = 3;
}
optional int32 d = 1 [default = 123];
optional NestedMessage nested_msg = 2;
optional NestedEnum default_nested_enum = 3 [default = BAZ];
}
Command line
./protoc '--javanano_out=java_package=google/protobuf/unittest_simple_nano.proto|com.google.protobuf.nano,java_outer_classname=google/protobuf/unittest_simple_nano.proto|UnittestSimpleNano:target/generated-test-sources' google/protobuf/unittest_simple_nano.proto
Test extracted from NanoTest.java
public void testSimpleMessageNano() throws Exception {
SimpleMessageNano msg = new SimpleMessageNano();
assertEquals(123, msg.d);
assertEquals(null, msg.nestedMsg);
assertEquals(SimpleMessageNano.BAZ, msg.defaultNestedEnum);
msg.d = 456;
assertEquals(456, msg.d);
SimpleMessageNano.NestedMessage nestedMsg = new SimpleMessageNano.NestedMessage();
nestedMsg.bb = 2;
assertEquals(2, nestedMsg.bb);
msg.nestedMsg = nestedMsg;
assertEquals(2, msg.nestedMsg.bb);
msg.defaultNestedEnum = SimpleMessageNano.BAR;
assertEquals(SimpleMessageNano.BAR, msg.defaultNestedEnum);
byte [] result = MessageNano.toByteArray(msg);
int msgSerializedSize = msg.getSerializedSize();
//System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length);
assertTrue(msgSerializedSize == 9);
assertEquals(result.length, msgSerializedSize);
SimpleMessageNano newMsg = SimpleMessageNano.parseFrom(result);
assertEquals(456, newMsg.d);
assertEquals(2, msg.nestedMsg.bb);
assertEquals(SimpleMessageNano.BAR, msg.defaultNestedEnum);
}
There are a lot of test cases in the same class, and looking at the project pom you can find a maven-antrun-plugin configuration to generate that test resource classes
<!-- java nano -->
<exec executable="../src/protoc">
<arg value="--javanano_out=java_package=google/protobuf/unittest_import_nano.proto|com.google.protobuf.nano,java_outer_classname=google/protobuf/unittest_import_nano.proto|UnittestImportNano:target/generated-test-sources" />
<arg value="--proto_path=../src" />
<arg value="--proto_path=src/test/java" />
<arg value="../src/google/protobuf/unittest_nano.proto" />
<arg value="../src/google/protobuf/unittest_simple_nano.proto" />
<arg value="../src/google/protobuf/unittest_stringutf8_nano.proto" />
<arg value="../src/google/protobuf/unittest_recursive_nano.proto" />
<arg value="../src/google/protobuf/unittest_import_nano.proto" />
<arg value="../src/google/protobuf/unittest_enum_multiplejava_nano.proto" />
<arg value="../src/google/protobuf/unittest_multiple_nano.proto" />
</exec>
Hope this helps.
If I execute set PATH=%PATH%;C:\\Something\\bin from the command line (cmd.exe) and then execute echo %PATH% I see this string added to the PATH. If I close and open the command line, that new string is not in PATH.
How can I update PATH permanently from the command line for all processes in the future, not just for the current process?
I don't want to do this by going to System Properties → Advanced → Environment variables and update PATH there.
This command must be executed from a Java application (please see my other question).
You can use:
setx PATH "%PATH%;C:\\Something\\bin"
However, setx will truncate the stored string to 1024 bytes, potentially corrupting the PATH.
/M will change the PATH in HKEY_LOCAL_MACHINE instead of HKEY_CURRENT_USER. In other words, a system variable, instead of the user's. For example:
SETX /M PATH "%PATH%;C:\your path with spaces"
You have to keep in mind, the new PATH is not visible in your current cmd.exe.
But if you look in the registry or on a new cmd.exe with "set p" you can see the new value.
The documentation on how to do this can be found on MSDN. The key extract is this:
To programmatically add or modify system environment variables, add them to the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment registry key, then broadcast a WM_SETTINGCHANGE message with lParam set to the string "Environment". This allows applications, such as the shell, to pick up your updates.
Note that your application will need elevated admin rights in order to be able to modify this key.
You indicate in the comments that you would be happy to modify just the per-user environment. Do this by editing the values in HKEY_CURRENT_USER\Environment. As before, make sure that you broadcast a WM_SETTINGCHANGE message.
You should be able to do this from your Java application easily enough using the JNI registry classes.
I caution against using the command
setx PATH "%PATH%;C:\Something\bin"
to modify the PATH variable because of a "feature" of its implementation. On many (most?) installations these days the variable will be lengthy - setx will truncate the stored string to 1024 bytes, potentially corrupting the PATH (see the discussion here).
(I signed up specifically to flag this issue, and so lack the site reputation to directly comment on the answer posted on May 2 '12. My thanks to beresfordt for adding such a comment)
This Python-script[*] does exactly that:
"""
Show/Modify/Append registry env-vars (ie `PATH`) and notify Windows-applications to pickup changes.
First attempts to show/modify HKEY_LOCAL_MACHINE (all users), and
if not accessible due to admin-rights missing, fails-back
to HKEY_CURRENT_USER.
Write and Delete operations do not proceed to user-tree if all-users succeed.
Syntax:
{prog} : Print all env-vars.
{prog} VARNAME : Print value for VARNAME.
{prog} VARNAME VALUE : Set VALUE for VARNAME.
{prog} +VARNAME VALUE : Append VALUE in VARNAME delimeted with ';' (i.e. used for `PATH`).
{prog} -VARNAME : Delete env-var value.
Note that the current command-window will not be affected,
changes would apply only for new command-windows.
"""
import winreg
import os, sys, win32gui, win32con
def reg_key(tree, path, varname):
return '%s\%s:%s' % (tree, path, varname)
def reg_entry(tree, path, varname, value):
return '%s=%s' % (reg_key(tree, path, varname), value)
def query_value(key, varname):
value, type_id = winreg.QueryValueEx(key, varname)
return value
def yield_all_entries(tree, path, key):
i = 0
while True:
try:
n,v,t = winreg.EnumValue(key, i)
yield reg_entry(tree, path, n, v)
i += 1
except OSError:
break ## Expected, this is how iteration ends.
def notify_windows(action, tree, path, varname, value):
win32gui.SendMessage(win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment')
print("---%s %s" % (action, reg_entry(tree, path, varname, value)), file=sys.stderr)
def manage_registry_env_vars(varname=None, value=None):
reg_keys = [
('HKEY_LOCAL_MACHINE', r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'),
('HKEY_CURRENT_USER', r'Environment'),
]
for (tree_name, path) in reg_keys:
tree = eval('winreg.%s'%tree_name)
try:
with winreg.ConnectRegistry(None, tree) as reg:
with winreg.OpenKey(reg, path, 0, winreg.KEY_ALL_ACCESS) as key:
if not varname:
for regent in yield_all_entries(tree_name, path, key):
print(regent)
else:
if not value:
if varname.startswith('-'):
varname = varname[1:]
value = query_value(key, varname)
winreg.DeleteValue(key, varname)
notify_windows("Deleted", tree_name, path, varname, value)
break ## Don't propagate into user-tree.
else:
value = query_value(key, varname)
print(reg_entry(tree_name, path, varname, value))
else:
if varname.startswith('+'):
varname = varname[1:]
value = query_value(key, varname) + ';' + value
winreg.SetValueEx(key, varname, 0, winreg.REG_EXPAND_SZ, value)
notify_windows("Updated", tree_name, path, varname, value)
break ## Don't propagate into user-tree.
except PermissionError as ex:
print("!!!Cannot access %s due to: %s" %
(reg_key(tree_name, path, varname), ex), file=sys.stderr)
except FileNotFoundError as ex:
print("!!!Cannot find %s due to: %s" %
(reg_key(tree_name, path, varname), ex), file=sys.stderr)
if __name__=='__main__':
args = sys.argv
argc = len(args)
if argc > 3:
print(__doc__.format(prog=args[0]), file=sys.stderr)
sys.exit()
manage_registry_env_vars(*args[1:])
Below are some usage examples, assuming it has been saved in a file called setenv.py somewhere in your current path.
Note that in these examples i didn't have admin-rights, so the changes affected only my local user's registry tree:
> REM ## Print all env-vars
> setenv.py
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied
HKEY_CURRENT_USER\Environment:PATH=...
...
> REM ## Query env-var:
> setenv.py PATH C:\foo
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied
!!!Cannot find HKEY_CURRENT_USER\Environment:PATH due to: [WinError 2] The system cannot find the file specified
> REM ## Set env-var:
> setenv.py PATH C:\foo
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied
---Set HKEY_CURRENT_USER\Environment:PATH=C:\foo
> REM ## Append env-var:
> setenv.py +PATH D:\Bar
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied
---Set HKEY_CURRENT_USER\Environment:PATH=C:\foo;D:\Bar
> REM ## Delete env-var:
> setenv.py -PATH
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied
---Deleted HKEY_CURRENT_USER\Environment:PATH
[*] Adapted from: http://code.activestate.com/recipes/416087-persistent-environment-variables-on-windows/
For reference purpose, for anyone searching how to change the path via code, I am quoting a useful post by a Delphi programmer from this web page: http://www.tek-tips.com/viewthread.cfm?qid=686382
TonHu (Programmer) 22 Oct 03 17:57 I found where I read the original
posting, it's here:
http://news.jrsoftware.org/news/innosetup.isx/msg02129....
The excerpt of what you would need is this:
You must specify the string "Environment" in LParam. In Delphi you'd
do it this way:
SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, Integer(PChar('Environment')));
It was suggested by Jordan Russell, http://www.jrsoftware.org, the
author of (a.o.) InnoSetup, ("Inno Setup is a free installer for
Windows programs. First introduced in 1997, Inno Setup today rivals
and even surpasses many commercial installers in feature set and
stability.") (I just would like more people to use InnoSetup )
HTH
In a corporate network, where the user has only limited access and uses portable apps, there are these command line tricks:
Query the user env variables: reg query "HKEY_CURRENT_USER\Environment". Use "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" for LOCAL_MACHINE.
Add new user env variable: reg add "HKEY_CURRENT_USER\Environment" /v shared_dir /d "c:\shared" /t REG_SZ. Use REG_EXPAND_SZ for paths containing other %% variables.
Delete existing env variable: reg delete "HKEY_CURRENT_USER\Environment" /v shared_dir.
This script
http://www.autohotkey.com/board/topic/63210-modify-system-path-gui/
includes all the necessary Windows API calls which can be refactored for your needs. It is actually an AutoHotkey GUI to change the System PATH easily. Needs to be run as an Administrator.
At the following link Calling clojure from java , it is illustrated how to write a piece of clojure code, whose functionality we can then invoke directly in java source code. To reproduce, we have a clojure project called tiny, which within it has a tiny.clj source file. The tiny.clj file contains the following code :
(ns tiny
(:gen-class
:name com.domain.tiny
:methods [#^{:static true} [binomial [int int] double]]))
(defn binomial
"Calculate the binomial coefficient."
[n k]
(let [a (inc n)]
(loop [b 1
c 1]
(if (> b k)
c
(recur (inc b) (* (/ (- a b) b) c))))))
(defn -binomial
"A Java-callable wrapper around the 'binomial' function."
[n k]
(binomial n k))
(defn -main []
(println (str "(binomial 5 3): " (binomial 5 3)))
(println (str "(binomial 10042 111): " (binomial 10042 111)))
)
This is then exported into a ttt.jar file, which is then added to the "Referenced Libraries" of the tinyJava project (which is a java project). Inside the tinyJava project there is a Main.java file, which has the following code :
import com.domain.tiny;
public class Main {
public static void main(String[] args) {
int j;
j = (int)tiny.binomial(5, 3);
System.out.println("(binomial 5 3): " + j);
System.out.println("(binomial 10042, 111): " + tiny.binomial(10042, 111));
}
}
The output is then :
(binomial 5 3): 10
(binomial 10042, 111): 4.9068389575068143E263
My question is, at the following point in Main.java :
j = (int)tiny.binomial(5, 3);
is it possible to step into the clojure source code? I have tried and it did not work.
Thanks
[EDIT] : the way I added the jar to the Referenced Libraries was as follows :
1)Rightclick on project tinyJava & choose properties
2)Choose : Java Build Path
3)Choose : Libraries
4)Click : "Add External Jars"
5)Then go to the location of ttt.jar
[EDIT 2] : For a scala project it seems that one can achieve this in a slightly different manner (see below), whereby one links the projects rather than explicitly exporting a jar file.
For example this seems to be quite easy to do with Scala (note the following article also discusses interop : How do you call Scala objects from Java?).
What I did was as follows :
1)Set up a Scala project called firstScala, and add a file MyPrinter.scala with the following code :
class MyPrinter{
def printTerm() {
print("hello");
}
}
2)Then I created a Java project called firstScalaJava, to which I then added a file called Main.java with the following code :
class Main {
public static void main(String args[]) {
MyPrinter myPrint = new MyPrinter();
myPrint.printTerm();
}
}
3)Then I right clicked on firstScalaJava, selected Properties, chose Java Build Path, chose Projects tab, clicked "Add", and then selected the firstScala project.
4)Then if I put a breakpoint at "myPrint.printTerm();" and clicked F5 when the debugger has stopped at this point, the debugger automatically walks into the Scala source file.
The nice thing about this is that I did not need to export any jar files or anything like that. This is appealing because upon changing the Scala code one does not need to do anything (eclipse automatically rebuilds the scala project when one attempts to run the java project thereafter), whereas if I changed the Clojure code I would need to re-export the jar etc... (note that linking the projects in the manner described above does not work for the clojure case)