Make it possible to specify a custom Java download URL + added an explicit Manifest file.
This commit is contained in:
parent
6858ffe791
commit
1139c65df2
32
Makefile
32
Makefile
@ -36,22 +36,22 @@ resources: init
|
|||||||
windres -o obj/registry.$(CPU_ARCH).o res/registry.rc
|
windres -o obj/registry.$(CPU_ARCH).o res/registry.rc
|
||||||
|
|
||||||
build: init resources
|
build: init resources
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=0 -DDETECT_REGISTRY=0 -DSTAY_ALIVE=1 -DENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH).exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=0 -DL5J_DETECT_REGISTRY=0 -DL5J_STAY_ALIVE=1 -DL5J_ENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH).exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=0 -DDETECT_REGISTRY=0 -DSTAY_ALIVE=1 -DENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=0 -DL5J_DETECT_REGISTRY=0 -DL5J_STAY_ALIVE=1 -DL5J_ENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=0 -DDETECT_REGISTRY=0 -DSTAY_ALIVE=0 -DENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH)_nowait.exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=0 -DL5J_DETECT_REGISTRY=0 -DL5J_STAY_ALIVE=0 -DL5J_ENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH)_nowait.exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=0 -DDETECT_REGISTRY=0 -DSTAY_ALIVE=0 -DENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_nowait_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=0 -DL5J_DETECT_REGISTRY=0 -DL5J_STAY_ALIVE=0 -DL5J_ENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_nowait_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=0 -DDETECT_REGISTRY=1 -DSTAY_ALIVE=1 -DENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH)_registry.exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=0 -DL5J_DETECT_REGISTRY=1 -DL5J_STAY_ALIVE=1 -DL5J_ENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH)_registry.exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=0 -DDETECT_REGISTRY=1 -DSTAY_ALIVE=1 -DENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_registry_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=0 -DL5J_DETECT_REGISTRY=1 -DL5J_STAY_ALIVE=1 -DL5J_ENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_registry_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=0 -DDETECT_REGISTRY=1 -DSTAY_ALIVE=0 -DENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH)_registry_nowait.exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=0 -DL5J_DETECT_REGISTRY=1 -DL5J_STAY_ALIVE=0 -DL5J_ENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH)_registry_nowait.exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=0 -DDETECT_REGISTRY=1 -DSTAY_ALIVE=0 -DENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_registry_nowait_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=0 -DL5J_DETECT_REGISTRY=1 -DL5J_STAY_ALIVE=0 -DL5J_ENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_registry_nowait_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=1 -DDETECT_REGISTRY=0 -DSTAY_ALIVE=1 -DENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH)_wrapped.exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=1 -DL5J_DETECT_REGISTRY=0 -DL5J_STAY_ALIVE=1 -DL5J_ENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH)_wrapped.exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=1 -DDETECT_REGISTRY=0 -DSTAY_ALIVE=1 -DENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_wrapped_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=1 -DL5J_DETECT_REGISTRY=0 -DL5J_STAY_ALIVE=1 -DL5J_ENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_wrapped_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=1 -DDETECT_REGISTRY=0 -DSTAY_ALIVE=0 -DENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH)_wrapped_nowait.exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=1 -DL5J_DETECT_REGISTRY=0 -DL5J_STAY_ALIVE=0 -DL5J_ENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH)_wrapped_nowait.exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=1 -DDETECT_REGISTRY=0 -DSTAY_ALIVE=0 -DENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_wrapped_nowait_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=1 -DL5J_DETECT_REGISTRY=0 -DL5J_STAY_ALIVE=0 -DL5J_ENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_wrapped_nowait_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=1 -DDETECT_REGISTRY=1 -DSTAY_ALIVE=1 -DENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH)_wrapped_registry.exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=1 -DL5J_DETECT_REGISTRY=1 -DL5J_STAY_ALIVE=1 -DL5J_ENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH)_wrapped_registry.exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=1 -DDETECT_REGISTRY=1 -DSTAY_ALIVE=1 -DENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_wrapped_registry_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=1 -DL5J_DETECT_REGISTRY=1 -DL5J_STAY_ALIVE=1 -DL5J_ENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_wrapped_registry_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=1 -DDETECT_REGISTRY=1 -DSTAY_ALIVE=0 -DENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH)_wrapped_registry_nowait.exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=1 -DL5J_DETECT_REGISTRY=1 -DL5J_STAY_ALIVE=0 -DL5J_ENABLE_SPLASH=1 -o bin/launch5j_$(CPU_ARCH)_wrapped_registry_nowait.exe src/head.c obj/common.$(CPU_ARCH).o obj/splash_screen.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
||||||
$(CC) $(CFLAGS) -DJAR_FILE_WRAPPED=1 -DDETECT_REGISTRY=1 -DSTAY_ALIVE=0 -DENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_wrapped_registry_nowait_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
$(CC) $(CFLAGS) -DL5J_JAR_FILE_WRAPPED=1 -DL5J_DETECT_REGISTRY=1 -DL5J_STAY_ALIVE=0 -DL5J_ENABLE_SPLASH=0 -o bin/launch5j_$(CPU_ARCH)_wrapped_registry_nowait_nosplash.exe src/head.c obj/common.$(CPU_ARCH).o obj/registry.$(CPU_ARCH).o
|
||||||
|
|
||||||
strip: build
|
strip: build
|
||||||
strip bin/launch5j_$(CPU_ARCH).exe
|
strip bin/launch5j_$(CPU_ARCH).exe
|
||||||
|
47
README.md
47
README.md
@ -77,42 +77,50 @@ Some options can be configured via the launcher executable's [STRINGTABLE](https
|
|||||||
* **`ID_STR_CMDARGS` (#3)**
|
* **`ID_STR_CMDARGS` (#3)**
|
||||||
Specifies *additional* fixed command-line parameters to be passed to the Java application.
|
Specifies *additional* fixed command-line parameters to be passed to the Java application.
|
||||||
|
|
||||||
* **`ID_STR_JAVAMIN` (#4)**
|
* **`ID_STR_JREPATH` (#4)**
|
||||||
|
Specifies the path to the Java runtime (`javaw.exe`) relative to the launcher executable location.
|
||||||
|
If not specified, then the *default* runtime path `runtime\\bin\\javaw.exe` is assumed.
|
||||||
|
|
||||||
|
(This option does **not** apply to the “registry” variant of Launch5j)
|
||||||
|
|
||||||
|
* **`ID_STR_MUTEXID` (#5)**
|
||||||
|
Specifies the application ID to be used when creating the [*single-instance*](http://www.bcbjournal.org/articles/vol3/9911/Single-instance_applications.htm) mutex.
|
||||||
|
The ID **must** be at least 5 characters in length and **should** be a *unique* string for each application!
|
||||||
|
If not specified, then **no** mutex will be created and thus *multiple* instances will be allowed.
|
||||||
|
|
||||||
|
*Hint:* If the specified application ID *starts* with an **`@`** character, then Launch5j will **not** show a message box when the application is already running; the **`@`** character is *not* considered a part of the actual ID.
|
||||||
|
|
||||||
|
(This option does **not** apply to the “nowait” variant of Launch5j)
|
||||||
|
|
||||||
|
* **`ID_STR_JAVAMIN` (#6)**
|
||||||
Specifies the ***minimum*** supported JRE version, in the **`w.x.y.z`** format (e.g. `11.0.0.0`).
|
Specifies the ***minimum*** supported JRE version, in the **`w.x.y.z`** format (e.g. `11.0.0.0`).
|
||||||
This values is *inclusive*, i.e. the specified JRE version or any newer JRE version will be accepted.
|
This values is *inclusive*, i.e. the specified JRE version or any newer JRE version will be accepted.
|
||||||
If not specified, then the *default* minimum supported JRE version `8.0.0.0` applies.
|
If not specified, then the *default* minimum supported JRE version `8.0.0.0` applies.
|
||||||
|
|
||||||
*Hint:* Old-style `1.x.y_z` Java versions are automatically translated to the new `x.y.z` format!
|
*Hint:* Old-style `1.x.y_z` Java versions are automatically translated to the new `x.y.z` format!
|
||||||
|
|
||||||
(This option only applies to the `registry` variant of Launch5j)
|
(This option only applies to the “registry” variant of Launch5j)
|
||||||
|
|
||||||
* **`ID_STR_JAVAMAX` (#5)**
|
* **`ID_STR_JAVAMAX` (#7)**
|
||||||
Specifies the ***maximum*** supported JRE version, in the **`w.x.y.z`** format (e.g. `12.0.0.0`).
|
Specifies the ***maximum*** supported JRE version, in the **`w.x.y.z`** format (e.g. `12.0.0.0`).
|
||||||
This values is *exclusive*, i.e. only JRE versions *older* than the specified JRE version will be accepted.
|
This values is *exclusive*, i.e. only JRE versions *older* than the specified JRE version will be accepted.
|
||||||
If not specified, then there is **no** upper limit on the supported JRE version.
|
If not specified, then there is **no** upper limit on the supported JRE version.
|
||||||
|
|
||||||
*Hint:* Old-style `1.x.y.z` Java versions are automatically translated to the `x.y.z.0` format!
|
*Hint:* Old-style `1.x.y.z` Java versions are automatically translated to the `x.y.z.0` format!
|
||||||
|
|
||||||
(This option only applies to the `registry` variant of Launch5j)
|
(This option only applies to the “registry” variant of Launch5j)
|
||||||
|
|
||||||
* **`ID_STR_BITNESS` (#6)**
|
* **`ID_STR_BITNESS` (#8)**
|
||||||
Specifies the required ***bitness*** of the JRE. This can be either **`32`** (x86, aka i586) or **`64`** (x86-64).
|
Specifies the required ***bitness*** of the JRE. This can be either **`32`** (x86, aka i586) or **`64`** (x86-64).
|
||||||
If not specified, 32-Bit *and* 64-Bit JREs are accepted, with a preference to 64-Bit.
|
If not specified, then 32-Bit *and* 64-Bit JREs are accepted, with a preference to 64-Bit.
|
||||||
|
|
||||||
(This option only applies to the `registry` variant of Launch5j)
|
(This option only applies to the “registry” variant of Launch5j)
|
||||||
|
|
||||||
* **`ID_STR_BITNESS` (#6)**
|
* **`ID_STR_JAVAURL` (#9)**
|
||||||
Specifies the required ***bitness*** of the JRE. This can be either **`32`** (x86, aka i586) or **`64`** (x86-64).
|
The Java download URL that will ne suggested, if **no** suitable JRE could be detected on the machine.
|
||||||
If not specified, 32-Bit *and* 64-Bit JREs are accepted, with a preference to 64-Bit.
|
If not specified, wes suggest downloading OpenJDK as provided by the [AdoptOpenJDK](https://adoptopenjdk.net/) project.
|
||||||
|
|
||||||
(This option only applies to the `registry` variant of Launch5j)
|
|
||||||
|
|
||||||
* **`ID_STR_MUTEXID` (#7)**
|
(This option only applies to the “registry” variant of Launch5j)
|
||||||
Specifies the application ID to be used when creating the [*single-instance*](http://www.bcbjournal.org/articles/vol3/9911/Single-instance_applications.htm) mutex.
|
|
||||||
The ID **must** be at least 5 characters in length and **should** be a *unique* string for each application!
|
|
||||||
If not specified, then **no** mutex will be created and thus *multiple* instances will be allowed.
|
|
||||||
|
|
||||||
*Hint:* If the specified application ID *starts* with an **`@`** character, then Launch5j will **not** show a message box when the application is already running; the **`@`** character is *not* considered a part of the actual ID.
|
|
||||||
|
|
||||||
*Note:* We use the convention that the default resource string value `"?"` is used to represent an “undefined” value, because resource strings cannot be empty. You can replace the default value as needed!
|
*Note:* We use the convention that the default resource string value `"?"` is used to represent an “undefined” value, because resource strings cannot be empty. You can replace the default value as needed!
|
||||||
|
|
||||||
@ -161,3 +169,6 @@ This work has been released under the MIT license:
|
|||||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
[∎](https://www.youtube.com/watch?v=EfbbjY9MlQs)
|
||||||
|
@ -40,7 +40,8 @@ public class MakefileGenerator {
|
|||||||
for(int registry = 0; registry < 2; ++registry) {
|
for(int registry = 0; registry < 2; ++registry) {
|
||||||
for(int stayAlive = 1; stayAlive > -1; --stayAlive) {
|
for(int stayAlive = 1; stayAlive > -1; --stayAlive) {
|
||||||
for(int enableSplash = 1; enableSplash > -1; --enableSplash) {
|
for(int enableSplash = 1; enableSplash > -1; --enableSplash) {
|
||||||
out.println(generateCommand(filenNames, wrapped, registry, stayAlive, enableSplash));
|
out.println(generateCommand(
|
||||||
|
filenNames, wrapped, registry, stayAlive, enableSplash));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,16 +84,17 @@ public class MakefileGenerator {
|
|||||||
out.println();
|
out.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String generateCommand(final List<String> filenNames, final int wrapped, final int registry, final int stayAlive, final int enableSplash)
|
private static String generateCommand(final List<String> filenNames,
|
||||||
|
final int wrapped, final int registry, final int stayAlive, final int enableSplash)
|
||||||
{
|
{
|
||||||
final String fileName = String.format("bin/launch5j_$(CPU_ARCH)%s.exe",
|
final String fileName = String.format("bin/launch5j_$(CPU_ARCH)%s.exe",
|
||||||
generateNameSuffix(wrapped, registry, stayAlive, enableSplash));
|
generateNameSuffix(wrapped, registry, stayAlive, enableSplash));
|
||||||
final StringBuilder cmdLine = new StringBuilder(String.format(
|
final StringBuilder cmdLine = new StringBuilder(String.format(
|
||||||
"\t$(CC) $(CFLAGS) "
|
"\t$(CC) $(CFLAGS) "
|
||||||
+ "-DJAR_FILE_WRAPPED=%d "
|
+ "-DL5J_JAR_FILE_WRAPPED=%d "
|
||||||
+ "-DDETECT_REGISTRY=%d "
|
+ "-DL5J_DETECT_REGISTRY=%d "
|
||||||
+ "-DSTAY_ALIVE=%d "
|
+ "-DL5J_STAY_ALIVE=%d "
|
||||||
+ "-DENABLE_SPLASH=%d "
|
+ "-DL5J_ENABLE_SPLASH=%d "
|
||||||
+ "-o %s "
|
+ "-o %s "
|
||||||
+ "src/head.c obj/common.$(CPU_ARCH).o",
|
+ "src/head.c obj/common.$(CPU_ARCH).o",
|
||||||
wrapped,
|
wrapped,
|
||||||
|
31
res/assets/manifest.xml
Normal file
31
res/assets/manifest.xml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="MuldeRsoft.Launch5j.ExeHead" type="win32"/>
|
||||||
|
<description>Launch5j</description>
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges>
|
||||||
|
<requestedExecutionLevel level="asInvoker"/>
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!--The ID below indicates application support for Windows Vista -->
|
||||||
|
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||||
|
<!--The ID below indicates application support for Windows 7 -->
|
||||||
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||||
|
<!--The ID below indicates application support for Windows 8 -->
|
||||||
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||||
|
<!--The ID below indicates application support for Windows 8.1 -->
|
||||||
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||||
|
<!--The ID below indicates application support for Windows 10 -->
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
</assembly>
|
@ -40,6 +40,7 @@ BEGIN
|
|||||||
ID_STR_HEADING L"?" /*custom application title*/
|
ID_STR_HEADING L"?" /*custom application title*/
|
||||||
ID_STR_JVMARGS L"?" /*additional JVM args*/
|
ID_STR_JVMARGS L"?" /*additional JVM args*/
|
||||||
ID_STR_CMDARGS L"?" /*additional command-line args*/
|
ID_STR_CMDARGS L"?" /*additional command-line args*/
|
||||||
|
ID_STR_JREPATH L"?" /*relative path to JRE*/
|
||||||
ID_STR_MUTEXID L"?" /*single instance mutex ID*/
|
ID_STR_MUTEXID L"?" /*single instance mutex ID*/
|
||||||
END
|
END
|
||||||
|
|
||||||
@ -81,3 +82,9 @@ BEGIN
|
|||||||
VALUE "Translation", 0x0, 1200
|
VALUE "Translation", 0x0, 1200
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Manifest
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
1 RT_MANIFEST "assets/manifest.xml"
|
||||||
|
@ -34,4 +34,5 @@ BEGIN
|
|||||||
ID_STR_JAVAMIN L"?" /*min supported JRE version, in "w.x.y.z" format*/
|
ID_STR_JAVAMIN L"?" /*min supported JRE version, in "w.x.y.z" format*/
|
||||||
ID_STR_JAVAMAX L"?" /*max supported JRE version, in "w.x.y.z" format*/
|
ID_STR_JAVAMAX L"?" /*max supported JRE version, in "w.x.y.z" format*/
|
||||||
ID_STR_BITNESS L"?" /*required JRE "bitness", i.e. "32" or "64"*/
|
ID_STR_BITNESS L"?" /*required JRE "bitness", i.e. "32" or "64"*/
|
||||||
|
ID_STR_JAVAURL L"?" /*Java download URL, if no usable JRE was found*/
|
||||||
END
|
END
|
||||||
|
77
src/head.c
77
src/head.c
@ -29,25 +29,25 @@
|
|||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
#ifndef JAR_FILE_WRAPPED
|
#ifndef L5J_JAR_FILE_WRAPPED
|
||||||
#define JAR_FILE_WRAPPED 0
|
#error L5J_JAR_FILE_WRAPPED flag is *not* defined!
|
||||||
#endif
|
#endif
|
||||||
#ifndef DETECT_REGISTRY
|
#ifndef L5J_DETECT_REGISTRY
|
||||||
#define DETECT_REGISTRY 0
|
#error L5J_DETECT_REGISTRY flag is *not* defined!
|
||||||
#endif
|
#endif
|
||||||
#ifndef ENABLE_SPLASH
|
#ifndef L5J_ENABLE_SPLASH
|
||||||
#define ENABLE_SPLASH 1
|
#error L5J_ENABLE_SPLASH flag is *not* defined!
|
||||||
#endif
|
#endif
|
||||||
#ifndef STAY_ALIVE
|
#ifndef L5J_STAY_ALIVE
|
||||||
#define STAY_ALIVE 1
|
#error L5J_STAY_ALIVE flag is *not* defined!
|
||||||
#endif
|
#endif
|
||||||
#ifndef WAIT_FOR_WINDOW
|
#ifndef L5J_WAIT_FOR_WINDOW
|
||||||
#define WAIT_FOR_WINDOW 1
|
#define L5J_WAIT_FOR_WINDOW 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Const
|
// Const
|
||||||
static const wchar_t *const JRE_RELATIVE_PATH = L"runtime\\bin\\javaw.exe";
|
static const wchar_t *const JRE_DOWNLOAD_LINK_DEFAULT = L"https://adoptopenjdk.net/";
|
||||||
static const wchar_t *const JRE_DOWNLOAD_LINK = L"https://adoptopenjdk.net/";
|
static const wchar_t *const JRE_RELATIVE_PATH_DEFAULT = L"runtime\\bin\\javaw.exe";
|
||||||
static const size_t MIN_MUTEXID_LENGTH = 5U;
|
static const size_t MIN_MUTEXID_LENGTH = 5U;
|
||||||
static const DWORD SPLASH_SCREEN_TIMEOUT = 30000U;
|
static const DWORD SPLASH_SCREEN_TIMEOUT = 30000U;
|
||||||
|
|
||||||
@ -451,7 +451,7 @@ static const wchar_t *get_executable_directory(const wchar_t *const executable_p
|
|||||||
|
|
||||||
static const wchar_t *get_jarfile_path(const wchar_t *const executable_path, const wchar_t *const executable_directory)
|
static const wchar_t *get_jarfile_path(const wchar_t *const executable_path, const wchar_t *const executable_directory)
|
||||||
{
|
{
|
||||||
#if JAR_FILE_WRAPPED
|
#if L5J_JAR_FILE_WRAPPED
|
||||||
return wcsdup(executable_path); /*JAR file is wrapped*/
|
return wcsdup(executable_path); /*JAR file is wrapped*/
|
||||||
#else
|
#else
|
||||||
const wchar_t *jarfile_path = NULL;
|
const wchar_t *jarfile_path = NULL;
|
||||||
@ -827,7 +827,7 @@ static BOOL wait_for_process_ready(const HWND hwnd, const HANDLE process_handle,
|
|||||||
const DWORD ticks_start = GetTickCount();
|
const DWORD ticks_start = GetTickCount();
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
if (input_idle || signaled_or_failed(WaitForInputIdle(process_handle, 125U)))
|
if (input_idle || signaled_or_failed(WaitForInputIdle(process_handle, 25U)))
|
||||||
{
|
{
|
||||||
const HWND child_hwnd = find_window_by_process_id(process_id);
|
const HWND child_hwnd = find_window_by_process_id(process_id);
|
||||||
if (child_hwnd)
|
if (child_hwnd)
|
||||||
@ -888,12 +888,13 @@ static int show_message_format(HWND hwnd, const DWORD flags, const wchar_t *cons
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_jre_download_notice(const HWND hwnd, const wchar_t *const title, const DWORD required_bitness, const ULONGLONG required_ver)
|
static void show_jre_download_notice(const HINSTANCE hinstance, const HWND hwnd, const wchar_t *const title, const DWORD required_bitness, const ULONGLONG required_ver)
|
||||||
{
|
{
|
||||||
const DWORD req_version_comp[] =
|
const DWORD req_version_comp[] =
|
||||||
{
|
{
|
||||||
(required_ver >> 48) & 0xFFFF, (required_ver >> 32) & 0xFFFF, (required_ver >> 16) & 0xFFFF, required_ver & 0xFFFF
|
(required_ver >> 48) & 0xFFFF, (required_ver >> 32) & 0xFFFF, (required_ver >> 16) & 0xFFFF, required_ver & 0xFFFF
|
||||||
};
|
};
|
||||||
|
wchar_t *const jre_download_link = load_string(hinstance, ID_STR_JAVAURL);
|
||||||
wchar_t *const version_str = (req_version_comp[3U] != 0U)
|
wchar_t *const version_str = (req_version_comp[3U] != 0U)
|
||||||
? awprintf(L"%u.%u.%u_%u", req_version_comp[0U], req_version_comp[1U], req_version_comp[2U], req_version_comp[3U])
|
? awprintf(L"%u.%u.%u_%u", req_version_comp[0U], req_version_comp[1U], req_version_comp[2U], req_version_comp[3U])
|
||||||
: ((req_version_comp[2U] != 0U)
|
: ((req_version_comp[2U] != 0U)
|
||||||
@ -901,22 +902,24 @@ static void show_jre_download_notice(const HWND hwnd, const wchar_t *const title
|
|||||||
: awprintf(L"%u.%u", req_version_comp[0U], req_version_comp[1U]));
|
: awprintf(L"%u.%u", req_version_comp[0U], req_version_comp[1U]));
|
||||||
if(version_str)
|
if(version_str)
|
||||||
{
|
{
|
||||||
|
const wchar_t *const jre_download_ptr = AVAILABLE(jre_download_link) ? jre_download_link : JRE_DOWNLOAD_LINK_DEFAULT;
|
||||||
const int result = (required_bitness == 0U)
|
const int result = (required_bitness == 0U)
|
||||||
? show_message_format(hwnd, MB_ICONWARNING | MB_OKCANCEL | MB_TOPMOST, title,
|
? show_message_format(hwnd, MB_ICONWARNING | MB_OKCANCEL | MB_TOPMOST, title,
|
||||||
L"This application requires the Java Runtime Environment, version %ls, or a compatible newer version.\n\n"
|
L"This application requires the Java Runtime Environment, version %ls, or a compatible newer version.\n\n"
|
||||||
L"We recommend downloading the OpenJDK runtime here:\n%ls",
|
L"We recommend downloading the OpenJDK runtime here:\n%ls",
|
||||||
version_str, JRE_DOWNLOAD_LINK)
|
version_str, jre_download_ptr)
|
||||||
: show_message_format(hwnd, MB_ICONWARNING | MB_OKCANCEL | MB_TOPMOST, title,
|
: show_message_format(hwnd, MB_ICONWARNING | MB_OKCANCEL | MB_TOPMOST, title,
|
||||||
L"This application requires the Java Runtime Environment, version %ls, or a compatible newer version.\n\n"
|
L"This application requires the Java Runtime Environment, version %ls, or a compatible newer version.\n\n"
|
||||||
L"Only the %u-Bit (%ls) version of the JRE is supported!\n\n",
|
L"Only the %u-Bit (%ls) version of the JRE is supported!\n\n",
|
||||||
L"We recommend downloading the OpenJDK runtime:\n%ls",
|
L"We recommend downloading the OpenJDK runtime:\n%ls",
|
||||||
version_str, required_bitness, (required_bitness == 64) ? L"x64" : L"x86", JRE_DOWNLOAD_LINK);
|
version_str, required_bitness, (required_bitness == 64) ? L"x64" : L"x86", jre_download_ptr);
|
||||||
if (result == IDOK)
|
if (result == IDOK)
|
||||||
{
|
{
|
||||||
ShellExecuteW(hwnd, NULL, JRE_DOWNLOAD_LINK, NULL, NULL, SW_SHOW);
|
ShellExecuteW(hwnd, NULL, jre_download_ptr, NULL, NULL, SW_SHOW);
|
||||||
}
|
}
|
||||||
free(version_str);
|
|
||||||
}
|
}
|
||||||
|
free(version_str);
|
||||||
|
free(jre_download_link);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ======================================================================== */
|
/* ======================================================================== */
|
||||||
@ -1001,11 +1004,13 @@ static wchar_t *const DEFAULT_HEADING = L"Launch5j";
|
|||||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
|
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
|
||||||
{
|
{
|
||||||
int result = -1;
|
int result = -1;
|
||||||
const wchar_t *app_heading = NULL, *mutex_name = NULL, *executable_path = NULL, *executable_directory = NULL, *jarfile_path = NULL, *java_runtime_path = NULL, *jvm_extra_args = NULL, *cmd_extra_args = NULL, *command_line = NULL;
|
const wchar_t *app_heading = NULL, *mutex_name = NULL, *executable_path = NULL, *executable_directory = NULL, *jarfile_path = NULL,
|
||||||
|
*java_runtime_path = NULL, *jre_relative_path = NULL, *jvm_extra_args = NULL, *cmd_extra_args = NULL, *command_line = NULL;
|
||||||
HANDLE mutex_handle = NULL;
|
HANDLE mutex_handle = NULL;
|
||||||
DWORD java_required_bitness = 0U;
|
DWORD java_required_bitness = 0U;
|
||||||
ULONGLONG java_required_ver_min = 0ULL, java_required_ver_max = 0ULL;
|
ULONGLONG java_required_ver_min = 0ULL, java_required_ver_max = 0ULL;
|
||||||
HGDIOBJ splash_image = NULL;
|
HGDIOBJ splash_image = NULL;
|
||||||
|
BOOL have_screen_created = FALSE;
|
||||||
PROCESS_INFORMATION process_info;
|
PROCESS_INFORMATION process_info;
|
||||||
STARTUPINFOW startup_info;
|
STARTUPINFOW startup_info;
|
||||||
|
|
||||||
@ -1020,6 +1025,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
|
|||||||
HWND hwnd = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, L"STATIC", APP_HEADING, WS_POPUP | SS_BITMAP, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
|
HWND hwnd = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, L"STATIC", APP_HEADING, WS_POPUP | SS_BITMAP, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
|
||||||
|
|
||||||
// Single instance
|
// Single instance
|
||||||
|
#if L5J_STAY_ALIVE
|
||||||
mutex_name = load_string(hInstance, ID_STR_MUTEXID);
|
mutex_name = load_string(hInstance, ID_STR_MUTEXID);
|
||||||
if (AVAILABLE(mutex_name) && (wcslen(mutex_name) >= MIN_MUTEXID_LENGTH + ((mutex_name[0U] == L'@') ? 0U : 1U)))
|
if (AVAILABLE(mutex_name) && (wcslen(mutex_name) >= MIN_MUTEXID_LENGTH + ((mutex_name[0U] == L'@') ? 0U : 1U)))
|
||||||
{
|
{
|
||||||
@ -1033,13 +1039,15 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Show the splash screen
|
// Show the splash screen
|
||||||
#if ENABLE_SPLASH
|
#if L5J_ENABLE_SPLASH
|
||||||
if (splash_image = LoadImage(hInstance, MAKEINTRESOURCE(ID_BITMAP_SPLASH), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE))
|
if (splash_image = LoadImage(hInstance, MAKEINTRESOURCE(ID_BITMAP_SPLASH), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE))
|
||||||
{
|
{
|
||||||
if (create_splash_screen(hwnd, splash_image))
|
if (create_splash_screen(hwnd, splash_image))
|
||||||
{
|
{
|
||||||
|
have_screen_created = TRUE;
|
||||||
process_window_messages(hwnd);
|
process_window_messages(hwnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1073,7 +1081,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Does the JAR file exist?
|
// Does the JAR file exist?
|
||||||
#if !JAR_FILE_WRAPPED
|
#if !L5J_JAR_FILE_WRAPPED
|
||||||
if (!file_exists(jarfile_path))
|
if (!file_exists(jarfile_path))
|
||||||
{
|
{
|
||||||
show_message_format(hwnd, MB_ICONERROR | MB_TOPMOST, APP_HEADING, L"The required JAR file could not be found:\n\n%ls\n\n\nRe-installing the application may fix the problem!", jarfile_path);
|
show_message_format(hwnd, MB_ICONERROR | MB_TOPMOST, APP_HEADING, L"The required JAR file could not be found:\n\n%ls\n\n\nRe-installing the application may fix the problem!", jarfile_path);
|
||||||
@ -1082,18 +1090,19 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Find the Java runtime executable path (possibly from the registry)
|
// Find the Java runtime executable path (possibly from the registry)
|
||||||
#if DETECT_REGISTRY
|
#if L5J_DETECT_REGISTRY
|
||||||
java_required_ver_min = load_java_version(hInstance, ID_STR_JAVAMIN, (8ull << 48));
|
java_required_ver_min = load_java_version(hInstance, ID_STR_JAVAMIN, (8ull << 48));
|
||||||
java_required_ver_max = load_java_version(hInstance, ID_STR_JAVAMAX, MAXULONGLONG);
|
java_required_ver_max = load_java_version(hInstance, ID_STR_JAVAMAX, MAXULONGLONG);
|
||||||
java_required_bitness = load_java_bitness(hInstance, ID_STR_BITNESS);
|
java_required_bitness = load_java_bitness(hInstance, ID_STR_BITNESS);
|
||||||
if (!(java_runtime_path = detect_java_runtime(java_required_bitness, java_required_ver_min, java_required_ver_max)))
|
if (!(java_runtime_path = detect_java_runtime(java_required_bitness, java_required_ver_min, java_required_ver_max)))
|
||||||
{
|
{
|
||||||
show_message(hwnd, MB_ICONERROR | MB_TOPMOST, APP_HEADING, L"Java Runtime Environment (JRE) could not be found!");
|
show_message(hwnd, MB_ICONERROR | MB_TOPMOST, APP_HEADING, L"Java Runtime Environment (JRE) could not be found!");
|
||||||
show_jre_download_notice(hwnd, APP_HEADING, java_required_bitness, java_required_ver_min);
|
show_jre_download_notice(hInstance, hwnd, APP_HEADING, java_required_bitness, java_required_ver_min);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (!(java_runtime_path = awprintf(L"%ls\\%ls", executable_directory, JRE_RELATIVE_PATH)))
|
jre_relative_path = load_string(hInstance, ID_STR_JREPATH);
|
||||||
|
if (!(java_runtime_path = awprintf(L"%ls\\%ls", executable_directory, AVAILABLE(jre_relative_path) ? jre_relative_path: JRE_RELATIVE_PATH_DEFAULT)))
|
||||||
{
|
{
|
||||||
show_message(hwnd, MB_ICONERROR | MB_TOPMOST, APP_HEADING, L"The path of the Java runtime could not be determined!");
|
show_message(hwnd, MB_ICONERROR | MB_TOPMOST, APP_HEADING, L"The path of the Java runtime could not be determined!");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -1124,7 +1133,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process pending window messages
|
// Process pending window messages
|
||||||
#if ENABLE_SPLASH
|
#if L5J_ENABLE_SPLASH
|
||||||
process_window_messages(hwnd);
|
process_window_messages(hwnd);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1145,16 +1154,23 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process pending window messages
|
// Process pending window messages
|
||||||
#if ENABLE_SPLASH
|
#if L5J_ENABLE_SPLASH
|
||||||
process_window_messages(hwnd);
|
process_window_messages(hwnd);
|
||||||
#if WAIT_FOR_WINDOW
|
|
||||||
|
// Wait until child-process window is showing
|
||||||
|
#if L5J_WAIT_FOR_WINDOW
|
||||||
wait_for_process_ready(hwnd, process_info.hProcess, process_info.dwProcessId);
|
wait_for_process_ready(hwnd, process_info.hProcess, process_info.dwProcessId);
|
||||||
#endif
|
#endif
|
||||||
destroy_window(&hwnd);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Hide the splash screen now
|
||||||
|
if (have_screen_created)
|
||||||
|
{
|
||||||
|
ShowWindow(hwnd, SW_HIDE);
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for process to exit, then get the exit code
|
// Wait for process to exit, then get the exit code
|
||||||
#if STAY_ALIVE
|
#if L5J_STAY_ALIVE
|
||||||
if (signaled_or_failed(WaitForSingleObject(process_info.hProcess, INFINITE)))
|
if (signaled_or_failed(WaitForSingleObject(process_info.hProcess, INFINITE)))
|
||||||
{
|
{
|
||||||
DWORD exit_code = 0U;
|
DWORD exit_code = 0U;
|
||||||
@ -1181,6 +1197,7 @@ cleanup:
|
|||||||
free((void*)command_line);
|
free((void*)command_line);
|
||||||
free((void*)java_runtime_path);
|
free((void*)java_runtime_path);
|
||||||
free((void*)jarfile_path);
|
free((void*)jarfile_path);
|
||||||
|
free((void*)jre_relative_path);
|
||||||
free((void*)executable_directory);
|
free((void*)executable_directory);
|
||||||
free((void*)executable_path);
|
free((void*)executable_path);
|
||||||
free((void*)mutex_name);
|
free((void*)mutex_name);
|
||||||
|
@ -21,7 +21,9 @@
|
|||||||
#define ID_STR_HEADING 1
|
#define ID_STR_HEADING 1
|
||||||
#define ID_STR_JVMARGS 2
|
#define ID_STR_JVMARGS 2
|
||||||
#define ID_STR_CMDARGS 3
|
#define ID_STR_CMDARGS 3
|
||||||
#define ID_STR_JAVAMIN 4
|
#define ID_STR_JREPATH 4
|
||||||
#define ID_STR_JAVAMAX 5
|
#define ID_STR_MUTEXID 5
|
||||||
#define ID_STR_BITNESS 6
|
#define ID_STR_JAVAMIN 6
|
||||||
#define ID_STR_MUTEXID 7
|
#define ID_STR_JAVAMAX 7
|
||||||
|
#define ID_STR_BITNESS 8
|
||||||
|
#define ID_STR_JAVAURL 9
|
||||||
|
Loading…
Reference in New Issue
Block a user