Enable support DllImport a native assembly whose name contains '.'#17505
Enable support DllImport a native assembly whose name contains '.'#17505jkotas merged 12 commits intodotnet:masterfrom
Conversation
|
Test? |
|
Ideally a test in Corefx so we can ensure it is fixed in Mono, CoreRT. |
|
CoreCLR has better setup for interop tests. And Mono and CoreRT can run CoreCLR tests too. |
So what happens if the specified name is "foo.exe"? Will it search for "foo.exe.dll"? |
|
I totally missed your PR. This approach of using |
|
@luqunl Could you please rebase away the commits not related to your change? |
|
Let me try to rebase my changes |
Fixed
Fixed
Thanks for catching this. Fixed
Thanks for catching this. Fixed |
src/vm/dllimport.cpp
Outdated
| containsSuffix = it == libName.End() || *it == (WCHAR)'.'; | ||
| } | ||
| #else | ||
| if (libName.EndsWithCaseInsensitive(PLATFORM_SHARED_LIB_SUFFIX_W) || libName.EndsWithCaseInsensitive(WINDOWS_SHARED_EXE_SUFFIX_W)) |
There was a problem hiding this comment.
I do not understand why we are special casing .exe suffix here. .exe suffix is non-standard suffix for shared libraries. If I want to, I can use any other non-standard suffix (e.g. .dat) for my .dlls and LoadLibrary will load them too.
There was a problem hiding this comment.
The only reason is to avoid to load/search .exe.dll file. if it is possible that someone name his file as a.exe.dll, then we can remove this special case.
There was a problem hiding this comment.
I am more worried about that cases that used to work fine before this change, but do not work anymore after this change. Consider:
[DllImport("mydll.dat")]
extern static int sum(int a, int b);
static void Main() {
Console.WriteLine(sum(1,2).ToString());
}And mydll.dat that is renamed mydll.dll to mydll.dat. It works fine before this change (mydll.dat is passed to LoadLibrary), but it does not work after this change (mydll.dat.dll is passed to LoadLibrary).
There was a problem hiding this comment.
For this case, try both name itself and name itself+extension.
' static const char* const SuffixFirst[] =
{
"%.0s%s%s", // name+suffix
#ifdef FEATURE_PAL
"%s%s%s", // prefix+name+suffix
#endif
"%.0s%s", // name
#ifdef FEATURE_PAL
"%s%s%.0s" // prefix+name
#endif
};
'
There was a problem hiding this comment.
I can check // name first, then //name + suffix to be more comp with current behavior.
| add_library (DllFileProbe SHARED ${SOURCES}) | ||
| target_link_libraries(DllFileProbe ${LINK_LIBRARIES_ADDITIONAL}) | ||
|
|
||
| #get_cmake_property(_variableNames VARIABLES) |
There was a problem hiding this comment.
Do we need these commented out blocks?
There was a problem hiding this comment.
I will remove these commented lines..just copy & paste..
jkotas
left a comment
There was a problem hiding this comment.
To summarize, I think there are 4 interesting cases on Windows:
- The name does not contain dot: Try the name only. No other suffixes. LoadLibrary will append
.dllsuffix internally. - The name ends with a dot: Try the name only. No other suffixes. Maintains the behavior documented in MSDN.
- The name ends with
.dll(or.exetoo?): Try the name only. No point in trying.dll.dll. - Otherwise: Try name +
.dllsuffix first, and then try name without.dllsuffix.
src/vm/dllimport.cpp
Outdated
| @@ -6078,9 +6096,13 @@ static void DetermineLibNameVariations(const char* const** libNameVariations, in | |||
| static const char* const SuffixFirst[] = | |||
| { | |||
| "%.0s%s%s", // name+suffix | |||
There was a problem hiding this comment.
For name without any dots, this is going to probe twice now:
- First time, we append the
.dll - Second time, we pass in name without
.dllsuffix and LoadLibrary appends it internally.
It would be nice to avoid this duplication (e.g. just let LoadLibrary take care of appending .dll to the simple names).
src/vm/dllimport.cpp
Outdated
| static const char* const SuffixFirst[] = | ||
| { | ||
| "%.0s%s%s", // name+suffix | ||
| #ifdef FEATURE_PAL |
There was a problem hiding this comment.
Similar for names that end with a .. We should not do any probing for them on Windows - to maintain the behavior documented for them on MSDN.
| #else // !FEATURE_PAL | ||
| #define PLATFORM_SHARED_LIB_SUFFIX_A ".dll" | ||
| #define PLATFORM_SHARED_LIB_PREFIX_A "" | ||
| #define PLATFORM_SHARED_LIB_SUFFIX_W W(".dll") |
There was a problem hiding this comment.
Nit: I do not think we need to have defines for these since they are used just once.
src/vm/dllimport.cpp
Outdated
| *numberOfVariations = COUNTOF(NameOnly); | ||
| } | ||
| } | ||
| #else |
There was a problem hiding this comment.
Nit: It would be nice to add // FEATURE_PAL comment here.
src/vm/dllimport.cpp
Outdated
| *numberOfVariations = COUNTOF(SuffixLast); | ||
| } | ||
| } | ||
| #endif |
src/vm/dllimport.cpp
Outdated
| bool containsSuffix = libName.EndsWithCaseInsensitive(W(".dll")) || libName.EndsWithCaseInsensitive(W(".exe")); | ||
| if (endWithDot || notContainDot || containsSuffix || !libNameIsRelativePath) | ||
| { | ||
| // file name end with '.' |
There was a problem hiding this comment.
This comment is not very useful. It is a literal rewrite of the above condition in plain English.
Instead, it would be useful to have comment that describes the reasons for this logic.
|
Build errors: |
|
Fixed build break due to compiler warning about BOOL to bool conversation. |
|
|
src/vm/dllimport.cpp
Outdated
| bool containsSuffix = libName.EndsWithCaseInsensitive(W(".dll")) || libName.EndsWithCaseInsensitive(W(".exe")); | ||
|
|
||
| // The purpose of following code is to workaround LoadLibrary limitation: | ||
| // LoadLibray won't append extension if filename itself contains '.'. Thus It will break the following scenario: |
src/vm/dllimport.cpp
Outdated
| #else // FEATURE_PAL | ||
| static void DetermineLibNameVariations(const char* const** libNameVariations, int* numberOfVariations, const SString& libName, bool libNameIsRelativePath) | ||
| { | ||
| bool endWithDot = libName.EndsWith(W(".")) == TRUE; |
There was a problem hiding this comment.
It is a bad practice to compare BOOL to TRUE. BOOL is actually integer and any non-zero value can mean true. TRUE is defined as 1. So comparing BOOL to TRUE assumes that the non-zero true value is 1 that is not always the case.
Use either !!libName.EndsWith(W(".")) or libName.EndsWith(W(".")) != FALSE.
There was a problem hiding this comment.
Thanks for this. Previous I had searched the implementation for SString.EndWith and it always return TRUE or FALSE. I will use !! instead.
Thanks a lot for this info. I saw this issue but didn't realize that this is file encoding issue. |
|
|
|
||
| public class FileExtensionProbe | ||
| { | ||
| private static int Fails = 0; |
|
@jkotas, is there a chance to include this in 2.1 release? |
src/vm/dllimport.cpp
Outdated
| #else // FEATURE_PAL | ||
| static void DetermineLibNameVariations(const char* const** libNameVariations, int* numberOfVariations, const SString& libName, bool libNameIsRelativePath) | ||
| { | ||
| bool endWithDot = !!libName.EndsWith(W(".")); |
There was a problem hiding this comment.
Now that I am looking at this again ... caching these in bool variables makes this less efficient than it needs to be. If you put these directly into the if statement without caching them in locals like this:
if (!libName.Find(libName.Begin(), W('.')) ||
libName.EndsWith(W(".")) ||
libName.EndsWithCaseInsensitive(W(".dll")) || libName.EndsWithCaseInsensitive(W(".exe"))
{
It will be more efficient because of it will stop at !libName.Find(libName.Begin(), W('.')) for names without dot; and the remaining two statements won't be executed at all.
It also avoids the !! wart.
In 2.0 and older versions, we are using build time conditions: #if WINNT
[DllImport("shared.library.dll")]
#else
[DllImport("shared.library")]
#endifWith this change, it will be reduced to |
src/vm/dllimport.cpp
Outdated
| libName.EndsWith(W(".")) || | ||
| libName.EndsWithCaseInsensitive(W(".dll")) || | ||
| libName.EndsWithCaseInsensitive(W(".exe")) || | ||
| !libNameIsRelativePath) |
There was a problem hiding this comment.
libNameIsRelativePath should be first because of it is the cheapest condition to evaluate?
There was a problem hiding this comment.
Also, is the relative path condition really correct here? libNameIsRelativePath is going to be true for a paths like:
XXX\YYY\library
or
\XXX\YYY\library.
On Unix, we will end up trying paths like:
libXXX\YYY\library.so
or
lib\XXX\YYY\library.so.
There was a problem hiding this comment.
Move libNameIsRelativePath to the first one.
is the relative path condition really correct here?
This is incorrect. since this is an old issue and I will create another issue to track and solve this problem
src/vm/dllimport.cpp
Outdated
| { | ||
| LPCWSTR currLibNameVariation = wszLibName; | ||
| #endif | ||
| currLibNameVariation.Printf(prefixSuffixCombinations[i], PLATFORM_SHARED_LIB_PREFIX_A, name, PLATFORM_SHARED_LIB_SUFFIX_A); |
There was a problem hiding this comment.
We have converted the name from UTF8 to Unicode above once already.
The PrintF is going to convert it again; but I do not think it is going to use UTF8 on Windows. I think this needs to use the converted name to Unicode wszLibName; not the UTF8 name.
There was a problem hiding this comment.
Update all of these argument to WCHAR* instead of char*
There seems to be way too many subtle issues in this change. I do not think we should be considering this for 2.1 release. |
|
LGTM |
|
@jkotas, if merge here indicates that we had confidence in implementation, is it now an good candidate for 2.1 if it is sent it to the shiproom for review? |
|
master is open for 2.2 now. Merge means that we believe it is a good change for 2.2. It would be up to @luqunl to present it to the shiproom to consider it for 2.1. My guess is that it would not meet the bar. |
|
This file introduced corrupt line endings https://github.com/dotnet/coreclr/pull/17505/files#diff-23d6cee28d9638de3cc99b9da9fbb3b9 |
Currently, In Windows, if the name your native dll specified in DllImport contains '.', such as Test.Foo, DllImport will only search for file Test.Foo and it won't search for file Test.Foo.dll
The fix is to append ".dll" if the native dll name specified in DllImport doesn't contain ".dll" .
Fix #17150