unity下HybridCLR热更新简易框架

2023-10-26

简易打AB包工具

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using HybridCLR.Editor;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using System.Text;
using System.Security.Cryptography;

public class ABTool : Editor
{
    static string dllpath = Application.dataPath + "/../" + HybridCLRSettings.Instance.hotUpdateDllCompileOutputRootDir;
    static string aotpath = Application.dataPath + "/../" + HybridCLRSettings.Instance.strippedAOTDllOutputRootDir;

    public static List<string> AOTMetaAssemblyNames { get; } = new List<string>()
    {
        "mscorlib.dll",
        "System.dll",
        "System.Core.dll",
    };

    [MenuItem("ABTool/生成AB包")]
    public static void CreatAB()
    {
        BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
        //找到对应目录
        DirectoryInfo direction = new DirectoryInfo(dllpath + "/" + target);
        //获取目录下所有文件
        FileInfo[] files = direction.GetFiles("*", SearchOption.AllDirectories);
        //把所有dll文件拷贝到s目录下
        foreach (var item in files)
        {
            if (item.Name.EndsWith(".dll"))
            {
                string dllPath = $"{dllpath + "/" + target}/{item.Name}";
                string dllBytesPath = $"{Application.streamingAssetsPath}/{item.Name}.bytes";
                File.Copy(dllPath, dllBytesPath, true);
                Debug.Log($"hotfix dll {dllPath} -> {dllBytesPath}");
            }
        }
        //预制体
        List<AssetBundleBuild> abs = new List<AssetBundleBuild>();
        //找到预制体目录下的所有目录
        AddABPath(abs, $"{Application.dataPath}/Prefabs/");
        AddABPath(abs, $"{Application.dataPath}/Resources/");
        //构建AB包放到S目录下
        BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath, abs.ToArray(), BuildAssetBundleOptions.None, target);
        //加载补充元数据
        CopyAOTAssembliesToStreamingAssets();

        AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);

    }

    public static void CopyAOTAssembliesToStreamingAssets()
    {
        var target = EditorUserBuildSettings.activeBuildTarget;
        string aotAssembliesSrcDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(target);
        string aotAssembliesDstDir = Application.streamingAssetsPath;

        foreach (var dll in AOTMetaAssemblyNames)
        {
            string srcDllPath = $"{aotAssembliesSrcDir}/{dll}";
            if (!File.Exists(srcDllPath))
            {
                Debug.LogError($"ab中添加AOT补充元数据dll:{srcDllPath} 时发生错误,文件不存在。裁剪后的AOT dll在BuildPlayer时才能生成,因此需要你先构建一次游戏App后再打包。");
                continue;
            }
            string dllBytesPath = $"{aotAssembliesDstDir}/{dll}.bytes";
            File.Copy(srcDllPath, dllBytesPath, true);
            Debug.Log($"[CopyAOTAssembliesToStreamingAssets] copy AOT dll {srcDllPath} -> {dllBytesPath}");
        }
    }

    public static void AddABPath(List<AssetBundleBuild> abs, string path)
    {
        DirectoryInfo directionInfo = new DirectoryInfo(path);
        DirectoryInfo[] directions = directionInfo.GetDirectories();
        //每个目录打一个AB包
        for (int i = 0; i < directions.Length; i++)
        {
            List<string> prefabAssets = new List<string>();
            Debug.Log("打包文件夹:" + directions[i].Name);
            FileInfo[] fileinfos = directions[i].GetFiles("*", SearchOption.AllDirectories);
            Debug.Log("目录下文件个数:" + fileinfos.Length);
            foreach (var item in fileinfos)
            {
                if (item.Name.EndsWith(".meta"))
                {
                    continue;
                }
                Debug.Log("打包文件:" + item.Name);
                prefabAssets.Add(item.FullName.Replace("\\", "/"));
                AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
            }

            AssetBundleBuild PrefabsAb = new AssetBundleBuild
            {
                assetBundleName = directions[i].Name,
                assetNames = prefabAssets.Select(s => ToReleateAssetPath(s)).ToArray(),
            };
            abs.Add(PrefabsAb);
        }
    }
    public static string ToReleateAssetPath(string s)
    {
        string path = s.Substring(s.IndexOf("Assets/"));
        Debug.Log(path);
        return path;
    }

    [MenuItem("ABTool/生成version")]
    public static void MakeVersion()
    {
        string json = AssetDatabase.LoadAssetAtPath<TextAsset>("Assets/Resources/Config/Config.json").text;
        Config config = JsonConvert.DeserializeObject<Config>(json);
        List<VersionData> versionDatas = new List<VersionData>();
        //第一条是假数据,用来判断是否需要热更新
        VersionData version = new VersionData();
        //判断使用测试版还是正式版热更地址
        config.upid++;
        version.abName = config.isBate ? config.hotPath_Bate : config.hotPath;
        version.len = config.upid;//更新号
        version.md5 = config.version;//版本号
        versionDatas.Add(version);
        json = JsonConvert.SerializeObject(config);
        File.WriteAllText(Application.dataPath +"/Resources/Config/Config.json", json);

        //找到S目录下所有文件
        string[] files = Directory.GetFiles(Application.streamingAssetsPath, ".", SearchOption.AllDirectories);

        foreach (var item in files)
        {
            //取后缀名
            string extensionName = Path.GetExtension(item);
            //排除.meta和.manifest文件
            if (extensionName.Contains("meta") || extensionName.Contains("manifest")) continue;
            Debug.Log(item);
            string abName = item.Replace(Application.dataPath, "Assets");
            abName = abName.Replace("\\", "/");
            string assetBundleName = "";
            assetBundleName = abName.Replace("Assets/StreamingAssets/", string.Empty);

            VersionData versionData = new VersionData();

            versionData.abName = assetBundleName;
            versionData.len = File.ReadAllBytes(item).Length;
            versionData.md5 = FileMD5(item);
            versionDatas.Add(versionData);

        }
        //写入文件
        string versionInfo = JsonConvert.SerializeObject(versionDatas);
        File.WriteAllText(Application.streamingAssetsPath + "/version.txt", versionInfo, Encoding.UTF8);
        AssetDatabase.Refresh();
        Debug.Log("version.txt创建成功");
    }

    static string FileMD5(string filepath)
    {
        FileStream fileStream = new FileStream(filepath, FileMode.Open);
        MD5 mD5 = new MD5CryptoServiceProvider();
        byte[] bytes = mD5.ComputeHash(fileStream);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.Length; i++)
        {
            sb.Append(bytes[i].ToString("x2"));
        }

        return sb.ToString();
    }
}

简易AB包管理器

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

public class ABData
{
    public string abname;
    public AssetBundle ab;
    public int num;
}

public class ABManager : Singleton<ABManager>
{
    public Dictionary<string, List<string>> reflist = new Dictionary<string, List<string>>();
    public string SPath = Main.config.isEditor ? Application.streamingAssetsPath + "/" : Application.persistentDataPath + "/AB/";
    public Dictionary<string, byte[]> assetDatas = new Dictionary<string, byte[]>();
    public Dictionary<string, ABData> abDic = new Dictionary<string, ABData>();
    Assembly ass;

    public byte[] GetAssetData(string dllName)
    {
        return assetDatas[dllName];
    }

    public AssetBundle LoadAB(string dllName)
    {
        return AssetBundle.LoadFromMemory(GetAssetData(dllName));
    }
    public T Load<T>(string refname, string abName, string assetName) where T : UnityEngine.Object
    {
        AssetBundle ab;
        if (abDic.ContainsKey(abName))
        {
            ab = abDic[abName].ab;
            abDic[abName].num++;
        }
        else
        {
            ab = AssetBundle.LoadFromFile(SPath + abName);
            ABData data = new ABData();
            data.abname = abName;
            data.ab = ab;
            data.num = 1;
            abDic.Add(abName, data);
        }
        if (ab != null)
        {
            if (!reflist.ContainsKey(refname))
            {
                reflist.Add(refname, new List<string>());
            }
            reflist[refname].Add(abName);
        }
        return ab.LoadAsset<T>(assetName);
    }
    public GameObject Load(string abName, string assetName)
    {
        AssetBundle ab;
        if (abDic.ContainsKey(abName))
        {
            ab = abDic[abName].ab;
            abDic[abName].num++;
        }
        else
        {
            ab = AssetBundle.LoadFromFile(SPath + abName);
            ABData data = new ABData();
            data.abname = abName;
            data.ab = ab;
            data.num = 1;
            abDic.Add(abName, data);
        }
        if (ab != null)
        {
            if (!reflist.ContainsKey(assetName))
            {
                reflist.Add(assetName, new List<string>());
            }
            reflist[assetName].Add(abName);
        }
        return ab.LoadAsset<GameObject>(assetName);
    }
    public void InitScript()
    {
        ass = Assembly.Load(GetAssetData("Script.dll.bytes"));
    }
    public Type LoadScript(string assetName)
    {
        if (ass == null)
        {
            ass = Assembly.Load(GetAssetData("Script.dll.bytes"));
        }
        return ass.GetType(assetName);
    }
    public void Release(string refname)
    {
        if (reflist.ContainsKey(refname))
        {
            foreach (var abName in reflist[refname])
            {
                if (abDic.ContainsKey(abName))
                {
                    abDic[abName].num--;
                    if (abDic[abName].num <= 0)
                    {
                        ScheduleOnce.Ins.AddSchedule(9, (obj) =>
                        {
                            object[] arr = obj as object[];
                            ABData data = arr[0] as ABData;
                            if (data.num <= 0)
                            {
                                data.ab.Unload(true);
                                abDic.Remove(abName);
                                Debug.Log("释放成功");
                            }
                        }, abDic[abName]);
                    }
                }
            }
            reflist.Remove(refname);
        }
    }
}

热更脚本加载器

using HybridCLR;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEngine;
using UnityEngine.Networking;

public class LoadDll : MonoBehaviour
{
    string SPath;
    string PPath;
    void Start()
    {
        SPath = Application.streamingAssetsPath;
        PPath = Application.persistentDataPath + "/AB";
        StartCoroutine(this.DownLoadAssets( ()=>
        {
#if !UNITY_EDITOR
            ABManager.Ins.InitScript();
#endif
            Main.main.gameObject.AddComponent(ABManager.Ins.LoadScript("Language"));
            Instantiate(ABManager.Ins.Load("Login", "Notice"), Main.GetUILayout(UILayout.Tips));
            Instantiate(ABManager.Ins.Load("Login", "Login"), Main.GetUILayout(UILayout.UIScene));
        }));
    }

    IEnumerator DownLoadAssets(Action action)
    {
        //找到对应目录
        DirectoryInfo direction = new DirectoryInfo(Main.config.isEditor ? SPath : PPath);
        //获取目录下所有文件
        FileInfo[] files = direction.GetFiles("*", SearchOption.AllDirectories);
        List<string> AOTMetaAssemblyNames = new List<string>();
        //加载所有dll文件
        foreach (var item in files)
        {
            Debug.Log("文件:" + item.Name);
            if (item.Name.EndsWith(".bytes"))
            {
                Debug.Log("开始加载:" + item.Name);
                string dllPath = item.FullName.Replace("\\", "/");
                UnityWebRequest www = UnityWebRequest.Get(dllPath);
                yield return www.SendWebRequest();
                if (www.result != UnityWebRequest.Result.Success)
                {
                    Debug.Log(www.error);
                }
                else
                {
                    byte[] data = www.downloadHandler.data;
                    ABManager.Ins.assetDatas.Add(item.Name, data);
                    Debug.Log("加载成功:" + item.Name);
                    AOTMetaAssemblyNames.Add(item.Name);
                }
            }
        }
        LoadMetadataForAOTAssemblies(AOTMetaAssemblyNames);
        action();
    }
    private static void LoadMetadataForAOTAssemblies(List<string> AOTMetaAssemblyNames)
    {
        // 不限补充元数据dll文件的读取方式,你可以从ab、StreamingAssets、或者裸文件下载等办法获得
        HomologousImageMode mode = HomologousImageMode.SuperSet;
        foreach (var aotDllName in AOTMetaAssemblyNames)
        {
            Debug.Log(aotDllName);
            byte[] dllBytes = ABManager.Ins.GetAssetData(aotDllName); // 获得某个aot dll文件所有字节
            Debug.Log(dllBytes);
            // 加载assembly对应的dll,会自动为它hook。一旦aot泛型函数的native函数不存在,用解释器版本代码
            LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
            Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
        }
    }
}

版本数据

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class VersionData
{
	public string abName;
	public int len;
	public string md5;
}

简易热更框架

using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class HotUpDate : MonoBehaviour
{
    public Scrollbar scrollbar;
    public Text text;
	// Start is called before the first frame update

	string streamingAssetsPath;
	string persistentDataPath;
	// Start is called before the first frame update
	void Start()
	{
		Debug.Log("开始热更新");
		text.text = "开始热更新";
		streamingAssetsPath = Application.streamingAssetsPath + "/";
		//p目录在unity运行时就会自动创建,咱们要在下面新建一个目录使用
		persistentDataPath = Application.persistentDataPath + "/AB/";
		//没有p目录,创建p目录,并把s目录下的拷贝到p目录
		//有p目录,直接热更新
		//拷贝跟热更都是使用协程异步完成的
		if (!Directory.Exists(persistentDataPath))
		{
			Directory.CreateDirectory(persistentDataPath);
			// -- todo  S 拷贝到P
			StartCoroutine(Copy());
		}
		else
		{
			//  todo  更新
			StartCoroutine(CheckUpdate());
		}
	}
    public void Init()
    {
		GameObject.Find("Main").AddComponent<LoadDll>();
		Destroy(gameObject);
    }
    IEnumerator Copy()
	{
		Debug.Log("S 拷贝到P");
		//找到版本文件地址
		string localVersion = streamingAssetsPath + "version.txt";
		//读取版本文件
		string localVersionContent = File.ReadAllText(localVersion);
		//解析版本文件
		List<VersionData> localVersionDataList = JsonConvert.DeserializeObject<List<VersionData>>(localVersionContent);
		for (int i = 1; i < localVersionDataList.Count; i++)
		{
			text.text = "验证本地文件";
			//更新进度条
			scrollbar.size = (float)i / (float)localVersionDataList.Count;
			//ab包地址
			string tempS = streamingAssetsPath + localVersionDataList[i].abName;
			//获取文件名
			string fileName = Path.GetFileName(tempS);
			//获取文件夹名称
			string dir = Path.GetDirectoryName(tempS) + "/";
			dir = dir.Replace("\\", "/");//Assets/StreamingAssets/ABTest/xxxx/xxxxx
										 //把对应的s目录替换成p目录
			dir = dir.Replace(streamingAssetsPath, persistentDataPath);
			//创建对应目录
			if (!Directory.Exists(dir))
			{
				Directory.CreateDirectory(dir);
			}
			//拷贝内容
			File.Copy(tempS, dir + fileName);
		}
		//p目录下写入版本文件
		File.WriteAllText(persistentDataPath + "version.txt", localVersionContent);

		yield return null;
		//从p目录下开始热更新
		StartCoroutine(CheckUpdate());
	}
	IEnumerator CheckUpdate()
	{
		Debug.Log("检查更新");
		//检查p目录下的版本文件
		string localVersion = persistentDataPath + "version.txt";
		string localVersionContent = File.ReadAllText(localVersion);
		List<VersionData> localVersionDataList = JsonConvert.DeserializeObject<List<VersionData>>(localVersionContent);
		//创建版本对应字典
		Dictionary<string, VersionData> dicVersion = new Dictionary<string, VersionData>();
		for (int i = 1; i < localVersionDataList.Count; i++)
		{
			text.text = "检查更新";
			//更新进度条
			scrollbar.size = (float)i / (float)localVersionDataList.Count;
			//把p目录下的版本信息全部存入字典
			dicVersion.Add(localVersionDataList[i].abName, localVersionDataList[i]);
		}
		//获取热更新服务器上的版本文件地址
		string serverUrl = localVersionDataList[0].abName + "version.txt";
		Debug.Log(serverUrl);
		//创建用于接收版本文件的字符串
		string serverVesionContent = string.Empty;
		Debug.Log(serverVesionContent);
		//从热更新服务器上获取版本文件
		UnityWebRequest unityWebRequest = UnityWebRequest.Get(serverUrl);
		yield return unityWebRequest.SendWebRequest();
		//判断是否获取成功
		if (unityWebRequest.result == UnityWebRequest.Result.ConnectionError)
		{
			text.text = "无法连接更新服务器";
			Debug.Log(unityWebRequest.error);
			yield break;
		}
		else
		{
			//获取成功后,给字符串赋值
			serverVesionContent = unityWebRequest.downloadHandler.text;
			//Debug.Log(serverVesionContent);
			//把热更版本文件写入p目录
			File.WriteAllText(persistentDataPath + "serverVesion.txt", serverVesionContent);
		}
		//Debug.Log(persistentDataPath);
		//重新读取热更版本文件
		string serverVesion = File.ReadAllText(persistentDataPath + "serverVesion.txt");
		//解析热更版本文件
		List<VersionData> serverVersionDataList = JsonConvert.DeserializeObject<List<VersionData>>(serverVesion);

		//创建一个需要更新的文件集合
		List<VersionData> updateList = new List<VersionData>();
		//本地文件更新号小于网络文件更新号,说明需要更新
		if (localVersionDataList[0].len < serverVersionDataList[0].len) //需要更新
		{
			// 不一样的要更新?????md5 不一样
			//新增的文件
			for (int i = 1; i < serverVersionDataList.Count; i++)
			{
				text.text = "查找需要更新的资源";
				scrollbar.size = (float)i / (float)serverVersionDataList.Count;
				Debug.Log("资源验证" + i);
				//判断本地是否有相同名称的资源
				if (dicVersion.ContainsKey(serverVersionDataList[i].abName))
				{
					//有该资源,则通过MD5码判断该资源是否修改过,修改过则加入更新集合
					if (dicVersion[serverVersionDataList[i].abName].md5 != serverVersionDataList[i].md5)
					{
						updateList.Add(serverVersionDataList[i]);
					}
				}
				else
				{
					//没有该资源,直接加入更新集合
					updateList.Add(serverVersionDataList[i]);
				}

			}
		}
		else
		{
			Debug.Log("localVersionDataList[0].len " + localVersionDataList[0].len);
			Debug.Log("serverVersionDataList[0].len " + serverVersionDataList[0].len);
			Debug.Log("不更新");
			//初始化游戏
			Init();
			//到此结束,停止该协程
			yield break;
		}
		//循环更新集合
		for (int i = 0; i < updateList.Count; i++)
		{
			text.text = "资源下载中";
			scrollbar.size = (float)i / (float)updateList.Count;
			Debug.Log("资源下载中" + i);
			//下载0是网络地址 + 资源i的名字
			UnityWebRequest unityWebRequest1 = UnityWebRequest.Get(localVersionDataList[0].abName + updateList[i].abName);
			yield return unityWebRequest1.SendWebRequest();
			//判断下载是否成功
			if (unityWebRequest.result == UnityWebRequest.Result.ConnectionError)
			{
				Debug.Log(unityWebRequest1.error);
			}
			else
			{
				//创建一个p目录下的地址
				string filePath = persistentDataPath + updateList[i].abName;
				//获取相对路径
				string dir = Path.GetDirectoryName(filePath);
				dir = dir.Replace("\\", "/");
				if (!Directory.Exists(dir))
				{
					Directory.CreateDirectory(dir);
				}
				//写入p目录下的地址
				File.WriteAllBytes(filePath, unityWebRequest1.downloadHandler.data);
			}
		}
		//重新写入版本文件
		File.WriteAllText(persistentDataPath + "version.txt", serverVesionContent);
		yield return null;

		Debug.Log("更新完成");
		Init();
	}
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

unity下HybridCLR热更新简易框架 的相关文章

  • 将“正常”std::string 转换为 utf-8

    让我们看看我是否可以在没有太多事实错误的情况下解释这一点 我正在编写一个字符串类 我希望它使用utf 8 存储在 std string 中 因为它是内部存储 我希望它能够同时 正常 std string and std wstring作为输
  • 命令未存储在命令历史记录中

    背景 https stackoverflow com questions 76566358 这个答案 https stackoverflow com a 47595405 2153235依赖于 readline 模块并且write hist
  • 如何使 F# 脚本文件和其他脚本语言在 Windows 中像 .exe、.cmd 和 .bat 文件一样工作

    可以配置 F 脚本文件 以便可以直接调用它们 而无需直接指定脚本运行程序应用程序及其文件扩展名 并且可以通过命令 PATH 环境变量进行访问 操作步骤如下 使用 Windows 资源管理器将特定脚本引擎设置为脚本文件类型扩展名的默认 打开方
  • Laragon 和 Laravel - sendmail 不工作

    我使用 Windows 10 Laragon 和 Laravel 框架 我通过调用设置默认身份验证php artisan make auth 问题是当我尝试使用 忘记密码 组件时 sendmail 不起作用 我点击后Send Passwor
  • 使用批处理变量重命名文件

    我有以下代码 set DATE date echo DATE set DAY DATE 0 2 echo DAY rename file09 txt file DAY 09 txt pause 它应该重命名一个文本文件并将该月的日期放入文件
  • 处理数据库模式中的更改

    我正处于构建应用程序的初始阶段 该应用程序有望以 1000 的价格出售 更有可能的是数百 但无论哪种方式都足以引起数据库架构更改的严重头痛 我相当受限于 windows net sql 服务器环境 因为我们的许多客户将自行托管我在他们的网络
  • Odoo 服务器无法在 Windows 10 中启动

    我在其官方社区版本页面上下载并安装了 odoo 13 然后当我安装一切正常并启动 odoo 时 浏览器会打开 localhost 8069 即 odoo 端口 并且不会打开任何内容 服务器只是不打开开始 我运行 odoo bin 这是我的错
  • 在 Windows 中设置 cron 作业

    我每天都必须从 SFTP 服务器下载文件 我有一个从服务器检索文件的程序 但我正在考虑设置一个 cron 作业 或任何类似的作业 来自动执行该操作 我们是一家 Windows 商店 需要在 Windows 中设置 cron 作业 windo
  • TerminateProcess 不适用于经过验证的进程 ID

    我正在开发应该杀死具有给定名称的进程的应用程序 我使用的操作系统是 Windows 7 问题是 对于任务管理器中列出的具有非空白用户名字段的所有进程 它都可以正常工作 但是 当进程的用户名为空时 即使任务管理器也无法杀死该进程 谁能告诉我为
  • 在 CMD 批处理脚本中调用标签时如何利用超过 9 个参数?

    我想知道如何在调用标签时在批处理脚本中调用超过 9 个参数 例如 下面显示我分配了 12 个参数 并尝试回显所有这些参数 CALL LABEL one two three four five six seven eight nine ten
  • FOR /F 命令循环中的错误级别返回意外结果

    我正在尝试记录输出net stop同时还捕捉其ERRORLEVEL 基于这个问题 https stackoverflow com questions 3088712 我在嵌套子例程中尝试了以下操作 set a loopIndex 0 for
  • Python - 如何检查文件是否被另一个应用程序使用?

    我想打开一个由另一个应用程序定期写入的文件 该应用程序无法修改 因此 我只想在知道该文件未被其他应用程序写入时才打开该文件 有没有Pythonic的方法来做到这一点 否则 我如何在 Unix 和 Windows 中实现这一点 edit 我会
  • mingw 应用程序中的清单被忽略

    我有一个老式的 Windows GDI 应用程序 用 C 编写 正在使用 Mingw 工具链进行编译 我的一些用户一直在抱怨 Windows Vista 和 Windows 7 的虚拟存储 其中写入应用程序不应访问的目录的文件会被抽走并存储
  • 如何构建这个项目? (jpeg 库)

    Edit 我现在正在使用http code google com p jpeg compressor http code google com p jpeg compressor 所以我不再关心让这个工作了 我下载了http ijg org
  • 从 Visual Studio 2017 连接到适用于 Linux 的 Windows 子系统

    我希望从 VS2017 调试 Linux C 控制台应用程序 如以下教程所示 使用适用于 Linux 的 Windows 子系统从 Visual Studio 编写和调试 Linux C 应用程序 https www hanselman c
  • 即使启用了假脱机文件池,如何找到具有给定 ID 的作业的假脱机文件?

    我想将给定 Windows 打印作业的假脱机数据复制到磁盘上的任意位置 我有打印作业的作业 ID后台打印程序 API http msdn microsoft com en us library windows desktop dd16286
  • 如何更改年龄不匹配的 PDB 以使其正确匹配?

    我们的夜间构建过程被破坏了很长一段时间 因此它生成的 PDB 文件与相应的图像文件的年龄相差几个小时 我已经解决了这个问题 但是 我想开始使用符号服务器 但由于必须使用这些年龄不匹配的 pdb 文件而无法开始 我通过使用 Windbg 中的
  • 确定 Windows 当前是否正在播放声音

    所以我对这个问题思考了一段时间 但我不知道解决这个问题的正确方法是什么 我想使用以下命令确定 Windows 是否在某个时间输出声音Powershell脚本 我可以确定音频驱动程序是否有错误 但我无法确定系统是否正在播放声音 我看了看 NE
  • 在 Windows 上通过 SSH 进行 Git 推送不起作用

    我有一个谜要问你们 在这个问题上苦苦挣扎了半天之后 我需要你的经验来启发我在 Windows 上使用 msysgit 来使用 git 上下文 视窗8 msysgit 最新版本 Using SSH密钥认证 Pagent使用 valid 处理
  • Windows docker 容器上的 Chrome 无法工作

    我正在尝试在 Docker 容器上运行 Google Chrome 这个docker容器是基于microsoft windowsservercore ltsc2016 我想使用 docker 容器构建 AngularCLI 应用程序 因此

随机推荐

  • Numpy 实现全连接神经网络

    神经网络与深度学习实验报告 一 实验名称 Numpy 实现全连接神经网络 二 实验要求 用 python 的 numpy 模块实现全连接神经网络 网络结构为一个输入层 一个隐藏层 一个输出层 隐藏层的激活函数为 Relu 函数 输出层的激活
  • Qt系列文章之 QTabWidget

    上一篇文章介绍如何对QMessgeBox进行使用 本文紧接上文内容继续对Qt的窗体文件开发介绍 一般主界面会有很多控件和交互区域 如果把所有的控件都放在一个界面全部显示 整个界面就会显得非常臃肿繁琐 那么使用分页式的表格窗体布局就能将不同类
  • STM32 从APP跳入BootLoader问题

    在这次项目中 程序从APP跳入BootLoader主要遇到两个问题 做个记录 1 现象 跳入BootLoader后还没开始升级便重启 原因 APP程序中开启了独立看门狗 当跳入BootLoader时看门狗也继续计时 但并没有重新喂狗 因此导
  • uniapp开发日志

    bug 将Base64的编码解码看错 uniapp封装uni request方法 import utils from js utils js let debug false if process env NODE ENV developme
  • Advanced Computer Network Review(3)——BBR

    这是复习系列的第三篇 主要梳理BBR拥塞控制有关的一些要点 老师给出的复习要点如下 1 基于loss的拥塞控制存在什么问题 为什么 2 理解下面这张图 这篇文章的梳理部分参考了中科大郑烇老师 高级计算机网络 的相关部分 特此声明 一 基于l
  • windows命令行更改文件夹权限

    echo off rem windows命令行更改文件夹权限 Cacls命令使用格式如下 Cacls filename T E C G user perm R user P user perm D user Filename 显示访问控制列
  • Linux下如何查看分区文件系统类型

    1 fdisk l fdisk l 只能列出硬盘的分区表 容量大小以及分区类型 但看不到文件系统类型 2 df h df 命令是用来查看文件系统磁盘空间使用量的 但df 命令只会列出已挂载的文件系统信息 对于没有挂载的文件系统是查看不到的
  • LeetCode -数组数据的插入位置(二分法)

    搜索数组数据插入位置 给定一个排序数组和一个目标值 在数组中找到目标值 并返回其索引 如果目标值不存在于数组中 返回它将会被按顺序插入的位置 数组可能有重复数据 有重复数据时返回 插入到重复数据的第一个位置 示例 1 输入 1 3 6 7
  • Vuforia 官方Demo讲解

    官方原文地址 https library vuforia com articles Solution Native Sample Application Template 今天看到的36氪新闻 高通发布面向VR AR一体机的骁龙XR1芯片
  • MySQL的自定义排序函数 FIELD(str,str1,str2,str3,...)

    FIELD是mysql的自定义排序函数 ORDER BY FIELD str str1 str2 str3 说明 str是字段 str1 str2 是自定义排序的值 举例说明 以上两幅图就看懂FIELD 的用法了吧
  • Vue——get调用后端接口并将数据回显到table中

    get调用 呈现效果 动态获取后台数据 1 HTML lt template gt
  • vue —— 项目启动时无法识别es6的扩展语法

    启动项目报错 解决 ES6的拓展运算符报错 1 切换淘宝镜像 npm install g cnpm registry http registry npm taobao org cnpm install legacy peer deps sa
  • “三项能力超过ChatGPT”,科大讯飞星火大模型现场接受观众挑战,写稿制表PPT通通拿下...

    杨净 发自 合肥量子位 公众号 QbitAI 三项能力超过ChatGPT 1024将整体超过GPT水平 在科大讯飞星火认知大模型发布会现场 董事长刘庆峰拍着胸脯保证 引起现场掌声雷动 而真机演示效果和多场景产品展示直接把观众们看呆 信息量太
  • anaconda pytorch配置

    wvscode配置python编译器时 发现即使在右下角改变编译器版本 任然无法使用anaconda的python 通过在setting中搜索code runner 修改Execution Map中python u中python为anaco
  • JS anonymous:无名函数的使用

    这个无名函数名字是我起的 起这个名字的原因有两条 原因一是在改前端代码的时候发现这个东西 在调试台console用debug调试 会显示一个anonymous 但你发现他是一个函数 原因二是 这个在w3school上也有 在下面连接页面搜索
  • 解决:500 Internal Privoxy Error

    500 Internal Privoxy Error Privoxy encountered an error while processing your request Could not load template file no se
  • 内网穿透 VScodeSHH

    准备 腾讯云服务器 linux xshell xftp frp https github com fatedier frp 服务端为腾讯云服务器 linux 客户端为自己工作站 linux 服务端操作 用xshell登录腾讯云服务器 下载
  • audio标签与video标签的常用属性及方法

    一 常用的css属性 1 src 用于指明video标签需要播放的音频的地址
  • C语言学习之认识exit()函数

    C语言学习之认识exit 函数 在C语言的main函数中我们通常使用return 0 exit 0 表示程序正常退出 exit exit 1 表示程序异常退出 exit 结束当前进程 当前程序 在整个程序中 只要调用 exit 就结束 但在
  • unity下HybridCLR热更新简易框架

    简易打AB包工具 using System Collections using System Collections Generic using UnityEngine using UnityEditor using HybridCLR E