最近整理到很久之前项目中的一段偷懒处理,主要针对新加入U3D项目的插件、脚本、动态链接库等没有正常设置,导致在使用VS打开时报错,也没办法使用其自动补齐、智能提示等偷懒功能,甚至连相关定义都看不到,全局搜索时甚至搜索不到的情况。
主要原理是根据文件所在路径判断其编译关系,是放在4种csproj工程文件的具体哪一个。其他废话不多说了,直接上代码:
using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; using System.IO; using System.Xml; public class CSProjTools { [MenuItem("Assets/Add2CSProj")] static void Add2CSProj() { //判断路径是否包含Editor、Plugins、Standard Assets //根据包含情况,加载读取项目目录下的.csproj文件 if(Selection.activeObject == null) { return; } string tProjPath = Application.dataPath.Replace("Assets", ""); var tArr = tProjPath.Split('/'); string tProjName = tArr[tArr.Length - 2]; string tSelectPath = tProjPath + AssetDatabase.GetAssetPath(Selection.activeObject); if(Directory.Exists(tSelectPath)) //选中文件夹,须递归遍历文件 { var tFileArr = Directory.GetFiles(tSelectPath, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWith(".cs") || s.EndsWith(".dll")); foreach(var filepath in tFileArr) { string tCSProjFilePath = tProjPath + tProjName + getCSProjFileName(filepath); insertCSProjFile(tCSProjFilePath, filepath); } } else //if(File.Exists(tSelectPath)) //选中的是文件 { tSelectPath = tSelectPath.Replace(tProjPath, ""); string tCSProjFilePath = tProjPath + tProjName + getCSProjFileName(tSelectPath); insertCSProjFile(tCSProjFilePath, tSelectPath); } } static string getCSProjFileName(string _filePath) { _filePath = _filePath.Replace("\", "/"); if(_filePath.Contains("/Editor/")) { if(_filePath.Contains("Assets/Plugins/") || _filePath.Contains("Assets/Standard Assets/")) { return ".CSharp.Editor.Plugins.csproj"; } else { return ".CSharp.Editor.csproj"; } } else { if(_filePath.Contains("Assets/Plugins/") || _filePath.Contains("Assets/Standard Assets/")) { return ".CSharp.Plugins.csproj"; } else { return ".CSharp.csproj"; } } } static void insertCSProjFile(string _csprojFilePath, string _filePath) { XmlDocument tXmlDoc = new XmlDocument(); tXmlDoc.Load(_csprojFilePath); string tNameSpace = "http://schemas.microsoft.com/developer/msbuild/2003"; XmlNamespaceManager tNameSpaceMgr = new XmlNamespaceManager(tXmlDoc.NameTable); tNameSpaceMgr.AddNamespace("ns", tNameSpace); //不加入namespace会找不到 XmlNode tRootNode = tXmlDoc.SelectSingleNode("ns:Project", tNameSpaceMgr); XmlNodeList tGroupLst = tRootNode.SelectNodes("ns:ItemGroup", tNameSpaceMgr); //XmlNodeList tGroupLst = tXmlDoc.SelectNodes("/ns:Project/ns:ItemGroup", tNameSpaceMgr); foreach(XmlNode item in tGroupLst) { XmlElement tGroupElem = (XmlElement)item; //暂不考虑不包含子节点的情况 if(_filePath.EndsWith(".dll") && item.FirstChild.Name == "Reference") //dll动态链接库 { var tArr = _filePath.Split('/'); string tValue = tArr[tArr.Length - 1].Replace(".dll", ""); foreach(XmlNode child in item.ChildNodes) { foreach(XmlAttribute attr in child.Attributes) { if(attr.Name == "Include" && attr.Value == tValue) { return; } } } XmlElement tChild = (XmlElement)item.FirstChild.CloneNode(true); tChild.SetAttribute("Include", tValue); XmlElement tSubChild = tXmlDoc.CreateElement("HintPath", tNameSpace); tSubChild.InnerText = _filePath.Replace("/", "\"); tChild.AppendChild(tSubChild); item.AppendChild(tChild); break; } else if((_filePath.EndsWith(".cs") && item.FirstChild.Name == "Compile") || item.FirstChild.Name == "None") //CS脚本或资源 { foreach(XmlNode child in item.ChildNodes) { foreach(XmlAttribute attr in child.Attributes) { if(attr.Name == "Include" && attr.Value == _filePath) { return; } } } XmlElement tChild = (XmlElement)item.FirstChild.CloneNode(true); //XmlElement tChild = tXmlDoc.CreateElement(item.FirstChild.Name, tNameSpace); tChild.SetAttribute("Include", _filePath.Replace("/", "\")); item.AppendChild(tChild); break; } } tXmlDoc.Save(_csprojFilePath); } }
美中不足之处是每次都需要重新加载工程,不如使用右键的方式逐个加入文件和引用来的方便。后来也有考虑使用VSLangProj.dll结合EnvDTE一起使用来进一步完善,不过当时够用就罢手了。再后来也就一直没有时间重新拿起。
//todo:考虑将同一dll加入到多个csproj的情况 [MenuItem("Assets/Add2ExtraCSProj/CS")] static void Add2CSProjCS() { if(Selection.activeObject == null) { return; } string tProjPath = Application.dataPath.Replace("Assets", ""); var tArr = tProjPath.Split('/'); string tProjName = tArr[tArr.Length - 2]; string tSelectPath = tProjPath + AssetDatabase.GetAssetPath(Selection.activeObject); //暂不考虑选中多文件的情况 tSelectPath = tSelectPath.Replace(tProjPath, ""); string tCSProjFilePath = tProjPath + tProjName + ".CSharp.csproj"; insertCSProjFile(tCSProjFilePath, tSelectPath); } [MenuItem("Assets/Add2ExtraCSProj/CSPlugin")] static void Add2CSProjCSPlugin() { if(Selection.activeObject == null) { return; } string tProjPath = Application.dataPath.Replace("Assets", ""); var tArr = tProjPath.Split('/'); string tProjName = tArr[tArr.Length - 2]; string tSelectPath = tProjPath + AssetDatabase.GetAssetPath(Selection.activeObject); //暂不考虑选中多文件的情况 tSelectPath = tSelectPath.Replace(tProjPath, ""); string tCSProjFilePath = tProjPath + tProjName + ".CSharp.Plugins.csproj"; insertCSProjFile(tCSProjFilePath, tSelectPath); } [MenuItem("Assets/Add2ExtraCSProj/CSEditor")] static void Add2CSProjCSEditor() { if(Selection.activeObject == null) { return; } string tProjPath = Application.dataPath.Replace("Assets", ""); var tArr = tProjPath.Split('/'); string tProjName = tArr[tArr.Length - 2]; string tSelectPath = tProjPath + AssetDatabase.GetAssetPath(Selection.activeObject); //暂不考虑选中多文件的情况 tSelectPath = tSelectPath.Replace(tProjPath, ""); string tCSProjFilePath = tProjPath + tProjName + ".CSharp.Editor.csproj"; insertCSProjFile(tCSProjFilePath, tSelectPath); } [MenuItem("Assets/Add2ExtraCSProj/CSEditorPlugin")] static void Add2CSProjCSEditorPlugin() { if(Selection.activeObject == null) { return; } string tProjPath = Application.dataPath.Replace("Assets", ""); var tArr = tProjPath.Split('/'); string tProjName = tArr[tArr.Length - 2]; string tSelectPath = tProjPath + AssetDatabase.GetAssetPath(Selection.activeObject); //暂不考虑选中多文件的情况 tSelectPath = tSelectPath.Replace(tProjPath, ""); string tCSProjFilePath = tProjPath + tProjName + ".CSharp.Editor.Plugins.csproj"; insertCSProjFile(tCSProjFilePath, tSelectPath); } [MenuItem("Assets/Add2CSProj")] static void Add2CSProj() { //判断路径是否包含Editor、Plugins、Standard Assets等 //根据包含情况,加载读取项目目录下的.csproj文件 if(Selection.activeObject == null) { return; } string tProjPath = Application.dataPath.Replace("Assets", ""); var tArr = tProjPath.Split('/'); string tProjName = tArr[tArr.Length - 2]; string tSelectPath = tProjPath + AssetDatabase.GetAssetPath(Selection.activeObject); //暂不考虑选中多文件的情况 if(Directory.Exists(tSelectPath)) //选中文件夹,须递归遍历文件 { var tFileArr = Directory.GetFiles(tSelectPath, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWith(".cs") || s.EndsWith(".dll")); foreach(var filepath in tFileArr) { string tCSProjFilePath = tProjPath + tProjName + getCSProjFileName(filepath); insertCSProjFile(tCSProjFilePath, filepath, false); } //统一存储,防止IO效率及冲突 foreach(var kv in xmlDocCacheMap) { if(insertCountMap.ContainsKey(kv.Key) && insertCountMap[kv.Key] > 0) { kv.Value.Save(kv.Key); } } xmlDocCacheMap.Clear(); xmlNodeLstCacheMap.Clear(); } else //if(File.Exists(tSelectPath)) //选中的是文件 { tSelectPath = tSelectPath.Replace(tProjPath, ""); string tCSProjFilePath = tProjPath + tProjName + getCSProjFileName(tSelectPath); insertCSProjFile(tCSProjFilePath, tSelectPath); } string tTipStr = "Add2CSProj Finished: "; foreach(var kv in insertCountMap) { tTipStr += " " + kv.Key + ": " + kv.Value; } insertCountMap.Clear(); Debug.LogError(tTipStr); } static string getCSProjFileName(string _filePath) { _filePath = _filePath.Replace("\", "/"); if(_filePath.Contains("/Editor/")) { if(_filePath.Contains("Assets/Plugins/") || _filePath.Contains("Assets/Standard Assets/")) { return ".CSharp.Editor.Plugins.csproj"; } else { return ".CSharp.Editor.csproj"; } } else { if(_filePath.Contains("Assets/Plugins/") || _filePath.Contains("Assets/Standard Assets/")) { return ".CSharp.Plugins.csproj"; } else { return ".CSharp.csproj"; } } } static string xmlNameSpace = "http://schemas.microsoft.com/developer/msbuild/2003"; static IDictionary<string, XmlDocument> xmlDocCacheMap = new Dictionary<string, XmlDocument>(); static IDictionary<string, XmlNodeList> xmlNodeLstCacheMap = new Dictionary<string, XmlNodeList>(); static IDictionary<string, int> insertCountMap = new Dictionary<string, int>(); static void insertCSProjFile(string _csprojFilePath, string _filePath, bool _syncSave = true) { if(_filePath.StartsWith(Application.dataPath)) { _filePath = _filePath.Replace(Application.dataPath, "Assets"); } //Debug.LogError(_csprojFilePath + " " + _filePath); XmlDocument tXmlDoc = null; XmlNodeList tGroupLst = null; if(xmlDocCacheMap.ContainsKey(_csprojFilePath)) { tXmlDoc = xmlDocCacheMap[_csprojFilePath]; tGroupLst = xmlNodeLstCacheMap[_csprojFilePath]; } else { tXmlDoc = new XmlDocument(); tXmlDoc.Load(_csprojFilePath); XmlNamespaceManager tNameSpaceMgr = new XmlNamespaceManager(tXmlDoc.NameTable); tNameSpaceMgr.AddNamespace("ns", xmlNameSpace); //不加入namespace会找不到 XmlNode tRootNode = tXmlDoc.SelectSingleNode("ns:Project", tNameSpaceMgr); tGroupLst = tRootNode.SelectNodes("ns:ItemGroup", tNameSpaceMgr); //tGroupLst = tXmlDoc.SelectNodes("/ns:Project/ns:ItemGroup", tNameSpaceMgr); xmlDocCacheMap[_csprojFilePath] = tXmlDoc; xmlNodeLstCacheMap[_csprojFilePath] = tGroupLst; } //Debug.LogError(_filePath); _filePath = _filePath.Replace("/", "\"); foreach(XmlNode item in tGroupLst) { XmlElement tGroupElem = (XmlElement)item; //Debug.LogError(item.FirstChild.Name); //暂不考虑不包含子节点的情况 if(_filePath.EndsWith(".dll") && item.FirstChild.Name == "Reference") //dll动态链接库 { var tArr = _filePath.Split('\'); string tValue = tArr[tArr.Length - 1].Replace(".dll", ""); foreach(XmlNode child in item.ChildNodes) { foreach(XmlAttribute attr in child.Attributes) { if(attr.Name == "Include" && attr.Value == tValue) { return; } } } XmlElement tChild = (XmlElement)item.FirstChild.CloneNode(true); tChild.SetAttribute("Include", tValue); XmlElement tSubChild = tXmlDoc.CreateElement("HintPath", xmlNameSpace); tSubChild.InnerText = _filePath; tChild.AppendChild(tSubChild); item.AppendChild(tChild); if(!insertCountMap.ContainsKey(_csprojFilePath)) { insertCountMap[_csprojFilePath] = 0; } insertCountMap[_csprojFilePath]++; break; } else if((_filePath.EndsWith(".cs") && item.FirstChild.Name == "Compile") || item.FirstChild.Name == "None") //CS脚本或资源 { //Debug.LogError(item.FirstChild.InnerText + " " + item.FirstChild.Value); //foreach(XmlAttribute attr in item.FirstChild.Attributes) //{ // Debug.LogError(attr.Name + " " + attr.Value); //} foreach(XmlNode child in item.ChildNodes) { foreach(XmlAttribute attr in child.Attributes) { if(attr.Name == "Include" && attr.Value == _filePath) { return; } } } XmlElement tChild = (XmlElement)item.FirstChild.CloneNode(true); //XmlElement tChild = tXmlDoc.CreateElement(item.FirstChild.Name, tNameSpace); tChild.SetAttribute("Include", _filePath); item.AppendChild(tChild); if(!insertCountMap.ContainsKey(_csprojFilePath)) { insertCountMap[_csprojFilePath] = 0; } insertCountMap[_csprojFilePath]++; break; } } if(_syncSave) { tXmlDoc.Save(_csprojFilePath); //todo:try-catch,防止IO异常 xmlDocCacheMap.Remove(_csprojFilePath); xmlNodeLstCacheMap.Remove(_csprojFilePath); } }