最近整理到很久之前项目中的一段偷懒处理,主要针对新加入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);
}
}