mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
[wpiutil] Check MSVC Runtime (#7301)
This commit is contained in:
@@ -10,8 +10,10 @@
|
||||
#include <hal/DriverStation.h>
|
||||
#include <hal/HALBase.h>
|
||||
#include <hal/Main.h>
|
||||
#include <wpi/RuntimeCheck.h>
|
||||
#include <wpi/condition_variable.h>
|
||||
#include <wpi/mutex.h>
|
||||
#include <wpi/string.h>
|
||||
|
||||
#include "frc/Errors.h"
|
||||
#include "frc/RuntimeType.h"
|
||||
@@ -53,6 +55,21 @@ void RunRobot(wpi::mutex& m, Robot** robot) {
|
||||
|
||||
template <class Robot>
|
||||
int StartRobot() {
|
||||
uint32_t foundMajor;
|
||||
uint32_t foundMinor;
|
||||
uint32_t expectedMajor;
|
||||
uint32_t expectedMinor;
|
||||
WPI_String runtimePath;
|
||||
if (!WPI_IsRuntimeValid(&foundMajor, &foundMinor, &expectedMajor,
|
||||
&expectedMinor, &runtimePath)) {
|
||||
// We could make this error better, however unlike Java, there is only a
|
||||
// single scenario that could be occuring. The entirety of VS is too out
|
||||
// of date. In most cases the linker should detect this, but not always.
|
||||
fmt::println(
|
||||
"Your copy of Visual Studio is out of date. Please update it.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int halInit = RunHALInitialization();
|
||||
if (halInit != 0) {
|
||||
return halInit;
|
||||
|
||||
@@ -397,6 +397,9 @@ public abstract class RobotBase implements AutoCloseable {
|
||||
* @param robotSupplier Function that returns an instance of the robot subclass.
|
||||
*/
|
||||
public static <T extends RobotBase> void startRobot(Supplier<T> robotSupplier) {
|
||||
// Check that the MSVC runtime is valid.
|
||||
WPIUtilJNI.checkMsvcRuntime();
|
||||
|
||||
if (!HAL.initialize(500, 0)) {
|
||||
throw new IllegalStateException("Failed to initialize. Terminating");
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ ext {
|
||||
groupId = 'edu.wpi.first.wpiutil'
|
||||
|
||||
nativeName = 'wpiutil'
|
||||
devMain = 'edu.wpi.first.wpiutil.DevMain'
|
||||
devMain = 'edu.wpi.first.util.DevMain'
|
||||
def generateTask = createGenerateResourcesTask('main', 'WPI', 'wpi', project)
|
||||
|
||||
splitSetup = {
|
||||
|
||||
@@ -9,6 +9,7 @@ public final class DevMain {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello World!");
|
||||
System.out.println(CombinedRuntimeLoader.getPlatformPath());
|
||||
WPIUtilJNI.checkMsvcRuntime();
|
||||
}
|
||||
|
||||
private DevMain() {}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.util;
|
||||
|
||||
/** Exception thrown due to a bad MSVC Runtime. */
|
||||
public class MsvcRuntimeException extends RuntimeException {
|
||||
private static final long serialVersionUID = -9155939328084105142L;
|
||||
|
||||
private static String generateMessage(
|
||||
int foundMajor, int foundMinor, int expectedMajor, int expectedMinor, String runtimePath) {
|
||||
String jvmLocation = ProcessHandle.current().info().command().orElse("Unknown");
|
||||
|
||||
StringBuffer builder = new StringBuffer(100);
|
||||
builder
|
||||
.append("Invalid MSVC Runtime Detected.\n")
|
||||
.append(
|
||||
String.format(
|
||||
"Expected at least %d.%d, but found %d.%d\n",
|
||||
expectedMajor, expectedMinor, foundMajor, foundMinor))
|
||||
.append(String.format("JVM Location: %s\n", jvmLocation))
|
||||
.append(String.format("Runtime DLL Location: %s\n", runtimePath))
|
||||
.append("See https://wpilib.org/jvmruntime for more information\n");
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a runtime exception.
|
||||
*
|
||||
* @param foundMajor found major
|
||||
* @param foundMinor found minor
|
||||
* @param expectedMajor expected major
|
||||
* @param expectedMinor expected minor
|
||||
* @param runtimePath path of runtime
|
||||
*/
|
||||
public MsvcRuntimeException(
|
||||
int foundMajor, int foundMinor, int expectedMajor, int expectedMinor, String runtimePath) {
|
||||
super(generateMessage(foundMajor, foundMinor, expectedMajor, expectedMinor, runtimePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a runtime exception.
|
||||
*
|
||||
* @param msg message
|
||||
*/
|
||||
public MsvcRuntimeException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -63,6 +63,9 @@ public class WPIUtilJNI {
|
||||
libraryLoaded = true;
|
||||
}
|
||||
|
||||
/** Checks if the MSVC runtime is valid. Throws a runtime exception if not. */
|
||||
public static native void checkMsvcRuntime();
|
||||
|
||||
/**
|
||||
* Write the given string to stderr.
|
||||
*
|
||||
|
||||
73
wpiutil/src/main/native/cpp/RuntimeCheck.cpp
Normal file
73
wpiutil/src/main/native/cpp/RuntimeCheck.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "wpi/RuntimeCheck.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
|
||||
#include "Windows.h"
|
||||
extern "C" int32_t WPI_IsRuntimeValid(uint32_t* foundMajor,
|
||||
uint32_t* foundMinor,
|
||||
uint32_t* expectedMajor,
|
||||
uint32_t* expectedMinor,
|
||||
WPI_String* runtimePath) {
|
||||
HMODULE msvcRuntimeModule = GetModuleHandleW(L"msvcp140.dll");
|
||||
if (!msvcRuntimeModule) {
|
||||
return 1;
|
||||
}
|
||||
HRSRC versionResource = FindResourceW(msvcRuntimeModule, MAKEINTRESOURCEW(1),
|
||||
MAKEINTRESOURCEW(16));
|
||||
if (!versionResource) {
|
||||
return 1;
|
||||
}
|
||||
HGLOBAL loadedVersion = LoadResource(msvcRuntimeModule, versionResource);
|
||||
if (!loadedVersion) {
|
||||
return 1;
|
||||
}
|
||||
// No need to unlock. Thats vestigial of windows before my time...
|
||||
LPVOID lockedResource = LockResource(loadedVersion);
|
||||
if (!lockedResource) {
|
||||
return 1;
|
||||
}
|
||||
DWORD resourceSize = SizeofResource(msvcRuntimeModule, versionResource);
|
||||
if (resourceSize == 0) {
|
||||
return 1;
|
||||
}
|
||||
std::unique_ptr<char[]> resourceBuffer = // NOLINT
|
||||
std::make_unique<char[]>(resourceSize); // NOLINT
|
||||
std::memcpy(resourceBuffer.get(), lockedResource, resourceSize);
|
||||
VS_FIXEDFILEINFO* fileInfo = nullptr;
|
||||
UINT fileInfoLen = 0;
|
||||
if (!VerQueryValueW(resourceBuffer.get(), L"\\",
|
||||
reinterpret_cast<void**>(&fileInfo), &fileInfoLen)) {
|
||||
return 1;
|
||||
}
|
||||
*foundMajor = HIWORD(fileInfo->dwProductVersionMS);
|
||||
*foundMinor = LOWORD(fileInfo->dwProductVersionMS);
|
||||
*expectedMajor = 14;
|
||||
*expectedMinor = 40;
|
||||
bool ValidRuntime =
|
||||
*foundMajor != *expectedMajor || *foundMinor >= *expectedMinor;
|
||||
if (!ValidRuntime) {
|
||||
DWORD Size = MAX_PATH;
|
||||
char* ValidBuffer = WPI_AllocateString(runtimePath, Size);
|
||||
DWORD RetVal = GetModuleFileNameA(msvcRuntimeModule, ValidBuffer, Size);
|
||||
while (RetVal == Size) {
|
||||
Size *= 2;
|
||||
WPI_FreeString(runtimePath);
|
||||
ValidBuffer = WPI_AllocateString(runtimePath, Size);
|
||||
RetVal = GetModuleFileNameA(msvcRuntimeModule, ValidBuffer, Size);
|
||||
}
|
||||
runtimePath->len = RetVal;
|
||||
}
|
||||
return ValidRuntime ? 1 : 0;
|
||||
}
|
||||
#else
|
||||
extern "C" int32_t WPI_IsRuntimeValid(uint32_t*, uint32_t*, uint32_t*,
|
||||
uint32_t*, WPI_String*) {
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "wpi/DataLog.h"
|
||||
#include "wpi/FileLogger.h"
|
||||
#include "wpi/RawFrame.h"
|
||||
#include "wpi/RuntimeCheck.h"
|
||||
#include "wpi/Synchronization.h"
|
||||
#include "wpi/jni_util.h"
|
||||
#include "wpi/print.h"
|
||||
@@ -25,13 +26,15 @@ static JException indexOobEx;
|
||||
static JException interruptedEx;
|
||||
static JException ioEx;
|
||||
static JException nullPointerEx;
|
||||
static JException msvcRuntimeEx;
|
||||
|
||||
static const JExceptionInit exceptions[] = {
|
||||
{"java/lang/IllegalArgumentException", &illegalArgEx},
|
||||
{"java/lang/IndexOutOfBoundsException", &indexOobEx},
|
||||
{"java/lang/InterruptedException", &interruptedEx},
|
||||
{"java/io/IOException", &ioEx},
|
||||
{"java/lang/NullPointerException", &nullPointerEx}};
|
||||
{"java/lang/NullPointerException", &nullPointerEx},
|
||||
{"edu/wpi/first/util/MsvcRuntimeException", &msvcRuntimeEx}};
|
||||
|
||||
void wpi::ThrowIllegalArgumentException(JNIEnv* env, std::string_view msg) {
|
||||
illegalArgEx.Throw(env, msg);
|
||||
@@ -78,6 +81,34 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_util_WPIUtilJNI
|
||||
* Method: checkMsvcRuntime
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_util_WPIUtilJNI_checkMsvcRuntime
|
||||
(JNIEnv* env, jclass)
|
||||
{
|
||||
uint32_t foundMajor;
|
||||
uint32_t foundMinor;
|
||||
uint32_t expectedMajor;
|
||||
uint32_t expectedMinor;
|
||||
WPI_String runtimePath;
|
||||
|
||||
if (!WPI_IsRuntimeValid(&foundMajor, &foundMinor, &expectedMajor,
|
||||
&expectedMinor, &runtimePath)) {
|
||||
static jmethodID ctor =
|
||||
env->GetMethodID(msvcRuntimeEx, "<init>", "(IIIILjava/lang/String;)V");
|
||||
jstring jmsvcruntime = MakeJString(env, wpi::to_string_view(&runtimePath));
|
||||
jobject exception =
|
||||
env->NewObject(msvcRuntimeEx, ctor, foundMajor, foundMinor,
|
||||
expectedMajor, expectedMinor, jmsvcruntime);
|
||||
WPI_FreeString(&runtimePath);
|
||||
env->Throw(static_cast<jthrowable>(exception));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_util_WPIUtilJNI
|
||||
* Method: writeStderr
|
||||
|
||||
21
wpiutil/src/main/native/include/wpi/RuntimeCheck.h
Normal file
21
wpiutil/src/main/native/include/wpi/RuntimeCheck.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "wpi/string.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int32_t WPI_IsRuntimeValid(uint32_t* FoundMajor, uint32_t* FoundMinor,
|
||||
uint32_t* ExpectedMajor, uint32_t* ExpectedMinor,
|
||||
WPI_String* RuntimePath);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
Reference in New Issue
Block a user