.NET MAUI FileProvider on Android does not work in release mode - .net-6.0

I am attempting to install an apk file using .NET MAUI on an Android device like so:
var bytes = DownloadApkFile();
var file = Path.Combine(FileSystem.Current.CacheDirectory, "app.apk");
File.WriteAllBytes(file, bytes);
var context = Android.App.Application.Context;
Java.IO.File file = new Java.IO.File(path);
using (Android.Content.Intent install = new Android.Content.Intent(Android.Content.Intent.ActionView))
{
var uri = Microsoft.Maui.Storage.FileProvider.GetUriForFile(context, context.ApplicationContext.PackageName + ".fileProvider", file);
install.SetDataAndType(uri, "application/vnd.android.package-archive");
install.AddFlags(Android.Content.ActivityFlags.NewTask);
install.AddFlags(Android.Content.ActivityFlags.GrantReadUriPermission);
install.AddFlags(Android.Content.ActivityFlags.ClearTop);
install.PutExtra(Android.Content.Intent.ExtraNotUnknownSource, true);
Platform.CurrentActivity.StartActivity(install);
}
This works perfectly in debug mode, but in release mode I get the following exception: Java.Lang.IllegalArgumentException: Couldn't find meta-data for provider with authority com.mycompany.mobile.fileProvider
I thought maybe I needed to add a [ContentProvider] attribute to the application, but then it no longer builds because of duplicate content providers for androidx.core.content.FileProvider, so I assume MAUI is including this content provider automatically?

The Android/iOS linkers are removing chunks of the assemblies of your app due to the heavy use of reflection. You can verify this by adding code as follows in the csproj file as follows :
<PropertyGroup>
<AndroidLinkMode>None</AndroidLinkMode>
</PropertyGroup>
And you can also instruct the linker to keep some of the important assemblies .
The steps are as follows: :
Add an XML file named Linker.xml to your Android project.
Right click Linker.xml in your Solution Explorer then select Properties.
Change Build Action to LinkDescription.
There is a simimar thread,you can check it here Linker.

Related

C++ Windows Forms application icon exception

I want to set an icon for my Windows Form application. My actions step by step:
I created an 'icon.ico' (256x256) and put it into my project folder
In my 'Form1.h [Design]' I chose this file using Properties
This code appeared in 'Form1.h'
void InitializeComponent(void)
{ ...
this->Icon = (cli::safe_cast<System::Drawing::Icon^>(resources->GetObject(L"$this.Icon")));
... }
The object '$this.Icon' appeared in 'Form1.resx'
I rebuilt the whole project -> no errors
During execution the line 'this->Icon = ...' causes an exception: System.Resources.MissingManifestResourceException: 'Could not find any resources appropriate for the specified culture or the neutral culture. Make sure "WinForm.Form1.resources" was correctly embedded or linked into assembly "MyProject" at compile time, or that all the satellite assemblies required are loadable and fully signed.'
Here I found numerous advices, such as remove/add icon or set some custom properties for 'Form1.resx', but none of them works for me
Just like above, change line to:
this->Icon = gcnew System::Drawing::Icon(L"ICON_NAME.ico");
You might get error/exception while opening form creator but you shouldn't lose any data.
Been there, done that :)
Place the Icon you want to connect to your Form in the project directory.
Then replace the line above with the following:
this->Icon = gcnew System::Drawing::Icon(L"app.ico");
This should neutralize the bug that Microsoft has not fixed.
If you are using visual studio 2019 , you need to change the name of namespace in Form1 the same as your project name, i did it and it works
and make sure you change it in main :
[STAThread]
int main() {
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
Application::Run(gcnew your_namespace_name::Form1());
return 0;
}

Xamarin read a file in shared Project

I'm trying to read a File in my Shared Project.
var currentPath = Environment.CurrentDirectory;
var filename = Path.Combine(currentPath, "File.txt");
var content = File.ReadAllText(filename);
return content;
If i start my application in iOS this works fine, but in Android i get the FileNotFound exception.
The file system structure and the underlying APIs vary from platform to platform. Environment.CurrentDirectory exists on iOS, but it does not exist on Android nor on UWP, so it will have a null value, and therefore it won't be able to find the file.
Because the file system is so different between platforms it is recommended you abstract environment level file accessing code to platform specific projects instead of shared ones - although you could make it work with compiler directives.
#if __ANDROID__
var currentPath = Environment.DataDirectory;
#endif
Alternatively, you can experiment getting the current directory from the Directory.GetCurrentDirectory(); method (in the System.IO namespace)

Visual Studio custom wizard - add additional configuration

I am writing a visual studio custom wizard for creating C++ project.
I need to define additional build configuration, that inherits the debug configuration.
I googled a lot, but couldn't find anything.
I guess this should be done in the JScript file (default.js), AddConfig function, by calling proj.Object.AddConfiguration. But I couldn't find examples, nor syntax rules.
The only thing I found is : http://www.codeproject.com/Articles/200039/A-Visual-Studio-Wizard-to-add-more-project-configu but it is way too complicated, and I couldn't figure it out.
Can you please help?
Found it. It can be done using C# code:
solution.SolutionBuild.SolutionConfigurations.Add("Conf", "Debug", true);
//set Conf to be active build configuration
solution.SolutionBuild.SolutionConfigurations.Item("Conf").Activate();
//Get project
VCProject pro = solution.Projects.Item(1).Object;
//Get comiler tool for project
VCCLCompilerTool tool = pro.Configurations.item("Conf").Tools("VCCLCompilerTool");
//set Prprocessor definition for Conf
tool.PreprocessorDefinitions = "NEW";

Using ClickOnce exe as a Visual Studio external tool

I have a command line executable I built which is published on the network via ClickOnce. The main use of this tool is through Visual Studio as an external tool. When I set this up in Visual Studio I am able to set the command path to the shortcut under roaming data for my profile.
However, Visual Studio resolves this to a path such as:
C:\Users\ME\AppData\Local\Apps\2.0\CGR50YPV.W5E\RXBXM176.HH8\crea..tion_f423fce0316e1dfa_0001.0000_adecafbe6c6acba3\MyAppp.exe
So what happens is if I launch the exe and grab a new version, Visual Studio is still pointing at the old version (as indicated above). I can fix this by re-pointing the command value of my external tool to the shortcut of my exe, but this is a bit frustrating to deal with.
How can I make this work without having to update my command path every time?
You shouldn't access a ClickOnce application via the exe file. If you're going to do that, just xcopy the \bin folder of the application to the other machine. If you want to use the update features, you should always invoke the ClickOnce application by using the shortcut or by invoking the link to the deployment manifest on the webserver. (The deployment manifest is the application file). You can do a process.start on that link.
[edit -- add new info]
Ohhhhh, so you're accessing the shortcut in the folder under the user's profile? Am I getting that? Instead of looking for that one, can you point to the shortcut on the start menu? It will add one automatically when the user installs the application, if the application is online/offline. The shortcut is added to the start menu to the location of the Publishing Company / Product Name using those fields from the Options dialog.
I do this by setting the assembly information to the same values, and retrieving the assembly information programmatically. I always set the assembly description to be identical to the product name, and the assembly company to be the same as the publishing company. Then I can do this:
Assembly code = Assembly.GetExecutingAssembly();
string company = string.Empty;
string description = string.Empty;
if (Attribute.IsDefined(code, typeof(AssemblyCompanyAttribute)))
{
AssemblyCompanyAttribute ascompany =
(AssemblyCompanyAttribute)Attribute.GetCustomAttribute(code,
typeof(AssemblyCompanyAttribute));
company = ascompany.Company;
}
if (Attribute.IsDefined(code, typeof(AssemblyDescriptionAttribute)))
{
AssemblyDescriptionAttribute asdescription =
(AssemblyDescriptionAttribute)Attribute.GetCustomAttribute(code,
typeof(AssemblyDescriptionAttribute));
description = asdescription.Description;
}
if (company != string.Empty && description != string.Empty)
{
string shortcutName =
string.Concat(Environment.GetFolderPath(Environment.SpecialFolder.Programs),
\", company, "\\", description, ".appref-ms");
}
(Sorry, I can't figure out how to make the code format prettier and show the indents properly, but you get the idea.)

"Run Custom Tool" for resx files in MVC2 project (from an external application/script)

I am trying to get the localization for my MVC project working with our existing infrastructure for editing string resources. We store all our resource string in database tables and have a front end web UI to edit them with, and an export application which generated the .resx files. This all works great, but I am having a little difficulty with a new project using MVC2 and VS2010.
I have asked another question on this, the answer to which almost got me there, but not quite.
I have now changed the resources to be in a Resources folder (instead of App_GlobalResources), as recommended by a number of people. And have the following settings against my .resx files ...
Build Action = Embedded Resource
Copy to Output Directory = Do not copy
Custom Tool = PublicResXFileCodeGenerator
Custom Tool Namespace = Resources
File Name = MyApp.resx
I have changed my export application to run the resgen.exe tool with the following parameters ...
string args = string.Format("/publicClass \"{0}\" /str:cs,Resources,{1},\"{2}\"", resourceFile, Path.GetFileNameWithoutExtension(resourceFile), csFilename);
... which generates an almost identical .designer.cs file as I get when I add the .resx file to my project initially. The only difference is the
The generated .designer.cs file differs slightly from the file I get when I run the resgen.exe tool from within my export application.
This is the code generated by VS2010 when I first add the .resx file to my Resources folder ...
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Resources.MyApp", typeof(MyApp).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
... the difference when I run the resgen.exe tool is that is prefixs MyCompany.MyApp to the namespace in the constructor to ResourceManager
new global::System.Resources.ResourceManager("MyCompany.MyApp.Resources.MyApp", typeof(MyApp).Assembly);
Now, this to me seems to be a bug in the resgen.exe tool, because I've told it that the Namespace for my resources is Resources, not MyCompany.MyApp.Resources.
So, is there a fix/work-around for this problem?
The only thing I can think to do at the moment is to post-process the generated .designer.cs file with powershell and fix it!
Finally, I have solved the problem.
I decided to simplify things a bit by breaking my resources out in to a new assembly called Resources. I then added my resx files and set the properties for them as below ...
Build Action = Embedded Resource
Copy to Output Directory = Do not copy
Custom Tool = PublicResXFileCodeGenerator
Custom Tool Namespace = Resources
File Name = MyApp.resx
I then changed my export application to run ...
resgen MyApp.resx /str:c#,Resources,MyApp,MyApp.designer.cs /publicClass
... and to delete *.resources from the folder (created by the resgen.exe utility, but not needed)
This got rid of the prefix on the constructor to ResourceManager, and then i just added a reference to my new Resources assembly to my web application.
I've run a few tests to make sure all is good, including going in to the .designer.cs file and deleting one of the properties to cause a compiler error. Then re-ran my export app, and everything worked again.
So, I am a happy bunny!

Resources