parse-community / Parse-SDK-dotNET

Parse SDK for .NET, Xamarin, Unity.
http://parseplatform.org
Apache License 2.0
323 stars 260 forks source link

Xamarin project error System.EntryPointNotFoundException: GetModuleFileName #300

Closed jjhii closed 4 years ago

jjhii commented 5 years ago

When I add from the NuGet, I get the following error when attempt to retrieve data. Using the standard release for .NET Core, the same code works fine. I posted the issue on stack Overflow.

https://stackoverflow.com/questions/53683871/using-parse-netstandard2

[0:] System.AggregateException: One or more errors occurred. ---> System.EntryPointNotFoundException: GetModuleFileName at (wrapper managed-to-native) StandardStorage.StorageUtilities.GetModuleFileName(System.Runtime.InteropServices.HandleRef,System.Text.StringBuilder,int) at StandardStorage.StorageUtilities.GetModuleFileNameLongPath (System.Runtime.InteropServices.HandleRef hModule) [0x00042] in <492e9128255c4fa2885761586862f4f3>:0 at StandardStorage.StorageUtilities.get_ExecutablePath () [0x00021] in <492e9128255c4fa2885761586862f4f3>:0 at StandardStorage.StorageUtilities.GetAppFileVersionInfo () [0x00046] in <492e9128255c4fa2885761586862f4f3>:0 at StandardStorage.StorageUtilities.get_CompanyName () [0x00042] in <492e9128255c4fa2885761586862f4f3>:0 at StandardStorage.StorageUtilities.GetAppSpecificStoragePathFromBasePath (System.String basePath) [0x00000] in <492e9128255c4fa2885761586862f4f3>:0 at StandardStorage.FileSystem.get_LocalStorage () [0x00007] in <492e9128255c4fa2885761586862f4f3>:0 at Parse.Common.Internal.StorageController+<>c.<.ctor>b51 (System.Threading.Tasks.Task ) [0x00006] in :0 at System.Threading.Tasks.ContinuationResultTaskFromTask`1[TResult].InnerInvoke () [0x00024] in :0 at System.Threading.Tasks.Task.Execute () [0x00000] in :0 --- End of stack trace from previous location where exception was thrown --- at Parse.Common.Internal.InternalExtensions+<>cDisplayClass7_01[TResult].<OnSuccess>b__0 (System.Threading.Tasks.Task t) [0x0003c] in <bafb02353d284fa488514259b803efcb>:0 at System.Threading.Tasks.ContinuationResultTaskFromTask1[TResult].InnerInvoke () [0x00024] in :0 at System.Threading.Tasks.Task.Execute () [0x00000] in :0 --- End of inner exception stack trace --- at System.Threading.Tasks.Task.ThrowIfExceptional (System.Boolean includeTaskCanceledExceptions) [0x00011] in :0 at System.Threading.Tasks.Task.Wait (System.Int32 millisecondsTimeout, System.Threading.CancellationToken cancellationToken) [0x00043] in :0 at System.Threading.Tasks.Task.Wait () [0x00000] in :0 at Parse.ParseUser.get_CurrentUser () [0x00007] in :0 at Parse.ParseQuery1[T].FirstOrDefaultAsync (System.Threading.CancellationToken cancellationToken) [0x0000d] in <bafb02353d284fa488514259b803efcb>:0 at Parse.ParseQuery1[T].FirstOrDefaultAsync () [0x00006] in :0 at TheBabyQuoteApp.Views.MainPage..ctor () [0x0001b] in D:\WebSites\TheBabyQuote_Projects\TheBabyQuoteApp\TheBabyQuoteApp\TheBabyQuoteApp\Views\MainPage.xaml.cs:22 ---> (Inner Exception #0) System.EntryPointNotFoundException: GetModuleFileName at (wrapper managed-to-native) StandardStorage.StorageUtilities.GetModuleFileName(System.Runtime.InteropServices.HandleRef,System.Text.StringBuilder,int) at StandardStorage.StorageUtilities.GetModuleFileNameLongPath (System.Runtime.InteropServices.HandleRef hModule) [0x00042] in <492e9128255c4fa2885761586862f4f3>:0 at StandardStorage.StorageUtilities.get_ExecutablePath () [0x00021] in <492e9128255c4fa2885761586862f4f3>:0 at StandardStorage.StorageUtilities.GetAppFileVersionInfo () [0x00046] in <492e9128255c4fa2885761586862f4f3>:0 at StandardStorage.StorageUtilities.get_CompanyName () [0x00042] in <492e9128255c4fa2885761586862f4f3>:0 at StandardStorage.StorageUtilities.GetAppSpecificStoragePathFromBasePath (System.String basePath) [0x00000] in <492e9128255c4fa2885761586862f4f3>:0 at StandardStorage.FileSystem.get_LocalStorage () [0x00007] in <492e9128255c4fa2885761586862f4f3>:0 at Parse.Common.Internal.StorageController+<>c.<.ctor>b51 (System.Threading.Tasks.Task ) [0x00006] in :0 at System.Threading.Tasks.ContinuationResultTaskFromTask`1[TResult].InnerInvoke () [0x00024] in :0 at System.Threading.Tasks.Task.Execute () [0x00000] in :0 --- End of stack trace from previous location where exception was thrown --- at Parse.Common.Internal.InternalExtensions+<>cDisplayClass7_01[TResult].<OnSuccess>b__0 (System.Threading.Tasks.Task t) [0x0003c] in <bafb02353d284fa488514259b803efcb>:0 at System.Threading.Tasks.ContinuationResultTaskFromTask1[TResult].InnerInvoke () [0x00024] in :0 at System.Threading.Tasks.Task.Execute () [0x00000] in :0 <---

TheFanatr commented 5 years ago

This issue is present because the .NET Standard 2.0 version of the Parse SDK tries to provide a single, mostly platform-agnostic, implementation of all the APIs Parse provides. One of those APIs attempts to cache data to the device that the code is running on. The problem is that on most platforms, the persistent storage location is somewhere public, so anything stored needs to be named according to the application being run; unfortunately, the way that that name is extracted from the environment is not infallible, as in certain cases it requires being able to find the executing assembly file via reflection, and in other cases requires the ability to reflectively analyze said assembly, which is not always possible on all platforms. The latest version of the NuGet package, and the master branch of this repository has yet to reflect the progress being made on that front. You can try to build my fork of this project from source and set the name manually if still needed.

BillNewton-fxPress commented 5 years ago

Hi I have had a similar problem to the above. I have downloaded your Fork and compiled it and added the libraries to my project (Parse2.0.0-develop-00120180926.

I am initializing Parse in the AppDelegate and MainActivity.cs

    void InitialiseParse(){
        //Parse Settings
        try
        {

            ParseClient.Initialize(new ParseClient.Configuration
            {
                ApplicationID = Constants.K_Parse_App_ID,
                ServerURI = Constants.K_Parse_Server
            });
            Console.WriteLine("Parse Initialised");
        }
        catch (Exception ex)
        {
            Console.WriteLine("Parse Initialise error");
            Console.WriteLine(ex.Message);
        }
    }

Seems to work OK on IOS but on Android I get the following error.

{System.TypeInitializationException: The type initializer for 'VersionInformation' threw an exception. ---> System.NullReferenceException: Object reference not set to an instance of an object. at Parse.ParseClient+Configuration+VersionInformation..cctor () [0x0000f] in :0 --- End of inner exception stack trace --- at Parse.ParseClient.Initialize (Parse.ParseClient+Configuration configuration) [0x00033] in :0 at fx_tools.Droid.MainActivity.InitialiseParse () [0x00002] in /Users/Bill/Projects/fx_tools/fx_tools.Android/MainActivity.cs:42 }

TheFanatr commented 5 years ago

Hello @BillNewton-fxPress, the reason that there is still an error in your project is because, other than fixing a few issues, my SDK does not necessarily behave differently than the master branch of this repository upon initialization; my fork, among other things, allows you to specifically set some metadata details which are needed for project-specific data caching, so that the SDK does not have to infer them, as inferring sometimes does not work. There are two options to use this feature, the first one, based on company and product metadata, is as follows.

new ParseClient.Configuration
{
    /* Other Properties */

    StorageConfiguration = new ParseClient.Configuration.MetadataBasedStorageConfiguration 
    {
        CompanyName = "<company name here>",
        ProductName = "<product name here>"
    }
}

The other, based on simply an identifier, is as follows. Note that the project identifier can be anything you want, as long as it is the same every time the project launches.

new ParseClient.Configuration
{
    /* Other Properties */

    StorageConfiguration = new ParseClient.Configuration.IdentifierBasedStorageConfiguration { Identifier = "<project identifier>" }
}
TheFanatr commented 5 years ago

If that still does not work, you can try setting the VersionInfo property on the Configuration struct to the version of your application, so that it does not need to be inferred either, in case that is the problem. Please ask if you need more direction on how to do this.

sidiabale commented 5 years ago

I'm facing this same problem as mentioned above. I',m considering using Parse as backend for a Xamarin.Forms project I'm working on but it's not clear to me at this point what the state/future of this SDK is :(

On NuGet, I see the Parse.NETStandard2 package which seems to be a fork of @TheFanatr fork. However, I don't see an official release 2.0.0 matching that package here. Moreover, in this thread, I see that there are some updates/new features like the configuration parameter that are still in development?

@TheFanatr From what I gather, you've done some significant work on reviving this SDK which is highly appreciated. Could you please clarify what the short term plans are and how soon a working version (including updated documentation) will be released? I'm also curious if push notifications are supported in your version (at least for Android and iOS)... If not, any plans to get that working? If possible, please create a page where the current status is explained. I think that will also encourage others to participate. I for one would like to chip in if possible though I must say my C# knowledge is currently limited. But that's something I'm working on :)

sidiabale commented 5 years ago

An update: In the meantime, I figured out that all calls Assembly.GetEntryAssembly()... cause a NullReferenceException as mentioned by @BillNewton-fxPress above. A quick search online suggests that getting platform-specific data such as app name and version info requires native code but I'm not 100% sure if we can work around that via the Assembly.

Anyway my primary finding is that the code to infer version information is currently not working properly (tested on Android using a Xamarin.Forms sample project).

As per push notifications, I've succeeded in creating an installation object in Parse which is one of the basic building blocks for push. I expect some additional configuration would be needed in platform-specific code...

TheFanatr commented 5 years ago

Hi @sidiabale, as you have pointed out, the code that infers metadata about the project using reflection does not always execute successfully; that is a side effect of how certain platforms compile the application that is using the Parse SDK, and/or a bug in those platforms' implementations of certain .NET Standard 2.0 APIs. I must however stress that this is not always the case; the code used to infer project metadata does work in certain environments, and is not broken, but rather does not yield expected results in certain circumstances, causing exceptions. The NuGet package called Parse.NETStandard2 is some derivative work of the master branch of this repository as far as I know, and not based on my fork; however, I am unaware of who posted it, thus have not asked them, and thus I am not fully aware of it's composition. The official package has not yet been updated because the work being done on the SDK for version 2.0.0 is incomplete, so releasing a package to NuGet could be irresponsible because people may want to treat the in-development version as if it were a final product, and actually use it for production, which could yield unfortunate consequences. For example, compiling the master branch of this repository will yield a Parse SDK that will not properly respect chained query restrictions, meaning that querying will appear that it is working correctly, but will return invalid results. I have fixed that particular issue in my fork, but there may be others. When I feel that enough bugs have been fixed, and the SDK has been tested enough, I can ask to have a preview version released to the official NuGet, but until then, I would be hesitant. What my fork adds, other than a few bug fixes, is mainly the removal of external dependencies, and the addition of properties in the ParseClient.Configuration structure that allow SDK users to specify certain metadata details that would normally need to be inferred, so that the SDK can be used on platforms where the inferring process would complete unsuccessfully. The future plan is to have much of platform specific behaviours be available to be implemented by SDK users when configuring the Parse SDK, so that the SDK can be used on any platform that implements .NET Standard 2.0, and be able to behave properly for that platform. I also plan to have Parse-provided implementations for such features for major platforms; this is how push notification support will be available eventually. What I wanted to move away from by refactoring the project into .NET Standard was writing base features differently for every platform, not only because it would eventually get unmanageable, but also because it would allow the SDK to work with any compliant platform in theory, and would mostly eradicate the ceremony around adding or removing features. The issue with this approach is that it relies on the platform's implementation of .NET Standard APIs to actually work properly and/or as expected, but this is not always the case, especially with reflection APIs, which is why I have created a way to specify certain details and bypass the usage of those APIs. As time goes along, I expect that there will be fewer and fewer failures on a diminishing subset of platforms.

TheFanatr commented 5 years ago

I also plan to eventually make Parse compatible with server-side environments by making certain APIs instantiable instead of static, allowing for code patterns that more easily deal with multiple logged-in users, and other necessities for this use case.

flovilmart commented 5 years ago

@TheFanatr you refer to your fork, do you plan to take over this repository or should we close it and say that you maintain now a .Net SDK?

TheFanatr commented 5 years ago

I'd be happy to take over if that is an option; I was working out of a fork because I only recently gained commit access to this repository. I have a pull request to put all my changes into master, which I am leaving up for around 24 hours, but afterwards I can simply work out of feature branches. I'd also appreciate access to the repository settings if I will be taking over so I can change up the services (such as substituting Azure Pipelines instead of AppVeyor), and the description and tags, contributors, etc. but I'd understand if that is not possible/wanted. Happy to help with this project. Thanks!

sidiabale commented 5 years ago

@TheFanatr Thanks for the clarification! Also glad to see that there's effort to move forward with this repository. Speaking about the Parse.NETStandard2 that's on NuGet. I suspect it was created by JonMcPherson based on a fork of this repo (with your changes at that point). I just sent him the following message via NuGet:

Hi Jon,

I'm contacting you regarding your NuGet package: https://www.nuget.org/packages/Parse.NETStandard2/2.0.0. I was excited when I came across it but further investigations suggests that this package isn't really an Official packaged provided by the Parse community as the info on the NuGet page suggests. There's currently an ongoing discussion to revive the .NET Parse SDK and proceed via the official project (see for instance this thread: https://github.com/parse-community/Parse-SDK-dotNET/issues/300).

May I ask you to update the description of your NuGet package to clearly reflect that it is not the official Parse SDK for .NET and also update other information like author, URL, etc.? Of course, it would be great if you can join in the efforts to get the Parse SDK up and running again via the official community repo.

Thanks in advance for your cooperation.

As I already mentioned, I'm willing to chip in as much as I can provided there's some clarity and focus (which we're working towards at the moment). In the initial stage, I suppose I'll focus on reviewing and testing while I up my C# knowledge. By the way, I previously worked on a Parse port for CodenameOne called parse4cn1 which included "cross-platform" push support. So I have some relevant experience with Parse :D

BillNewton-fxPress commented 5 years ago

@TheFanatr Thanks for the update. I am indeed having problems setting the VersionInformation property on the configuration struct. My init function in Mainactivity.cs is now

    void InitialiseParse(){
        //Parse Settings
        try
        {
            var storageConfiguration = new ParseClient.Configuration.IdentifierBasedStorageConfiguration { Identifier = context.PackageName };

            var versionInfo = new ParseClient.Configuration.VersionInformation
            {
                BuildVersion = Xamarin.Essentials.AppInfo.BuildString,
                DisplayVersion = Xamarin.Essentials.AppInfo.VersionString
            };
            // ********   Error gets thown here   *********
            ParseClient.Initialize(new ParseClient.Configuration
            {
                ApplicationID = Constants.K_Parse_App_ID,
                ServerURI = Constants.K_Parse_Server,
                StorageConfiguration = storageConfiguration,
                VersionInfo = versionInfo
            });
            Console.WriteLine("Parse Initialised");
        }

        catch (Exception ex)
        {
            Console.WriteLine("Parse Initialise error");
            Console.WriteLine(ex.Message);
            Console.WriteLine(ex.InnerException.Message);
        }
    }

but get ......... Parse Initialise error The type initializer for 'VersionInformation' threw an exception. Object reference not set to an instance of an object.

Any pointers where I am slipping up here would be very welcome.

Thanks

TheFanatr commented 5 years ago

Hi @BillNewton-fxPress, I will look into this as it seems like you are doing everything correctly yet it is still throwing an exception. Is this only working on iOS but not Android as mentioned previously? Could you please post a stack trace? I am guessing the exclusion of the Key property is intentional; could you please try providing one, as the Parse SDK may currently be incompatible with that.

BillNewton-fxPress commented 5 years ago

Hi Alex Thanks for looking at this. I am running VS2017 for Mac on MacOS Mojave. It does work fine on iOS but errors on Android. It does seem to be the reflection issue mentioned above by @sidiabale. Adding the Key does not make any difference. Any call to Assembly.GetEntryAssembly() throws an error. I will continue experimenting but not done c# for many years. Most recently worked with Objective C and Swift. Thought I would look to port an existing iOS app but it's proving to be an uphill struggle :-)

Thanks again

Bill

BillNewton-fxPress commented 5 years ago

Alex

I have had some success. I am not sure about any knock effects as I am not that familiar with c# yet. However the changes I made were to amend the Struct Initializers for the config to use Assembly.GetCallingAssembly() rather than Assembly.GetEntryAssembly(). I rebuilt the library and updated my project. Early days but it seems to work now on both iOS and Android.

            //public static VersionInformation Inferred { get; } = new VersionInformation { 
                //BuildVersion = Assembly.GetEntryAssembly().GetName().Version.Build.ToString(), 
                //DisplayVersion = Assembly.GetEntryAssembly().GetName().Version.ToString(), 
                //OSVersion = Environment.OSVersion.ToString() };

            public static VersionInformation Inferred { get; } = new VersionInformation
            {
                BuildVersion = Assembly.GetCallingAssembly().GetName().Version.Build.ToString(),
                DisplayVersion = Assembly.GetCallingAssembly().GetName().Version.ToString(),
                OSVersion = Environment.OSVersion.ToString()
            };

Also GetExecutingAssembly works but not sure of the subtleties of each yet. Hope this helps - let me know If I am heading in the wrong direction :-)

Thanks

Bill

TheFanatr commented 5 years ago

Hi Bill, thanks for your contributions. I actually came a cross a similar solution, but what you have should work for you for now; GetCallingAssembly will return the assembly that called the SDK, which is not always ideal in the case of a library that use Parse, because then it will always return its name, not the name of the project using the library. It turns out, however, that a lot of that code may not be needed because it is already somewhat specified in StorageConfiguration. I will push and update soon that tries to fix this issue.

TheFanatr commented 5 years ago

This issue with GetEntryAssembly I believe is a side effect of how Xamarin handles app launches, where the entry assembly is inaccessible via reflection because it is native, but I'm not completely sure.

joechihe commented 5 years ago

Hi guys, is there any update on this? Or a Fork I can use? This is really blocking issue, but maybe is there any workaround we can take?

TobiasPott commented 4 years ago

This issue should be solved using the latest version of the SDK from the master branch and the new option to change the SDK behaviour using mutators. If you have any problem doing so or encounter error, feel free to create a new issue to discuss possible solutions.