Common "Debug" function within class library - c#

I am using several class libraries within a project, and one of them is a typical "project.common.dll" library containing some common helper functions. One of these functions is a debug function that creates debug output.
Now I would like to enable/disable debug output by using a user-level property (application settings). How can I reference variables defined in main application's program.cs within this class library ?
Update : Thanks all. I will probably create a static method in my base application that performs the check (to debug or not) and then calls the Debug function in the common library.

You can't.
What you will need to do is have the properties in the class library itself and when you create the instance of it in your main application pass the user setting in:
var debug = new DebugInstance { Output = this.Output };
or set the parameters if it's a static class:
StaticDebug.Output = this.Output;

You can use the ConditionalAtrribute:
[Conditional("DEBUG")]
public static void WriteDebugInfo()
{
Trace.WriteLine("what ever...")
}
This way when you build in Debug mode, the method is invoked; in Release mode not.

Use parameters on the constructor of the Debug class (or a static constructor if the class is static).

Related

WebView2 AddHostObjectToScript in UWP crashes

I'm trying to pass a C# object to a WebView2 using AddHostObjectToScript. After not succeeding to retrieve the object from the webview, I've used the debugger and found out that the AddHostObjectToScript call is never completing.
Here is the full code snippet:
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class Example
{
public string Prop { get; set; } = "example";
}
namespace Example_UWP
{
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
InitializeAsync();
}
public async Task InitializeAsync()
{
await ExampleView.EnsureCoreWebView2Async();
ExampleView.Source = new Uri("http://localhost:3000");
ExampleView.CoreWebView2.OpenDevToolsWindow();
ExampleView.CoreWebView2.AddHostObjectToScript("example", new Example());
}
}
}
The example object is as a result not available in chrome.webview.hostObjects or chrome.webview.hostObjects.sync. The function throws the following error:
The group or resource is not in the correct state to perform the requested operation.
I've tried different alternatives without success, such as:
Keeping a reference to the Example instance in an attribute inside Example_UWP to avoid potential GC
Adding the host object before and after each of the previous steps within InitializeAsync
Wait for the event NavigationCompleted to add the host object.
Wait for 5 seconds before adding the host object.
I'm using Microsoft.Web.WebView2 version 1.0.1264.42
In order to interact with your third-party lib, you need to add a very specific C++ project, Windows Runtime Component (C++/WinRT), to your solution that must be called WinRTAdapter.
Next, you must install a lib to your C++ project from NuGet called Microsoft.Web.WebView2:
After this is done, you must your third-party lib as a reference.
Next, go to your C++ project properties go to Common Properties and choose WebView2:
Here you have to do four changes:
Set Use WebView2 WinRT APIs to No.
Set Use the wv2winrt tool to Yes.
Set Use Javascript case to Yes.
Edit Include filters and add the following ones:
Windows.System.UserProfile
Windows.Globalization.Language
CallJSInterface
CallJSInterface is the name of my third-party's namespace.
You click on OK and build your C++ lib.
After you have built your C++ lib (WinRTAdapter), you must add it to your main project as a reference.
Now, we need to do some changes to be able to invoke the functions from our third-party lib. The first one is to register it. We do it in the same LoadLocalPage() function from before or on NavigationCompleted:
var namespacesName = "CallJSInterface";
var dispatchAdapter = new WinRTAdapter.DispatchAdapter();
core_wv2.AddHostObjectToScript(namespacesName, dispatchAdapter.WrapNamedObject(namespacesName, dispatchAdapter));
Where CallJSInterface is your namespace. After this, you need to register your function in your JS like this:
var callJS;
if (chrome && chrome.webview) {
chrome.webview.hostObjects.options.defaultSyncProxy = true;
chrome.webview.hostObjects.options.forceAsyncMethodMatches = [/Async$/];
chrome.webview.hostObjects.options.ignoreMemberNotFoundError = true;
window.CallJSInterface = chrome.webview.hostObjects.sync.CallJSInterface;
callJS = new CallJSInterface.CallJSCSharp();
}
Where CallJSInterface is one more time your namespace. Now, you can invoke JS like this (the async() is mandatory):
callJS.async().KeepScreenOn()
If you need more details, I have a full tutorial on my website:
https://supernovaic.blogspot.com/2022/10/from-webview-to-webview2-in-uwp.html

Specifying cs file to build with dotnet CLI

Suppose I have two files in my current working directory:
// file1.cs
Console.WriteLine("file1");
//file 2.cs
Console.WriteLine("file2");
In powershell, I do a dotnet new and delete the automatically generated Program.cs file. Then I do a dotnet build and get an error:
Only one compilation unit can have top level statements
I understand why this occurs, but I would like to be able to have full control of which .cs file is being targetted, while the other ones get ignored.
Is there any way to achieve this without having to create a whole new project for every file?
Doing this with .NET doesn't seem to be possible as of now. An issue on the dotnet/sdk GitHub has requested for this feature to be implemented.
However, you can use the C Sharp Compiler to compile a Windows executable and specify a .cs file with csc file1.cs
file1.cs:
using System;
Console.WriteLine("File 1");
https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/top-level-statements
These files both use top-level statements. It implies that they both contain the Main method where program execution starts. You can only have one entry point. Generally, C# code is going to be contained within classes. Define a class in one (or both) files and put your methods within.
// Program.cs
public class Program
{
static void Main()
{
Console.WriteLine("Program.cs");
}
}
// Util.cs
public class Util
{
public static void Display()
{
Console.WriteLine("Util.cs");
}
}

String from getter is empty when called in another Project C#

I am a total beginner in C# programming language. I am trying to use Getter and Setter in order to set the string in ProjectA and the retrieve it in Project B.
Project B uses Windows Forms, and I wasnt to set the value of TextBox
with the retrieved string.
Project A is a Console Project and it just reads out some stuff from
file and stores it in string, which I want to retrieve.
However, this is my call in Project B:
string cardOwner = Transmit.Program.CardOwner;
Debug.WriteLine("Card owner = " + cardOwner);
tb_cardholder.Text = cardOwner;
And this is my Getter / Setter in Project A:
private static string _cardOwner;
public static string CardOwner
{
get
{
return _cardOwner;
}
set
{
_cardOwner = value;
}
}
_cardOwner = System.Text.Encoding.ASCII.GetString(bCardOwner);
But in Project B I get "" empty string.
I have included Project A in Project B (added Reference and wrote "using ProjectA").
Any ideas what's going wrong?
Thanks.
Just because you include a project and use its classes in your project B, it doesn't mean that you also use the instances of these classes.
Take the following class:
public class Test
{
public string Message { get; set; }
}
You can put this class into a DLL project (Tools) and reference it from other projects, like a WinForms project ProjectA and a console project ProjectB.
In both projects, you can write something like:
Test t = new Test() { Message = "Hello" };
That creates a new instance of the Test class, but the two running applications ProjectA and ProjectB do not exchange the data! They are completely separated.
The same is true for class properties.
You can't share information between two different applications so easily. Static properties only share data within the same Application Domain, that is in most simple constellations within the same Windows process.
If you want to transfer data between two different processes, you need to use an explicit mechanism for interprocess communication.
When is this line executed?
_cardOwner = System.Text.Encoding.ASCII.GetString(bCardOwner);
You'll need to put that in a method and call that method (and knowing when the call happens will help you understand why _cardOwner is not set:
public static void Init()
{
_cardOwner = System.Text.Encoding.ASCII.GetString(bCardOwner);
}
Then call this method somewhere that you know will be executed before you need _cardOwner:
Transmit.Program.Init();
string cardOwner = Transmit.Program.CardOwner;
tb_cardholder.Text = cardOwner;

PowerShell: how does the PowerShell instantiate C# classes

Initially, I think PowerShell instantiate one class only when the cmdlet tagged on this class is called. On execution, each cmdlet falls into the BeginProcess -> ProcessRecord -> EndProcess(StopProcess) path, and after the EndProcess is done, it seems the process will end and then the memory will collect all these class objects as garbage.Therefore each class should live in their own life cycle and not share any resources. When we are calling these cmdlets,
However I find that classes do share the same static values in the same module. For example, assume in my project I have two classes:
namespace PSDSL
{
[Cmdlet(VerbsCommon.Get, "MyTest")]
public class GetMyTest : Cmdlet
{
public static GlobalUserName = "";
[Parameter(Mandatory = false)]
public string Filepath { get; set; }
protected override void InnerProcessRecord()
{
if (_filepath != null)
{
GlobalUserName = _filepath;
}
Console.WriteLine(GlobalUserName);
}
}
}
namespace PSDSL
{
[Cmdlet(VerbsCommon.Get, "MyTest2")]
public class GetMyTest2 : Cmdlet
{
[Parameter(Mandatory = false)]
public string Filepath { get; set; }
protected override void InnerProcessRecord()
{
if (_filepath != null)
{
GlobalUserName = _filepath;
}
Console.WriteLine(GlobalUserName);
}
}
}
The two commands are pretty similar except one defines a static GlobalUserName. Calling these 2 cmdlets shows that the GlobalUserName can be read\write from both cmdlets.
My confusion is that, when are the classes be instaniated?
Whole assembly loaded at once and stays loaded till restart of the PowerShell prompt.
Details:
Smallest unit of code isolation in .Net is Assembly (in most cases single managed DLL).
Process that uses managed runtime can't load less than single assembly at a time - so all classes from that assembly (and related once on demand) will be loaded together. As result all static fields will be present at the same time in memory (note that static fields are initialized "before first use of the class" which mean they are not necessary initialized on load of the assembly).
There also no way to "unload" class or even assembly without using separate AppDomains. PowerShell does not use multiple AppDomains to load assemblies for different modules (generally cross-AddDomain calls require special attention during implementation and you'd know about it by now). As result once loaded module stays in memory till you quit PowerShell (covered in Powershell Unload Module... completely).
Since assembly is loaded once for all commandlets in it all static fields will be present at once and keep they values till exiting of PowerShell.
Side note: I'd strongly recommend avoiding static fields for anything but really static immutable data in general. It is way to easy to leave some random values there and impact future code. In PowerShell pipeline is the way to pass information between commandlets, other types of processes (WinForms, ASP.Net,...) have they own preferred mechanism to pass data instead of using static.

How to redirect a method call?

I have got a DLL which works fine in Windows, but Inside one of its private functions the static System.IO.File.ReadAllBytes() is called. I have to use this DLL on a windows CE smart Device Project with Compact Framework 3.5 and there is no such method in the System.IO.File. I have tried to create a class named "File" inside the project like this:
public class File
{
internal static byte[] ReadAllBytes(string path)
{
}
internal static void WriteAllBytes(string path, byte[] bytes)
{
}
}
My own calls to the static Methods of the class File are redirected here But the calls inside the DLL methods still go to the System.Io.File class and I still get the MissingMethodException. I tried the methods with public modifiers but saw no change.
I even tried to rewrite the public method that calls the private method inside which the ReadAllbytes was invoked and used MethodInfo.Invoke with no success.
The question: Is there a way to force the method inside the Dll to accept my ReadAllbytes Method instead of System.File.IO.ReadAllBytes()? The invocation inside the DLL is like this:
using System.IO.File;
namespace Something
{
class SomeClass
{
public Boolean someMethod()
{
byte[] myBytes = File.ReadAllBytes(filePath);
}
}
}
Static methods like File.ReadAllBytes are resolved at compile time to a specific Assembly name and class. You will need to modify the DLL to change the call. Prehaps you could decompile and recompile it, or edit the IL.
*It is possible to redirect the call using the profiling hooks (the sameone the debugger uses), and that is how the Moles Mocking framework works. However it would not be suitable for production use.

Categories