482 lines
18 KiB
C#
482 lines
18 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Configuration;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Tor;
|
|
using Tor.Config;
|
|
using MarketData;
|
|
using MarketData.Utils;
|
|
using Utility=MarketData.Utils.Utility;
|
|
|
|
// This project must be built in 32 bit mode. Due to the size of IntPtr being 64 and not 32 as needed.
|
|
|
|
// Tor Current Version
|
|
// https://www.torproject.org/download/tor/
|
|
|
|
// Tor older versions
|
|
// https://archive.torproject.org/tor-package-archive/torbrowser/
|
|
|
|
// Tor Command Line Options
|
|
// https://2019.www.torproject.org/docs/tor-manual.html.en
|
|
|
|
#pragma warning disable 618, 3021, 169
|
|
|
|
namespace TorWebClient
|
|
{
|
|
public class Program
|
|
{
|
|
private static readonly int TIMER_INTERVAL_MINUTES=2;
|
|
static readonly string agentName="Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko";
|
|
|
|
[DllImport("user32.dll")]
|
|
static extern bool SetProcessDPIAware();
|
|
|
|
[DllImport("wininet.dll",SetLastError=true,CharSet=CharSet.Auto)]
|
|
static extern IntPtr InternetOpen(string lpszAgent,int dwAccessType,string lpszProxyName,string lpszProxyBypass,int dwFlags);
|
|
|
|
[DllImport("wininet.dll",SetLastError=true)]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
static extern bool InternetCloseHandle(IntPtr hInternet);
|
|
|
|
[DllImport("wininet.dll",CharSet=CharSet.Ansi,SetLastError=true)]
|
|
static extern bool InternetSetOption(IntPtr hInternet,INTERNET_OPTION dwOption,IntPtr lpBuffer,int lpdwBufferLength);
|
|
|
|
[DllImport("wininet.dll",CharSet=CharSet.Ansi,SetLastError=true)]
|
|
static extern bool InternetQueryOptionList(IntPtr handle,INTERNET_OPTION optionFlag,ref INTERNET_PER_CONN_OPTION_LIST optionList,ref int size);
|
|
|
|
[DllImport("Kernel32")]
|
|
public static extern bool SetConsoleCtrlHandler(HandlerRoutine handler,bool add);
|
|
|
|
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
|
|
struct INTERNET_PER_CONN_OPTION_LIST
|
|
{
|
|
public int Size;
|
|
public IntPtr Connection;
|
|
public int OptionCount;
|
|
public int OptionError;
|
|
public IntPtr pOptions;
|
|
}
|
|
|
|
enum INTERNET_OPTION
|
|
{
|
|
INTERNET_OPTION_PER_CONNECTION_OPTION=75,
|
|
INTERNET_OPTION_SETTINGS_CHANGED=39,
|
|
INTERNET_OPTION_REFRESH=37
|
|
}
|
|
|
|
enum INTERNET_PER_CONN_OPTIONENUM
|
|
{
|
|
INTERNET_PER_CONN_FLAGS=1,
|
|
INTERNET_PER_CONN_PROXY_SERVER=2,
|
|
INTERNET_PER_CONN_PROXY_BYPASS=3,
|
|
INTERNET_PER_CONN_AUTOCONFIG_URL=4,
|
|
INTERNET_PER_CONN_AUTODISCOVERY_FLAGS=5,
|
|
INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL=6,
|
|
INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS=7,
|
|
INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME=8,
|
|
INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL=9,
|
|
INTERNET_PER_CONN_FLAGS_UI=10
|
|
}
|
|
|
|
const int INTERNET_OPEN_TYPE_DIRECT=1;
|
|
const int INTERNET_OPEN_TYPE_PRECONFIG=0;
|
|
|
|
enum INTERNET_OPTION_PER_CONN_FLAGS
|
|
{
|
|
PROXY_TYPE_DIRECT=0x00000001, // direct to net
|
|
PROXY_TYPE_PROXY=0x00000002, // via named proxy
|
|
PROXY_TYPE_AUTO_PROXY_URL=0x00000004, // autoproxy URL
|
|
PROXY_TYPE_AUTO_DETECT=0x00000008 // use autoproxy detection
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Explicit)]
|
|
struct INTERNET_PER_CONN_OPTION_OPTIONUNION
|
|
{
|
|
[FieldOffset(0)]
|
|
public int dwValue;
|
|
[FieldOffset(0)]
|
|
public IntPtr pszValue;
|
|
[FieldOffset(0)]
|
|
public FILETIME ftValue;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
struct INTERNET_PER_CONN_OPTION
|
|
{
|
|
public int dwOption;
|
|
public INTERNET_PER_CONN_OPTION_OPTIONUNION value;
|
|
}
|
|
private static Program instance=null;
|
|
private RouterCollection allRouters;
|
|
private volatile bool closing;
|
|
private CircuitCollection circuits;
|
|
private Client client;
|
|
private Timer dispatcherTimer;
|
|
|
|
private Program()
|
|
{
|
|
closing=false;
|
|
dispatcherTimer=new Timer(new TimerCallback(OnDispatcherTimerTick),null,0,(TIMER_INTERVAL_MINUTES*60*1000)); // every minute
|
|
}
|
|
public ManualResetEvent ResetEvent{get;set;}
|
|
public static Program GetInstance()
|
|
{
|
|
lock(typeof(Program))
|
|
{
|
|
if(null==instance)instance=new Program();
|
|
return instance;
|
|
}
|
|
}
|
|
private void OnDispatcherTimerTick(object sender)
|
|
{
|
|
lock(this)
|
|
{
|
|
if(null==client) return;
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} DispatcherTimerTick : Total Uploaded: {1}, Total Downloaded: {2}",Utility.DateTimeToStringMMSDDSYYYYHHMMSS(DateTime.Now),client.Status.TotalBytesUploaded.ToString(),client.Status.TotalBytesDownloaded.ToString()));
|
|
}
|
|
}
|
|
private void OnClientBandwidthChanged(object sender,BandwidthEventArgs e)
|
|
{
|
|
//lock(this)
|
|
//{
|
|
// MDTrace.WriteLine(LogLevel.DEBUG,"OnClientBandwidthChanged");
|
|
// if(closing) return;
|
|
//}
|
|
}
|
|
private void OnClientCircuitsChanged(object sender,EventArgs e)
|
|
{
|
|
//lock(this)
|
|
//{
|
|
// MDTrace.WriteLine(LogLevel.DEBUG,"OnClientCircuitsChanged");
|
|
// circuits=client.Status.Circuits;
|
|
// if(closing) return;
|
|
//}
|
|
}
|
|
private void OnClientConnectionsChanged(object sender,EventArgs e)
|
|
{
|
|
//lock(this)
|
|
//{
|
|
//MDTrace.WriteLine(LogLevel.DEBUG,"OnClientConnectionsChanged");
|
|
//if(closing) return;
|
|
//}
|
|
}
|
|
private void OnClientStreamsChanged(object sender,EventArgs e)
|
|
{
|
|
//lock(this)
|
|
//{
|
|
// MDTrace.WriteLine(LogLevel.DEBUG,"OnClientStreamsChanged");
|
|
// if(closing) return;
|
|
//}
|
|
}
|
|
private void OnClientShutdown(object sender,EventArgs e)
|
|
{
|
|
lock(this)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,"OnClientShutdown");
|
|
if(!closing)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,"OnClientShutdown : The tor client has been terminated without warning");
|
|
return;
|
|
}
|
|
client=null;
|
|
}
|
|
}
|
|
public void Start()
|
|
{
|
|
lock(this)
|
|
{
|
|
Process[] previous=Process.GetProcessesByName("tor");
|
|
if(previous!=null&&previous.Length>0)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,"Killing previous tor instances..");
|
|
foreach(Process process in previous) process.Kill();
|
|
}
|
|
MDTrace.WriteLine(LogLevel.DEBUG,"Creating the tor client..");
|
|
|
|
ClientCreateParams createParams=new ClientCreateParams();
|
|
createParams.CaptureTorConsoleOutput=Boolean.Parse(ConfigurationManager.AppSettings["captureTorConsoleOuput"]);
|
|
createParams.ConfigurationFile=ConfigurationManager.AppSettings["torConfigurationFile"];
|
|
createParams.ControlPassword=ConfigurationManager.AppSettings["torControlPassword"];
|
|
createParams.ControlPort=Convert.ToInt32(ConfigurationManager.AppSettings["torControlPort"]);
|
|
createParams.DefaultConfigurationFile=ConfigurationManager.AppSettings["torDefaultConfigurationFile"];
|
|
createParams.Path=ConfigurationManager.AppSettings["torPath"];
|
|
createParams.SetConfig(ConfigurationNames.AvoidDiskWrites,true);
|
|
|
|
createParams.SetConfig(ConfigurationNames.GeoIPFile,ConfigurationManager.AppSettings["geoipfile"]);
|
|
createParams.SetConfig(ConfigurationNames.GeoIPv6File,ConfigurationManager.AppSettings["geoip6file"]);
|
|
createParams.SetConfig(ConfigurationNames.ExitNodes,"{US}"); // making the final exit node a U.S. node provides better ligitimacy
|
|
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Current Working Directory:{0}",Directory.GetCurrentDirectory()));
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TORPath:{0}",ConfigurationManager.AppSettings["torPath"]));
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TORConfiguration:{0}",ConfigurationManager.AppSettings["torConfigurationFile"]));
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TORControlPort:{0}",ConfigurationManager.AppSettings["torControlPort"]));
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GEOIPFile:{0}",ConfigurationManager.AppSettings["geoipfile"]));
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GEOIPV6File:{0}",ConfigurationManager.AppSettings["geoip6file"]));
|
|
|
|
CreateTorConfig(createParams.ConfigurationFile);
|
|
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TOR Arguments:{0}",createParams.ToString()));
|
|
|
|
|
|
|
|
client=Client.Create(createParams);
|
|
try{Thread.Sleep(2500);}catch(Exception){;}
|
|
client.Configuration.Save();
|
|
|
|
if(!client.IsRunning)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,"The tor client could not be created");
|
|
return;
|
|
}
|
|
|
|
if(!client.Proxy.IsRunning)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,"The proxy is not running");
|
|
return;
|
|
}
|
|
client.Status.BandwidthChanged+=OnClientBandwidthChanged;
|
|
client.Status.CircuitsChanged+=OnClientCircuitsChanged;
|
|
client.Status.ORConnectionsChanged+=OnClientConnectionsChanged;
|
|
client.Status.StreamsChanged+=OnClientStreamsChanged;
|
|
client.Shutdown+=new EventHandler(OnClientShutdown);
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Setting Proxy Configuration: 127.0.0.1:{0}",client.Proxy.Port));
|
|
if(!Program.SetConnectionProxy(string.Format("127.0.0.1:{0}",client.Proxy.Port)))
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,"The application could not set the default connection proxy. The browser control is not using the tor service as a proxy!");
|
|
}
|
|
|
|
allRouters=client.Status.GetAllRouters();
|
|
|
|
if(null!=allRouters)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Got {0} routers",allRouters.Count));
|
|
}
|
|
//DisplayRouters(allRouters);
|
|
//if(null==allRouters)
|
|
//{
|
|
// MDTrace.WriteLine(LogLevel.DEBUG,"Could not download routers");
|
|
//}
|
|
}
|
|
}
|
|
|
|
public bool CreateTorConfig(String pathTorConfig)
|
|
{
|
|
try
|
|
{
|
|
String currentWorkingDirectory = Directory.GetCurrentDirectory();
|
|
pathTorConfig=currentWorkingDirectory+@"\Tor\Tor\"+pathTorConfig;
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Creating {0} ",pathTorConfig));
|
|
String pathBackupTorConfig=pathTorConfig+".bak";
|
|
if(File.Exists(pathTorConfig))
|
|
{
|
|
if(File.Exists(pathBackupTorConfig))File.Delete(pathBackupTorConfig);
|
|
File.Copy(pathTorConfig, pathBackupTorConfig);
|
|
File.Delete(pathTorConfig);
|
|
}
|
|
FileStream outStream = new FileStream(pathTorConfig,FileMode.CreateNew, FileAccess.Write);
|
|
StreamWriter streamWriter = new StreamWriter(outStream);
|
|
streamWriter.WriteLine("# Created by TorWebClient");
|
|
streamWriter.WriteLine("# If non-zero, try to write to disk less frequently than we would otherwise.");
|
|
streamWriter.WriteLine("AvoidDiskWrites 1");
|
|
streamWriter.Flush();
|
|
streamWriter.Close();
|
|
streamWriter.Dispose();
|
|
outStream.Close();
|
|
outStream.Dispose();
|
|
return true;
|
|
}
|
|
catch(Exception exception)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("CreateTorConfig: Exception {0}",exception.ToString()));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
lock(this)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} Disposing client.",Utility.DateTimeToStringMMSDDSYYYYHHMMSS(DateTime.Now)));
|
|
client.Dispose();
|
|
}
|
|
}
|
|
private String[] GetRouterNicknames(List<Router> routers)
|
|
{
|
|
lock(this)
|
|
{
|
|
List<String> nicknames=new List<String>();
|
|
foreach(Router router in routers)
|
|
{
|
|
nicknames.Add(router.Nickname);
|
|
}
|
|
return nicknames.ToArray<String>();
|
|
}
|
|
}
|
|
public void DisplayRouters(RouterCollection routers)
|
|
{
|
|
lock(this)
|
|
{
|
|
if(null==routers)return;
|
|
MDTrace.WriteLine(LogLevel.DEBUG,"Nickname,Identity,IP Address,Digest,Published,Bandwidth,Country,Flags");
|
|
foreach(Router router in routers)
|
|
{
|
|
string countryCode = client.Status.GetCountryCode(router);
|
|
StringBuilder sb=new StringBuilder();
|
|
sb.Append(router.Nickname).Append(",");
|
|
sb.Append(router.Identity).Append(",");
|
|
sb.Append(router.IPAddress).Append(",");
|
|
sb.Append(router.Digest).Append(",");
|
|
sb.Append((router.Publication == DateTime.MinValue ? "Unknown" : Convert.ToString(router.Publication))).Append(",");
|
|
sb.Append(router.Bandwidth).Append(",");
|
|
sb.Append(countryCode).Append(",");
|
|
sb.Append(router.Flags);
|
|
MDTrace.WriteLine(LogLevel.DEBUG,sb.ToString());
|
|
}
|
|
}
|
|
}
|
|
public void DisplayRouters(List<Router> routers)
|
|
{
|
|
lock(this)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,"Nickname,Identity,IP Address,Digest,Published,Bandwidth,Country,Flags");
|
|
foreach(Router router in routers)
|
|
{
|
|
string countryCode=client.Status.GetCountryCode(router);
|
|
StringBuilder sb=new StringBuilder();
|
|
sb.Append(router.Nickname).Append(",");
|
|
sb.Append(router.Identity).Append(",");
|
|
sb.Append(router.IPAddress).Append(",");
|
|
sb.Append(router.Digest).Append(",");
|
|
sb.Append((router.Publication==DateTime.MinValue?"Unknown":Convert.ToString(router.Publication))).Append(",");
|
|
sb.Append(router.Bandwidth).Append(",");
|
|
sb.Append(countryCode).Append(",");
|
|
sb.Append(router.Flags);
|
|
MDTrace.WriteLine(LogLevel.DEBUG,sb.ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool SetConnectionProxy(string proxyServer)
|
|
{
|
|
IntPtr hInternet=InternetOpen(agentName,INTERNET_OPEN_TYPE_DIRECT,null,null,0);
|
|
|
|
INTERNET_PER_CONN_OPTION[] options=new INTERNET_PER_CONN_OPTION[2];
|
|
options[0]=new INTERNET_PER_CONN_OPTION();
|
|
options[0].dwOption=(int)INTERNET_PER_CONN_OPTIONENUM.INTERNET_PER_CONN_FLAGS;
|
|
options[0].value.dwValue=(int)INTERNET_OPTION_PER_CONN_FLAGS.PROXY_TYPE_PROXY;
|
|
|
|
options[1]=new INTERNET_PER_CONN_OPTION();
|
|
options[1].dwOption=(int)INTERNET_PER_CONN_OPTIONENUM.INTERNET_PER_CONN_PROXY_SERVER;
|
|
options[1].value.pszValue=Marshal.StringToHGlobalAnsi(proxyServer);
|
|
|
|
IntPtr buffer=Marshal.AllocCoTaskMem(Marshal.SizeOf(options[0])+Marshal.SizeOf(options[1]));
|
|
IntPtr current=buffer;
|
|
|
|
for(int i=0;i<options.Length;i++)
|
|
{
|
|
Marshal.StructureToPtr(options[i],current,false);
|
|
current=(IntPtr)((int)current+Marshal.SizeOf(options[i]));
|
|
}
|
|
|
|
INTERNET_PER_CONN_OPTION_LIST optionList=new INTERNET_PER_CONN_OPTION_LIST();
|
|
optionList.pOptions=buffer;
|
|
optionList.Size=Marshal.SizeOf(optionList);
|
|
optionList.Connection=IntPtr.Zero;
|
|
optionList.OptionCount=options.Length;
|
|
optionList.OptionError=0;
|
|
|
|
int size=Marshal.SizeOf(optionList);
|
|
IntPtr intPtrStruct=Marshal.AllocCoTaskMem(size);
|
|
Marshal.StructureToPtr(optionList,intPtrStruct,true);
|
|
|
|
bool bReturn=InternetSetOption(hInternet,INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION,intPtrStruct,size);
|
|
|
|
Marshal.FreeCoTaskMem(buffer);
|
|
Marshal.FreeCoTaskMem(intPtrStruct);
|
|
|
|
InternetCloseHandle(hInternet);
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
public delegate bool HandlerRoutine(CtrlTypes handler);
|
|
public enum CtrlTypes { CTRL_C_EVENT=0, CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT=5, CTRL_SHUTDOWN_EVENT }
|
|
private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
|
|
{
|
|
switch(ctrlType)
|
|
{
|
|
case CtrlTypes.CTRL_C_EVENT:
|
|
Program.GetInstance().Stop();
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} Received CTRL_C_EVENT.",Utility.DateTimeToStringMMSDDSYYYYHHMMSS(DateTime.Now)));
|
|
Program.GetInstance().ResetEvent.Set();
|
|
break;
|
|
case CtrlTypes.CTRL_BREAK_EVENT:
|
|
Program.GetInstance().Stop();
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} Received CTRL_BREAK_EVENT.",Utility.DateTimeToStringMMSDDSYYYYHHMMSS(DateTime.Now)));
|
|
Program.GetInstance().ResetEvent.Set();
|
|
break;
|
|
case CtrlTypes.CTRL_CLOSE_EVENT:
|
|
Program.GetInstance().Stop();
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} Received CTRL_CLOSE_EVENT.",Utility.DateTimeToStringMMSDDSYYYYHHMMSS(DateTime.Now)));
|
|
Program.GetInstance().ResetEvent.Set();
|
|
break;
|
|
case CtrlTypes.CTRL_LOGOFF_EVENT:
|
|
Program.GetInstance().Stop();
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} Received CTRL_LOGOFF_EVENT.",Utility.DateTimeToStringMMSDDSYYYYHHMMSS(DateTime.Now)));
|
|
Program.GetInstance().ResetEvent.Set();
|
|
break;
|
|
case CtrlTypes.CTRL_SHUTDOWN_EVENT:
|
|
Program.GetInstance().Stop();
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} Received CTRL_SHUTDOWN_EVENT.",Utility.DateTimeToStringMMSDDSYYYYHHMMSS(DateTime.Now)));
|
|
Program.GetInstance().ResetEvent.Set();
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
static void Main(string[] args)
|
|
{
|
|
try
|
|
{
|
|
try{Program.SetConsoleCtrlHandler(new HandlerRoutine(Program.ConsoleCtrlCheck),true);}
|
|
catch(Exception exception){Console.WriteLine(exception.ToString());}
|
|
if(!Utility.IsAdministrator())
|
|
{
|
|
Console.WriteLine("TorProxy needs to be under administrator account. Press any key to exit.");
|
|
Console.ReadKey();
|
|
return;
|
|
}
|
|
MDTrace.LogLevel=LogLevel.DEBUG;
|
|
String strLogFile="torwebclient.log";
|
|
Utility.CopyFile(strLogFile,Utility.DateTimeToStringYYYYMMDDMMSSTT(DateTime.Now)+strLogFile);
|
|
Utility.DeleteFile(strLogFile);
|
|
Trace.Listeners.Add(new TextWriterTraceListener(strLogFile));
|
|
|
|
ManualResetEvent[] resetEvents=new ManualResetEvent[1];
|
|
for(int index=0;index<resetEvents.Length;index++) resetEvents[index]=new ManualResetEvent(false);
|
|
resetEvents[0].Reset();
|
|
ThreadPool.QueueUserWorkItem(delegate
|
|
{
|
|
Program.GetInstance().Start();
|
|
Program.GetInstance().ResetEvent=resetEvents[0];
|
|
MDTrace.WriteLine(LogLevel.DEBUG,"Running...");
|
|
});
|
|
MDTrace.WriteLine(LogLevel.DEBUG,"Waiting for terminal event...");
|
|
WaitHandle.WaitAll(resetEvents);
|
|
MDTrace.WriteLine(LogLevel.DEBUG,"Terminated...");
|
|
}
|
|
catch(Exception exception)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception: {0}",exception.ToString()));
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
}
|