跟我一起用C#做LOL换肤工具
小编是一个资深的lol游戏玩家,虽然比较坑,但是也躺赢到了铂金三。
话说小编也是个屌丝初级程序员,身为程序员的我,看到lol 有617个皮肤而身为屌丝的我不能使用,真心伤,后来我就goole baidu各种查资料,最后总结出来,目前修改皮肤的有三种。
第一种这个很牛逼了,修改服务器那边的参数,这个涉及到的东西太多,小编不多说,也没有那个能力。
第二种修改本地内存,达到移花接木的目的,目前市场上收费或者免费的,都已经停止了这种修改的方法,具体原因不详(貌似某讯很厉害,大家都懂的)。
第三种就是传说中的,皮肤挂载(不知道的百度一下)
好了废话不多少,要想做出这个换肤的工具,我分析的结果如下:
1.我们需要准确快速的找到lol的路径
2.创建漂亮的页面
3.用我们的工具去读取lol的数据库 获取他的英雄列表和皮肤图片
4.搭建皮肤文件的FTP 或者放到传说中的云上
5.用工具下载皮肤然后挂载皮肤
6.构建工具的官网
7.工具在线升级的实现
目前我想到的大体就这么多,具体的实现稍后我们一起来搞,小编新手,大神飘过。
小编我下载了几个网上收费或者免费的换肤工具,试了试,发现第一次打开的时候需要,指定游戏目录或自动搜索目录,结果用了一下自动搜索功能,一个字坑,可能不是收费的缘故做的没有那么给力吧!3分钟后搜出来了,你敢信?身为一个经常使用Everything的我怎么能忍受这个速度,我有试用了一些商业的工具,发现人家打开的时候不用指定目录。
那么问题来了,搜索 C#高效搜索文件 baidu google 哪家强(好吧这TM还用说当然是baidu,不要问我为什么,我会告诉你长城是世界八大奇迹之一吗?)
结果找到一篇博友写的http://www.cnblogs.com/TianFang/p/3427776.html(大家可以去看看)。
既然有现成的代码,身为懒惰程序员的我必须的Ctrl+a,Ctrl+c,Ctrl+v 你懂的.
当我满心欢喜的用的时候结果:
什么意思啊这不是坑爹啊!赶紧百度一下,哇秒懂,原来是超出int32上限了啊!(我本地C盘的文件比较多导致的 一般32够用了),那么知道原因了,那就好办了,改成int64不就搞定了,(是不是很“牛逼”!!!!!!)。
不过当我看到
这个的时候我又迷糊了,为什么DriveInfo能点出来EnumerateFiles方法,难道跟
这个代码有关系?为了不浪费时间我果断找来我同事(牛x程序员)给看看,牛x就是牛x,一语道破,这就是方法的扩展,既然不懂我就搜了一下 http://www.cnblogs.com/potential/archive/2012/11/04/2754148.html看看巩固一下什么是方法的扩展(相信有很多人跟我一样不知道这是什么东东),哎再不学习就老了。
搞明白大神写的代码后,我发现人家只实现了获取某盘的所有文件,但是跟我要找到lol的路径貌似没有什么关系!后来我在人家代码的基础上修改了一下,代码如下:
FileSearch.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Runtime.InteropServices; //using Microsoft.VisualBasic; public class FileSearch { private static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); private const uint GENERIC_READ = 0x80000000; private const int FILE_SHARE_READ = 0x1; private const int FILE_SHARE_WRITE = 0x2; private const int OPEN_EXISTING = 3; private const int FILE_READ_ATTRIBUTES = 0x80; private const int FILE_NAME_IINFORMATION = 9; private const int FILE_FLAG_BACKUP_SEMANTICS = 0x2000000; private const int FILE_OPEN_FOR_BACKUP_INTENT = 0x4000; private const int FILE_OPEN_BY_FILE_ID = 0x2000; private const int FILE_OPEN = 0x1; private const int OBJ_CASE_INSENSITIVE = 0x40; private const int FSCTL_ENUM_USN_DATA = 0x900b3; [StructLayout(LayoutKind.Sequential)] private struct MFT_ENUM_DATA { public long StartFileReferenceNumber; public long LowUsn; public long HighUsn; } [StructLayout(LayoutKind.Sequential)] private struct USN_RECORD { public int RecordLength; public short MajorVersion; public short MinorVersion; public long FileReferenceNumber; public long ParentFileReferenceNumber; public long Usn; public long TimeStamp; public int Reason; public int SourceInfo; public int SecurityId; public FileAttributes FileAttribute; public short FileNameLength; public short FileNameOffset; } [StructLayout(LayoutKind.Sequential)] private struct IO_STATUS_BLOCK { public int Status; public int Information; } [StructLayout(LayoutKind.Sequential)] private struct UNICODE_STRING { public short Length; public short MaximumLength; public IntPtr Buffer; } [StructLayout(LayoutKind.Sequential)] private struct OBJECT_ATTRIBUTES { public int Length; public IntPtr RootDirectory; public IntPtr ObjectName; public int Attributes; public int SecurityDescriptor; public int SecurityQualityOfService; } //// MFT_ENUM_DATA [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] private static extern bool DeviceIoControl(IntPtr hDevice, int dwIoControlCode, ref MFT_ENUM_DATA lpInBuffer, int nInBufferSize, IntPtr lpOutBuffer, int nOutBufferSize, ref int lpBytesReturned, IntPtr lpOverlapped); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile); [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] private static extern Int32 CloseHandle(IntPtr lpObject); [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] private static extern int NtCreateFile(ref IntPtr FileHandle, int DesiredAccess, ref OBJECT_ATTRIBUTES ObjectAttributes, ref IO_STATUS_BLOCK IoStatusBlock, int AllocationSize, int FileAttribs, int SharedAccess, int CreationDisposition, int CreateOptions, int EaBuffer, int EaLength); [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] private static extern int NtQueryInformationFile(IntPtr FileHandle, ref IO_STATUS_BLOCK IoStatusBlock, IntPtr FileInformation, int Length, int FileInformationClass); private IntPtr m_hCJ; private IntPtr m_Buffer; private int m_BufferSize; private string m_DriveLetter; private class FSNode { public long FRN; public long ParentFRN; public string FileName; public bool IsFile; public FSNode(long lFRN, long lParentFSN, string sFileName, bool bIsFile) { FRN = lFRN; ParentFRN = lParentFSN; FileName = sFileName; IsFile = bIsFile; } } private IntPtr OpenVolume(string szDriveLetter) { IntPtr hCJ = default(IntPtr); //// volume handle m_DriveLetter = szDriveLetter; hCJ = CreateFile("\\\\.\\" + szDriveLetter, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); return hCJ; } private void Cleanup() { if (m_hCJ != IntPtr.Zero) { // Close the volume handle. CloseHandle(m_hCJ); m_hCJ = INVALID_HANDLE_VALUE; } if (m_Buffer != IntPtr.Zero) { // Free the allocated memory Marshal.FreeHGlobal(m_Buffer); m_Buffer = IntPtr.Zero; } } public IEnumerable<String> EnumerateFiles(string szDriveLetter) { try { var usnRecord = default(USN_RECORD); var mft = default(MFT_ENUM_DATA); var dwRetBytes = 0; var cb = 0; var dicFRNLookup = new Dictionary<long, FSNode>(); var bIsFile = false; // This shouldn't be called more than once. if (m_Buffer.ToInt64() != 0) { throw new Exception("invalid buffer"); } // Assign buffer size m_BufferSize = 65536; //64KB // Allocate a buffer to use for reading records. m_Buffer = Marshal.AllocHGlobal(m_BufferSize); // correct path szDriveLetter = szDriveLetter.TrimEnd('\\'); // Open the volume handle m_hCJ = OpenVolume(szDriveLetter); // Check if the volume handle is valid. if (m_hCJ == INVALID_HANDLE_VALUE) { throw new Exception("Couldn't open handle to the volume."); } mft.StartFileReferenceNumber = 0; mft.LowUsn = 0; mft.HighUsn = long.MaxValue; do { if (DeviceIoControl(m_hCJ, FSCTL_ENUM_USN_DATA, ref mft, Marshal.SizeOf(mft), m_Buffer, m_BufferSize, ref dwRetBytes, IntPtr.Zero)) { cb = dwRetBytes; // Pointer to the first record IntPtr pUsnRecord = new IntPtr(m_Buffer.ToInt32() + 8); while ((dwRetBytes > 8)) { // Copy pointer to USN_RECORD structure. usnRecord = (USN_RECORD)Marshal.PtrToStructure(pUsnRecord, usnRecord.GetType()); // The filename within the USN_RECORD. string FileName = Marshal.PtrToStringUni(new IntPtr(pUsnRecord.ToInt64() + usnRecord.FileNameOffset), usnRecord.FileNameLength / 2); bIsFile = !usnRecord.FileAttribute.HasFlag(FileAttributes.Directory); dicFRNLookup.Add(usnRecord.FileReferenceNumber, new FSNode(usnRecord.FileReferenceNumber, usnRecord.ParentFileReferenceNumber, FileName, bIsFile)); // Pointer to the next record in the buffer. pUsnRecord = new IntPtr(pUsnRecord.ToInt64() + usnRecord.RecordLength); dwRetBytes -= usnRecord.RecordLength; } // The first 8 bytes is always the start of the next USN. mft.StartFileReferenceNumber = Marshal.ReadInt64(m_Buffer, 0); } else { break; // TODO: might not be correct. Was : Exit Do } } while (!(cb <= 8)); // Resolve all paths for Files foreach (FSNode oFSNode in dicFRNLookup.Values.Where(o => o.IsFile)) { string sFullPath = oFSNode.FileName; FSNode oParentFSNode = oFSNode; while (dicFRNLookup.TryGetValue(oParentFSNode.ParentFRN, out oParentFSNode)) { sFullPath = string.Concat(oParentFSNode.FileName, "\\", sFullPath); } sFullPath = string.Concat(szDriveLetter, "\\", sFullPath); yield return sFullPath; } } finally { //// cleanup Cleanup(); } } public string EnumerateFiles(string szDriveLetter, string _fileName) { try { string _fileUrl = string.Empty; var usnRecord = default(USN_RECORD); var mft = default(MFT_ENUM_DATA); var dwRetBytes = 0; var cb = 0; var dicFRNLookup = new Dictionary<long, FSNode>(); var bIsFile = false; // This shouldn't be called more than once. if (m_Buffer.ToInt64() != 0) { throw new Exception("invalid buffer"); } // Assign buffer size m_BufferSize = 65536; //64KB // Allocate a buffer to use for reading records. m_Buffer = Marshal.AllocHGlobal(m_BufferSize); // correct path szDriveLetter = szDriveLetter.TrimEnd('\\'); // Open the volume handle m_hCJ = OpenVolume(szDriveLetter); // Check if the volume handle is valid. if (m_hCJ == INVALID_HANDLE_VALUE) { throw new Exception("Couldn't open handle to the volume."); } mft.StartFileReferenceNumber = 0; mft.LowUsn = 0; mft.HighUsn = long.MaxValue; do { if (DeviceIoControl(m_hCJ, FSCTL_ENUM_USN_DATA, ref mft, Marshal.SizeOf(mft), m_Buffer, m_BufferSize, ref dwRetBytes, IntPtr.Zero)) { cb = dwRetBytes; // Pointer to the first record IntPtr pUsnRecord = new IntPtr(m_Buffer.ToInt64() + 8); while ((dwRetBytes > 8)) { // Copy pointer to USN_RECORD structure. usnRecord = (USN_RECORD)Marshal.PtrToStructure(pUsnRecord, usnRecord.GetType()); // The filename within the USN_RECORD. string FileName = Marshal.PtrToStringUni(new IntPtr(pUsnRecord.ToInt64() + usnRecord.FileNameOffset), usnRecord.FileNameLength / 2); bIsFile = !usnRecord.FileAttribute.HasFlag(FileAttributes.Directory); dicFRNLookup.Add(usnRecord.FileReferenceNumber, new FSNode(usnRecord.FileReferenceNumber, usnRecord.ParentFileReferenceNumber, FileName, bIsFile)); // Pointer to the next record in the buffer. pUsnRecord = new IntPtr(pUsnRecord.ToInt64() + usnRecord.RecordLength); dwRetBytes -= usnRecord.RecordLength; if (!string.IsNullOrEmpty(_fileName) && _fileName.Equals(FileName)) { cb = 0; break; } } // The first 8 bytes is always the start of the next USN. mft.StartFileReferenceNumber = Marshal.ReadInt64(m_Buffer, 0); } else { break; // TODO: might not be correct. Was : Exit Do } } while (!(cb <= 8)); // Resolve all paths for Files foreach (FSNode oFSNode in dicFRNLookup.Values.Where(o => o.IsFile)) { string sFullPath = oFSNode.FileName; FSNode oParentFSNode = oFSNode; if (!string.IsNullOrEmpty(_fileName) && _fileName.Equals(oFSNode.FileName)) { while (dicFRNLookup.TryGetValue(oParentFSNode.ParentFRN, out oParentFSNode)) { sFullPath = string.Concat(oParentFSNode.FileName, "\\", sFullPath); } _fileUrl = string.Concat(szDriveLetter, "\\", sFullPath); break; } } return _fileUrl; } finally { //// cleanup Cleanup(); } } }
DriveInfoExtension.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO; 6 7 public static class DriveInfoExtension 8 { 9 public static IEnumerable<String> EnumerateFiles(this DriveInfo drive) 10 { 11 return new FileSearch().EnumerateFiles(drive.Name); 12 } 13 14 public static string GetPathByFile(this DriveInfo drive, string _filename) 15 { 16 return new FileSearch().EnumerateFiles(drive.Name, _filename); 17 } 18 }
执行代码


哈哈就问你服不服!!理论上我只获取游戏路径的话大概在3000毫秒左右,不服你可以用windows自带的搜索去搜搜你就知道这个有多牛X,
既然做出来了,那必须去买个萌,吸收吸收欢呼声(是不是很贱)

本期先这样下期我们继续。
浙公网安备 33010602011771号