A bundle of dotnet utilities
- Nuget:
Core library
Install-Package Blackcat -Version 1.0.1
Some utilities for WinForm flatform
Install-Package Blackcat.WinForm -Version 1.0.0
- Dll files (comming soon): release page
- Clone this repo as a submodule and add reference to your .net project
Saves or loads configurations from file automatically.
- Defines your configuration class
public class MyConfig { public string Config1 {get;set;} public int Config2 {get;set;} }
- Gets configuration and use it
// MyConfig will be loaded from file or create new one if needed var myConfig = ConfigLoader.Default.Get<MyConfig>(); textBox1.Text = myConfig.Config1;
- Saves configuration: your configuration will be saved to json file automatically when winform application's closed. Json file will be something like:
{
"Metadata": {
"Modified": "2019年11月11日T14:29:35.3268223+07:00"
},
"Configs": [
{
"Key": "MyObject",
"Data": {
"Config1": "This is a string config",
"Config2": 123,
}
}
]
}Saves immediately whenever config property changes
// Inherits from AutoNotifyPropertyChanged class and marks properties as virtual. public class MyConfig : AutoNotifyPropertyChanged { public virtual string Config1 {get;set;} public virtual int Config2 {get;set;} } // Change save mode to: // OnChange: save immediately when config changes // OnExit: save when app's closed // ReadOnly: don't save ConfigLoader.Default.SaveMode = SaveMode.OnChange; var myConfig = ConfigLoader.Default.Get<MyConfig>(); myConfig.Config1 = "New config";// config will be saved after this call
Edits config at runtime
[ConfigClass(Description = "Describe your config here")] public class MyConfig { [Description("First string config")] public string Config1 { get; set; } [Description("Second int config")] public int Config2 { get; set; } } var myConfig = ConfigLoader.Default.Get<MyConfig>(); using (var form = new SettingsForm { Settings = myConfig }) { form.ShowDialog(this); var changed = form.SettingsChanged; }
Lightweight event aggregator/messenger for loosely coupled communication
- Defines an event which will carry data from caller to subscriber
public class MyEvent { public string Data1 {get; set;} public int Data2 {get; set;} }
- Subscribes events
class MyService { MyService() { // Start subscribing incomming events (call Unregister to unsubscribe events) EventBus.Default.Register(this); } [Subscribe] private void SubscribeMethod1(MyEvent myEvent) { // do you stuff here } [Subscribe] private void SubscribeMethod2(MyEvent2 myEvent2) { // do you stuff here } }
- Raises an event
// Somewhere else in your project EventBus.Default.Post(new MyEvent {Data1 = "my data1", Data2 = 123});// SubscribeMethod1 will be called EventBus.Default.Post(new MyEvent1 {...});// SubscribeMethod2 will be called
Subscribes an event in background thread
// Currently we're supporting several thread modes: // Post: default mode, invokes subscribers immediately in current caller thread // Thread: invokes subscribers in a new background thread if caller thread is main thread, otherwise invokes subscribers immediately in current caller thread // Async: Always invokes subsribers in a new background thread // Main: Invokes subscribers in main thread (UI thread) in blocking mode // MainOrder: Invokes subsribers in main thread (UI thread) in non-blocking mode [Subscribe(ThreadMode = ThreadMode.Thread)] private void WillBeCalledInBackgroundThread(MyEvent myEvent) { // this stuff will be called in background thread }
Prevents further propagation of the current event
[Subscribe] private PostHandled CancelableSubscriber(MyEvent myEvent) { // do you stuff here return new PostHandled {Canceled = true}; }
Returns data for caller (only supports for Post or Main ThreadMode)
// From subscribers [Subscribe] private PostHandled ReturnValueForCaller1(MyEvent myEvent) { // do you stuff here return new PostHandled {Data = "any data here"}; } [Subscribe] private string ReturnValueForCaller2(MyEvent myEvent) { // do you stuff here return "any data here"; } // From caller var results = EventBus.Default.Post(new MyEvent{...});
Keeps the last sticky event of a certain type in memory. Then the sticky event can be delivered to subscribers or queried explicitly.
// Posts a sticky event EventBus.Default.Post(new MyEvent{...}, true);// MyEvent likes other events but it's still remaining in memory after called EventBus.Default.Post(new MyEvent{...}, true);// this will replace the first event // Queries a specific sticky event var myStickyEvent = EventBus.Default.GetStickyEvent(typeof(MyEvent)); // Removes a specific sticky event EventBus.Default.RemoveStickyEvent(myStickyEvent);
Communicates with another process uses EventBus
Client process
var eventbus = ClientEventBus.StartNew("my-eventbus"); eventbus.Post(new MyEvent{...});
Main process (ex: a windows service)
var eventbus = ServerEventBus.StartNew("my-eventbus); eventbus.Register(this); ..... [Subscribe] private void ListenMyEvent(MyEvent myEvent) { ... }
Handles and reports unhandled-exception automatically
private AppCrash appCrash = new AppCrash(); // defines it as a global variable to prevent GC collects this.
If an unhandled-exception occurs, a crash report will be shown with notepad, the log file is also saved in CrashLogs folder. Crash report
Show report with a custom form.
// defines you report form public class YourReportForm : Form, IReportWindow { .... public void Initialize(Exception exception, string productName, string devMail) { textBox1.Text = exception.Message; textBox2.Text = productName; textBox3.Text = devMail; } } // Registers you report form var appCrash = new AppCrash { ProductName = "My product", DeveloperMail = "xuanson33bk@gmail.com" }; appCrash.Register(typeof(YourReportForm));
Custom crash logs folder location
var appCrash = new AppCrash {CrashDir = @"C:\newCrashLogsDir"};
Inversion of Control Container (bases on https://github.com/grumpydev/TinyIoC)
// You can annotate your class by Component, Service, Repository or Controller, they are the same but for easier to understand their roles. [Controller] public class MyApp { // Properties which are annotated with Autowired attribute will // be injected automatically [Autowired] public MyComponent MyComponent {get; set;} // myService will be injected automatically public MyApp(MyService myService) {...} .... } [Service] public class MyService {....} [Component] public class MyComponent{....} // Somewhere else... // App32Context will scan the entry assembly to find classes which are annotated with Controller, Component, Repository or Service attributes. using (var context = new App32Context()) { var myApp = context.Resolve<MyApp>(); myApp.Start(); }
Scans classes in specific assemblies
// If you don't pass any assembly, the entry assembly will be used using (var context = new App32Context(assembly1, assemly2, assembly3)) { .... }
Creates multiple instances of components
// By default, only one instance of a component will be created (singleton mode). If you want to create a new instance of a component each time it's injected to another component, set Singleton to false [Service(Singleton = false)] public class MyService {....}
IConfigLoader, IEventBus and AppCrash are registered automatically so you don't need to worry about these guys
using (var context = new App32Context()) { var config = context.Resolve<IConfigLoader>(); .... }
Manipulates on types which don't need to reference explicitly.
Gets type
// Gets Control class from winform namespace, if current project doesn't reference to winform, return type will be null var type = DynamicInvoker.GetType("System.Windows.Forms", "Control");
Registers event
// Registers ApplicationExit event on Application class var type = DynamicInvoker.GetType("System.Windows.Forms", "Application"); if (type != null) { DynamicInvoker.AddEventHandler<EventHandler>(type, "ApplicationExit", Application_ApplicationExit); }
Invokes method
// Invokes CreateControl method on Control instance var type = DynamicInvoker.GetType("System.Windows.Forms", "Control"); if (type != null) { var control = Activator.CreateInstance(type); DynamicInvoker.InvokeMethod(control, "CreateControl"); }
Gets or sets property value
// Gets ProductName from Application class var type1 = DynamicInvoker.GetType("System.Windows.Forms", "Application"); if (type1 != null) { var productName = DynamicInvoker.GetPropertyValue(type1, "ProductName") as string; } // Sets "My text" to Text property var type2 = DynamicInvoker.GetType("System.Windows.Forms", "Control"); if (type2 != null) { var control = Activator.CreateInstance(type2); DynamicInvoker.SetPropertyValue(control, "Text", "My text"); }
Inter process communication which allows processes to communicate each other and synchronize their actions
1 client - 1 server communication
Sender process (client)
using (var sender = new Sender("my-intercomm")) { var response = await sender.SendAsync<string>("Hi server, I'm client1"); Console.WriteLine("Response from the other process is: " + response); }
Receiver process (server)
using (var receiver = new SingleReceiver("my-intercomm")) { var request = await receiver.ReceiveAsync<string>(); await receiver.SendAsync("Hello " + request); }
n client - 1 server communication
Setup your sender process likes the basic usage section.
Receiver process (server)
using (var receiver = new MultiReceiver("my-intercomm")) { await receiver.WaitForSessionAsync(async session => { var request = await session.ReceiveAsync<string>(); await session.SendAsync("Hello " + request); }); }
Currently this library supports communicating through tcp and pipe:
- Blackcat.Intercomm.Pipe: for pipe supported
- Blackcat.Intercomm.Tcp: for tcp supported