Category: .NET Core

RSCG – Ling.Audit

RSCG – Ling.Audit
 
 

name Ling.Audit
nuget https://www.nuget.org/packages/Ling.Audit/
link https://github.com/ling921/dotnet-lib/
author Jing Ling

Generating audit data from class implementation of interfaces

 

This is how you can use Ling.Audit .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Ling.Audit" Version="1.1.0" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


// See https://aka.ms/new-console-template for more information
using LingDemo;

Console.WriteLine("Hello, World!");
var p = new Person();
await Task.Delay(2000);
p.FirstName = "Andrei";
p.LastName = "Ignat";
Console.WriteLine(p.CreationTime);
Console.WriteLine(p.LastModificationTime);


using Ling.Audit;

namespace LingDemo;
partial class Person :IFullAudited<Guid>
{
    public int ID { get; set; }
    public string FirstName { get; set; }= string.Empty;
    public string LastName { get; set; } = string.Empty;
}


 

The code that is generated is

// <auto-generated/>

#nullable enable annotations
#nullable disable warnings

namespace LingDemo
{
    partial class Person
    {
        /// <summary>
        /// Gets or sets the creation time of this entity.
        /// </summary>
        public virtual global::System.DateTimeOffset CreationTime { get; set; }
    
        /// <summary>
        /// Gets or sets the creator Id of this entity.
        /// </summary>
        public virtual global::System.Nullable<global::System.Guid> CreatorId { get; set; }
    
        /// <summary>
        /// Gets or sets the last modification time of this entity.
        /// </summary>
        public virtual global::System.Nullable<global::System.DateTimeOffset> LastModificationTime { get; set; }
    
        /// <summary>
        /// Gets or sets the last modifier Id of this entity.
        /// </summary>
        public virtual global::System.Nullable<global::System.Guid> LastModifierId { get; set; }
    
        /// <summary>
        /// Gets or sets whether this entity is soft deleted.
        /// </summary>
        public virtual global::System.Boolean IsDeleted { get; set; }
    
        /// <summary>
        /// Gets or sets the deletion time of this entity.
        /// </summary>
        public virtual global::System.Nullable<global::System.DateTimeOffset> DeletionTime { get; set; }
    
        /// <summary>
        /// Get or set the deleter Id of this entity.
        /// </summary>
        public virtual global::System.Nullable<global::System.Guid> DeleterId { get; set; }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Ling.Audit

Blazor and RowNumber in Grid

The data that comes from the backend does not, usually, provide a row number . So how to obtain this ? Create a class, will say any programmer …

public  record DataWithNumber<T>(int number, T data) 
    where T: class
{
}

And the usage

 var data = HttpClient_WebApi.GetFromJsonAsAsyncEnumerable<Order_Details_Extended>(url);
 ArgumentNullException.ThrowIfNull(data);
 int i = 0;

 await foreach (var item in data)
 {
     if (item == null) continue;
     i++;
     dataArr.Add(new DataWithNumber<Order_Details_Extended>(i, item));
     if ((i < 500 && i % 100 == 0) || (i > 500 && i % 1000 == 0))
     {
         Console.WriteLine($"i={i}");
         nrRecordsLoaded = i;

         await InvokeAsync(StateHasChanged);
         await Task.Delay(1000);
     }
 }
 nrRecordsLoaded = i;

And the grid:

<FluentDataGrid Items="@dataForQuery" Virtualize="true" GenerateHeader="GenerateHeaderOption.Sticky">
    <PropertyColumn Property="@(p => p.number)" Sortable="true" />
    <PropertyColumn Property="@(p => p.data.OrderID)" Sortable="true">
        <ColumnOptions>
            <div class="search-box">
                <FluentSearch type="search" Autofocus=true @bind-Value=nameOrderIdFilter @oninput="HandleOrderIdFilter" @bind-Value:after="HandleClearIdFilter" Placeholder="Order Id..." />
            </div>
        </ColumnOptions>
    </PropertyColumn>
    
    <PropertyColumn Property="@(p => p.data.UnitPrice)" Sortable="true" />
    <PropertyColumn Property="@(p => p.data.ExtendedPrice)" Sortable="true" />
    <PropertyColumn Property="@(p => p.data.ProductID)" Sortable="true" />
    <PropertyColumn Property="@(p => p.data.Quantity)" Sortable="true" />
    <PropertyColumn Property="@(p => p.data.Discount)" Sortable="true" />
</FluentDataGrid>

Aspire Blazor WebAssembly and WebAPI

 

Aspire is the new visualizer – see https://github.com/dotnet/aspire

I am very fond of WebAPI  –  it allows for all people to see the functionality of a site , in a programmatic way ( side note: , my nuget package, https://www.nuget.org/packages/NetCore2Blockly , allows to make workflows from your WebAPI)

And Blazor WebAssembly is a nice addition that the WebAPI . I am talking about Interactive WebAssembly (https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes?preserve-view=true&view=aspnetcore-8.0  )  . I do want ( for the moment ) to use Interactive Server because

  1. it is easy to forget to add functionality to the WebAPI
  2. it is not separating UI from BL

So I decided to add an Blazor WebAssembly and WebAPI into Aspire to see how they work together.

The first problem that  I have is how to transmit the WebAPI URL to the Blazor WebAssembly . Think that is not Interactive Server or Auto – in order to have the environment or configuration . Blazor Interactive WebAssembly  are just static files that are downloaded to the client. And they are executed in the browser.

But I have tried with adding to the Environment in usual way

builder.AddProject<projects.exampleblazorapp>(nameof(Projects.ExampleBlazorApp))
.WithEnvironment(ctx =&gt;
{
if (api.Resource.TryGetAllocatedEndPoints(out var end))
{
if (end.Any())
	ctx.EnvironmentVariables["HOSTAPI"] = end.First().UriString;
}

 

And no use!

After reading ASP.NET Core Blazor configuration | Microsoft Learn  and aspire/src/Microsoft.Extensions.ServiceDiscovery at main · dotnet/aspire (github.com) and API review for Service Discovery · Issue #789 · dotnet/aspire (github.com) I realized that the ONLY way is to put in wwwroot/appsettings.json

So I came with the following code that tries to write DIRECTLY to wwwroot/appsettings.json file


namespace Aspire.Hosting;
public static class BlazorWebAssemblyProjectExtensions
{
    public static IResourceBuilder<ProjectResource> AddWebAssemblyProject<TProject>(
        this IDistributedApplicationBuilder builder, string name,
        IResourceBuilder<ProjectResource> api) 
        where TProject : IServiceMetadata, new()
    {
        var projectbuilder = builder.AddProject<TProject>(name);
        var p=new TProject();
        string hostApi= p.ProjectPath;
        var dir = Path.GetDirectoryName(hostApi);
        ArgumentNullException.ThrowIfNull(dir);
        var wwwroot = Path.Combine(dir, "wwwroot");
        if (!Directory.Exists(wwwroot)) {
            Directory.CreateDirectory(wwwroot);
        }
        var file = Path.Combine(wwwroot, "appsettings.json");
        if (!File.Exists(file))
            File.WriteAllText(file, "{}");
        projectbuilder =projectbuilder.WithEnvironment(ctx =>
        {
            if (api.Resource.TryGetAllocatedEndPoints(out var end))
            {
                if (end.Any())
                {
                    
                    var fileContent = File.ReadAllText(file);

                    Dictionary<string, object>? dict;
                    if (!string.IsNullOrWhiteSpace(fileContent))
                        dict = new Dictionary<string, object>();
                    else
                        dict = JsonSerializer.Deserialize<Dictionary<string,object>>(fileContent!);

                    ArgumentNullException.ThrowIfNull(dict);
                    dict["HOSTAPI"] = end.First().UriString;                    
                    JsonSerializerOptions opt = new JsonSerializerOptions(JsonSerializerOptions.Default)
                            { WriteIndented=true};
                    File.WriteAllText(file,JsonSerializer.Serialize(dict,opt));
                    ctx.EnvironmentVariables["HOSTAPI"]=end.First().UriString;
                    
                }
                    
            }

        });
        return projectbuilder;

    }
}

And in Aspire

var api = builder.AddProject<Projects.ExampleWebAPI>(nameof(Projects.ExampleWebAPI));
builder.AddWebAssemblyProject<Projects.ExampleBlazorApp>(nameof(Projects.ExampleBlazorApp), api);

And in Blazor Interactive WebAssembly


var hostApi = builder.Configuration["HOSTAPI"];
if (string.IsNullOrEmpty(hostApi))
{
    hostApi = builder.HostEnvironment.BaseAddress;
    var dict = new Dictionary<string, string?> { { "HOSTAPI", hostApi } };
    builder.Configuration.AddInMemoryCollection(dict.ToArray());
}

builder.Services.AddKeyedScoped("db",(sp,_) => new HttpClient { BaseAddress = new Uri(hostApi) });

What about deploying the code to production ? Well, I think that is better to wrote yourself to wwwroot/appsettings.json and remove the data . But I will try to deploy and let you know….

Aspire , containers and dotnet watch

Aspire is the new visualizer – see https://github.com/dotnet/aspire

If you use dotnet run ( or Visual Studio)  with an Aspire host that instantiate some containers  , then , when you stop the project, the container is released.

But, if you use

dotnet watch run –nohotreload

then the containers are not  deleted. The nice solution is to investigate dotnet watch and Aspire . And maybe fill a bug.

The easy ? Delete the containers first !


void DeleteDockerContainers()
{
    var process = new Process
    {
        StartInfo = new ProcessStartInfo
        {
            FileName = "powershell.exe",
            Arguments = $$"""
-Command "docker rm -f $(docker ps -a -q)"
""",
            RedirectStandardOutput = true,
            UseShellExecute = false,
            CreateNoWindow = true,
        }
    };
    process.Start();
    while (!process.StandardOutput.EndOfStream)
    {
        var line = process.StandardOutput.ReadLine();
        Console.WriteLine(line);
    }
}

Aspire, Sql Server Docker Container and multiple Connections strings

Aspire is the new visualizer – see https://github.com/dotnet/aspire

When creating a Docker container with Sql Server , the connection that is returned is without database – means that , usually, is connecting to the master.

That’s not that we usually want, so the following code is means that WebAPI will be connected to master

var builder = DistributedApplication.CreateBuilder(args);
var rb= builder.AddSqlServerContainer("Db2Gui", "<YourStrong@Passw0rd>");
builder.AddProject<Projects.ExampleWebAPI>(nameof(Projects.ExampleWebAPI))
   .WithReference(rb)
    ;

Instead , do what they do – but add the database

var builder = DistributedApplication.CreateBuilder(args);

var rb= builder.AddSqlServerContainer("Db2Gui", "<YourStrong@Passw0rd>");

builder.AddProject<Projects.ExampleWebAPI>(nameof(Projects.ExampleWebAPI))
    .WithEnvironment(ctx=>
    {
        var connectionStringName = $"ConnectionStrings__";
        var res=rb.Resource;
        var cn = res.GetConnectionString();
        ctx.EnvironmentVariables[connectionStringName+ "ApplicationDBContext"] = cn+ $";database=tests;";
        ctx.EnvironmentVariables[connectionStringName+ "NorthwindDBContext"] = cn + $";database=northwind;";
        ctx.EnvironmentVariables[connectionStringName+ "PubsDBContext"] = cn + $";database=pubs;";
    })
    //.WithReference(rb, "")
    ;

It is true that you can make the following :

builder.AddProject<Projects.ExampleWebAPI>(nameof(Projects.ExampleWebAPI))
    .WithReference(rb.AddDatabase("tests"), "ApplicationDBContext")
    .WithReference(rb.AddDatabase("northwind"), "NorthwindDBContext")
    .WithReference(rb.AddDatabase("pubs"), "PubsDBContext")
    .WithReference(rb.AddDatabase("NotCreated"), "NotCreated")

    ;

But it does not CREATE the database ( and it is a good thing …. EF EnsureCreated verifies JUST the existence of the database not of the tables within)
So thats why I prefer WithEnvironment rather than .WithReference(rb.AddDatabase

Deploy Blazor WASM to Github Pages in 7 steps

Assumptions:

You have an Blazor Interactive WebAssembly ( CSR ) , not a Server ( static or interactive)

I will make as the repo is  https://github.com/ignatandrei/tilt  . Change my name with yours and TILT  with your repo

So let’s start

Step 1   You must configure GitHub Pages – create a docs folder and put an index.html  . Then  goto github Settings => Pages (https://github.com/ignatandrei/tilt/settings/pages )  and put there main / docs  . 

Step 2  Verify it is working. If your repo is https://github.com/ignatandrei/tilt , then browse to https://ignatandrei.github.io/TILT/ and ensure that you can see the index. If not, goto Step 1

Step 3  Add 2 files .nojekyll (content : null , empty …. just create it ) and .gitattributes ( content : *.js binary )

Step 4 dotnet publish your Blazor WASM csproj . Find the folder wwwroot where was published.

Step  5  Find index.html  in the folder . Edit base href , put the repo name  (<base href=”/TILT/” />  ). Also you can modify the css/js  by adding the date ( e.g.  <link rel=”stylesheet” href=”css/app.css?202312162300″ /> ) .

Step 6  Copy the index.html and the other files inside the docs folder . Also, copy the index.html as 404.html file

Step 7 Commit and push. Now you can enjoy your Blazor site hosted for free in github  : https://github.com/ignatandrei/tilt 

Note 1 : If some url’s do not work , then try to add the following

@inject IWebAssemblyHostEnvironment HostEnvironment
@{
     var baseAddress = HostEnvironment.BaseAddress;
     if (!baseAddress.EndsWith(“/”)) baseAddress += “/”;

}

to the url

Note 2:  For more deployments please read https://learn.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/webassembly?view=aspnetcore-8.0

RSCG – TelemetryLogging

RSCG – TelemetryLogging
 
 

name TelemetryLogging
nuget https://www.nuget.org/packages/Microsoft.Extensions.Telemetry.Abstractions/
link https://andrewlock.net/behind-logproperties-and-the-new-telemetry-logging-source-generator/
author Microsoft

Generating deep logging messages for a class

 

This is how you can use TelemetryLogging .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
	<PropertyGroup>
        <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
        <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
    </PropertyGroup>
	<ItemGroup>
		<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
		<PackageReference Include="Microsoft.Extensions.Telemetry.Abstractions" Version="8.0.0" />
	</ItemGroup>
</Project>


The code that you will use is


using System.Text.Json;
using Microsoft.Extensions.Logging;

using ILoggerFactory loggerFactory = LoggerFactory.Create(
    builder =>
    {
        //builder.AddSimpleConsole();
        builder.AddJsonConsole(
            options =>
            options.JsonWriterOptions = new JsonWriterOptions()
            {
                Indented = true
            });
    }
        
    ) ;

ILogger<Person> logger = loggerFactory.CreateLogger<Person>();
logger.LogInformation("test");
(new LoggingSample(logger)).TestLogging();
public record Person (string firstName, string LastName)
{
}



using Microsoft.Extensions.Logging;

public partial class LoggingSample
{
    private readonly ILogger _logger;

    public LoggingSample(ILogger logger)
    {
        _logger = logger;
    }

    [LoggerMessage(
        EventId = 20,
        Level = LogLevel.Critical,
        Message = "Value is {value:E}")]
    public static partial void UsingFormatSpecifier(
        ILogger logger, double value);

    [LoggerMessage(
        EventId = 19,
        Level = LogLevel.Information,
        Message = "Logging all person properties",
        EventName = "PersonLogging")]
    public partial void LogWithProperties([LogProperties] Person person);


    [LoggerMessage(
        EventId = 9,
        Level = LogLevel.Trace,
        Message = "Fixed message",
        EventName = "CustomEventName")]
    public partial void LogWithCustomEventName();

    [LoggerMessage(
        EventId = 10,
        Message = "Welcome to {city} {province}!")]
    public partial void LogWithDynamicLogLevel(
        string city, LogLevel level, string province);

    public void  TestLogging()
    {
        LogWithProperties(new Person("Andrei", "Ignat"));
        //LogWithCustomEventName();

        //LogWithDynamicLogLevel("Vancouver", LogLevel.Warning, "BC");
        //LogWithDynamicLogLevel("Vancouver", LogLevel.Information, "BC");

        //UsingFormatSpecifier(_logger, 12345.6789);
    }
}

 

The code that is generated is


// <auto-generated/>
#nullable enable
#pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103
partial class LoggingSample
{
    /// <summary>
    /// Logs "Value is {value:E}" at "Critical" level.
    /// </summary>
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Gen.Logging", "8.0.0.0")]
    public static partial void UsingFormatSpecifier(global::Microsoft.Extensions.Logging.ILogger logger, double value)
    {
        var state = global::Microsoft.Extensions.Logging.LoggerMessageHelper.ThreadLocalState;

        _ = state.ReserveTagSpace(2);
        state.TagArray[1] = new("value", value);
        state.TagArray[0] = new("{OriginalFormat}", "Value is {value:E}");

        logger.Log(
            global::Microsoft.Extensions.Logging.LogLevel.Critical,
            new(20, nameof(UsingFormatSpecifier)),
            state,
            null,
            static (s, _) =>
            {
                var value = s.TagArray[1].Value;
                return global::System.FormattableString.Invariant($"Value is {value:E}");
            });

        state.Clear();
    }

    /// <summary>
    /// Logs "Logging all person properties" at "Information" level.
    /// </summary>
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Gen.Logging", "8.0.0.0")]
    public partial void LogWithProperties(global::Person person)
    {
        if (!_logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Information))
        {
            return;
        }

        var state = global::Microsoft.Extensions.Logging.LoggerMessageHelper.ThreadLocalState;

        _ = state.ReserveTagSpace(3);
        state.TagArray[2] = new("person.firstName", person?.firstName);
        state.TagArray[1] = new("person.LastName", person?.LastName);
        state.TagArray[0] = new("{OriginalFormat}", "Logging all person properties");

        _logger.Log(
            global::Microsoft.Extensions.Logging.LogLevel.Information,
            new(19, "PersonLogging"),
            state,
            null,
            static (s, _) =>
            {
                return "Logging all person properties";
            });

        state.Clear();
    }

    /// <summary>
    /// Logs "Fixed message" at "Trace" level.
    /// </summary>
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Gen.Logging", "8.0.0.0")]
    public partial void LogWithCustomEventName()
    {
        if (!_logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Trace))
        {
            return;
        }

        var state = global::Microsoft.Extensions.Logging.LoggerMessageHelper.ThreadLocalState;

        _ = state.ReserveTagSpace(1);
        state.TagArray[0] = new("{OriginalFormat}", "Fixed message");

        _logger.Log(
            global::Microsoft.Extensions.Logging.LogLevel.Trace,
            new(9, "CustomEventName"),
            state,
            null,
            static (s, _) =>
            {
                return "Fixed message";
            });

        state.Clear();
    }

    /// <summary>
    /// Logs "Welcome to {city} {province}!".
    /// </summary>
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Gen.Logging", "8.0.0.0")]
    public partial void LogWithDynamicLogLevel(string city, global::Microsoft.Extensions.Logging.LogLevel level, string province)
    {
        if (!_logger.IsEnabled(level))
        {
            return;
        }

        var state = global::Microsoft.Extensions.Logging.LoggerMessageHelper.ThreadLocalState;

        _ = state.ReserveTagSpace(3);
        state.TagArray[2] = new("city", city);
        state.TagArray[1] = new("province", province);
        state.TagArray[0] = new("{OriginalFormat}", "Welcome to {city} {province}!");

        _logger.Log(
            level,
            new(10, nameof(LogWithDynamicLogLevel)),
            state,
            null,
            static (s, _) =>
            {
                var city = s.TagArray[2].Value ?? "(null)";
                var province = s.TagArray[1].Value ?? "(null)";
                return global::System.FormattableString.Invariant($"Welcome to {city} {province}!");
            });

        state.Clear();
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/TelemetryLogging

RSCG – InterceptorTemplate

RSCG – InterceptorTemplate
 
 

name InterceptorTemplate
nuget https://www.nuget.org/packages/RSCG_InterceptorTemplate/
link https://github.com/ignatandrei/RSCG_InterceptorTemplate
author Andrei Ignat

Andrei Ignat

 

This is how you can use InterceptorTemplate .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
	  <IsPackable>false</IsPackable>

  </PropertyGroup>
  
  <ItemGroup>
    <AdditionalFiles Include="Interceptors\TestFullNameWithArguments.txt">
      <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    </AdditionalFiles>
    <AdditionalFiles Include="Interceptors\GenericInterceptorForAllMethods.txt">
      <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    </AdditionalFiles>
    <AdditionalFiles Include="Interceptors\FullName.txt">
      <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    </AdditionalFiles>
  </ItemGroup>
  

  <ItemGroup>
	  <PackageReference Include="RSCG_InterceptorTemplate" Version="8.2023.2811.446" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
  </ItemGroup>
  

  <ItemGroup>
    <ProjectReference Include="..\RSCG_DemoObjects\RSCG_DemoObjects.csproj" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
		<InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);RSCG_InterceptorTemplate</InterceptorsPreviewNamespaces>
	</PropertyGroup>
	<PropertyGroup>
		<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
	</PropertyGroup>

</Project>


The code that you will use is


Console.WriteLine("Run the compile.ps1 file!");
var p=new Person();
p.FirstName="Andrei";
p.LastName="Ignat";
Console.WriteLine("debug for "+p.FullName());
Console.WriteLine("this is "+p.FullName());
var x = p.Test();
Console.WriteLine(x);
var newPerson = new Person();
newPerson.FirstName = "Andrei";
newPerson.LastName = "Ignat";
var namePerson = newPerson.FullName();
Console.WriteLine(namePerson);

Console.WriteLine("loaded "+Person.PersonsLoaded());
Console.WriteLine("loaded " + RSCG_DemoObjects.Person.PersonsLoaded());
Console.WriteLine("and again  " + RSCG_DemoObjects.Person.PersonsLoaded());

Console.WriteLine("and now with argument " + newPerson.TestFullNameWithArguments("<","!+",">",2));
Console.WriteLine("and a random person " + Person.ShowRandomPersonNumber(1));
var q= await PersonLoader.SavePerson(newPerson);
PersonLoader.Connect();
//Console.ReadLine();
IPersonLoader ipl=new PersonLoader();
await ipl.InsertPerson(newPerson);


//example generating for full name {{Version}}
#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{

{{ for loc in ser.dataForEachIntercepts }}
//replace code:{{loc.code}}";
//replace code:{{loc.CodeNumbered}}";
[System.Runtime.CompilerServices.InterceptsLocation(@"{{loc.Path}}", {{loc.Line}}, {{loc.StartMethod}})]
{{ end }}

//[System.Diagnostics.DebuggerStepThrough()]
public static {{(ser.item.HasTaskReturnType?"async":"")}} {{ser.item.TypeReturn}} {{ser.item.MethodSignature}}({{ser.item.ThisArgument}} {{ser.item.ArgumentsForCallMethod}} )  
{
    var cc=Console.BackgroundColor ;
    try{
    Console.BackgroundColor = ConsoleColor.DarkGreen;
    Console.WriteLine("start specific FullName template-->{{ser.item.MethodSignature}}");
    {{ser.item.ReturnString}} {{(ser.item.HasTaskReturnType ? "await" : "")}} {{ser.item.CallMethod}};
    }
    finally{
        Console.WriteLine("end specific template-->{{ser.item.MethodSignature}}");
        Console.BackgroundColor = cc;
    }
}
                

}//end class

}//namespace RSCG_InterceptorTemplate


//example generating generic for all methods in a class {{Version}}
#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{

{{ for loc in ser.dataForEachIntercepts }}
//replace code:{{loc.code}}";
//replace code:{{loc.CodeNumbered}}";
[System.Runtime.CompilerServices.InterceptsLocation(@"{{loc.Path}}", {{loc.Line}}, {{loc.StartMethod}})]
{{ end }}

//[System.Diagnostics.DebuggerStepThrough()]
public static {{(ser.item.HasTaskReturnType?"async":"")}} {{ser.item.TypeReturn}} {{ser.item.MethodSignature}}({{ser.item.ThisArgument}} {{ser.item.ArgumentsForCallMethod}} )  
{
    try{
        Console.WriteLine("start from generic template-->{{ser.item.MethodSignature}}");
        {{ser.item.ReturnString}} {{(ser.item.HasTaskReturnType ? "await" : "")}} {{ser.item.CallMethod}};
    }
    finally{
        Console.WriteLine("end from generic template-->{{ser.item.MethodSignature}}");
    }
}
                

}//end class

}//namespace RSCG_InterceptorTemplate


//example generating for full name {{Version}}
#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{

{{ for loc in ser.dataForEachIntercepts }}
//replace code:{{loc.code}}";
//replace code:{{loc.CodeNumbered}}";
[System.Runtime.CompilerServices.InterceptsLocation(@"{{loc.Path}}", {{loc.Line}}, {{loc.StartMethod}})]
{{ end }}

//[System.Diagnostics.DebuggerStepThrough()]
public static {{(ser.item.HasTaskReturnType?"async":"")}} {{ser.item.TypeReturn}} {{ser.item.MethodSignature}}({{ser.item.ThisArgument}} {{ser.item.ArgumentsForCallMethod}} )  
{
    var cc=Console.BackgroundColor ;
    try{
    Console.BackgroundColor = ConsoleColor.DarkGreen;
    Console.WriteLine("start specific FullName template-->{{ser.item.MethodSignature}}");
    {{ser.item.ReturnString}} {{(ser.item.HasTaskReturnType ? "await" : "")}} {{ser.item.CallMethod}};
    }
    finally{
        Console.WriteLine("end specific template-->{{ser.item.MethodSignature}}");
        Console.BackgroundColor = cc;
    }
}
                

}//end class

}//namespace RSCG_InterceptorTemplate

 

The code that is generated is

//example generating generic for all methods in a class 8.2023.2811.446
#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{


//replace code:await ipl.InsertPerson(newPerson);";
//replace code:123456789!123456789!123456789!1234";
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\InterceptorTemplate\src\RSCG_InterceptorTemplateConsole\Program.cs", 25, 11)]


//[System.Diagnostics.DebuggerStepThrough()]
public static async System.Threading.Tasks.Task<RSCG_DemoObjects.Person> Intercept_ipl_InsertPerson(this RSCG_DemoObjects.IPersonLoader ipl ,RSCG_DemoObjects.Person p )  
{
    try{
        Console.WriteLine("start from generic template-->Intercept_ipl_InsertPerson");
        return await ipl.InsertPerson(p);
    }
    finally{
        Console.WriteLine("end from generic template-->Intercept_ipl_InsertPerson");
    }
}
                

}//end class

}//namespace RSCG_InterceptorTemplate
//example generating generic for all methods in a class 8.2023.2811.446
#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{


//replace code:PersonLoader.Connect();";
//replace code:123456789!123456789!123";
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\InterceptorTemplate\src\RSCG_InterceptorTemplateConsole\Program.cs", 22, 14)]


//[System.Diagnostics.DebuggerStepThrough()]
public static  void Intercept_PersonLoader_Connect(  )  
{
    try{
        Console.WriteLine("start from generic template-->Intercept_PersonLoader_Connect");
          RSCG_DemoObjects.PersonLoader.Connect();
    }
    finally{
        Console.WriteLine("end from generic template-->Intercept_PersonLoader_Connect");
    }
}
                

}//end class

}//namespace RSCG_InterceptorTemplate
//example generating generic for all methods in a class 8.2023.2811.446
#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{


//replace code:var q= await PersonLoader.SavePerson(newPerson);";
//replace code:123456789!123456789!123456789!123456789!12345678";
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\InterceptorTemplate\src\RSCG_InterceptorTemplateConsole\Program.cs", 21, 27)]


//[System.Diagnostics.DebuggerStepThrough()]
public static async System.Threading.Tasks.Task<RSCG_DemoObjects.Person> Intercept_PersonLoader_SavePerson( RSCG_DemoObjects.Person p )  
{
    try{
        Console.WriteLine("start from generic template-->Intercept_PersonLoader_SavePerson");
        return await RSCG_DemoObjects.PersonLoader.SavePerson(p);
    }
    finally{
        Console.WriteLine("end from generic template-->Intercept_PersonLoader_SavePerson");
    }
}
                

}//end class

}//namespace RSCG_InterceptorTemplate
//example generating for full name 8.2023.2811.446
#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{


//replace code:Console.WriteLine("debug for "+p.FullName());";
//replace code:123456789!123456789!123456789!123456789!12345";
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\InterceptorTemplate\src\RSCG_InterceptorTemplateConsole\Program.cs", 5, 34)]

//replace code:Console.WriteLine("this is "+p.FullName());";
//replace code:123456789!123456789!123456789!123456789!123";
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\InterceptorTemplate\src\RSCG_InterceptorTemplateConsole\Program.cs", 6, 32)]


//[System.Diagnostics.DebuggerStepThrough()]
public static  string Intercept_p_FullName(this RSCG_DemoObjects.Person p  )  
{
    var cc=Console.BackgroundColor ;
    try{
    Console.BackgroundColor = ConsoleColor.DarkGreen;
    Console.WriteLine("start specific FullName template-->Intercept_p_FullName");
    return  p.FullName();
    }
    finally{
        Console.WriteLine("end specific template-->Intercept_p_FullName");
        Console.BackgroundColor = cc;
    }
}
                

}//end class

}//namespace RSCG_InterceptorTemplate
//example generating for full name 8.2023.2811.446
#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{


//replace code:var namePerson = newPerson.FullName();";
//replace code:123456789!123456789!123456789!12345678";
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\InterceptorTemplate\src\RSCG_InterceptorTemplateConsole\Program.cs", 12, 28)]


//[System.Diagnostics.DebuggerStepThrough()]
public static  string Intercept_newPerson_FullName(this RSCG_DemoObjects.Person newPerson  )  
{
    var cc=Console.BackgroundColor ;
    try{
    Console.BackgroundColor = ConsoleColor.DarkGreen;
    Console.WriteLine("start specific FullName template-->Intercept_newPerson_FullName");
    return  newPerson.FullName();
    }
    finally{
        Console.WriteLine("end specific template-->Intercept_newPerson_FullName");
        Console.BackgroundColor = cc;
    }
}
                

}//end class

}//namespace RSCG_InterceptorTemplate
//example generating generic for all methods in a class 8.2023.2811.446
#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{


//replace code:Console.WriteLine("loaded "+Person.PersonsLoaded());";
//replace code:123456789!123456789!123456789!123456789!123456789!12";
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\InterceptorTemplate\src\RSCG_InterceptorTemplateConsole\Program.cs", 15, 36)]


//[System.Diagnostics.DebuggerStepThrough()]
public static  int Intercept_Person_PersonsLoaded(  )  
{
    try{
        Console.WriteLine("start from generic template-->Intercept_Person_PersonsLoaded");
        return  RSCG_DemoObjects.Person.PersonsLoaded();
    }
    finally{
        Console.WriteLine("end from generic template-->Intercept_Person_PersonsLoaded");
    }
}
                

}//end class

}//namespace RSCG_InterceptorTemplate
//example generating generic for all methods in a class 8.2023.2811.446
#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{


//replace code:Console.WriteLine("loaded " + RSCG_DemoObjects.Person.PersonsLoaded());";
//replace code:123456789!123456789!123456789!123456789!123456789!123456789!123456789!1";
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\InterceptorTemplate\src\RSCG_InterceptorTemplateConsole\Program.cs", 16, 55)]

//replace code:Console.WriteLine("and again  " + RSCG_DemoObjects.Person.PersonsLoaded());";
//replace code:123456789!123456789!123456789!123456789!123456789!123456789!123456789!12345";
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\InterceptorTemplate\src\RSCG_InterceptorTemplateConsole\Program.cs", 17, 59)]


//[System.Diagnostics.DebuggerStepThrough()]
public static  int Intercept_RSCG_DemoObjects_Person_PersonsLoaded(  )  
{
    try{
        Console.WriteLine("start from generic template-->Intercept_RSCG_DemoObjects_Person_PersonsLoaded");
        return  RSCG_DemoObjects.Person.PersonsLoaded();
    }
    finally{
        Console.WriteLine("end from generic template-->Intercept_RSCG_DemoObjects_Person_PersonsLoaded");
    }
}
                

}//end class

}//namespace RSCG_InterceptorTemplate
//example generating generic for all methods in a class 8.2023.2811.446
#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{


//replace code:Console.WriteLine("and a random person " + Person.ShowRandomPersonNumber(1));";
//replace code:123456789!123456789!123456789!123456789!123456789!123456789!123456789!1234567";
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\InterceptorTemplate\src\RSCG_InterceptorTemplateConsole\Program.cs", 20, 51)]


//[System.Diagnostics.DebuggerStepThrough()]
public static  int Intercept_Person_ShowRandomPersonNumber( int min )  
{
    try{
        Console.WriteLine("start from generic template-->Intercept_Person_ShowRandomPersonNumber");
        return  RSCG_DemoObjects.Person.ShowRandomPersonNumber(min);
    }
    finally{
        Console.WriteLine("end from generic template-->Intercept_Person_ShowRandomPersonNumber");
    }
}
                

}//end class

}//namespace RSCG_InterceptorTemplate
//example generating for TestFullNameWithArguments 8.2023.2811.446
#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{


//replace code:Console.WriteLine("and now with argument " + newPerson.TestFullNameWithArguments("<","!+",">",2));";
//replace code:123456789!123456789!123456789!123456789!123456789!123456789!123456789!123456789!123456789!12345678";
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\InterceptorTemplate\src\RSCG_InterceptorTemplateConsole\Program.cs", 19, 56)]


//[System.Diagnostics.DebuggerStepThrough()]
public static  string Intercept_newPerson_TestFullNameWithArguments(this RSCG_DemoObjects.Person newPerson ,string start,string separator,string end,int repeat )  
{
    var cc=Console.BackgroundColor ;
    try{
        Console.BackgroundColor = ConsoleColor.DarkRed;
        Console.WriteLine("start specific TestFullNameWithArguments template-->Intercept_newPerson_TestFullNameWithArguments");
        Console.WriteLine("number of arguments = 4");
        
                Console.WriteLine("argument 1 type string and value = "+ start);
                
                Console.WriteLine("argument 2 type string and value = "+ separator);
                
                Console.WriteLine("argument 3 type string and value = "+ end);
                
                Console.WriteLine("argument 4 type int and value = "+ repeat);
                
        return  newPerson.TestFullNameWithArguments(start,separator,end,repeat);
    }
    finally{
        Console.WriteLine("end specific template-->Intercept_newPerson_TestFullNameWithArguments");
        Console.BackgroundColor = cc;
    }
}
                

}//end class

}//namespace RSCG_InterceptorTemplate
//example generating generic for all methods in a class 8.2023.2811.446
#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{


//replace code:var x = p.Test();";
//replace code:123456789!1234567";
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\InterceptorTemplate\src\RSCG_InterceptorTemplateConsole\Program.cs", 7, 11)]


//[System.Diagnostics.DebuggerStepThrough()]
public static  string Intercept_p_Test(this RSCG_DemoObjects.Person p  )  
{
    try{
        Console.WriteLine("start from generic template-->Intercept_p_Test");
        return  p.Test();
    }
    finally{
        Console.WriteLine("end from generic template-->Intercept_p_Test");
    }
}
                

}//end class

}//namespace RSCG_InterceptorTemplate

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/InterceptorTemplate

RSCG – Com

RSCG – Com    

name Com
nuget https://www.nuget.org/packages/System.Runtime.InteropServices/
link https://learn.microsoft.com/en-us/dotnet/standard/native-interop/comwrappers-source-generation
author Microsoft

Generating Com Declarations

 

This is how you can use Com .

The code that you start with is

  <Project Sdk="Microsoft.NET.Sdk">    <PropertyGroup>     <OutputType>Exe</OutputType>     <TargetFramework>net8.0</TargetFramework>     <ImplicitUsings>enable</ImplicitUsings>     <Nullable>enable</Nullable> 	  <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <!--
<PackageReference Include="System.Runtime.InteropServices" />
-->   </PropertyGroup> 	<PropertyGroup> 		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> 		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath> 	</PropertyGroup> </Project>   

The code that you will use is

  using test;  IShellExecute shellExecute = new ShellExecuteClass();  // Open a file using the default associated program IntPtr result = shellExecute.ShellExecute(     IntPtr.Zero, // HWND (handle to parent window)     "open",      // Operation to perform     "notepad.exe", // File to open (replace with your file or URL)     "",           // Parameters     "",           // Working directory     1            // Show command (SW_SHOWNORMAL) );  Console.WriteLine($"ShellExecute Result: {result}");   
  using System; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling;  namespace test;  // Import the ShellExecute function from Shell32.dll using ComImport [ComImport] [Guid("00000000-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IUnknown {     void QueryInterface(ref Guid riid, out IntPtr ppvObject);     void AddRef();     void Release(); }  //[ComImport] [GeneratedComInterface(StringMarshalling = StringMarshalling.Utf8)] [Guid("000214F9-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public partial interface IShellExecute {     IntPtr ShellExecute(IntPtr hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd); }   // Replace this with your actual ShellExecute COM class public class ShellExecuteClass : IShellExecute {     public IntPtr ShellExecute(IntPtr hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd)     {         // Implement the ShellExecute functionality         return NativeMethods.ShellExecute(hwnd, lpOperation, lpFile, lpParameters, lpDirectory, nShowCmd);     } }  // NativeMethods class to import necessary functions from Shell32.dll static class NativeMethods {     [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]     public static extern IntPtr ShellExecute(         IntPtr hwnd,         string lpOperation,         string lpFile,         string lpParameters,         string lpDirectory,         int nShowCmd     ); }   

  The code that is generated is

 // <auto-generated /> #pragma warning disable CS0612, CS0618 file unsafe class InterfaceInformation : global::System.Runtime.InteropServices.Marshalling.IIUnknownInterfaceType {     public static global::System.Guid Iid { get; } = new(new global::System.ReadOnlySpan<byte>(new byte[] { 249, 20, 2, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 70 }));      private static void** _vtable;     public static void** ManagedVirtualMethodTable => _vtable != null ? _vtable : (_vtable = InterfaceImplementation.CreateManagedVirtualFunctionTable()); }  [global::System.Runtime.InteropServices.DynamicInterfaceCastableImplementationAttribute] file unsafe partial interface InterfaceImplementation : global::test.IShellExecute {     [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.ComInterfaceGenerator", "8.0.9.3103")]     [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute]     nint global::test.IShellExecute.ShellExecute(nint hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd)     {         var(__this, __vtable_native) = ((global::System.Runtime.InteropServices.Marshalling.IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(typeof(global::test.IShellExecute));         byte* __lpOperation_native = default;         byte* __lpFile_native = default;         byte* __lpParameters_native = default;         byte* __lpDirectory_native = default;         nint __retVal = default;         int __invokeRetVal = default;         // Setup - Perform required setup.         scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __lpDirectory_native__marshaller = new();         scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __lpParameters_native__marshaller = new();         scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __lpFile_native__marshaller = new();         scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __lpOperation_native__marshaller = new();         try         {             // Marshal - Convert managed data to native data.             __lpDirectory_native__marshaller.FromManaged(lpDirectory, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]);             __lpParameters_native__marshaller.FromManaged(lpParameters, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]);             __lpFile_native__marshaller.FromManaged(lpFile, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]);             __lpOperation_native__marshaller.FromManaged(lpOperation, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]);             {                 // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned.                 __lpDirectory_native = __lpDirectory_native__marshaller.ToUnmanaged();                 __lpParameters_native = __lpParameters_native__marshaller.ToUnmanaged();                 __lpFile_native = __lpFile_native__marshaller.ToUnmanaged();                 __lpOperation_native = __lpOperation_native__marshaller.ToUnmanaged();                 __invokeRetVal = ((delegate* unmanaged[MemberFunction]<void*, nint, byte*, byte*, byte*, byte*, int, nint*, int> )__vtable_native[3])(__this, hwnd, __lpOperation_native, __lpFile_native, __lpParameters_native, __lpDirectory_native, nShowCmd, &__retVal);             }              // NotifyForSuccessfulInvoke - Keep alive any managed objects that need to stay alive across the call.             global::System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(__invokeRetVal);             global::System.GC.KeepAlive(this);         }         finally         {             // CleanupCallerAllocated - Perform cleanup of caller allocated resources.             __lpDirectory_native__marshaller.Free();             __lpParameters_native__marshaller.Free();             __lpFile_native__marshaller.Free();             __lpOperation_native__marshaller.Free();         }          return __retVal;     } }  file unsafe partial interface InterfaceImplementation {     [global::System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute(CallConvs = new[] { typeof(global::System.Runtime.CompilerServices.CallConvMemberFunction) })]     internal static int ABI_ShellExecute(global::System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch* __this_native, nint hwnd, byte* __lpOperation_native, byte* __lpFile_native, byte* __lpParameters_native, byte* __lpDirectory_native, int nShowCmd, nint* __invokeRetValUnmanaged__param)     {         global::test.IShellExecute @this = default;         string lpOperation = default;         string lpFile = default;         string lpParameters = default;         string lpDirectory = default;         ref nint __invokeRetValUnmanaged = ref *__invokeRetValUnmanaged__param;         nint __invokeRetVal = default;         int __retVal = default;         try         {             // Unmarshal - Convert native data to managed data.             lpDirectory = global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ConvertToManaged(__lpDirectory_native);             lpParameters = global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ConvertToManaged(__lpParameters_native);             lpFile = global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ConvertToManaged(__lpFile_native);             lpOperation = global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ConvertToManaged(__lpOperation_native);             @this = global::System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch.GetInstance<global::test.IShellExecute>(__this_native);             __invokeRetVal = @this.ShellExecute(hwnd, lpOperation, lpFile, lpParameters, lpDirectory, nShowCmd);             // NotifyForSuccessfulInvoke - Keep alive any managed objects that need to stay alive across the call.             __retVal = 0; // S_OK             // Marshal - Convert managed data to native data.             __invokeRetValUnmanaged = __invokeRetVal;         }         catch (global::System.Exception __exception)         {             __retVal = global::System.Runtime.InteropServices.Marshalling.ExceptionAsHResultMarshaller<int>.ConvertToUnmanaged(__exception);         }          return __retVal;     } }  file unsafe partial interface InterfaceImplementation {     internal static void** CreateManagedVirtualFunctionTable()     {         void** vtable = (void**)global::System.Runtime.CompilerServices.RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(global::test.IShellExecute), sizeof(void*) * 4);         {             nint v0, v1, v2;             global::System.Runtime.InteropServices.ComWrappers.GetIUnknownImpl(out v0, out v1, out v2);             vtable[0] = (void*)v0;             vtable[1] = (void*)v1;             vtable[2] = (void*)v2;         }          {             vtable[3] = (void*)(delegate* unmanaged[MemberFunction]<global::System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch*, nint, byte*, byte*, byte*, byte*, int, nint*, int> )&ABI_ShellExecute;         }          return vtable;     } }  namespace test {     [global::System.Runtime.InteropServices.Marshalling.IUnknownDerivedAttribute<InterfaceInformation, InterfaceImplementation>]     public partial interface IShellExecute     {     } }  namespace test {     public partial interface IShellExecute     {     } } 

Code and pdf at https://ignatandrei.github.io/RSCG_Examples/v2/docs/Com

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.