Skip to content

Commit a75cb43

Browse files
committed
Creating .NET MAUI Project sometimes causes error *.png is being used by another process
Fixes dotnet#25207 Context dotnet/android-tools#245 dotnet/android#9409 So there is a problem where the design time builds (DTB) of android sometimes lock files. This can happen when two processes try to write to the same file. This is not a great experience for our users as it just fails the build. So lets try a few things to fix this. 1. Move the Resizetizer output for a DTB into the android `$(IntermediateOutputPath)designtime` folder. This will keep the DTB files completely separate. This should prevent clashes. 2. Add some retry code to the `SkiaSharpTools.Save` method. This will catch `UnauthorizedAccessException` exceptions as well as specific `IOException` types (Access Denied and Sharing Violation). This will allow us to catch when this happens and retry the write. There is a small delay before attempting to write the file again. Note these code uses the Identical code as we are going to use in Android. We have introduced two new environment variables which can be used to control the new behavior. 1. `DOTNET_ANDROID_FILE_WRITE_RETRY_ATTEMPTS`. Integer, controls the number of times we try to write the file. The default is 10. 2. `DOTNET_ANDROID_FILE_WRITE_RETRY_DELAY_MS`. Integer, controls the delay in milliseconds between retry attempts. Default is 1000ms (or 1 second).
1 parent e7556f0 commit a75cb43

File tree

2 files changed

+90
-18
lines changed

2 files changed

+90
-18
lines changed

src/SingleProject/Resizetizer/src/SkiaSharpTools.cs

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,54 @@
11
using System;
22
using System.Diagnostics;
33
using System.IO;
4+
using System.Threading;
5+
using System.Runtime.InteropServices;
46
using SkiaSharp;
57

68
namespace Microsoft.Maui.Resizetizer
79
{
810
internal abstract class SkiaSharpTools
911
{
12+
const int ERROR_ACCESS_DENIED = -2147024891;
13+
const int ERROR_SHARING_VIOLATION = -2147024864;
14+
const int DEFAULT_FILE_WRITE_RETRY_ATTEMPTS = 10;
15+
const int DEFAULT_FILE_WRITE_RETRY_DELAY_MS = 1000;
16+
17+
static int fileWriteRetry = -1;
18+
static int fileWriteRetryDelay = -1;
19+
20+
/// <summary>
21+
/// Checks for the environment variable DOTNET_ANDROID_FILE_WRITE_RETRY_ATTEMPTS to
22+
/// see if a custom value for the number of times to retry writing a file has been
23+
/// set.
24+
/// </summary>
25+
/// <returns>The value of DOTNET_ANDROID_FILE_WRITE_RETRY_ATTEMPTS or the default of DEFAULT_FILE_WRITE_RETRY_ATTEMPTS</returns>
26+
public static int GetFileWriteRetryAttempts ()
27+
{
28+
if (fileWriteRetry == -1) {
29+
var retryVariable = Environment.GetEnvironmentVariable ("DOTNET_ANDROID_FILE_WRITE_RETRY_ATTEMPTS");
30+
if (string.IsNullOrEmpty (retryVariable) || !int.TryParse (retryVariable, out fileWriteRetry))
31+
fileWriteRetry = DEFAULT_FILE_WRITE_RETRY_ATTEMPTS;
32+
}
33+
return fileWriteRetry;
34+
}
35+
36+
/// <summary>
37+
/// Checks for the environment variable DOTNET_ANDROID_FILE_WRITE_RETRY_DELAY_MS to
38+
/// see if a custom value for the delay between trying to write a file has been
39+
/// set.
40+
/// </summary>
41+
/// <returns>The value of DOTNET_ANDROID_FILE_WRITE_RETRY_DELAY_MS or the default of DEFAULT_FILE_WRITE_RETRY_DELAY_MS</returns>
42+
public static int GetFileWriteRetryDelay ()
43+
{
44+
if (fileWriteRetryDelay == -1) {
45+
var delayVariable = Environment.GetEnvironmentVariable ("DOTNET_ANDROID_FILE_WRITE_RETRY_DELAY_MS");
46+
if (string.IsNullOrEmpty (delayVariable) || !int.TryParse (delayVariable, out fileWriteRetryDelay))
47+
fileWriteRetryDelay = DEFAULT_FILE_WRITE_RETRY_DELAY_MS;
48+
}
49+
return fileWriteRetryDelay;
50+
}
51+
1052
static SkiaSharpTools()
1153
{
1254
// DO NOT DELETE!
@@ -150,8 +192,35 @@ void Draw(SKBitmap tempBitmap, double additionalScale, SKSize originalSize, floa
150192

151193
void Save(string destination, SKBitmap tempBitmap)
152194
{
153-
using var stream = File.Create(destination);
154-
tempBitmap.Encode(stream, SKEncodedImageFormat.Png, 100);
195+
int attempt = 0;
196+
int attempts = GetFileWriteRetryAttempts ();
197+
int delay = GetFileWriteRetryDelay ();
198+
while (attempt <= attempts)
199+
{
200+
try
201+
{
202+
using var stream = File.Create(destination);
203+
tempBitmap.Encode(stream, SKEncodedImageFormat.Png, 100);
204+
}
205+
catch (Exception ex)
206+
{
207+
switch (ex)
208+
{
209+
case UnauthorizedAccessException:
210+
case IOException:
211+
var code = Marshal.GetHRForException(ex);
212+
if ((code != ERROR_ACCESS_DENIED && code != ERROR_SHARING_VIOLATION) || attempt == attempts)
213+
{
214+
throw;
215+
}
216+
break;
217+
default:
218+
throw;
219+
}
220+
attempt++;
221+
Thread.Sleep(delay);
222+
}
223+
}
155224
}
156225

157226
public abstract SKSize GetOriginalSize();

src/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,6 @@
5959
_CleanResizetizer;
6060
</CleanDependsOn>
6161

62-
<_ResizetizerInputsFile>$(IntermediateOutputPath)mauiimage.inputs</_ResizetizerInputsFile>
63-
<_ResizetizerOutputsFile>$(IntermediateOutputPath)mauiimage.outputs</_ResizetizerOutputsFile>
64-
<_ResizetizerStampFile>$(IntermediateOutputPath)mauiimage.stamp</_ResizetizerStampFile>
65-
<_MauiFontInputsFile>$(IntermediateOutputPath)mauifont.inputs</_MauiFontInputsFile>
66-
<_MauiFontStampFile>$(IntermediateOutputPath)mauifont.stamp</_MauiFontStampFile>
67-
<_MauiSplashInputsFile>$(IntermediateOutputPath)mauisplash.inputs</_MauiSplashInputsFile>
68-
<_MauiSplashStampFile>$(IntermediateOutputPath)mauisplash.stamp</_MauiSplashStampFile>
69-
<_MauiManifestStampFile>$(IntermediateOutputPath)mauimanifest.stamp</_MauiManifestStampFile>
70-
71-
<_ResizetizerIntermediateOutputRoot>$(IntermediateOutputPath)resizetizer\</_ResizetizerIntermediateOutputRoot>
72-
<_MauiIntermediateImages>$(_ResizetizerIntermediateOutputRoot)r\</_MauiIntermediateImages>
73-
<_MauiIntermediateFonts>$(_ResizetizerIntermediateOutputRoot)f\</_MauiIntermediateFonts>
74-
<_MauiIntermediateSplashScreen>$(_ResizetizerIntermediateOutputRoot)sp\</_MauiIntermediateSplashScreen>
75-
<_MauiIntermediateManifest>$(_ResizetizerIntermediateOutputRoot)m\</_MauiIntermediateManifest>
76-
7762
<_ResizetizerPlatformIdentifier>$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))</_ResizetizerPlatformIdentifier>
7863
<_ResizetizerNoTargetPlatform Condition="'$(_ResizetizerPlatformIdentifier)' == ''">True</_ResizetizerNoTargetPlatform>
7964
<_ResizetizerPlatformIsAndroid Condition="'$(_ResizetizerPlatformIdentifier)' == 'android'">True</_ResizetizerPlatformIsAndroid>
@@ -84,6 +69,24 @@
8469
<_ResizetizerPlatformIsWindows Condition="$(_ResizetizerPlatformIdentifier.Contains('windows')) == 'True'">True</_ResizetizerPlatformIsWindows>
8570
<_ResizetizerPlatformIsTizen Condition="'$(_ResizetizerPlatformIdentifier)' == 'tizen'">True</_ResizetizerPlatformIsTizen>
8671

72+
<_ResizetizerIntermediateOutputPath Condition=" '$(_ResizetizerIntermediateOutputPath)' == '' And '$(_ResizetizerPlatformIsAndroid)' == 'True' And '$(DesignTimeBuild)' == 'True' ">$(IntermediateOutputPath)designtime</_ResizetizerIntermediateOutputPath>
73+
<_ResizetizerIntermediateOutputPath Condition=" '$(_ResizetizerIntermediateOutputPath)' == '' " >$(IntermediateOutputPath)</_ResizetizerIntermediateOutputPath>
74+
75+
<_ResizetizerInputsFile>$(_ResizetizerIntermediateOutputPath)mauiimage.inputs</_ResizetizerInputsFile>
76+
<_ResizetizerOutputsFile>$(_ResizetizerIntermediateOutputPath)mauiimage.outputs</_ResizetizerOutputsFile>
77+
<_ResizetizerStampFile>$(_ResizetizerIntermediateOutputPath)mauiimage.stamp</_ResizetizerStampFile>
78+
<_MauiFontInputsFile>$(_ResizetizerIntermediateOutputPath)mauifont.inputs</_MauiFontInputsFile>
79+
<_MauiFontStampFile>$(_ResizetizerIntermediateOutputPath)mauifont.stamp</_MauiFontStampFile>
80+
<_MauiSplashInputsFile>$(_ResizetizerIntermediateOutputPath)mauisplash.inputs</_MauiSplashInputsFile>
81+
<_MauiSplashStampFile>$(_ResizetizerIntermediateOutputPath)mauisplash.stamp</_MauiSplashStampFile>
82+
<_MauiManifestStampFile>$(_ResizetizerIntermediateOutputPath)mauimanifest.stamp</_MauiManifestStampFile>
83+
84+
<_ResizetizerIntermediateOutputRoot>$(_ResizetizerIntermediateOutputPath)resizetizer\</_ResizetizerIntermediateOutputRoot>
85+
<_MauiIntermediateImages>$(_ResizetizerIntermediateOutputRoot)r\</_MauiIntermediateImages>
86+
<_MauiIntermediateFonts>$(_ResizetizerIntermediateOutputRoot)f\</_MauiIntermediateFonts>
87+
<_MauiIntermediateSplashScreen>$(_ResizetizerIntermediateOutputRoot)sp\</_MauiIntermediateSplashScreen>
88+
<_MauiIntermediateManifest>$(_ResizetizerIntermediateOutputRoot)m\</_MauiIntermediateManifest>
89+
8790
<ResizetizerIncludeSelfProject Condition="'$(ResizetizerIncludeSelfProject)' == ''">False</ResizetizerIncludeSelfProject>
8891

8992
<_ResizetizerDefaultInvalidFilenamesErrorMessage>One or more invalid file names were detected. File names must be lowercase, start and end with a letter character, and contain only alphanumeric characters or underscores: </_ResizetizerDefaultInvalidFilenamesErrorMessage>
@@ -504,7 +507,7 @@
504507
</ItemGroup>
505508

506509
<!-- Stamp file for Outputs -->
507-
<MakeDir Directories="$(IntermediateOutputPath)"/>
510+
<MakeDir Directories="$(_ResizetizerIntermediateOutputPath)"/>
508511
<Touch Files="$(_MauiSplashStampFile)" AlwaysCreate="True" />
509512

510513
<ItemGroup>

0 commit comments

Comments
 (0)