how to specify C# / WPF resource files in cmake - c#

I need to specify resource files for c-sharp / WPF via cmake, these are image files that need to ship with the application GUI.
In Visual Studio you just:
select the image -> Advanced -> Build Action -> select "Resource", this makes it possible to access the images directly in xaml, ex:
<Image Source="png/login.png"/>
For reference when done manually in VS2017 this adds the following lines to the project.csproj:
<ItemGroup>
<Resource Include="png/login.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Resource>
</ItemGroup>
So how do I specify resource files in cmake?
I tried the suggestion here, to no avail:
https://gitlab.kitware.com/cmake/cmake/issues/17368
My current CMakeLists.txt looks as follows:
cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR)
project(sampleApp VERSION 2.2.0 LANGUAGES CSharp)
include(CSharpUtilities)
add_executable(sampleApp app.config
App.xaml
App.xaml.cs
MainWindow.xaml
MainWindow.xaml.cs
Styles.xaml
Properties/AssemblyInfo.cs
Properties/Resources.Designer.cs
Properties/Resources.resx
Properties/Settings.Designer.cs
Properties/Settings.settings
)
target_link_libraries(sampleApp PRIVATE otherLibrary)
#
# Tried this setting (does not work)
set(RESOURCES "png/login.png")
set_property(SOURCE ${RESOURCES} PROPERTY VS_TOOL_OVERRIDE "Resource")
csharp_set_designer_cs_properties(
Properties/AssemblyInfo.cs
Properties/Resources.Designer.cs
Properties/Resources.resx
Properties/Settings.Designer.cs
Properties/Settings.settings)
csharp_set_xaml_cs_properties(
App.xaml
App.xaml.cs
MainWindow.xaml
MainWindow.xaml.cs
Styles.xaml)
target_compile_options(sampleApp PRIVATE "/langversion:6")
target_compile_options(sampleApp PRIVATE "/unsafe")
set_property(SOURCE App.xaml PROPERTY VS_XAML_TYPE "ApplicationDefinition")
set_property(TARGET sampleApp PROPERTY VS_DOTNET_TARGET_FRAMEWORK_VERSION "v4.6.1")
set_target_properties(sampleApp PROPERTIES WIN32_EXECUTABLE TRUE)
set_property(TARGET sampleApp PROPERTY VS_DOTNET_REFERENCES
"System"
"System.Configuration"
"System.Configuration.Install"
"System.Data"
"System.Drawing"
"System.Management"
"System.Security"
"System.Transactions"
"System.Web"
"System.Xml"
"System.Core"
"System.Xaml"
"System.Xml.Linq"
"System.Data.DataSetExtensions"
"PresentationCore"
"PresentationFramework"
"Microsoft.CSharp"
"Microsoft.Office.Interop.Excel"
"WindowsBase"
)
#
# Install
#
install(TARGETS sampleApp EXPORT sampleAppTargets
RUNTIME DESTINATION sampleApp/
ARCHIVE DESTINATION sampleApp/
LIBRARY DESTINATION sampleApp/
)
install(
FILES
${RESOURCES}
DESTINATION
sampleApp/
COMPONENT
Devel
)

I had a similar issue with my project. To fix the issue, I had to explicitly add the image resource file in add_executable using CMake. So try something like this:
set(IMAGE_RESOURCE "png/login.png")
# Include the resource here with the other source files.
add_executable(sampleApp app.config
App.xaml
App.xaml.cs
MainWindow.xaml
MainWindow.xaml.cs
Styles.xaml
Properties/AssemblyInfo.cs
Properties/Resources.Designer.cs
Properties/Resources.resx
Properties/Settings.Designer.cs
Properties/Settings.settings
${IMAGE_RESOURCE}
)
# Then set the file property.
set_property(SOURCE ${IMAGE_RESOURCE} PROPERTY VS_TOOL_OVERRIDE "Resource")
This let CMake pull in the image file into my Visual Studio project. Then, upon examining the PNG file properties in Visual Studio, the "Build Action" was set to "Resource".

Overriding the build action using SET_PROPERTY did not quite do the job for me. The build action remained Image. However, the ticket you've linked uses SET_SOURCE_FILES_PROPERTIES, which fixed the issue for me:
SET(IMAGE_RESOURCES
"Icons/Foo.png"
"Icons/Bar.png"
)
ADD_EXECUTABLE(MyApp
App.config
Application.cs
${IMAGE_RESOURCES}
)
SET_SOURCE_FILES_PROPERTIES(${IMAGE_RESOURCES} PROPERTIES VS_TOOL_OVERRIDE "Resource")

I also couldn't get resources to embed themselves in the assembly. I just found what was missing.
Your CMakeLists.txt file needs
set_property(TARGET yourTargetName PROPERTY VS_GLOBAL_RootNamespace yourRootNamespace)
Substitute your own values for yourTargetName and yourRootNamespace, obviously.

Related

Creating Folders in C# Project Using CMake

I've used source_group dozens of times with C++ Projects to organize my files, yet for C# Projects this does not seem to work:
set (FOO myfoo.cs)
set (BAR mybar.cs)
set (FILES
App.xaml
App.xaml.cs
MainWindow.xaml
MainWindow.xaml.cs
)
set (PROPERTIES <property files here>)
add_executable(
${CMAKE_PROJECT_NAME}
App.config
${FILES}
${FOO}
${BAR}
${PROPERTIES}
)
source_group(Models FILES ${FOO})
source_group(ViewModels FILES ${BAR})
Do folders not work for C# Projects via CMake? Or what am I doing wrong?
EDIT: Forgot to mention, in my main CMakeLists.txt file I already have:
if (WIN32)
SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON)
endif (WIN32)
I then call add_subdirectory where the above CMakeLists.txt is. This setup seems to work okay for my other C++ project that is added in the same way.
You can use the VS_CSHARP_Link property to set how a file is viewed in a C# project. (Don't ask for documentation; I can't find any. I learned about this setting by perusing the source code. Looks like it first appeared in version 3.18.)
For example, after adding ${SOME_ROOT_DIR}/some/path/to/someFile.cs to your project's sources, control how it's organized with:
set_source_files_properties("${SOME_ROOT_DIR}/some/path/to/someFile.cs"
PROPERTIES VS_CSHARP_Link "My own folder\someFile.cs")
It seems that in C# Projects, source_group is not honored. However, folders are still possible simply by giving it a directory structure in the list, like so:
set(MODELS
Models/CompileOptionsModel.cs
)
set(VIEWMODELS
ViewModels/CompileOptionsViewModel.cs
)
set(PROPERTIES
...
)
set(Files
...
)
add_executable(
${APPNAME}
App.config
${MODELS}
${VIEWMODELS}
${FILES}
${PROPERTIES}
)
This will result in folders Models and ViewModels appearing.

asp.net core with resource file (resx)

I have a project in ASP.Net Core that need to include a image from a resource file (to generate a PDF).
So, I create a new resource file using Visual Studio (Add > New Item > Resources File), named Resource.resx
Using the Managed Resource Editor (default editor of Visual Studio), I included a new image named logo.png.
A new file named Resource.Designer.cs was created with a method listed below:
public static System.Drawing.Bitmap logo {
get {
object obj = ResourceManager.GetObject("logo", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
Now, only to test, I created the following code:
var logo = Resources.logo;
This threw a new exception, with the following content:
An unhandled exception of type 'System.InvalidCastException' occurred.
Additional Information: Unable to cast object of type 'System.String' to type 'System.Drawing.Bitmap'.
I tried all from this link too:
https://weblogs.asp.net/jeff/beating-localization-into-submission-on-asp-net-5
but the results are the same.
If I make this code on a console application, everything works correctly.
I found another approach that worked good for my problem.
http://codeopinion.com/asp-net-core-embedded-resource/
Just need to create a folder on project (Resources in my case), and then, in project.json, I included the following code:
"buildOptions": {
"embed": ["Resources/**/*"]
}
and then, my code:
var assembly = Assembly.GetExecutingAssembly();
var logoStream = assembly.GetManifestResourceStream("ProjectNamespace.Resources.logo.png");
If you are using .net core 3.1 api, you may-
Add services.AddLocalization(); in ConfigureServices method (Startup.cs)
Add Resource file in the project say Resource.en-US.resx, add TestKey in Name and Hello in Value column for testing purpose.
Add a class file in the same hierarchy with name as Resource.cs
In controller, add a variable-
private readonly IStringLocalizer _localizer;
and inject it in constructor-
public TestController(IStringLocalizer<Resource> localizer)
{
_localizer = localizer;
}
Read the value of resource names as-
_localizer["TestKey"] and you will get Hello (i.e. entered in step#2)
More details at- [https://www.syncfusion.com/blogs/post/how-to-use-localization-in-an-asp-net-core-web-api.aspx]
I'm using Visual Studio 2017 (csproj files) and this solution worked for me:
https://stackoverflow.com/a/39368856/812610
Open Solution Explorer add files you want to embed. Right click on the files then click on Properties. In Properties window and change Build Action to Embedded Resource.
The embedded resource's name is "[DefaultNamespace].[Folder].filename". I saved a cert (cert.pfx) to "Resources" folder so for me it's "MyProjectName.Resources.cert.pfx"
And this was added to my .csproj:
<ItemGroup>
<None Remove="Resources\testcert.pfx" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\testcert.pfx" />
</ItemGroup>

How create ProjectExtensions tag with MSBuild for generating .csproj?

From the MS Developer website:
Allows MSBuild project files to contain non-MSBuild information.
Anything inside of a ProjectExtensions element will be ignored by
MSBuild.
See this example :
<ProjectExtensions>
<VisualStudio>
<UserProperties RESOURCE_FILE="projectName.RC" />
</VisualStudio>
</ProjectExtensions>
I want to create something like above
for creating .csproj file programmatically, I use
Namespace: Microsoft.Build.Construction
Assembly: Microsoft.Build (in Microsoft.Build.dll)
for example :
var root = ProjectRootElement.Create();
var group = root.AddPropertyGroup();
group.AddProperty("Configuration", "Debug");
group.AddProperty("Platform", "x64");
// references
AddItems(root, "Reference", "System", "System.Core");
// items to compile
AddItems(root, "Compile", "test.cs");
var target = root.AddTarget("Build");
var task = target.AddTask("Csc");
task.SetParameter("Sources", "#(Compile)");
task.SetParameter("OutputAssembly", "test.dll");
root.Save("test.csproj");
I want to know, How can I use these classes and namespace for creating ProjectExtensions tag
I saw a method with name CreateProjectExtension() but I dont know how use it.
please guide me How create any ProjectExtensions tag

Dynamically display current year in assembly info

How to set current year in AssemblyInfo file?
I used
Instead of this:
<Assembly: AssemblyCopyright("Copyright 2012, Company Name.")>
tried this:
<Assembly: AssemblyCopyright("Copyright" + DateTime.Now.Year.ToString() + ", Company Name.")>
I get invalid constant error.
I don't want to use registry key entries, what is the optimum way of doing this? (so that when a user right clicks on EXE & looks for assembly information can see current year).
Thanks.
I saw something on another post (https://stackoverflow.com/a/827209/857307) that could really be helpful here.
Try this.
Add a new file to your source repository somewhere common to all of the projects in your solution. Call the file something like AssemblyCopyright.tt
In the file, add the following code for c#
<## template language="C#" #>
using System;
using System.Reflection;
[assembly: AssemblyCopyright("Copyright © CompanyName <#=DateTime.Now.Year#>")]
or vb
<## template language="VB" #>
<## output extension=".vb" #>
Imports System
Imports System.Reflection
<Assembly: AssemblyCopyright("Copyright © CompanyName <#=DateTime.Now.Year#>")>
Then, remove the AssemblyCopyright attribute from each of your AssemblyInfo.cs files.
Finally, add a link to the AssemblyCopyright.tt file to each of your projects.
The template file will recreate a new AssemblyCopyright.cs file on every build with the correct year.
Try this:
<Assembly: AssemblyCopyright("Copyright $([System.DateTime]::Now.ToString('yyyy')), Company Name.")>
I haven't tried this exactly. Since i define some other parts of the assembly info via the .csproj using a WriteCodeFragment as described in this answer, I do it this way too - I define properties
<!-- We started in 2021. So, if it's still 2021 we just want to show '2021' -->
<PropertyGroup Condition="$([System.DateTime]::Now.ToString('yyyy')) == '2021'">
<CopyrightYears>2021</CopyrightYears>
</PropertyGroup>
<!-- But for later years we want a range. E.g. in 2023 it will show '2021 - 2023' -->
<PropertyGroup Condition="$([System.DateTime]::Now.ToString('yyyy')) != '2021'">
<CopyrightYears>2021 - $([System.DateTime]::Now.ToString('yyyy'))</CopyrightYears>
</PropertyGroup>
Then, within the task that updates the assembly info i have an ItemGroup including
<AssemblyAttributes Include="AssemblyCopyright">
<_Parameter1>"Copyright © $(CopyrightYears) ..."</_Parameter1>
</AssemblyAttributes>
The after the ItemGroup I have the following within that task
<MakeDir Directories="$(IntermediateOutputPath)" />
<WriteCodeFragment Language="C#"
OutputFile="$(IntermediateOutputPath)Version.cs"
AssemblyAttributes="#(AssemblyAttributes)" />
<ItemGroup>
<Compile Include="$(IntermediateOutputPath)Version.cs" />
</ItemGroup>
The functions you can use as properties are documented here
Probably the best way is to integrate this into your build process using tools like NAnt or MSBuild.
Here is an article that explains how to change your AssemblyInfo using MSBuild:
Updating Assemblies with A Version Number.
You can use NANT/MSBuild tasks to modify the AssemblyInfo.cs file like we do to change the Version of each assembly for every build.
For more information, visit http://msbuildtasks.tigris.org/
Typically, for that kind of substitution, you use a pre-build step that invokes a script that automatically generates your assemblyInfo.cs file.
You can have a look at this thread : How can you find and replace text in a file using the Windows command-line environment?
I had similar question, but for new style csproj file; that contains the file version info. I found a great answer here How to define current system date in post build event. Oddly, that is about a question that is somewhat different than my and the OP question. But, I think the accepted answer to that is what I want. Namely, $([System.DateTime]::Now.Year) in the csproj files evaluates to the current year.
Here's my solution using a prebuild task. Should work on windows and unix. Will work with dotnet (if you run dotnet build, before dotnet run).
Add this to .csproj
<!-- update assembly info -->
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Condition="$(OS) == Windows_NT" Command="set TargetDir=$(TargetDir)
..\scripts\updateAssemblyInfo.cmd" />
<Exec Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' OR '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'" Command="export TargetDir=$(TargetDir)
bash ../scripts/updateAssemblyInfo.sh" />
</Target>
AssemblyInfo.base.txt
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCopyrightAttribute("Copyright © 2009 - <#=DateTime.Now.Year#> COMPANYNAME. All rights reserved.")]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("xxx")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// [assembly: AssemblyVersion("1.0.0.0")]
// [assembly: AssemblyFileVersion("1.0.0.0")]
in scripts folder
updateAssemblyInfo.cmd
for /f %%i in ('"powershell (Get-Date).ToString(\"yyyy\")"') do set currentYear=%%i
echo %currentYear%
powershell -Command "(gc 'AssemblyInfo.base.txt' -encoding "UTF8") -replace '<#=DateTime.Now.Year#>', '%currentYear%' | Out-File -encoding "UTF8" 'AssemblyInfo.cs'"
updateAssemblyInfo.sh
#!/bin/bash
year=$(date +%Y)
echo "Current Year : $year"
contents=$(cat AssemblyInfo.base.txt)
# echo $contents
echo $contents | awk -v yr=$year '{gsub("<#=DateTime.Now.Year#>",yr); print}' > AssemblyInfo.cs

C# - Sharing resources file between project

I would like two how to do to share Resources files between 2 (or more) projects?
So, to resume, I've three project :
the development project (CF.NET) that include the resource file (with all definition).
I've two other projects that are empty BUT linking to the development projects, it's just a different build each time, so when I modify the development project, all three projects are updated too. (Modification of the csproj file.)
Question is, what about Resources files? When I try to access from the development project I get all resources but when I try from the 2 others, it throws an "MissingManifestResourceException".
Any idea how to solve this issue?
Thanks.
[EDIT]
Here is what I've done :
Create a project named "RealProject" which contains all code (including resources files)
Create a project named "LinkedProject" which contains nothing (I deleted all files into it and modify the csproj file as the following :
<ItemGroup>
<Compile Include="..\RealProject\**\*.cs" />
</ItemGroup>
So in LinkedProject directory I've only :
[Directory] bin
[Directory] obj
[File ] LinkedProject.csproj
The whole LinkedProject uses the RealProject files, it's just a different configuration build (see here to know why : C# - Code compiler for .NET & CF.NET )
Once in that configuration, I've no access to the resources files from the RealProject ...
If you need screens or more detailed explanation, just ask.
[EDIT]
With this code, it works, Resource manager isn't loaded on the good Assembly name, but it should exists a better solution !!!
Assembly ass = Assembly.ReflectionOnlyLoadFrom(#"..\..\..\RealProject\bin\Debug\RealProject.dll");
ResourceManager manager = new ResourceManager("RealProject.Properties.Resources", ass);
[Solution]
Things to check :
The LinkedProject as the same
namespace as the RealProject
Add Resources as links
Clean up all your solution
Rebuild it
Test !
Try to add the resource file as a link to the other two projects and make sure the namespaces as defined in the project file is the same.
Try adding existing file in other projects as a link.
The problem with sharing resources files between different projects is that the root namespace has to be the same in all the projects you use the same file in.
Or not.
You can get the root namespace at runtime in the *Resources.designer.cs file. Note the links in the comments to related answers. Make sure you commit this and keep an eye on it, the code-generator has a habit of overwriting it which would break its universality. I used the xml doc comments to remind me what's going on, if the code-gen obliterates it I'll see it in the commit diff.
/// <summary> Returns the cached ResourceManager instance used by this class. </summary>
/// <remarks> DO NOT commit any version of this file the tool overwrites, it will break it for other projects</remarks>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null))
{
// https://stackoverflow.com/a/1329631/492 https://stackoverflow.com/a/51978052/492
Assembly thisAssembly = typeof(/*thisClassName*/).Assembly;
// you need a class called "App", or just change the name on the next line to one you have in the root namespace
var apps = thisAssembly?.GetTypes().Where(t => t.Name == "App");
if (apps.Count() != 1)
throw new InvalidOperationException($"Too Many App classes. Count: {apps.Count()}, Apps: {apps}");
Type appType = apps.FirstOrDefault();
string ns = appType.Namespace;
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager($"{ns}.OtherNames.Spaces.thisClassName", typeof(thisClassName).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}

Categories