坚果云是我常用的一个工具,但是我一般喜欢使用绿色版,不喜欢程序到处写文件,也不喜欢重装以后还要各种登录。所以要折腾“绿色版”来使用。坚果云的完整版下载链接为

https://pkg-cdn.jianguoyun.com/static/exe/installer/NutstoreWindowsWPF_Full.exe

手动IL处理

一般手动处理坚果云的个人用户资料目录。需要使用dnspyEx,然后修改NutstoreLib.dll中的Utils.DirectoryUtils下的APPDATA_NUTSTORE_DIR,下面是我的一个手动IL修改列表:

0    0000    call    string NutstoreLib.Utils.DirectoryUtils::get_NUTSTORE_INSTALL_DIR()
1    0005    newobj    instance void [mscorlib]System.IO.DirectoryInfo::.ctor(string)
2    000A    call    instance class [mscorlib]System.IO.DirectoryInfo [mscorlib]System.IO.DirectoryInfo::get_Parent()
3    000F    callvirt    instance string [mscorlib]System.IO.FileSystemInfo::get_FullName()
4    0014    ldstr    "UserData"
5    0019    call    string [Pri.LongPath]Pri.LongPath.Path::Combine(string, string)
6    001E    ret

自动IL处理

借助Mono.Cecil我们可以实现上面的功能,自动进行dll的patch修改。

注:因为我不喜欢调用太多的库,所以Pri.LongPath.Path::Combine(string, string)我改为了系统的库

完整代码如下:

using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace NutstoreAutoPatch
{
    internal class Program
    {
        public static void Main(string[] args)
        {
           const string nutstoreLib = "NutstoreLib.dll"; 
           const string directoryutils = "NutstoreLib.Utils.DirectoryUtils"; 
           const string appdataNutstoreDir = "get_APPDATA_NUTSTORE_DIR"; 
           
           
            // Read the assembly
            var assemblyDefinition = AssemblyDefinition.ReadAssembly(nutstoreLib);
            var directoryUtil = assemblyDefinition.MainModule.Types.First(t => t.FullName == directoryutils);
            var appdataDirMethod = directoryUtil.Methods.First(m => m.Name == appdataNutstoreDir);
            
            // 清除当前指令
            appdataDirMethod.Body.Instructions.Clear();

            // 获取IL处理器
            var ilProcessor = appdataDirMethod.Body.GetILProcessor();

            // 构造调用NutstoreLib.Utils.DirectoryUtils.get_NUTSTORE_INSTALL_DIR的指令
            var getInstallDirMethod = appdataDirMethod.Module.ImportReference(
                appdataDirMethod.DeclaringType.Module.Types
                    .First(t => t.Name == "DirectoryUtils" && t.Namespace == "NutstoreLib.Utils")
                    .Methods.First(m => m.Name == "get_NUTSTORE_INSTALL_DIR")
            );
            ilProcessor.Append(ilProcessor.Create(OpCodes.Call, getInstallDirMethod));

            // 构造调用System.IO.DirectoryInfo构造函数的指令
            var directoryInfoConstructor = appdataDirMethod.Module.ImportReference(
                typeof(System.IO.DirectoryInfo).GetConstructor(new[] { typeof(string) })
            );
            ilProcessor.Append(ilProcessor.Create(OpCodes.Newobj, directoryInfoConstructor));

            // 构造获取DirectoryInfo.Parent属性的调用指令
            var getParentMethod = appdataDirMethod.Module.ImportReference(
                typeof(System.IO.DirectoryInfo).GetProperty("Parent").GetGetMethod()
            );
            ilProcessor.Append(ilProcessor.Create(OpCodes.Call, getParentMethod));

            // 构造调用System.IO.FileSystemInfo.FullName属性的指令
            var getFullNameMethod = appdataDirMethod.Module.ImportReference(
                typeof(System.IO.FileSystemInfo).GetProperty("FullName").GetGetMethod()
            );
            ilProcessor.Append(ilProcessor.Create(OpCodes.Callvirt, getFullNameMethod));

            // 加载字符串"UserData"
            ilProcessor.Append(ilProcessor.Create(OpCodes.Ldstr, "UserData"));

            // 构造调用Pri.LongPath.Path.Combine的指令
            var pathCombineMethod = appdataDirMethod.Module.ImportReference(
                    typeof(System.IO.Path).GetMethod("Combine", new[] { typeof(string), typeof(string) })
                );
            ilProcessor.Append(ilProcessor.Create(OpCodes.Call, pathCombineMethod));

          // 返回指令
            ilProcessor.Append(ilProcessor.Create(OpCodes.Ret));

            // Write the modified assembly
            assemblyDefinition.Write("NutstoreLib_patched.dll");
        }
    }
}

程序编译后,将最新的NutstoreLib.dll拷贝进来,执行下,再使用NutstoreLib_patched.dll替换原来安装目录的NutstoreLib.dll即可。