logic: fix more Visual Studio plugin issues

* using .Net framework version 4.6.1 in all projects
* implemented exception handling in VCFileWrapperFactory
* moved logging in separate assembly
* enabled logging in wrapper code
This commit is contained in:
malte_langkabel
2017-06-06 15:46:14 +02:00
parent fc36cc25ca
commit b659e81515
25 changed files with 326 additions and 116 deletions
@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
namespace CoatiSoftware.SourcetrailPlugin.Logging
{
public class FileLogger : ILogger
{
private static string _directory = "";
private static string _fileNamePrefix = "Log_SourcetrailPlugin_";
private static string _fileNameSufix = ".txt";
private string _fileName = "";
private Queue<string> _messageBacklog = new Queue<string>(); // stores messages if the log file was in use at original logging time
public FileLogger()
{
DateTime time = DateTime.Now;
string dateString = "";
dateString += time.Year.ToString() + "-" + time.Month.ToString() + "-" + time.Day.ToString() + "_";
dateString += time.Hour.ToString() + "-" + time.Minute.ToString() + "-" + time.Second.ToString();
_fileName = _fileNamePrefix + dateString + _fileNameSufix;
_directory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
_directory += "\\Coati Software\\Plugins\\VS\\";
if(System.IO.Directory.Exists(_directory) == false)
{
System.IO.Directory.CreateDirectory(_directory);
}
}
public void LogMessage(LogMessage message)
{
System.IO.StreamWriter writer = null;
try
{
writer = System.IO.File.AppendText(_directory + _fileName);
// write backlog to file first
while(_messageBacklog.Count > 0)
{
string bm = _messageBacklog.Dequeue();
writer.WriteLine(bm);
}
writer.WriteLine(message.ToString());
writer.Close();
}
catch(Exception e)
{
// well...file is still in use
_messageBacklog.Enqueue(message.ToString());
}
finally
{
if(writer != null)
{
writer.Close();
}
}
}
}
}
@@ -0,0 +1,7 @@
namespace CoatiSoftware.SourcetrailPlugin.Logging
{
public interface ILogger
{
void LogMessage(LogMessage message);
}
}
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
namespace CoatiSoftware.SourcetrailPlugin.Logging
{
public class LogManager
{
private static LogManager _instance = null;
private List<ILogger> _loggers = new List<ILogger>();
private bool _loggingEnabled = false;
public List<ILogger> Loggers
{
get { return _loggers; }
set { _loggers = value; }
}
public bool LoggingEnabled
{
get { return _loggingEnabled; }
set { _loggingEnabled = value; }
}
private LogManager()
{
}
public static LogManager GetInstance()
{
if(_instance == null)
{
_instance = new LogManager();
}
return _instance;
}
public void LogInfo(string message, string sourceFile, string callingFunction, int lineNumber)
{
LogMessage logMessage = new LogMessage();
logMessage.Message = message;
logMessage.MessageType = LogMessage.LogMessageType.INFO;
logMessage.Time = DateTime.Now;
logMessage.SourceFile = sourceFile;
logMessage.CallingFunction = callingFunction;
logMessage.LineNumber = lineNumber;
Log(logMessage);
}
public void LogWarning(string message, string sourceFile, string callingFunction, int lineNumber)
{
LogMessage logMessage = new LogMessage();
logMessage.Message = message;
logMessage.MessageType = LogMessage.LogMessageType.WARNING;
logMessage.Time = DateTime.Now;
logMessage.SourceFile = sourceFile;
logMessage.CallingFunction = callingFunction;
logMessage.LineNumber = lineNumber;
Log(logMessage);
}
public void LogError(string message, string sourceFile, string callingFunction, int lineNumber)
{
LogMessage logMessage = new LogMessage();
logMessage.Message = message;
logMessage.MessageType = LogMessage.LogMessageType.ERROR;
logMessage.Time = DateTime.Now;
logMessage.SourceFile = sourceFile;
logMessage.CallingFunction = callingFunction;
logMessage.LineNumber = lineNumber;
Log(logMessage);
}
private void Log(LogMessage message)
{
if(_loggingEnabled == true)
{
foreach (ILogger logger in _loggers)
{
logger.LogMessage(message);
}
}
}
}
}
@@ -0,0 +1,86 @@
using System;
namespace CoatiSoftware.SourcetrailPlugin.Logging
{
public class LogMessage
{
public enum LogMessageType
{
UNKNOWN = 0,
INFO,
WARNING,
ERROR
}
private string _message = "";
private DateTime _time = new DateTime();
private LogMessageType _messageType = LogMessageType.UNKNOWN;
private string _sourceFile = "";
private string _callingFunction = "";
private int _lineNumber = -1;
public string Message
{
get { return _message; }
set { _message = value; }
}
public DateTime Time
{
get { return _time; }
set { _time = value; }
}
public LogMessageType MessageType
{
get { return _messageType; }
set { _messageType = value; }
}
public string SourceFile
{
get { return _sourceFile; }
set { _sourceFile = value; }
}
public string CallingFunction
{
get { return _callingFunction; }
set { _callingFunction = value; }
}
public int LineNumber
{
get { return _lineNumber; }
set { _lineNumber = value; }
}
public override string ToString()
{
string result = "";
result += _time.Hour.ToString() + ":" + _time.Minute.ToString() + ":"+ _time.Second.ToString();
result += "\t";
result += _messageType.ToString();
result += "\t";
result += _sourceFile + ":" + _lineNumber.ToString();
result += " (";
result += _callingFunction;
result += ")";
result += "\t\t";
result += _message;
return result;
}
}
}
@@ -0,0 +1,40 @@
using System.Runtime.CompilerServices;
namespace CoatiSoftware.SourcetrailPlugin.Logging
{
public class Logging
{
public static void LogInfo(string message, [CallerFilePath] string file = "", [CallerMemberName] string member = "", [CallerLineNumber] int line = 0)
{
int idx = file.LastIndexOf('\\');
if(idx > -1)
{
file = file.Substring(idx + 1);
}
LogManager.GetInstance().LogInfo(message, file, member, line);
}
public static void LogWarning(string message, [CallerFilePath] string file = "", [CallerMemberName] string member = "", [CallerLineNumber] int line = 0)
{
int idx = file.LastIndexOf('\\');
if (idx > -1)
{
file = file.Substring(idx + 1);
}
LogManager.GetInstance().LogWarning(message, file, member, line);
}
public static void LogError(string message, [CallerFilePath] string file = "", [CallerMemberName] string member = "", [CallerLineNumber] int line = 0)
{
int idx = file.LastIndexOf('\\');
if (idx > -1)
{
file = file.Substring(idx + 1);
}
LogManager.GetInstance().LogError(message, file, member, line);
}
}
}
@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
namespace CoatiSoftware.SourcetrailPlugin.Logging.Obfuscation
{
public class NameObfuscator
{
private static NameObfuscator _instance = null;
private Dictionary<string, string> _dictionary = new Dictionary<string, string>();
private bool _enabled = false;
char _currentChar = 'a';
int _currentInt = 0;
private static string _directory = "";
private static string _fileNamePrefix = "Dictionary_SourcetrailPlugin_";
private static string _fileNameSufix = ".txt";
private string _fileName = "";
private Queue<string> _messageBacklog = new Queue<string>(); // stores messages if the log file was in use at original logging time
private NameObfuscator()
{
DateTime time = DateTime.Now;
string dateString = "";
dateString += time.Year.ToString() + "-" + time.Month.ToString() + "-" + time.Day.ToString() + "_";
dateString += time.Hour.ToString() + "-" + time.Minute.ToString() + "-" + time.Second.ToString();
_fileName = _fileNamePrefix + dateString + _fileNameSufix;
_directory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
_directory += "\\Coati Software\\Plugins\\VS\\";
if (System.IO.Directory.Exists(_directory) == false)
{
System.IO.Directory.CreateDirectory(_directory);
}
}
private static void CreateInstance()
{
if (_instance == null)
{
_instance = new NameObfuscator();
}
}
public static string GetObfuscatedName(string originalName)
{
CreateInstance();
if (_instance._enabled)
{
if (_instance._dictionary.ContainsKey(originalName))
{
return _instance._dictionary[originalName];
}
else
{
string newName = _instance.GetNewName();
_instance._dictionary[originalName] = newName;
_instance.WriteDictionaryEntryToFile(newName, originalName); // key and value reversed because that's the way the dictionary file is to be used...
return newName;
}
}
else
{
return originalName;
}
}
public static void Enabled(bool enabled)
{
CreateInstance();
_instance._enabled = enabled;
}
private string GetNewName()
{
string name = "";
name = _currentChar.ToString() + _currentInt.ToString();
++_currentChar;
if((int)_currentChar > 122)
{
_currentChar = 'a';
++_currentInt;
}
return name;
}
private void WriteDictionaryEntryToFile(string key, string value)
{
System.IO.StreamWriter writer = null;
string message = key + " - " + value;
try
{
writer = System.IO.File.AppendText(_directory + _fileName);
// write backlog to file first
while (_messageBacklog.Count > 0)
{
string bm = _messageBacklog.Dequeue();
writer.WriteLine(bm);
}
writer.WriteLine(message.ToString());
writer.Close();
}
catch (Exception e)
{
// well...file is still in use
_messageBacklog.Enqueue(message.ToString());
}
finally
{
if (writer != null)
{
writer.Close();
}
}
}
}
}
@@ -0,0 +1,77 @@
using EnvDTE;
using System;
using System.Diagnostics;
namespace CoatiSoftware.SourcetrailPlugin.Logging
{
public class VSOutputLogger : ILogger
{
private EnvDTE.DTE _dte = null;
private OutputWindowPane _pane = null;
public VSOutputLogger(EnvDTE.DTE dte)
{
_dte = dte;
}
public void LogMessage(LogMessage message)
{
if (message.MessageType == SourcetrailPlugin.Logging.LogMessage.LogMessageType.INFO)
{
Debug.WriteLine(message.Message, "Info");
WriteToOutputWindow("Info: " + message.Message);
}
if (message.MessageType == SourcetrailPlugin.Logging.LogMessage.LogMessageType.WARNING)
{
Debug.WriteLine(message.Message, "Warning");
WriteToOutputWindow("Warning: " + message.Message);
}
if (message.MessageType == SourcetrailPlugin.Logging.LogMessage.LogMessageType.ERROR)
{
Debug.WriteLine(message.Message, "Error");
WriteToOutputWindow("Error: " + message.Message);
}
}
private void WriteToOutputWindow(string message)
{
string paneName = "Sourcetrail Log";
if (_dte.Windows.Count > 0)
{
Window window = _dte.Windows.Item(EnvDTE.Constants.vsWindowKindOutput);
OutputWindow outputWindow = (OutputWindow)window.Object;
OutputWindowPanes panes = outputWindow.OutputWindowPanes;
if(_pane == null)
{
try
{
for (int i = 0; i < panes.Count; i++)
{
OutputWindowPane pane = panes.Item(i);
if (pane.Name.Equals(paneName, StringComparison.CurrentCultureIgnoreCase))
{
_pane = outputWindow.OutputWindowPanes.Item(i);
break;
}
}
}
catch (Exception e)
{
}
}
if (_pane == null)
{
_pane = outputWindow.OutputWindowPanes.Add(paneName);
}
_pane.OutputString(message + '\n');
}
}
}
}
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SourcetrailPluginUtility")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Coati Software")]
[assembly: AssemblyProduct("SourcetrailPluginUtility")]
[assembly: AssemblyCopyright("Copyright © Coati Software 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("604b5751-af34-4ff9-92c3-c85a6bcdf98e")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.*")]
//[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{604B5751-AF34-4FF9-92C3-C85A6BCDF98E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CoatiSoftware.SourcetrailPlugin</RootNamespace>
<AssemblyName>SourcetrailPluginUtility</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>Key.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<EmbedInteropTypes>False</EmbedInteropTypes>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.Shell.11.0, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Logging\FileLogger.cs" />
<Compile Include="Logging\ILogger.cs" />
<Compile Include="Logging\Logging.cs" />
<Compile Include="Logging\LogManager.cs" />
<Compile Include="Logging\LogMessage.cs" />
<Compile Include="Logging\Obfuscation\NameObfuscator.cs" />
<Compile Include="Logging\VSOutputLogger.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Key.snk" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>