diff --git a/src/main/Cpp/FridaNative/FridaNative.vcxproj b/src/main/Cpp/FridaNative/FridaNative.vcxproj
new file mode 100644
index 0000000..fa20f3e
--- /dev/null
+++ b/src/main/Cpp/FridaNative/FridaNative.vcxproj
@@ -0,0 +1,164 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {e7f36e99-a575-47f5-ba40-c4877b08c517}
+ FridaNative
+ 10.0
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ C:\Users\Administrator\.jdks\corretto-20.0.2.1\include\win32\bridge;C:\Users\Administrator\.jdks\corretto-20.0.2.1\include\win32;C:\Users\Administrator\.jdks\corretto-20.0.2.1\include;C:\Users\Administrator\Desktop\WindowsHook资源\Frida资源包\include;$(IncludePath)
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;FRIDANATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;FRIDANATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ true
+ true
+ false
+
+
+
+
+ Level3
+ true
+ _DEBUG;FRIDANATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;FRIDANATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ true
+ true
+ false
+ C:\Users\Administrator\Desktop\WindowsHook资源\Frida资源包\lib.x64;%(AdditionalLibraryDirectories)
+ frida-core.lib;winmm.lib;setupapi.lib;dbghelp.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/Cpp/FridaNative/FridaNative.vcxproj.filters b/src/main/Cpp/FridaNative/FridaNative.vcxproj.filters
new file mode 100644
index 0000000..83e48fa
--- /dev/null
+++ b/src/main/Cpp/FridaNative/FridaNative.vcxproj.filters
@@ -0,0 +1,39 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ 头文件
+
+
+ 头文件
+
+
+ 头文件
+
+
+
+
+ 源文件
+
+
+ 源文件
+
+
+ 源文件
+
+
+
\ No newline at end of file
diff --git a/src/main/Cpp/FridaNative/FridaNative.vcxproj.user b/src/main/Cpp/FridaNative/FridaNative.vcxproj.user
new file mode 100644
index 0000000..5df420f
--- /dev/null
+++ b/src/main/Cpp/FridaNative/FridaNative.vcxproj.user
@@ -0,0 +1,6 @@
+
+
+
+ false
+
+
\ No newline at end of file
diff --git a/src/main/Cpp/FridaNative/dllmain.cpp b/src/main/Cpp/FridaNative/dllmain.cpp
new file mode 100644
index 0000000..daed8c8
--- /dev/null
+++ b/src/main/Cpp/FridaNative/dllmain.cpp
@@ -0,0 +1,19 @@
+// dllmain.cpp : 定义 DLL 应用程序的入口点。
+#include "pch.h"
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/src/main/Cpp/FridaNative/framework.h b/src/main/Cpp/FridaNative/framework.h
new file mode 100644
index 0000000..80cbbc9
--- /dev/null
+++ b/src/main/Cpp/FridaNative/framework.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
+// Windows 头文件
+#include
diff --git a/src/main/Cpp/FridaNative/org_tzd_frida_windows_FridaNative.cpp b/src/main/Cpp/FridaNative/org_tzd_frida_windows_FridaNative.cpp
new file mode 100644
index 0000000..9786f1f
--- /dev/null
+++ b/src/main/Cpp/FridaNative/org_tzd_frida_windows_FridaNative.cpp
@@ -0,0 +1,224 @@
+#include "pch.h"
+
+#include "org_tzd_frida_windows_FridaNative.h"
+
+
+#include
+#include
+#include
+
+static void on_message(FridaScript* script, const gchar* message, GBytes* data, gpointer user_data);
+
+static gboolean stop(gpointer user_data);
+static GMainLoop* loop = NULL;
+FridaScript* script;
+FridaSession* session;
+JavaVM* jvm;
+
+/*
+ * Class: org_tzd_frida_windows_FridaNative
+ * Method: injection
+ * Signature: (JLjava/lang/String;)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_tzd_frida_windows_FridaNative_injection
+(JNIEnv* env, jclass clazz, jlong pid, jstring jsCode)
+{
+ if (jsCode == NULL) {
+ std::cerr << "The injected code is a null pointer " << std::endl;
+ return JNI_FALSE;
+ }
+ if (env == NULL)
+ {
+ std::cerr << "JNI environmental issues " << std::endl;
+ return JNI_FALSE;
+ }
+
+ env->GetJavaVM(&jvm);
+
+ const char* functionName = env->GetStringUTFChars(jsCode, nullptr);
+ frida_init();
+
+ GMainContext* context = g_main_context_new();
+ if (context == nullptr) {
+ std::cerr << "Failed to create a new GMainContext" << std::endl;
+ return JNI_FALSE; //
+ }
+
+ loop = g_main_loop_new(NULL, TRUE);
+
+ FridaDeviceManager* manager = frida_device_manager_new();
+ GError* error = nullptr;
+ FridaDeviceList* devices = frida_device_manager_enumerate_devices_sync(manager, nullptr, &error);
+
+ if (error != nullptr) {
+ std::cerr << "Failed to enumerate devices: " << error->message << std::endl;
+ g_error_free(error);
+ return JNI_FALSE;
+ }
+ FridaDevice* local_device = nullptr;
+ guint num_devices = frida_device_list_size(devices);
+ for (guint i = 0; i < num_devices; i++) {
+ FridaDevice* device = frida_device_list_get(devices, i);
+ if (frida_device_get_dtype(device) == FRIDA_DEVICE_TYPE_LOCAL) {
+ local_device = g_object_ref(device);
+ break;
+ }
+ }
+ if (local_device == nullptr) {
+ std::cerr << "Local device not found" << std::endl;
+ return JNI_FALSE;
+ }
+ session = frida_device_attach_sync(local_device, pid, nullptr, nullptr, &error);
+ if (error != nullptr) {
+ std::cerr << "Failed to attach to process: " << error->message << std::endl;
+ g_error_free(error);
+ return JNI_FALSE;
+ }
+ script = frida_session_create_script_sync(session, functionName, nullptr, nullptr, &error);
+ if (error != nullptr) {
+ jclass jsCodeErrorClass = env->FindClass("org/tzd/frida/windows/JsCodeError");
+ if (jsCodeErrorClass == nullptr) {
+ std::cerr << "Failed to find JsCodeError class" << std::endl;
+ return JNI_FALSE;
+ }
+ jmethodID jsCodeErrorCtor = env->GetMethodID(jsCodeErrorClass, "", "(Ljava/lang/String;)V");
+ if (jsCodeErrorCtor == nullptr) {
+ std::cerr << "Failed to find JsCodeError constructor" << std::endl;
+ return JNI_FALSE;
+ }
+ jstring errorMessage = env->NewStringUTF(error->message);
+ jobject jsCodeErrorObj = env->NewObject(jsCodeErrorClass, jsCodeErrorCtor, errorMessage);
+ env->ThrowNew(jsCodeErrorClass, error->message);
+ return JNI_FALSE;
+ }
+ g_signal_connect(script, "message", G_CALLBACK(on_message), NULL);
+ frida_script_load_sync(script, nullptr, &error);
+ if (error != nullptr) {
+ std::cerr << "Failed to load script: " << error->message << std::endl;
+ return JNI_FALSE;
+ }
+ env->ReleaseStringUTFChars(jsCode, functionName);
+ return JNI_TRUE;
+}
+
+/*
+ * Class: org_tzd_frida_windows_FridaNative
+ * Method: update
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_tzd_frida_windows_FridaNative_update
+(JNIEnv*, jclass)
+{
+ g_main_loop_run(loop);
+ return JNI_TRUE;
+}
+
+/*
+ * Class: org_tzd_frida_windows_FridaNative
+ * Method: isRunning
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_tzd_frida_windows_FridaNative_isRunning
+(JNIEnv*, jclass)
+{
+ return g_main_loop_is_running(loop);
+}
+/*
+ * Class: org_tzd_frida_windows_FridaNative
+ * Method: release
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_tzd_frida_windows_FridaNative_release
+(JNIEnv*, jclass)
+{
+ frida_unref(script);
+ frida_unref(session);
+ return JNI_TRUE;
+}
+
+/*
+ * Class: org_tzd_frida_windows_FridaNative
+ * Method: getStringMember
+ * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_tzd_frida_windows_FridaNative_getStringMember
+(JNIEnv* env, jclass, jstring message, jstring member_name)
+{
+ const char* message_cstr = env->GetStringUTFChars(message, nullptr);
+ const char* member_cstr = env->GetStringUTFChars(member_name, nullptr);
+
+ JsonParser* parser = json_parser_new();
+ GError* error = nullptr;
+ if (!json_parser_load_from_data(parser, message_cstr, -1, &error)) {
+ std::cerr << "Failed to parse JSON: " << error->message << std::endl;
+ g_error_free(error);
+ env->ReleaseStringUTFChars(message, message_cstr);
+ env->ReleaseStringUTFChars(member_name, member_cstr);
+ return nullptr;
+ }
+ JsonObject* root = json_node_get_object(json_parser_get_root(parser));
+ const char* value = json_object_get_string_member(root, member_cstr);
+
+ env->ReleaseStringUTFChars(message, message_cstr);
+ env->ReleaseStringUTFChars(member_name, member_cstr);
+
+ if (value == nullptr) {
+ g_object_unref(parser);
+ return env->NewStringUTF("");
+ }
+
+ jstring result = env->NewStringUTF(value);
+ g_object_unref(parser);
+ return result;
+}
+
+void callJavaOnMessage(const gchar* message) {
+ JNIEnv* env = nullptr;
+ jint attachResult = jvm->AttachCurrentThread((void**)&env, nullptr);
+ if (attachResult != JNI_OK) {
+ std::cerr << "Error: Unable to attach current thread to JVM" << std::endl;
+ return;
+ }
+ const char* className = "org/tzd/frida/windows/Frida0";
+ jclass cls = env->FindClass(className);
+ if (cls == nullptr) {
+ std::cerr << "Error: Unable to find class " << className << std::endl;
+ return;
+ }
+ jmethodID methodId = env->GetStaticMethodID(cls, "onMessage", "(Ljava/lang/String;)V");
+ if (methodId == nullptr) {
+ std::cerr << "Error: Unable to find method onMessage" << std::endl;
+ return;
+ }
+ jstring javaMessage = env->NewStringUTF(message);
+ env->CallStaticVoidMethod(cls, methodId, javaMessage);
+ env->DeleteLocalRef(javaMessage);
+ env->DeleteLocalRef(cls);
+}
+
+static void
+on_message(FridaScript* script,
+ const gchar* message,
+ GBytes* data,
+ gpointer user_data)
+{
+ JsonParser* parser;
+ parser = json_parser_new();
+ json_parser_load_from_data(parser, message, -1, NULL);
+ callJavaOnMessage(message);
+ g_object_unref(parser);
+}
+
+static void
+on_signal(int signo)
+{
+ g_idle_add(stop, NULL);
+}
+
+static gboolean
+stop(gpointer user_data)
+{
+ g_main_loop_quit(loop);
+
+ return FALSE;
+}
\ No newline at end of file
diff --git a/src/main/Cpp/FridaNative/org_tzd_frida_windows_FridaNative.h b/src/main/Cpp/FridaNative/org_tzd_frida_windows_FridaNative.h
new file mode 100644
index 0000000..a539a2b
--- /dev/null
+++ b/src/main/Cpp/FridaNative/org_tzd_frida_windows_FridaNative.h
@@ -0,0 +1,53 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include
+/* Header for class org_tzd_frida_windows_FridaNative */
+
+#ifndef _Included_org_tzd_frida_windows_FridaNative
+#define _Included_org_tzd_frida_windows_FridaNative
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_tzd_frida_windows_FridaNative
+ * Method: injection
+ * Signature: (JLjava/lang/String;)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_tzd_frida_windows_FridaNative_injection
+ (JNIEnv *, jclass, jlong, jstring);
+
+/*
+ * Class: org_tzd_frida_windows_FridaNative
+ * Method: update
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_tzd_frida_windows_FridaNative_update
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_tzd_frida_windows_FridaNative
+ * Method: isRunning
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_tzd_frida_windows_FridaNative_isRunning
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_tzd_frida_windows_FridaNative
+ * Method: release
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_tzd_frida_windows_FridaNative_release
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_tzd_frida_windows_FridaNative
+ * Method: getStringMember
+ * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_tzd_frida_windows_FridaNative_getStringMember
+ (JNIEnv *, jclass, jstring, jstring);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/main/Cpp/FridaNative/pch.cpp b/src/main/Cpp/FridaNative/pch.cpp
new file mode 100644
index 0000000..b6fb8f4
--- /dev/null
+++ b/src/main/Cpp/FridaNative/pch.cpp
@@ -0,0 +1,5 @@
+// pch.cpp: 与预编译标头对应的源文件
+
+#include "pch.h"
+
+// 当使用预编译的头时,需要使用此源文件,编译才能成功。
diff --git a/src/main/Cpp/FridaNative/pch.h b/src/main/Cpp/FridaNative/pch.h
new file mode 100644
index 0000000..9660927
--- /dev/null
+++ b/src/main/Cpp/FridaNative/pch.h
@@ -0,0 +1,13 @@
+// pch.h: 这是预编译标头文件。
+// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
+// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
+// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
+// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
+
+#ifndef PCH_H
+#define PCH_H
+
+// 添加要在此处预编译的标头
+#include "framework.h"
+
+#endif //PCH_H
diff --git a/src/main/Cpp/LM/LM.vcxproj b/src/main/Cpp/LM/LM.vcxproj
new file mode 100644
index 0000000..f3d2bc1
--- /dev/null
+++ b/src/main/Cpp/LM/LM.vcxproj
@@ -0,0 +1,165 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {a3131b71-dd4e-41c6-927a-20b8b287fd6c}
+ LM
+ 10.0
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ C:\Users\Administrator\Desktop\llama资源\include;C:\Users\Administrator\.jdks\corretto-20.0.2.1\include\win32;C:\Users\Administrator\.jdks\corretto-20.0.2.1\include;$(IncludePath)
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;LM_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;LM_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ true
+ true
+ false
+
+
+
+
+ Level3
+ true
+ _DEBUG;LM_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;LM_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+ stdcpp17
+
+
+ Windows
+ true
+ true
+ true
+ false
+ C:\Users\Administrator\Desktop\llama资源\lib;%(AdditionalLibraryDirectories)
+ llama.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/Cpp/LM/LM.vcxproj.filters b/src/main/Cpp/LM/LM.vcxproj.filters
new file mode 100644
index 0000000..0bb15f7
--- /dev/null
+++ b/src/main/Cpp/LM/LM.vcxproj.filters
@@ -0,0 +1,39 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ 头文件
+
+
+ 头文件
+
+
+ 头文件
+
+
+
+
+ 源文件
+
+
+ 源文件
+
+
+ 源文件
+
+
+
\ No newline at end of file
diff --git a/src/main/Cpp/LM/LM.vcxproj.user b/src/main/Cpp/LM/LM.vcxproj.user
new file mode 100644
index 0000000..c3809e5
--- /dev/null
+++ b/src/main/Cpp/LM/LM.vcxproj.user
@@ -0,0 +1,9 @@
+
+
+
+ false
+
+
+ WindowsLocalDebugger
+
+
\ No newline at end of file
diff --git a/src/main/Cpp/LM/dllmain.cpp b/src/main/Cpp/LM/dllmain.cpp
new file mode 100644
index 0000000..22af23d
--- /dev/null
+++ b/src/main/Cpp/LM/dllmain.cpp
@@ -0,0 +1,21 @@
+// dllmain.cpp : 定义 DLL 应用程序的入口点。
+#include "pch.h"
+
+#include "llama.h"
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/src/main/Cpp/LM/framework.h b/src/main/Cpp/LM/framework.h
new file mode 100644
index 0000000..80cbbc9
--- /dev/null
+++ b/src/main/Cpp/LM/framework.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
+// Windows 头文件
+#include
diff --git a/src/main/Cpp/LM/org_tzd_lm_LM.cpp b/src/main/Cpp/LM/org_tzd_lm_LM.cpp
new file mode 100644
index 0000000..0b529ab
--- /dev/null
+++ b/src/main/Cpp/LM/org_tzd_lm_LM.cpp
@@ -0,0 +1,419 @@
+#include "pch.h"
+#include "org_tzd_lm_LM.h"
+
+#include
+#include
+
+#include "llama.h"
+#include
+#include
+#include
+#include
+#include
+
+// صȫ
+static jmethodID gMessageCallbackMethodID = nullptr;
+static jmethodID gProgressCallbackMethodID = nullptr;
+JNIEnv* env_;
+jobject messageCallback_;
+jobject progressCallback__;
+
+static bool isRun = true;
+
+bool ToCppBool(jboolean value) {
+ return value == JNI_TRUE;
+}
+
+bool llamaProgressCallback(float progress, void* user_data) {
+ JNIEnv* env = (JNIEnv*)user_data;
+ jint j_progress = progress;
+ jboolean ret = env->CallBooleanMethod(progressCallback__, gProgressCallbackMethodID, j_progress);
+ return ToCppBool(ret);
+}
+
+//--------------------------------------------------
+// ģͼغͷ
+//--------------------------------------------------
+
+JNIEXPORT jlong JNICALL Java_org_tzd_lm_LM_llamaLoadModelFromFile
+(JNIEnv* env, jclass clazz, jstring pathModel,
+ jboolean vocab_only_jboolean,
+ jboolean use_mmap_jboolean, jboolean use_mlock_jboolean,jboolean check_tensors_jboolean, jobject progressCallback) {
+ const char* path = env->GetStringUTFChars(pathModel, nullptr);
+ if (!path) {
+ return 0;
+ }
+ progressCallback__ = progressCallback;
+ llama_model_params params = llama_model_default_params();
+ if (progressCallback && !gProgressCallbackMethodID) {
+ jclass callbackClass = env->GetObjectClass(progressCallback);
+ if (!callbackClass) {
+ return 0;
+ }
+ gProgressCallbackMethodID = env->GetMethodID(callbackClass, "onModelLoad", "(F)Z");
+ if (!gProgressCallbackMethodID) {
+ return 0;
+ }
+ params.progress_callback = llamaProgressCallback;
+ params.progress_callback_user_data = env;
+ }
+
+ bool vocab_only = ToCppBool(vocab_only_jboolean);
+ bool use_mmap = ToCppBool(use_mmap_jboolean);
+ bool use_mlock = ToCppBool(use_mlock_jboolean);
+ bool check_tensors = ToCppBool(check_tensors_jboolean);
+
+ params.vocab_only = static_cast(vocab_only);
+ params.use_mmap = static_cast(use_mmap);
+ params.use_mlock = static_cast(use_mlock);
+ params.check_tensors = static_cast(check_tensors);
+
+ llama_model* model = llama_model_load_from_file(path, params);
+ env->ReleaseStringUTFChars(pathModel, path);
+
+ if (!model) {
+ jclass exClass = env->FindClass("java/io/IOException");
+ if (exClass) {
+ env->ThrowNew(exClass, "Failed to load model: check path and parameters");
+ }
+ return 0;
+ }
+
+ return reinterpret_cast(model);
+}
+
+JNIEXPORT void JNICALL Java_org_tzd_lm_LM_llamaFreeModel
+(JNIEnv* env, jclass clazz, jlong modelHandle) {
+ llama_model* model = reinterpret_cast(modelHandle);
+ llama_model_free(model); // ʹµ API
+}
+
+//--------------------------------------------------
+// Ĵ
+//--------------------------------------------------
+JNIEXPORT jlong JNICALL Java_org_tzd_lm_LM_createContext
+(JNIEnv* env, jclass clazz, jlong modelHandle, jint nCtx,
+ jint nBatch,
+ jint nSeqMax,
+ jint nThreads,
+ jint nThreadsBatch,
+ jboolean logitsAll,
+ jboolean embeddings,
+ jboolean offloadKqv,
+ jboolean flashAttn,
+ jboolean noPerf
+) {
+ llama_model* model = reinterpret_cast(modelHandle);
+ if (!model) {
+ jclass exClass = env->FindClass("java/lang/IllegalArgumentException");
+ if (exClass) {
+ env->ThrowNew(exClass, "Invalid model handle");
+ }
+ return 0;
+ }
+
+ llama_context_params ctx_params = llama_context_default_params();
+
+ if (nCtx != 0) {
+ ctx_params.n_ctx = nCtx;
+ }
+ if (nBatch != 0) {
+ ctx_params.n_batch = nBatch;
+ }
+ if (nSeqMax != 0) {
+ ctx_params.n_seq_max = nSeqMax;
+ }
+ if (nThreads != 0) {
+ ctx_params.n_threads = nThreads;
+ }
+ if (nThreadsBatch != 0) {
+ ctx_params.n_threads_batch = nThreadsBatch;
+ }
+
+ ctx_params.logits_all = static_cast(logitsAll);
+ ctx_params.embeddings = static_cast(embeddings);
+ ctx_params.offload_kqv = static_cast(offloadKqv);
+ ctx_params.flash_attn = static_cast(flashAttn);
+ ctx_params.no_perf = static_cast(noPerf);
+
+ llama_context* ctx = llama_init_from_model(model, ctx_params);
+ if (!ctx) {
+ jclass exClass = env->FindClass("java/io/IOException");
+ if (exClass) {
+ env->ThrowNew(exClass, "Failed to create context");
+ }
+ return 0;
+ }
+
+ return reinterpret_cast(ctx);
+}
+
+JNIEXPORT void JNICALL Java_org_tzd_lm_LM_llamaFreeContext
+(JNIEnv* env, jclass clazz, jlong ctxHandle)
+{
+ llama_context* ctx = reinterpret_cast(ctxHandle);
+ llama_kv_cache_clear(ctx);
+ llama_free(ctx);
+ isRun = false;
+}
+
+static int tokenize_prompt(
+ const llama_vocab* vocab,
+ const std::string& prompt,
+ std::vector& prompt_tokens,
+ llama_context* context
+) {
+ const bool is_first = llama_get_kv_cache_used_cells(context) == 0;
+
+ const int n_prompt_tokens = -llama_tokenize(vocab,
+ prompt.c_str(),
+ prompt.size(),
+ NULL,
+ 0,
+ is_first,
+ true);
+ prompt_tokens.resize(n_prompt_tokens);
+ if (llama_tokenize(vocab, prompt.c_str(), prompt.size(), prompt_tokens.data(), prompt_tokens.size(), is_first,
+ true) < 0) {
+ printf("failed to tokenize the prompt\n");
+ return -1;
+ }
+ return n_prompt_tokens;
+}
+
+static int check_context_size(
+ const llama_context* ctx,
+ const llama_batch& batch
+) {
+ const int n_ctx = llama_n_ctx(ctx);
+ const int n_ctx_used = llama_get_kv_cache_used_cells(ctx);
+ if (n_ctx_used + batch.n_tokens > n_ctx) {
+ printf("context size exceeded\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int convert_token_to_string(const llama_vocab* vocab, const llama_token token_id, std::string& piece) {
+ char buf[256];
+ int n = llama_token_to_piece(vocab, token_id, buf, sizeof(buf), 0, true);
+ if (n < 0) {
+ printf("failed to convert token to piece\n");
+ return 1;
+ }
+
+ piece = std::string(buf, n);
+ return 0;
+}
+static void print_word_and_concatenate_to_response(const std::string& piece, std::string& response) {
+ jstring message = env_->NewStringUTF(piece.c_str());
+ if (message) {
+ env_->CallVoidMethod(messageCallback_, gMessageCallbackMethodID, message);
+ env_->DeleteLocalRef(message);
+ }
+ fflush(stdout);
+ response += piece;
+}
+
+static int apply_chat_template_with_error_handling(const bool append, std::string response, int& output_length) {
+ if (!append)
+ {
+ const int new_len = response.length();
+ if (new_len < 0) {
+ printf("failed to apply the chat template\n");
+ return -1;
+ }
+
+ output_length = new_len;
+ }
+ return 0;
+}
+
+std::vector tokens;
+
+static int generate(
+ llama_model* llama_data,
+ llama_context* context,
+ llama_sampler* smpl,
+ const std::string& prompt,
+ std::string& response
+) {
+ //llama_kv_cache_clear(context);
+
+ const llama_vocab* vocab = llama_model_get_vocab(llama_data);
+ isRun = true;
+ if (tokenize_prompt(vocab, prompt, tokens, context) < 0) {
+ return 1;
+ }
+
+ llama_batch batch = llama_batch_get_one(tokens.data(), tokens.size());
+ llama_token new_token_id;
+ while (true) {
+ check_context_size(context, batch);
+ if (llama_decode(context, batch)) {
+ printf("\nfailed to decode\n");
+ return 1;
+ }
+
+ new_token_id = llama_sampler_sample(smpl, context, -1);
+ if (llama_vocab_is_eog(vocab, new_token_id)) {
+ break;
+ }
+
+ std::string piece;
+ if (convert_token_to_string(vocab, new_token_id, piece)) {
+ return 1;
+ }
+ print_word_and_concatenate_to_response(piece, response);
+ batch = llama_batch_get_one(&new_token_id, 1);
+ if (!isRun) {
+ return 0;
+ }
+ }
+ return 0;
+}
+
+llama_sampler* initialize_sampler(float temperature,
+ float min_p,
+ float top_k,
+ float top_p,
+ float dist,
+ int32_t penalty_last_n,
+ float penalty_repeat,
+ float penalty_freq,
+ float penalty_present
+) {
+ if (!dist) {
+ dist = LLAMA_DEFAULT_SEED;
+ }
+ llama_sampler_chain_params params = llama_sampler_chain_default_params();
+ llama_sampler* sampler = llama_sampler_chain_init(params);
+
+ // ˳ʾ˳
+ llama_sampler_chain_add(sampler, llama_sampler_init_penalties(penalty_last_n, penalty_repeat, penalty_freq, penalty_present));
+ llama_sampler_chain_add(sampler, llama_sampler_init_top_k(top_k));
+ llama_sampler_chain_add(sampler, llama_sampler_init_top_p(top_p, 1));
+ llama_sampler_chain_add(sampler, llama_sampler_init_temp(temperature));
+ llama_sampler_chain_add(sampler, llama_sampler_init_min_p(min_p, 1));
+ llama_sampler_chain_add(sampler, llama_sampler_init_dist(dist));
+
+ // ƳDZҪ̰
+ // llama_sampler_chain_add(sampler, llama_sampler_init_greedy());
+
+ return sampler;
+}
+
+
+// UTF-16 jstring תΪ UTF-8 std::string
+std::string jstringToUTF8(JNIEnv* env, jstring jstr) {
+ if (!jstr) return "";
+
+ const jchar* raw = env->GetStringChars(jstr, nullptr);
+ if (!raw) return "";
+
+ jsize len = env->GetStringLength(jstr);
+
+ // UTF-16 תΪ UTF-8
+ int utf8Size = WideCharToMultiByte(CP_UTF8, 0, reinterpret_cast(raw), len, nullptr, 0, nullptr, nullptr);
+ std::string utf8(utf8Size, 0);
+ WideCharToMultiByte(CP_UTF8, 0, reinterpret_cast(raw), len, &utf8[0], utf8Size, nullptr, nullptr);
+
+ env->ReleaseStringChars(jstr, raw);
+ return utf8;
+}
+
+// UTF-8 std::string תΪ jstring
+jstring utf8ToJstring(JNIEnv* env, const std::string& utf8) {
+ // UTF-8 תΪ UTF-16
+ int utf16Size = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, nullptr, 0);
+ std::wstring utf16(utf16Size, 0);
+ MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, &utf16[0], utf16Size);
+
+ return env->NewString(reinterpret_cast(utf16.c_str()), utf16Size - 1);
+}
+
+//--------------------------------------------------
+// ʵ
+//--------------------------------------------------
+JNIEXPORT jstring JNICALL Java_org_tzd_lm_LM_inference
+(JNIEnv* env, jclass clazz, jlong modelHandle, jlong ctxHandle, jfloat temperature, jfloat minP,
+ jfloat topK, jfloat topP, jfloat dist, jint penaltyLastN, jfloat penaltyRepeat, jfloat penaltyFreq,
+ jfloat penaltyPresent, jstring prompt, jobject messageCallback) {
+ llama_context* ctx = reinterpret_cast(ctxHandle);
+ llama_model* model = reinterpret_cast(modelHandle);
+ env_ = env;
+ messageCallback_ = messageCallback;
+
+ // ctx ǷЧ
+ if (!ctx) {
+ jclass exClass = env->FindClass("java/lang/IllegalArgumentException");
+ if (exClass) env->ThrowNew(exClass, "Invalid context handle");
+ return nullptr;
+ }
+
+
+ // ʼصID
+ if (!gMessageCallbackMethodID) {
+ jclass callbackClass = env->GetObjectClass(messageCallback);
+ if (!callbackClass) {
+ return nullptr;
+ }
+ gMessageCallbackMethodID = env->GetMethodID(callbackClass, "onMessage", "(Ljava/lang/String;)V");
+ if (!gMessageCallbackMethodID) {
+ return nullptr;
+ }
+ }
+
+ // Ӧַ
+ std::string response;
+ std::string prompt_(jstringToUTF8(env, prompt));
+ // ʹóʼIJɽ
+ llama_sampler* sampler = initialize_sampler(temperature, minP, topK,
+ topP, dist, penaltyLastN, penaltyRepeat,
+ penaltyFreq, penaltyPresent);
+
+ if (generate(model, ctx, sampler, prompt_, response) != 0) {
+ return nullptr;
+ }
+
+ return utf8ToJstring(env, response);
+}
+
+JNIEXPORT jboolean JNICALL Java_org_tzd_lm_LM_llamaStateLoadFile(
+ JNIEnv* env, jobject obj, jlong ctx, jstring pathSession,
+ jlongArray tokensOut, jint nTokenCapacity, jintArray nTokenCountOut
+) {
+ const char* path = env->GetStringUTFChars(pathSession, NULL);
+
+ jlong* tokens_out = env->GetLongArrayElements(tokensOut, NULL);
+ jint* n_token_count_out = env->GetIntArrayElements(nTokenCountOut, NULL);
+
+ bool result = llama_state_load_file((struct llama_context*)ctx, path,
+ (llama_token*)tokens_out,
+ nTokenCapacity,
+ (size_t*)n_token_count_out);
+
+ env->ReleaseStringUTFChars(pathSession, path);
+ env->ReleaseLongArrayElements(tokensOut, tokens_out, 0);
+ env->ReleaseIntArrayElements(nTokenCountOut, n_token_count_out, 0);
+
+ return result ? JNI_TRUE : JNI_FALSE;
+}
+
+JNIEXPORT jboolean JNICALL Java_org_tzd_lm_LM_llamaStateSaveFile(
+ JNIEnv* env, jobject obj, jlong ctx, jstring pathSession,
+ jlongArray tokens, jint nTokenCount
+) {
+ const char* path = env->GetStringUTFChars(pathSession, NULL);
+
+ jlong* tokens_array = env->GetLongArrayElements(tokens, NULL);
+
+ bool result = llama_state_save_file((struct llama_context*)ctx, path,
+ (const llama_token*)tokens_array,
+ nTokenCount);
+
+ env->ReleaseStringUTFChars(pathSession, path);
+ env->ReleaseLongArrayElements(tokens, tokens_array, 0);
+ return result ? JNI_TRUE : JNI_FALSE;
+}
\ No newline at end of file
diff --git a/src/main/Cpp/LM/org_tzd_lm_LM.h b/src/main/Cpp/LM/org_tzd_lm_LM.h
new file mode 100644
index 0000000..8408a06
--- /dev/null
+++ b/src/main/Cpp/LM/org_tzd_lm_LM.h
@@ -0,0 +1,67 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include
+/* Header for class org_tzd_lm_LM */
+
+#ifndef _Included_org_tzd_lm_LM
+#define _Included_org_tzd_lm_LM
+#ifdef __cplusplus
+extern "C" {
+#endif
+ /*
+ * Class: org_tzd_lm_LM
+ * Method: llamaLoadModelFromFile
+ * Signature: (Ljava/lang/String;)J
+ */
+ JNIEXPORT jlong JNICALL Java_org_tzd_lm_LM_llamaLoadModelFromFile
+ (JNIEnv*, jclass, jstring, jboolean , jboolean, jboolean, jboolean, jobject);
+
+ /*
+ * Class: org_tzd_lm_LM
+ * Method: llamaFreeModel
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_org_tzd_lm_LM_llamaFreeModel
+ (JNIEnv*, jclass, jlong);
+
+ /*
+ * Class: org_tzd_lm_LM
+ * Method: createContext
+ * Signature: (J)J
+ */
+ JNIEXPORT jlong JNICALL Java_org_tzd_lm_LM_createContext
+ (JNIEnv* env, jclass clazz, jlong modelHandle, jint nCtx,
+ jint nBatch,
+ jint nSeqMax,
+ jint nThreads,
+ jint nThreadsBatch,
+ jboolean logitsAll,
+ jboolean embeddings,
+ jboolean offloadKqv,
+ jboolean flashAttn,
+ jboolean noPerf);
+
+ JNIEXPORT void JNICALL Java_org_tzd_lm_LM_llamaFreeContext
+ (JNIEnv*, jclass, jlong);
+
+ /*
+ * Class: org_tzd_lm_LM
+ * Method: inference
+ * Signature: (JLjava/lang/String;Lorg/tzd/lm/LM/MessageCallback;)Ljava/lang/String;
+ */
+ JNIEXPORT jstring JNICALL Java_org_tzd_lm_LM_inference
+ (JNIEnv* env, jclass clazz, jlong modelHandle, jlong ctxHandle, jfloat temperature, jfloat minP, jfloat topK, jfloat topP, jfloat dist, jint penaltyLastN, jfloat penaltyRepeat, jfloat penaltyFreq, jfloat penaltyPresent, jstring prompt, jobject messageCallback);
+
+ JNIEXPORT jboolean JNICALL Java_org_tzd_lm_LM_llamaStateSaveFile(
+ JNIEnv* env, jobject obj, jlong ctx, jstring pathSession,
+ jlongArray tokens, jint nTokenCount
+ );
+
+ JNIEXPORT jboolean JNICALL Java_org_tzd_lm_LM_llamaStateLoadFile(
+ JNIEnv* env, jobject obj, jlong ctx, jstring pathSession,
+ jlongArray tokensOut, jint nTokenCapacity, jintArray nTokenCountOut
+ );
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/main/Cpp/LM/pch.cpp b/src/main/Cpp/LM/pch.cpp
new file mode 100644
index 0000000..b6fb8f4
--- /dev/null
+++ b/src/main/Cpp/LM/pch.cpp
@@ -0,0 +1,5 @@
+// pch.cpp: 与预编译标头对应的源文件
+
+#include "pch.h"
+
+// 当使用预编译的头时,需要使用此源文件,编译才能成功。
diff --git a/src/main/Cpp/LM/pch.h b/src/main/Cpp/LM/pch.h
new file mode 100644
index 0000000..9660927
--- /dev/null
+++ b/src/main/Cpp/LM/pch.h
@@ -0,0 +1,13 @@
+// pch.h: 这是预编译标头文件。
+// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
+// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
+// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
+// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
+
+#ifndef PCH_H
+#define PCH_H
+
+// 添加要在此处预编译的标头
+#include "framework.h"
+
+#endif //PCH_H
diff --git a/src/main/Cpp/LM/tiktoken.h b/src/main/Cpp/LM/tiktoken.h
new file mode 100644
index 0000000..8624b34
--- /dev/null
+++ b/src/main/Cpp/LM/tiktoken.h
@@ -0,0 +1,269 @@
+#pragma once
+
+#include
+#include "unordered_dense.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace tiktoken {
+
+static auto _byte_pair_merge(
+ const std::string &piece,
+ const ankerl::unordered_dense::map &ranks,
+ std::function func
+) -> std::vector {
+ std::vector> parts;
+ parts.reserve(piece.size() + 1);
+ for (auto idx = 0U; idx < piece.size() + 1; ++idx) {
+ parts.emplace_back(idx, std::numeric_limits::max());
+ }
+
+ auto get_rank = [&piece, &ranks](
+ const std::vector> &parts,
+ int start_idx,
+ int skip
+ ) -> std::optional {
+ if (start_idx + skip + 2 < parts.size()) {
+ auto s = parts[start_idx].first;
+ auto e = parts[start_idx + skip + 2].first;
+ auto key = piece.substr(s, e - s);
+ auto iter = ranks.find(key);
+ if (iter != ranks.end()) {
+ return iter->second;
+ }
+ }
+ return std::nullopt;
+ };
+
+ for (auto i = 0U; i < parts.size() - 2; ++i) {
+ auto rank = get_rank(parts, i, 0);
+ if (rank) {
+ assert(*rank != std::numeric_limits::max());
+ parts[i].second = *rank;
+ }
+ }
+
+ while (true) {
+ if (parts.size() == 1) break;
+
+ auto min_rank = std::make_pair(std::numeric_limits::max(), 0);
+ for (auto i = 0U; i < parts.size() - 1; ++i) {
+ auto rank = parts[i].second;
+ if (rank < min_rank.first) {
+ min_rank = { rank, i };
+ }
+ }
+
+ if (min_rank.first != std::numeric_limits::max()) {
+ auto i = min_rank.second;
+ auto rank = get_rank(parts, i, 1);
+ if (rank) {
+ parts[i].second = *rank;
+ } else {
+ parts[i].second = std::numeric_limits::max();
+ }
+ if (i > 0) {
+ auto rank = get_rank(parts, i - 1, 1);
+ if (rank) {
+ parts[i - 1].second = *rank;
+ } else {
+ parts[i - 1].second = std::numeric_limits::max();
+ }
+ }
+
+ parts.erase(parts.begin() + (i + 1));
+ } else {
+ break;
+ }
+ }
+ std::vector out;
+ out.reserve(parts.size() - 1);
+ for (auto i = 0U; i < parts.size() - 1; ++i) {
+ out.push_back(func(parts[i].first, parts[i + 1].first));
+ }
+ return out;
+}
+
+static auto byte_pair_encode(
+ const std::string &piece,
+ const ankerl::unordered_dense::map &ranks
+) -> std::vector {
+ if (piece.size() == 1) {
+ return {ranks.at(piece)};
+ }
+
+ auto func = [&piece, &ranks](int start, int stop) -> int {
+ std::string key = piece.substr(start, stop - start);
+ return ranks.at(key);
+ };
+
+ return _byte_pair_merge(piece, ranks, func);
+}
+
+class tiktoken {
+ public:
+ tiktoken() = default;
+ tiktoken(
+ ankerl::unordered_dense::map encoder,
+ ankerl::unordered_dense::map special_encoder,
+ const std::string &pattern
+ ) {
+ regex_ = std::make_unique("(" + pattern + ")");
+
+ std::string special_pattern;
+ for (const auto &item : special_encoder) {
+ if (!special_pattern.empty()) {
+ special_pattern += "|";
+ }
+ special_pattern += re2::RE2::QuoteMeta(item.first);
+ }
+ if (special_pattern.empty()) {
+ special_regex_ = nullptr;
+ } else {
+ special_regex_ = std::make_unique("(" + special_pattern + ")");
+ }
+
+ encoder_ = std::move(encoder);
+ special_tokens_encoder = std::move(special_encoder);
+
+ for (const auto &[k, v] : encoder_) {
+ decoder_.emplace(v, k);
+ }
+ assert(encoder_.size() != decoder_.size() && "Encoder and decoder must be of equal length; maybe you had duplicate token indices in your encoder?");
+
+ for (const auto &[k, v] : special_tokens_encoder) {
+ special_tokens_decoder.emplace(v, k);
+ }
+ }
+
+ auto encode_ordinary(const std::string &text) const -> std::vector {
+ return _encode_ordinary_native(text);
+ }
+
+ auto encode(const std::string &text) const -> std::vector {
+ return _encode_native(text, special_tokens_encoder).first;
+ }
+
+ auto encode_single_piece(const std::string &text) const -> std::vector {
+ auto iter = encoder_.find(text);
+ if (iter != encoder_.end()) {
+ return {iter->second};
+ }
+ return byte_pair_encode(text, encoder_);
+ }
+
+ auto decode(const std::vector &tokens) const -> std::string {
+ return _decode_native(tokens);
+ }
+
+ private:
+ auto split_with_allowed_special_token(
+ re2::StringPiece &input,
+ const ankerl::unordered_dense::map &allowed_special
+ ) const -> std::pair, re2::StringPiece> {
+ if (special_regex_ == nullptr) return { std::nullopt, input };
+
+ auto start = input.begin();
+ std::string special;
+ while (true) {
+ if (!re2::RE2::FindAndConsume(&input, *special_regex_, &special)) {
+ break;
+ }
+
+ if (allowed_special.count(special) == 1) {
+ return { std::move(special), re2::StringPiece(start, input.begin() - start - special.size()) };
+ }
+ }
+
+ return { std::nullopt, input };
+ }
+
+ auto _encode_ordinary_native(const std::string &text) const -> std::vector {
+ std::vector ret;
+ re2::StringPiece input(text);
+
+ std::string piece;
+ while (re2::RE2::FindAndConsume(&input, *regex_, &piece)) {
+ auto iter = encoder_.find(piece);
+ if (iter != encoder_.end()) {
+ ret.push_back(iter->second);
+ continue;
+ }
+ auto tokens = byte_pair_encode(piece, encoder_);
+ ret.insert(ret.end(), tokens.begin(), tokens.end());
+ }
+ return ret;
+ }
+
+ auto _encode_native(
+ const std::string &text,
+ const ankerl::unordered_dense::map &allowed_special
+ ) const -> std::pair, int> {
+ std::vector ret;
+ int last_piece_token_len = 0;
+ re2::StringPiece input(text);
+
+ while (true) {
+ auto [special, sub_input] = split_with_allowed_special_token(input, allowed_special);
+ std::string piece;
+ while (re2::RE2::FindAndConsume(&sub_input, *regex_, &piece)) {
+ auto iter = encoder_.find(piece);
+ if (iter != encoder_.end()) {
+ last_piece_token_len = 1;
+ ret.push_back(iter->second);
+ continue;
+ }
+ auto tokens = byte_pair_encode(piece, encoder_);
+ last_piece_token_len = tokens.size();
+ ret.insert(ret.end(), tokens.begin(), tokens.end());
+ }
+
+ if (special) {
+ int token = special_tokens_encoder.at(*special);
+ ret.push_back(token);
+ last_piece_token_len = 0;
+ } else {
+ break;
+ }
+ }
+
+ return { ret, last_piece_token_len };
+ }
+
+ auto _decode_native(const std::vector &tokens) const -> std::string {
+ std::string ret;
+ ret.reserve(tokens.size() * 2);
+ for (auto token : tokens) {
+ std::string token_bytes;
+ auto iter = decoder_.find(token);
+ if (iter != decoder_.end()) {
+ token_bytes = iter->second;
+ } else {
+ iter = special_tokens_decoder.find(token);
+ if (iter != special_tokens_decoder.end()) {
+ token_bytes = iter->second;
+ } else {
+ throw std::runtime_error("unknown token: " + std::to_string(token));
+ }
+ }
+ ret += token_bytes;
+ }
+ return ret;
+ }
+
+ ankerl::unordered_dense::map encoder_;
+ ankerl::unordered_dense::map special_tokens_encoder;
+ ankerl::unordered_dense::map decoder_;
+ ankerl::unordered_dense::map special_tokens_decoder;
+ std::unique_ptr regex_;
+ std::unique_ptr special_regex_;
+};
+
+} // namespace tiktoken
diff --git a/src/main/Cpp/RegisterTray/RegisterTray.vcxproj b/src/main/Cpp/RegisterTray/RegisterTray.vcxproj
new file mode 100644
index 0000000..2d5a493
--- /dev/null
+++ b/src/main/Cpp/RegisterTray/RegisterTray.vcxproj
@@ -0,0 +1,164 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {8afd6796-6969-4cd3-a0e9-e9320a3d2163}
+ RegisterTray
+ 10.0.18362.0
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ C:\Users\Administrator\.jdks\corretto-20.0.2.1\include\win32;C:\Users\Administrator\.jdks\corretto-20.0.2.1\include;$(IncludePath)
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;REGISTERTRAY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;REGISTERTRAY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ true
+ true
+ false
+
+
+
+
+ Level3
+ true
+ _DEBUG;REGISTERTRAY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;REGISTERTRAY_EXPORTS;_WINDOWS;_USRDLL;NOMINMAX;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+ stdcpp17
+
+
+ Windows
+ true
+ true
+ true
+ false
+ jvm.lib;%(AdditionalDependencies)
+ C:\Users\Administrator\.jdks\corretto-20.0.2.1\lib
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/Cpp/RegisterTray/RegisterTray.vcxproj.filters b/src/main/Cpp/RegisterTray/RegisterTray.vcxproj.filters
new file mode 100644
index 0000000..e262026
--- /dev/null
+++ b/src/main/Cpp/RegisterTray/RegisterTray.vcxproj.filters
@@ -0,0 +1,36 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ 头文件
+
+
+ 头文件
+
+
+ 头文件
+
+
+
+
+ 源文件
+
+
+ 源文件
+
+
+
\ No newline at end of file
diff --git a/src/main/Cpp/RegisterTray/RegisterTray.vcxproj.user b/src/main/Cpp/RegisterTray/RegisterTray.vcxproj.user
new file mode 100644
index 0000000..88a5509
--- /dev/null
+++ b/src/main/Cpp/RegisterTray/RegisterTray.vcxproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/main/Cpp/RegisterTray/com_axis_innovators_box_tools_RegisterTray.h b/src/main/Cpp/RegisterTray/com_axis_innovators_box_tools_RegisterTray.h
new file mode 100644
index 0000000..cf07f79
--- /dev/null
+++ b/src/main/Cpp/RegisterTray/com_axis_innovators_box_tools_RegisterTray.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include
+/* Header for class com_axis_innovators_box_tools_RegisterTray */
+
+#ifndef _Included_com_axis_innovators_box_tools_RegisterTray
+#define _Included_com_axis_innovators_box_tools_RegisterTray
+#ifdef __cplusplus
+extern "C" {
+#endif
+ /*
+ * Class: com_axis_innovators_box_tools_RegisterTray
+ * Method: register
+ * Signature: (Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Lcom/axis/innovators/box/tools/RegisterTray/Event;)J
+ */
+ JNIEXPORT jlong JNICALL Java_com_axis_innovators_box_tools_RegisterTray_register
+ (JNIEnv*, jclass, jstring, jobject, jstring, jstring, jobject);
+
+ /*
+ * Class: com_axis_innovators_box_tools_RegisterTray
+ * Method: unregister
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_com_axis_innovators_box_tools_RegisterTray_unregister
+ (JNIEnv*, jclass, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/main/Cpp/RegisterTray/dllmain.cpp b/src/main/Cpp/RegisterTray/dllmain.cpp
new file mode 100644
index 0000000..940bcf2
--- /dev/null
+++ b/src/main/Cpp/RegisterTray/dllmain.cpp
@@ -0,0 +1,291 @@
+#include "pch.h"
+#include
+#include
+#include
+#include
+#include
+#include "com_axis_innovators_box_tools_RegisterTray.h"
+
+// 调试输出
+#define DEBUG_LOG(msg) OutputDebugStringW(L"[Tray] " msg L"\n")
+
+struct MenuItemData {
+ jmethodID onClickMethod;
+ jobject eventObj;
+ int menuId;
+};
+
+struct TrayData {
+ HMENU hMenu = NULL;
+ std::vector menuItems;
+ jobject eventObj = NULL;
+ jmethodID onClickMethod = NULL;
+ HWND hwnd = NULL;
+ UINT trayId = 0;
+ HICON hIcon = NULL;
+};
+
+std::vector trayDataList;
+
+HICON LoadTrayIcon(const wchar_t* path) {
+ HICON hIcon = (HICON)LoadImageW(
+ NULL, path, IMAGE_ICON,
+ GetSystemMetrics(SM_CXSMICON),
+ GetSystemMetrics(SM_CYSMICON),
+ LR_LOADFROMFILE | LR_SHARED
+ );
+ return hIcon ? hIcon : LoadIconW(NULL, IDI_APPLICATION);
+}
+
+LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+ TrayData* pData = (TrayData*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_USER + 1:
+ switch (lParam) {
+ case WM_RBUTTONUP:
+ if (pData && pData->hMenu) {
+ POINT pt;
+ GetCursorPos(&pt);
+ SetForegroundWindow(hwnd);
+ TrackPopupMenuEx(pData->hMenu,
+ TPM_RIGHTALIGN | TPM_BOTTOMALIGN,
+ pt.x, pt.y, hwnd, NULL);
+ PostMessageW(hwnd, WM_NULL, 0, 0);
+ }
+ return 0;
+ case WM_LBUTTONDOWN:
+ if (pData && pData->onClickMethod) {
+ JavaVM* jvm;
+ JNIEnv* env;
+ if (JNI_GetCreatedJavaVMs(&jvm, 1, NULL) == JNI_OK &&
+ jvm->AttachCurrentThread((void**)&env, NULL) == JNI_OK) {
+ env->CallVoidMethod(pData->eventObj, pData->onClickMethod, (jlong)pData->trayId);
+ jvm->DetachCurrentThread();
+ }
+ }
+ return 0;
+ }
+ break;
+ case WM_COMMAND: {
+ int menuId = LOWORD(wParam);
+ if (pData) {
+ for (auto& item : pData->menuItems) {
+ if (item.menuId == menuId) {
+ JavaVM* jvm;
+ JNIEnv* env;
+ if (JNI_GetCreatedJavaVMs(&jvm, 1, NULL) == JNI_OK &&
+ jvm->AttachCurrentThread((void**)&env, NULL) == JNI_OK) {
+ env->CallVoidMethod(item.eventObj, item.onClickMethod, (jlong)pData->trayId);
+ jvm->DetachCurrentThread();
+ }
+ break;
+ }
+ }
+ }
+ return 0;
+ }
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ return 0;
+ }
+ return DefWindowProcW(hwnd, msg, wParam, lParam);
+}
+
+JNIEXPORT jlong JNICALL Java_com_axis_innovators_box_tools_RegisterTray_register
+(JNIEnv* env, jclass, jstring name, jobject menuItems, jstring icon, jstring, jobject event) {
+ // 注册窗口类
+ WNDCLASSEXW wc = { sizeof(WNDCLASSEXW) };
+ wc.lpfnWndProc = WndProc;
+ wc.hInstance = GetModuleHandleW(NULL);
+ wc.lpszClassName = L"TrayWindowClass";
+ if (!RegisterClassExW(&wc)) return -1;
+
+ // 创建消息窗口
+ HWND hwnd = CreateWindowExW(0, L"TrayWindowClass", L"", 0, 0, 0, 0, 0,
+ HWND_MESSAGE, NULL, NULL, NULL);
+ if (!hwnd) return -1;
+
+ TrayData* pData = new TrayData();
+ pData->hwnd = hwnd;
+ pData->trayId = GetTickCount();
+ pData->hMenu = CreatePopupMenu();
+
+ // 解析菜单项
+ jclass listClass = env->GetObjectClass(menuItems);
+ jint size = env->CallIntMethod(menuItems, env->GetMethodID(listClass, "size", "()I"));
+
+ for (int i = 0; i < size; ++i) {
+ jobject item = env->CallObjectMethod(menuItems,
+ env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;"), i);
+
+ jstring name = (jstring)env->GetObjectField(item,
+ env->GetFieldID(env->GetObjectClass(item), "name", "Ljava/lang/String;"));
+ jobject eventObj = env->GetObjectField(item,
+ env->GetFieldID(env->GetObjectClass(item), "event",
+ "Lcom/axis/innovators/box/tools/RegisterTray$Event;"));
+
+ const jchar* nameChars = env->GetStringChars(name, NULL);
+ std::wstring menuName(nameChars, nameChars + env->GetStringLength(name));
+ env->ReleaseStringChars(name, nameChars);
+
+ MenuItemData menuItem;
+ menuItem.menuId = 1000 + i;
+ menuItem.eventObj = env->NewGlobalRef(eventObj);
+ jclass eventClass = env->GetObjectClass(eventObj);
+ menuItem.onClickMethod = env->GetMethodID(eventClass, "onClick", "(J)V");
+
+ AppendMenuW(pData->hMenu, MF_STRING, menuItem.menuId, menuName.c_str());
+ pData->menuItems.push_back(menuItem);
+ }
+
+ // 事件回调
+ jclass eventClass = env->GetObjectClass(event);
+ pData->onClickMethod = env->GetMethodID(eventClass, "onClick", "(J)V");
+ pData->eventObj = env->NewGlobalRef(event);
+
+ // 配置托盘
+ NOTIFYICONDATAW nid = { sizeof(NOTIFYICONDATAW) };
+ nid.hWnd = hwnd;
+ nid.uID = pData->trayId;
+ nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
+ nid.uCallbackMessage = WM_USER + 1;
+
+ // 加载图标
+ const wchar_t* iconPath = (const wchar_t*)env->GetStringChars(icon, NULL);
+ pData->hIcon = LoadTrayIcon(iconPath);
+ nid.hIcon = pData->hIcon;
+ env->ReleaseStringChars(icon, (const jchar*)iconPath);
+
+ // 设置提示
+ const wchar_t* tip = (const wchar_t*)env->GetStringChars(name, NULL);
+ wcsncpy_s(nid.szTip, _countof(nid.szTip), tip, _TRUNCATE);
+ env->ReleaseStringChars(name, (const jchar*)tip);
+
+ if (!Shell_NotifyIconW(NIM_ADD, &nid)) {
+ delete pData;
+ return -1;
+ }
+
+ SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)pData);
+ trayDataList.push_back(pData);
+
+ // 启动消息循环
+ MSG msg;
+ while (GetMessageW(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+
+ return (jlong)pData->trayId;
+}
+
+TrayData* FindTrayData(UINT trayId) {
+ for (auto data : trayDataList) {
+ if (data->trayId == trayId) return data;
+ }
+ return nullptr;
+}
+
+
+JNIEXPORT void JNICALL Java_com_axis_innovators_box_tools_RegisterTray_unregister
+(JNIEnv* env, jclass clazz, jlong id) {
+ const UINT trayId = static_cast(id);
+ TrayData* pData = FindTrayData(trayId);
+
+ if (!pData) {
+ OutputDebugStringW(L"[unregister] 找不到对应的托盘数据");
+ return;
+ }
+
+ // 1. 删除系统托盘图标
+ NOTIFYICONDATAW nid = { sizeof(NOTIFYICONDATAW) };
+ nid.hWnd = pData->hwnd;
+ nid.uID = trayId;
+
+ if (!Shell_NotifyIconW(NIM_DELETE, &nid)) {
+ DWORD err = GetLastError();
+ wchar_t errMsg[256];
+ swprintf_s(errMsg, _countof(errMsg), L"[unregister] 销毁窗口失败 (错误码: 0x%08X)", err);
+ OutputDebugStringW(errMsg);
+ }
+
+ // 2. 释放图标资源
+ if (pData->hIcon) {
+ if (!DestroyIcon(pData->hIcon)) {
+ OutputDebugStringW(L"[unregister] 销毁图标失败");
+ }
+ else {
+ OutputDebugStringW(L"[unregister] 图标资源已释放");
+ }
+ pData->hIcon = NULL;
+ }
+
+ // 3. 销毁菜单
+ if (pData->hMenu) {
+ if (!DestroyMenu(pData->hMenu)) {
+ OutputDebugStringW(L"[unregister] 销毁菜单失败");
+ }
+ else {
+ OutputDebugStringW(L"[unregister] 菜单已销毁");
+ }
+ pData->hMenu = NULL;
+ }
+
+ // 4. 销毁窗口
+ if (pData->hwnd) {
+ if (!DestroyWindow(pData->hwnd)) {
+ DWORD err = GetLastError();
+ wchar_t errMsg[256];
+ swprintf_s(errMsg, _countof(errMsg), // 修改点
+ L"[unregister] 销毁窗口失败 (错误码: 0x%08X)",
+ err);
+ OutputDebugStringW(errMsg);
+ }
+ else {
+ OutputDebugStringW(L"[unregister] 窗口已销毁");
+ }
+ pData->hwnd = NULL;
+ }
+
+ // 5. 释放JNI全局引用
+ if (pData->eventObj) {
+ env->DeleteGlobalRef(pData->eventObj);
+ pData->eventObj = NULL;
+ OutputDebugStringW(L"[unregister] 事件全局引用已释放");
+ }
+
+ // 6. 释放菜单项全局引用
+ for (auto& item : pData->menuItems) {
+ if (item.eventObj) {
+ env->DeleteGlobalRef(item.eventObj);
+ item.eventObj = NULL;
+ }
+ }
+ pData->menuItems.clear();
+ OutputDebugStringW(L"[unregister] 菜单项资源已清理");
+
+ // 7. 从全局列表移除
+ auto it = std::remove_if(trayDataList.begin(), trayDataList.end(),
+ [pData](TrayData* data) { return data == pData; });
+
+ if (it != trayDataList.end()) {
+ trayDataList.erase(it, trayDataList.end());
+ OutputDebugStringW(L"[unregister] 已从全局列表移除");
+ }
+
+ // 8. 释放内存
+ delete pData;
+ OutputDebugStringW(L"[unregister] 内存已释放");
+
+ // 9. 强制重绘任务栏
+ HWND taskbar = FindWindowW(L"Shell_TrayWnd", NULL);
+ if (taskbar) {
+ RedrawWindow(taskbar, NULL, NULL,
+ RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
+ }
+}
+
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) {
+ return TRUE;
+}
\ No newline at end of file
diff --git a/src/main/Cpp/RegisterTray/framework.h b/src/main/Cpp/RegisterTray/framework.h
new file mode 100644
index 0000000..80cbbc9
--- /dev/null
+++ b/src/main/Cpp/RegisterTray/framework.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
+// Windows 头文件
+#include
diff --git a/src/main/Cpp/RegisterTray/pch.cpp b/src/main/Cpp/RegisterTray/pch.cpp
new file mode 100644
index 0000000..b6fb8f4
--- /dev/null
+++ b/src/main/Cpp/RegisterTray/pch.cpp
@@ -0,0 +1,5 @@
+// pch.cpp: 与预编译标头对应的源文件
+
+#include "pch.h"
+
+// 当使用预编译的头时,需要使用此源文件,编译才能成功。
diff --git a/src/main/Cpp/RegisterTray/pch.h b/src/main/Cpp/RegisterTray/pch.h
new file mode 100644
index 0000000..9660927
--- /dev/null
+++ b/src/main/Cpp/RegisterTray/pch.h
@@ -0,0 +1,13 @@
+// pch.h: 这是预编译标头文件。
+// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
+// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
+// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
+// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
+
+#ifndef PCH_H
+#define PCH_H
+
+// 添加要在此处预编译的标头
+#include "framework.h"
+
+#endif //PCH_H
diff --git a/src/main/Cpp/ThrowSafely/ThrowSafely.vcxproj b/src/main/Cpp/ThrowSafely/ThrowSafely.vcxproj
new file mode 100644
index 0000000..4cf9f1e
--- /dev/null
+++ b/src/main/Cpp/ThrowSafely/ThrowSafely.vcxproj
@@ -0,0 +1,157 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {08b41ee2-1bc6-4aab-a7bb-5ae88d658d33}
+ ThrowSafely
+ 10.0
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;THROWSAFELY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;THROWSAFELY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ true
+ true
+ false
+
+
+
+
+ Level3
+ true
+ _DEBUG;THROWSAFELY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;THROWSAFELY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ true
+ true
+ false
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/Cpp/ThrowSafely/ThrowSafely.vcxproj.filters b/src/main/Cpp/ThrowSafely/ThrowSafely.vcxproj.filters
new file mode 100644
index 0000000..2689155
--- /dev/null
+++ b/src/main/Cpp/ThrowSafely/ThrowSafely.vcxproj.filters
@@ -0,0 +1,33 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ 头文件
+
+
+ 头文件
+
+
+
+
+ 源文件
+
+
+ 源文件
+
+
+
\ No newline at end of file
diff --git a/src/main/Cpp/ThrowSafely/ThrowSafely.vcxproj.user b/src/main/Cpp/ThrowSafely/ThrowSafely.vcxproj.user
new file mode 100644
index 0000000..88a5509
--- /dev/null
+++ b/src/main/Cpp/ThrowSafely/ThrowSafely.vcxproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/main/Cpp/ThrowSafely/dllmain.cpp b/src/main/Cpp/ThrowSafely/dllmain.cpp
new file mode 100644
index 0000000..a778850
--- /dev/null
+++ b/src/main/Cpp/ThrowSafely/dllmain.cpp
@@ -0,0 +1,172 @@
+// dllmain.cpp : 定义 DLL 应用程序的入口点。
+#include "pch.h"
+#include
+#include
+#include
+#include
+#include
+
+#pragma comment(lib, "DbgHelp.lib")
+
+// 函数声明
+LONG WINAPI TopLevelExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo);
+void GenerateMiniDump(EXCEPTION_POINTERS* pExceptionInfo);
+std::wstring GetExceptionInfo(EXCEPTION_POINTERS* pExceptionInfo);
+std::wstring GetStackTrace(PCONTEXT pContext);
+void ShowErrorDialog(const std::wstring& message);
+
+// 全局变量用于存储模块句柄
+HMODULE g_hModule = NULL;
+
+BOOL APIENTRY DllMain(HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved)
+{
+ g_hModule = hModule;
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ // 初始化符号系统
+ SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
+ SymInitialize(GetCurrentProcess(), NULL, TRUE);
+
+ // 设置未处理异常过滤器
+ SetUnhandledExceptionFilter(TopLevelExceptionFilter);
+ break;
+
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+
+ case DLL_PROCESS_DETACH:
+ //SymCleanup(GetCurrentProcess());
+ break;
+ }
+ return TRUE;
+}
+
+LONG WINAPI TopLevelExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
+{
+ // 生成崩溃转储文件
+ GenerateMiniDump(pExceptionInfo);
+
+ // 收集异常信息
+ std::wstring errorMessage = GetExceptionInfo(pExceptionInfo);
+ errorMessage += L"\n\nStack Trace:\n" + GetStackTrace(pExceptionInfo->ContextRecord);
+
+ // 显示错误对话框
+ ShowErrorDialog(errorMessage);
+
+ // 结束进程
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+void GenerateMiniDump(EXCEPTION_POINTERS* pExceptionInfo)
+{
+ HANDLE hFile = CreateFileW(
+ L"CrashReport.dmp",
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ MINIDUMP_EXCEPTION_INFORMATION mdei;
+ mdei.ThreadId = GetCurrentThreadId();
+ mdei.ExceptionPointers = pExceptionInfo;
+ mdei.ClientPointers = FALSE;
+
+ MiniDumpWriteDump(
+ GetCurrentProcess(),
+ GetCurrentProcessId(),
+ hFile,
+ MiniDumpWithDataSegs,
+ &mdei,
+ NULL,
+ NULL);
+
+ CloseHandle(hFile);
+ }
+}
+
+std::wstring GetExceptionInfo(EXCEPTION_POINTERS* pExceptionInfo)
+{
+ std::wstringstream ss;
+ ss << L"Exception Code: 0x" << std::hex << pExceptionInfo->ExceptionRecord->ExceptionCode << std::endl;
+ ss << L"Exception Address: 0x" << pExceptionInfo->ExceptionRecord->ExceptionAddress << std::endl;
+
+ // 添加更多异常信息...
+ return ss.str();
+}
+
+std::wstring GetStackTrace(PCONTEXT pContext)
+{
+ std::wstringstream ss;
+ STACKFRAME64 stackFrame = { 0 };
+ DWORD machineType = IMAGE_FILE_MACHINE_AMD64;
+
+#ifdef _M_IX86
+ machineType = IMAGE_FILE_MACHINE_I386;
+ stackFrame.AddrPC.Offset = pContext->Eip;
+ stackFrame.AddrFrame.Offset = pContext->Ebp;
+ stackFrame.AddrStack.Offset = pContext->Esp;
+#else
+ machineType = IMAGE_FILE_MACHINE_AMD64;
+ stackFrame.AddrPC.Offset = pContext->Rip;
+ stackFrame.AddrFrame.Offset = pContext->Rbp;
+ stackFrame.AddrStack.Offset = pContext->Rsp;
+#endif
+
+ stackFrame.AddrPC.Mode = AddrModeFlat;
+ stackFrame.AddrFrame.Mode = AddrModeFlat;
+ stackFrame.AddrStack.Mode = AddrModeFlat;
+
+ for (ULONG frameNum = 0; ; frameNum++)
+ {
+ if (!StackWalk64(
+ machineType,
+ GetCurrentProcess(),
+ GetCurrentThread(),
+ &stackFrame,
+ pContext,
+ NULL,
+ SymFunctionTableAccess64,
+ SymGetModuleBase64,
+ NULL))
+ {
+ break;
+ }
+
+ DWORD64 displacement = 0;
+ IMAGEHLP_SYMBOL64* pSymbol = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + 256);
+ pSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+ pSymbol->MaxNameLength = 255;
+
+ if (SymGetSymFromAddr64(GetCurrentProcess(), stackFrame.AddrPC.Offset, &displacement, pSymbol))
+ {
+ ss << L"0x" << (PVOID)stackFrame.AddrPC.Offset << L" " << pSymbol->Name;
+ if (displacement) ss << L" + 0x" << displacement;
+ ss << std::endl;
+ }
+
+ free(pSymbol);
+ }
+
+ return ss.str();
+}
+
+void ShowErrorDialog(const std::wstring& message)
+{
+ std::wstring fullMessage = message +
+ L"\n\nA crash dump file (CrashReport.dmp) has been generated." +
+ L"\nPlease contact support and provide this file for analysis." +
+ L"\n\nClick OK to terminate the application.";
+
+ MessageBoxW(NULL,
+ fullMessage.c_str(),
+ L"Critical Error Occurred",
+ MB_ICONERROR | MB_OK);
+}
\ No newline at end of file
diff --git a/src/main/Cpp/ThrowSafely/framework.h b/src/main/Cpp/ThrowSafely/framework.h
new file mode 100644
index 0000000..80cbbc9
--- /dev/null
+++ b/src/main/Cpp/ThrowSafely/framework.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
+// Windows 头文件
+#include
diff --git a/src/main/Cpp/ThrowSafely/pch.cpp b/src/main/Cpp/ThrowSafely/pch.cpp
new file mode 100644
index 0000000..b6fb8f4
--- /dev/null
+++ b/src/main/Cpp/ThrowSafely/pch.cpp
@@ -0,0 +1,5 @@
+// pch.cpp: 与预编译标头对应的源文件
+
+#include "pch.h"
+
+// 当使用预编译的头时,需要使用此源文件,编译才能成功。
diff --git a/src/main/Cpp/ThrowSafely/pch.h b/src/main/Cpp/ThrowSafely/pch.h
new file mode 100644
index 0000000..9660927
--- /dev/null
+++ b/src/main/Cpp/ThrowSafely/pch.h
@@ -0,0 +1,13 @@
+// pch.h: 这是预编译标头文件。
+// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
+// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
+// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
+// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
+
+#ifndef PCH_H
+#define PCH_H
+
+// 添加要在此处预编译的标头
+#include "framework.h"
+
+#endif //PCH_H