22// Licensed under the MIT License. See LICENSE in the project root for license information.
33
44#include < Windows.h>
5-
5+ # include < Pathcch.h >
66#include " mrm/BaseInternal.h"
77#include " mrm/common/platform.h"
88#include " mrm/readers/MrmReaders.h"
@@ -25,6 +25,7 @@ typedef struct
2525
2626constexpr wchar_t ResourceUriPrefix[] = L" ms-resource://" ;
2727constexpr int ResourceUriPrefixLength = ARRAYSIZE(ResourceUriPrefix) - 1 ;
28+ constexpr wchar_t c_defaultPriFilename[] = L" resources.pri" ;
2829
2930#define INDEX_RESOURCE_ID -1
3031#define INDEX_RESOURCE_URI -2
@@ -853,18 +854,26 @@ STDAPI_(void) MrmFreeResource(_In_opt_ void* resource)
853854 return ;
854855}
855856
856- // Append filename to current module path. If the file doesn't exist, it will
857+ // When filename is provided, append filename to current module path. If the file doesn't exist, it will
857858// append the filename to parent path. If none exists, file in current module
858859// path will be returned.
859- // Visual Studio usually builds the executable to a subfolder with project name
860+ // Visual Studio usually builds the executable to a subfolder with project name for packaged project
860861// unless TargetPlatformIdentifier is UAP. To accommodate this behavior, we will
861862// try to search resources.pri from both module path and parent path.
862- STDAPI MrmGetFilePathFromName (_In_ PCWSTR filename, _Outptr_ PWSTR* filePath)
863+ //
864+ // When filename is not provided or empty, we will search resources.pri under current module
865+ // path. If the file doesn't exist, search [modulename].pri instead.
866+ // For unpackaged app, the built resource name is [modulename].pri. We still search resources.pri
867+ // because that is a supported scenario for inbox MRT (Xaml islands).
868+ //
869+ // A file path is always returned even if none of the files under the above mentioned searching
870+ // criteria exists. In that case, we will return filename (if provided) or resources.pri (if name not
871+ // provided) under module file path. The reason is that we don't want to fail the creation of
872+ // ResourceManager even if an app doesn't have PRI file.
873+ STDAPI MrmGetFilePathFromName (_In_opt_ PCWSTR filename, _Outptr_ PWSTR* filePath)
863874{
864875 *filePath = nullptr ;
865876
866- RETURN_HR_IF (E_INVALIDARG, filename == nullptr || *filename == L' \0 ' );
867-
868877 wchar_t path[MAX_PATH];
869878 DWORD size = ARRAYSIZE (path);
870879 DWORD length = GetModuleFileName (nullptr , path, size);
@@ -899,24 +908,38 @@ STDAPI MrmGetFilePathFromName(_In_ PCWSTR filename, _Outptr_ PWSTR* filePath)
899908 pointerToPath++;
900909 }
901910
911+ pointerToPath = pathAllocated ? pathAllocated.get () : path;
912+
913+ wchar_t moduleFilename[MAX_PATH];
914+ RETURN_IF_FAILED (StringCchCopyW (moduleFilename, ARRAYSIZE (moduleFilename), lastSlash != nullptr ? lastSlash + 1 : pointerToPath));
915+
902916 if (lastSlash != nullptr )
903917 {
904918 *(lastSlash + 1 ) = 0 ;
905919 }
906920
907- pointerToPath = pathAllocated ? pathAllocated.get () : path;
908-
909921 std::unique_ptr<wchar_t , decltype (&MrmFreeResource)> finalPath (nullptr , MrmFreeResource);
910922
923+ PCWSTR filenameToUse = filename;
924+ if (filename == nullptr || *filename == L' \0 ' )
925+ {
926+ filenameToUse = c_defaultPriFilename;
927+ }
928+
911929 // Will build the path at most twice.
912- // First time using current path. If not exist, do another time with parent path.
930+ // If filename is provided:
931+ // - search under exe path
932+ // - if not exist, search parent path
933+ // If filename is not provided:
934+ // - search under exe path with default name (resources.pri)
935+ // - if not exist, search under same path with [modulename].pri
913936 for (int i = 0 ; i < 2 ; i++)
914937 {
915938 size_t lengthInSizeT;
916939 RETURN_IF_FAILED (StringCchLengthW (pointerToPath, STRSAFE_MAX_CCH, &lengthInSizeT));
917940 length = static_cast <DWORD>(lengthInSizeT);
918941
919- RETURN_IF_FAILED (StringCchLengthW (filename , STRSAFE_MAX_CCH, &lengthInSizeT));
942+ RETURN_IF_FAILED (StringCchLengthW (filenameToUse , STRSAFE_MAX_CCH, &lengthInSizeT));
920943 DWORD lengthOfName = static_cast <DWORD>(lengthInSizeT);
921944
922945 RETURN_IF_FAILED (DWordAdd (length, lengthOfName, &length));
@@ -929,9 +952,11 @@ STDAPI MrmGetFilePathFromName(_In_ PCWSTR filename, _Outptr_ PWSTR* filePath)
929952 std::unique_ptr<wchar_t , decltype (&MrmFreeResource)> outputPath (rawOutputPath, MrmFreeResource);
930953
931954 RETURN_IF_FAILED (StringCchCopyW (outputPath.get (), length, pointerToPath));
932- RETURN_IF_FAILED (StringCchCatW (outputPath.get (), length, filename ));
955+ RETURN_IF_FAILED (StringCchCatW (outputPath.get (), length, filenameToUse ));
933956
934- if (GetFileAttributes (outputPath.get ()) != INVALID_FILE_ATTRIBUTES)
957+ DWORD attributes = GetFileAttributes (outputPath.get ());
958+
959+ if ((attributes != INVALID_FILE_ATTRIBUTES) && !(attributes & FILE_ATTRIBUTE_DIRECTORY))
935960 {
936961 // The file exists. Done.
937962 finalPath.swap (outputPath);
@@ -940,16 +965,28 @@ STDAPI MrmGetFilePathFromName(_In_ PCWSTR filename, _Outptr_ PWSTR* filePath)
940965
941966 if (i == 0 )
942967 {
943- // If none of the file exists, will return the file in current path
968+ // The filename in the first iteration (the file under module path) will be returned if no file is found
969+ // in all iterations.
944970 finalPath.swap (outputPath);
945- }
946971
947- if (secondToLastSlash == nullptr )
948- {
949- break ;
950- }
972+ // Prep for second iteration
973+ if (filename == nullptr || *filename == L' \0 ' )
974+ {
975+ // Change to [modulename].pri
976+ RETURN_IF_FAILED (PathCchRenameExtension (moduleFilename, ARRAYSIZE (moduleFilename), L" pri" ));
977+ filenameToUse = moduleFilename;
978+ }
979+ else
980+ {
981+ // move to parent folder
982+ if (secondToLastSlash == nullptr )
983+ {
984+ break ;
985+ }
951986
952- *(secondToLastSlash + 1 ) = 0 ;
987+ *(secondToLastSlash + 1 ) = 0 ;
988+ }
989+ }
953990 }
954991
955992 *filePath = finalPath.release ();
0 commit comments