We have a software product that is currently released that is a .NET Framework 4.7.2 application (the "legacy" app). The legacy client-server implementation is built on System.Runtime.Remoting, which is not supported in .NET 5 and later, so the .NET 5 implementation is gRPC.
It is necessary to instantiate each of the two COM servers in turn because the legacy and the .NET 5 COM servers can only connect to the comm (not COM) server application that implements the same communications framework, which are System.Runtime.Remoting and gRPC, respectively.
The COM servers are used by third party applications to interface with the comm server application, so I am currently working on creating a static class that returns the interface from the COM server that can connect to the currently running instance of the comm server.
I have a .NET 5 WPF implementation of the product almost complete, but I've hit a roadblock in that, I am unable to register the .NET COM server.
I found these two articles:
Exposing .NET Core Components to COM
GitHub Issue
I have now been able to:
Create a Type Library
I found a comment from #SimonMourier suggesting copying the .NET 5 COM server code into a .NET Framework project and use RegAsm to export the type library to be used in the .NET 5 project. The type library was added to the .NET 5 COM server project folder and "" was added to an ItemGroup in the .csproj file per the first referenced article.
Register the .NET 5 COM server
This required using the "dotnet publish -r win-x64 -c Debug" command in the project folder from the Visual Studio Developer Command Line. I was then able to use regsvr32 to register the WinCalRemoting.comhost.dll in the "bin\Debug\net5.0\win-x64\publish" project directory.
Create an Instance of the COM Class
After registering the COM server, I am now able to create an instance of the COM class, but haven't been successful at getting the interface from it:
public static IWinCalClient LoadCompatibleRemotingClient(bool useClientEventWindow, string serverName, int serverPort, bool connectToServer = true)
{
UseClientEventWindow = useClientEventWindow;
WinCalServerName = serverName;
WinCalServerPort = serverPort;
ClassIdList = new Guid[]
{
LegacyWinCalClientClsId, // CAN'T GET INTERFACE FROM THIS COM SERVER
//WinCal5ClientClsId // THE .NET 5 COM SERVER WORKS
};
if (RemotingClassObject != null)
{
UnloadClient();
}
foreach (Guid clsId in ClassIdList)
{
try
{
RemotingClassObject = Activator.CreateInstance(Type.GetTypeFromCLSID(clsId, true));
}
catch (Exception e)
{
continue;
}
if (RemotingClassObject != null)
{
RemotingInterface = (IWinCalClient)RemotingClassObject;
if (RemotingInterface == null)
{
UnloadClient();
continue;
}
if (CanClientConnect(_RemotingInterface, connectToServer))
{
break;
}
}
if (Marshal.IsComObject(RemotingClassObject))
{
Marshal.FinalReleaseComObject(RemotingClassObject);
}
RemotingClassObject = null;
}
return RemotingInterface;
}
Update on the exception
After correcting the "bitness" of the test COM Client application that #SimonMourier clued me to, I am able to get the interface from the .NET 5 COM server. I have updated the code from the method.
HOWEVER, I'm now struggling with getting the interface from the .NET Framework COM server in the same way I get it from the .NET 5 COM server. I successfully register it using RegAsm.exe, but I get the following exception:
System.InvalidCastException: 'Unable to cast object of type 'CMI.WinCalRemoting.cWinCalClient' to type 'CMI.WinCalRemoting.IWinCalClient'.'.
I've done an exhaustive search to try to find out how to fix the .NET Framework COM project so that it can be used in the same way that the .NET 5 COM server is used so that it doesn't matter whether the COM client is a .NET Framework or a .NET Core assembly.
I added a .NET Framework COM server project to the shared directory below to replicate what I'm seeing. With the .NET Framework COM server.
I also switched the test application to be 32-bit to replicate how our sister application will be using the COM servers.
All of the projects are located here:
.NET 5 COM Interop
Unable to Add .NET Framework COM Type Library Reference
For a .NET Framework client assembly, I've attempted to add a reference to the .NET Framework COM server that was registered with regasm.exe, but that fails with the following message:
I am loading different assemblies using Mono.Cecil and I would like to know if the assembly is targeting .NET Framework or .NET Core.
What I have tried is retrieving the custom attribute TargetFrameworkAttribute :
[assembly:TargetFramework(".NETFramework,Version=v4.0", FrameworkDisplayName = ".NET Framework)]
using the code below
var targetFrameWorkAttributes = assemblyDefinition.CustomAttributes
.Where(attribute => attribute.AttributeType.Name == nameof(TargetFrameworkAttribute));
var customAttribute = targetFrameWorkAttributes.FirstOrDefault();
var customAttributeValue = customAttribute?.ConstructorArguments.FirstOrDefault().Value.ToString();
The problem here is that not all assemblies have this attribute in their metadata.
My question is :
Is there any other way to detect if an assembly is targeting .NET Core or .NET Framework runtime other than retrieving custom attribute?
N.B. I have thought for .NET Core I could somehow parse *.deps.json and retrieve runtimeTarget object, but it's only for .NET Core and it would be complicated.
"runtimeTarget": {
"name": ".NETCoreApp,Version=v3.1",
"signature": ""
}
I've currently a problem with the NReco.ImageGenerator in a .NET Core 2.x console application that I can't get an image because of course, the console application is not web service at all.
My question now is: Is there a way to use a somehow web service in a console application so I can generate an image from an HTML code/file or is there any other solution to generate an image from HTML code/file in a .NET Core 2.x console application?
Edit
It works on .NET Standard but not on .NET Core.
var html = string.Format("<h1>Hello World!</h1>");
var converter = new HtmlToImageConverter // Exception happens here
{
Width = 250,
Height = 70
};
var bytes = converter.GenerateImage(html, NReco.ImageGenerator.ImageFormat.Png);
Exception:
System.TypeLoadException: Could not load type 'System.Web.HttpContext' from assembly 'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
at NReco.ImageGenerator.HtmlToImageConverter..ctor()
at SESL.Modules.SESL.User.UserImage() in E:\Source\SESL-Discord\SESL\Modules\SESL\User.cs:line 36
I found out, that the NReco.ImageGenerator does not support .NET Core and for the .NET Core I need to pay the LT version.
But I discovered another NuGet Package which does somehow the same but I need to draw the image by myself. Therefore the alternative is Magick.NET
As stated in official document, I am trying to implement UseOwin in the Startup.cs.I am trying to use/port IAppBuilder (Microsoft.Owin.Builder.AppBuilder) inside IApplicationBuilder (Microsoft.AspNetCore.Builder.IApplicationBuilder). I had legacy code written using IAppBuilder running fine on .Net Framework 4.5.
I have seen couple of examples about using IAppBuilder in IAplicationBuilder e.g. example 1 example 2. These attempts were about .netcore 1.1 and not .net core 2.0. May be this is the reason i am unable to port.
Please share your thoughts whether i am trying to achieve something not possible at the moment in .net core 2.0 or there is some error in my code.
Note:
I am using dotnetcore 2.0 with Visual Studio 2017
Error
I am getting following error.
return owinAppBuilder.Build,
Task>>(); TypeLoadException: Could not load type
'System.Security.Cryptography.DpapiDataProtector' from assembly
'System.Security, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a'.
My attempt
app.UseOwin(setup => setup(next =>
{
var owinAppBuilder = new AppBuilder();
var aspNetCoreLifetime =
(IApplicationLifetime)app.ApplicationServices.GetService(typeof(IApplicationLifetime));
new AppProperties(owinAppBuilder.Properties)
{
OnAppDisposing = aspNetCoreLifetime?.ApplicationStopping ?? CancellationToken.None,
DefaultApp = next,
AppName = "test"
};
// Only required if CORS is used, configure it as you wish
var corsPolicy = new System.Web.Cors.CorsPolicy
{
AllowAnyHeader = true,
AllowAnyMethod = true,
AllowAnyOrigin = true,
SupportsCredentials = true
};
//corsPolicy.GetType()
// .GetProperty(nameof(corsPolicy.ExposedHeaders))
// .SetValue(corsPolicy, tusdotnet.Helpers.CorsHelper.GetExposedHeaders());
owinAppBuilder.UseCors(new Microsoft.Owin.Cors.CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = context => Task.FromResult(corsPolicy)
}
});
PublicClientId = "self";
OAuthAuthorizationServerOptions OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new Microsoft.Owin.PathString("/Login"),
Provider = new MyServiceProvider(PublicClientId),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),
AllowInsecureHttp = true,
RefreshTokenProvider = new MyRefreshTokenProvider(),
};
owinAppBuilder.UseOAuthBearerTokens(OAuthOptions);
//owinAppBuilder.UseTus(context => new DefaultTusConfiguration
//{
// // Excluded for brevity, use the same configuration as you would normally do
//});
return owinAppBuilder.Build<Func<IDictionary<string, object>, Task>>();
}));
Microsoft.Owin and related packages do not have targets for .NET Core, no for .NET Standard. All they have is dlls targeting full .NET. You can reference such libraries from your project targeting .NET Core, but they are not guaranteed to work, as you see yourself, because API (set of classes\methods\signatures) of full .NET and .NET Core are different. Visual Studio even will show a warning when you are doing that, for example:
Package 'Microsoft.Owin 3.1.0' was restored using
'.NETFramework,Version=v4.6.1' instead of the project target framework
'.NETCoreApp,Version=v2.0'. This package may not be fully compatible
with your project.
There is Microsoft.AspNetCore.Owin package and you can use OWIN middleware in .NET Core app as your first link describes, but almost all it provides is UseOwin extension method. There is no AppBuilder type there and so on, and there are no Microsoft.AspNetCore.Owin.Cors packages or similar. So you have to either implement all that yourself (no reason to, because you can use the same functionality provided by asp.net core framework) or wait for OWIN packages that target .NET Standard\Core and do that (didn't check, maybe they even exist already).
So, your code uses packages which are indeed not compatible with your target framework, as exception you have at runtime shows. So another answer (for some reason downvoted) is technically correct.
If you still want to use those packages reliably - you need to target full .NET Framework and not .NET Core. To do that, open your .csproj file and change
<TargetFramework>netcoreapp2.0</TargetFramework>
To some .NET framework version that supports .NET Standard 2.0, for example:
<TargetFramework>net47</TargetFramework>
Then go to nuget package manager and, if you have microsoft.aspnetcore.all package (or other packages targeting .NET Core) - uninstall it, you don't need it anyway. Then install Microsoft.AspNetCore package and all other asp.net core packages you need (if not installed already). Rebuild, run and it will work just fine.
That works because all (most?) AspNetCore packages target .NET Standard, not .NET Core, and you can use them in projects targeting full .NET Framework.
Note that by doing that you have asp.net Core project, but not on .NET Core, with all consequences that come from that (cannot run with dotnet run, on linux need to run with mono, and so on).
The Microsoft.Owin components will not work on dotnet core 2.0, they only work on .NET 4.5+
I have following code to retrieve application binary directory in desktop .NET Framework:
static string GetBinDirectory()
{
var relativePath = AppDomain.CurrentDomain.RelativeSearchPath;
var basePath = AppDomain.CurrentDomain.BaseDirectory;
return string.IsNullOrEmpty(relativePath) || !relativePath.IsSubdirectoryOf(basePath)
? basePath
: relativePath;
}
This code works correct in corner cases like assembly shadow copying running NUnit tests.
I started migration this code to .NET Core platform.
I found that there is IHostingEnvironment.ContentRootPath property in ASP.NET Core.
Which is the way to get correct path to application binaries in pure .NET Core?
Which is the replacement for AppDomain.RelativeSearchPath property?
You are probably looking for PlatformServices.Default.Application.ApplicationBasePath which is the path to the folder the application lives in.