In my efforts to create a progress indicator for uploading videos using HttpClient (SendAsync) in Xamarin Forms, I now have to ask for assistance.
The upload itself works fine, and all other API calls, but when I try to create a custom HttpContent to track the progress of the upload the project won't even build any more.
Error MT3001: Could not AOT the assembly
'[...].iOS/obj/iPhone/Debug/build-iphone7.2-10.1.1/mtouch-cache/Build/theproject.dll'
(MT3001) (theproject.iOS)
Using StreamContent or ByteArrayContent instead the project builds, but I can't get it working to track the progress.
A snippet of code (this is minimal example):
public class ProgressableContent : HttpContent
{
private const int defaultBufferSize = 4096;
private Stream content;
private int progress;
public ProgressableContent(Stream content)
{
this.content = content;
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
return Task.Run(async () =>
{
var buffer = new byte[defaultBufferSize];
var size = content.Length;
var uploaded = 0;
using (content) while (true)
{
var length = content.Read(buffer, 0, buffer.Length);
if (length <= 0) break;
uploaded += length;
progress = (int)((float)uploaded / size * 100);
await stream.WriteAsync(buffer, 0, length);
}
});
}
protected override bool TryComputeLength(out long length)
{
length = content.Length;
return true;
}
}
I use this by transforming my byte's to a stream, hopefully correctly:
//... building httpMessage.
httpMessage.Content = new ProgressableContent(await byteArrayContent.ReadAsStreamAsync());
//...
var response = await _httpClient.SendAsync(httpMessage, Cancellation.Token);
//...
The question(s):
Am I somehow causing the error? Is there a "better" way to do this?
Tagged this with Xamarin.iOS also since monotouch is complaining.
Double-click on the error from XS and it should bring you to a web page that provide more description about the issue. E.g.
MT3001 Could not AOT the assembly '*'
This generally indicates a bug in the AOT compiler. Please file a bug
http://bugzilla.xamarin.com with a project that can be used to
reproduce the error.
Sometimes it's possible to work around this by disabling incremental
builds in the project's iOS Build option (but it's still a bug, so
please report it anyways).
The main thing about 3001 is that the AOT compiler did not produce an output binary. There can be several reasons for this. Generally the process crashed and the build logs will give a bit more details why.
Even more important is to attach a self-contained test case to the bug report. Something else, beside the code you pasted, can be playing an important part that led to the crash (and it could be impossible to duplicate or guess what that piece could be). That also gives us a better chance to suggest a workaround to the issue.
Related
I have a NET 5.0 console application, from which I am trying to compile and execute external code BUT also be able to update the code, unload the previously created appdomain and re-compile everything.
This is my entire static class that handles code compilation and assembly loading
using System;
using System.IO;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Reflection;
using Microsoft.CodeAnalysis.Emit;
using System.Runtime.Loader;
namespace Scripting
{
public static class ScriptCompiler
{
public static Dictionary<string, AppDomain> _appDomainDict = new();
public static object CompileScript(string scriptpath)
{
var tree = SyntaxFactory.ParseSyntaxTree(File.ReadAllText(scriptpath));
//Adding basic references
List<PortableExecutableReference> refs = new List<PortableExecutableReference>();
var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "mscorlib.dll")));
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.dll")));
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Private.CoreLib.dll")));
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Core.dll")));
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Runtime.dll")));
// A single, immutable invocation to the compiler
// to produce a library
string hash_name = scriptpath.GetHashCode();
if (_appDomainDict.ContainsKey(hash_name))
{
AppDomain.Unload(_appDomainDict[hash_name]);
_appDomainDict.Remove(hash_name);
}
AppDomain new_domain = AppDomain.CreateDomain(hash_name);
_appDomainDict[hash_name] = new_domain;
var compilation = CSharpCompilation.Create(hash_name)
.WithOptions(
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release,
allowUnsafe:true))
.AddReferences(refs.ToArray())
.AddSyntaxTrees(tree);
MemoryStream ms = new MemoryStream();
EmitResult compilationResult = compilation.Emit(ms);
ms.Seek(0, SeekOrigin.Begin);
if (compilationResult.Success)
{
// Load the assembly
Assembly asm = new_domain.Load(ms.ToArray());
object main_ob = asm.CreateInstance("SomeClass");
ms.Close();
return main_ob;
}
else
{
foreach (Diagnostic codeIssue in compilationResult.Diagnostics)
{
string issue = $"ID: {codeIssue.Id}, Message: {codeIssue.GetMessage()}," +
$" Location: { codeIssue.Location.GetLineSpan()}," +
$" Severity: { codeIssue.Severity}";
Callbacks.Logger.Log(typeof(NbScriptCompiler), issue, LogVerbosityLevel.WARNING);
}
return null;
}
}
}
}
Its all good when I am trying load the assembly in the current domain and execute from the instantiated object. The problem with this case is that since I wanna do frequent updates to the code, even if I make sure that the assembly names are different. I'll end up loading a ton of unused assemblies to the current domain.
This is why I've been trying to create a new domain and load the assembly there. But for some reason I get a platform not supported exception. Is this not possible to do in NET 5? Are there any workarounds or am I doing something wrong here.
Ok, it turns out that AppDomain support for NET Core + is very limited and in particular there seems to be only one appdomain
On .NET Core, the AppDomain implementation is limited by design and
does not provide isolation, unloading, or security boundaries. For
.NET Core, there is exactly one AppDomain. Isolation and unloading are
provided through AssemblyLoadContext. Security boundaries should be
provided by process boundaries and appropriate remoting techniques.
Source: https://learn.microsoft.com/en-us/dotnet/api/system.appdomain?view=net-6.0
And indeed, when trying to use AssemblyLoadContext and create object instances through these contexts everything worked like a charm!
One last note is that if the created context is not marked as collectible, its not possible to unload it. But this can be very easily set during AssemblyLoadContext construction.
So I'm currently writing an extension for VS using their extensibility API, and I've run into a bit of a snag. I need some kind of event (or other solution) that triggers when you tab between files, and when that happens I need to get the path to the new active document.
The problem is everything in the API that I have tried either fires at the wrong time, or when it is called, the current active document hasn't been updated yet so when I get the path it's still the file I just tabbed out of.
I've tried implementing the IVsRunningDocTableEvents interface, I've tried using the Events.WindowEvents.WindowActivated event, nothing so far has worked correctly.
EditorClassifier1Provider.myEnvDTE.DTE.Events.WindowEvents.WindowActivated += WindowEvents_WindowActivated;
class RdtEvents : IVsRunningDocTableEvents
{
public RdtEvents()
{
ThreadHelper.ThrowIfNotOnUIThread();
var rdt = (IVsRunningDocumentTable)Package.GetGlobalService(typeof(IVsRunningDocumentTable));
rdt.AdviseRunningDocTableEvents(this, out _);
}
int IVsRunningDocTableEvents.OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame)
{
OpenConnectionCommand.docView = VS.Documents.GetActiveDocumentViewAsync().Result;
Message.Send(null, null);
OpenConnectionCommand.docView.Document.DirtyStateChanged += (s,e) => Message.Send();
OpenConnectionCommand.docView.TextView.Selection.SelectionChanged += (s,e) => Message.Send();
return Microsoft.VisualStudio.VSConstants.S_OK;
}
//...etc
Using Xamarin, I'd like to use an AVAudioSinkNode to store and eventually transfer incoming audio data from a mic at the lowest latency possible (without going straight into AudioUnits and the deprecated AUGraphs). See my commented code below where the SinkNode is connected to the default InputNode. It's giving me grief. I'm using Xamarin.Forms with a simple iOS dependency class. I can successfully hook up an InputNode through an fx node (Reverb for example) and on out to the OutputNode. In this case, I've minimized my code down to focus on the problem at hand:
public unsafe class AudioEngine : IAudioEngine
{
AVAudioEngine engine;
AVAudioInputNode input;
AVAudioSinkNode sink;
public AudioEngine()
{
ActivateAudioSession();
}
protected void ActivateAudioSession()
{
var session = AVAudioSession.SharedInstance();
session.SetCategory(AVAudioSessionCategory.Playback, AVAudioSessionCategoryOptions.DuckOthers);
session.SetActive(true);
session.SetPreferredIOBufferDuration(0.0007, out error); // 32 byte buffer, if possible!
engine = new AVAudioEngine();
input = engine.InputNode; // to save on typing
input.Volume = 0.5f;
var format = input.GetBusInputFormat(0); // used for fx connections, but not used in this snippet. If I use this in the Input -> Sink connection, it crashes.
sink = new AVAudioSinkNode(sinkReceiverHandler);
engine.AttachNode(sink);
try
{
//-----------------------------------------------------
// Param #3 (format) is nil in all the Apple Documentation and multiple examples
// In place of nil, **NSNull.Null** isn't accepted.
// In place of nil, **null** throws a System.NullReferenceException. (see stack dump)
// In place of nil, using the **InputNode's format** crashes with
// something about missing the Trampolines.g.cs file... no clue...
engine.Connect(input, sink, **null**); // null doesn't work in place of nil.
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace); // Exception messages included below
}
engine.Prepare();
engine.StartAndReturnError(out error);
}
private unsafe int sinkReceiverHandler(AudioToolbox.AudioTimeStamp timeStamp, uint frames, ref AudioToolbox.AudioBuffers inputData)
{
// Do stuff with the data...
return 0;
}
}
I found a post related to the use of nil as a parameter in Xamarin.iOS that says the author of the library needs to include the [NullAllowed] argument:
How to assign something to nil using Xamarin.iOS
My question is: Am I missing something obvious, or is this an oversight in the Xamarin library definition? I always assume it's my lack of expertise, but if this is a bug, how do I go about reporting it to Xamarin?
A follow up question: If this is a glitch, is there a viable workaround? Can I go in and tweak the Xamarin library definition manually? (which would break on any updates, I'm sure.) Or can I make a little library using Swift which I then import into my Xamarin project?
Just trying to think of options. Thanks for reading! Below is the Stack dump when I use null as a substitute for nil (again... NSNull.Null isn't considered a valid type in this case. It just doesn't compile):
{System.NullReferenceException: Object reference not set to an instance of an object
at AVFoundation.AVAudioFormat.op_Equality (AVFoundation.AVAudioFormat a, AVFoundation.AVAudioFormat b) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.3.2/src/Xamarin.iOS/AVFoundation/AVAudioFormat.cs:27
at AVFoundation.AVAudioEngine.Connect (AVFoundation.AVAudioNode sourceNode, AVFoundation.AVAudioNode targetNode, AVFoundation.AVAudioFormat format) [0x00024] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.3.2/src/Xamarin.iOS/AVAudioEngine.g.cs:120
at udptest.iOS.AudioEngine.ActivateAudioSession () [0x0009b] in /Users/eludema/dev/xamarin/udptest/udptest.iOS/AudioEngine.cs:43 }
THANKS!
This has been confirmed as a bug: The format parameter is [NullAllowable], but the current code to actually process that null wasn't linked up in the wrapper code. Here's the issue tracker on the Xamarin.Mac/iOS github repo:
https://github.com/xamarin/xamarin-macios/issues/9267
Thanks for the github issue submission link, #SushiHangover!
I have a large proces that I need to debug and the proces could stop at anytime. I have configured Visual Studio 2017, to stop at any thrown exception, as in, even if it is handled, because I want to see what caused the exception. What I need is some sort of alarm when this happens, so that I can leave the program to run and then alert me if anything comes up. The only thing I have found is an alarm sound when a break point is hit, but it might not be a break point and I need more than a sound, I need to be able to execute some code, so that I can make my Phone go nuts or whatever. Is there any way I can trigger code when the debugger enters break mode?
Thanks in advance.
It is, using a VS package. You'll need to add this attribute on top of the class in order for code to run on package startup:
[ProvideAutoLoad(Microsoft.VisualStudio.Shell.Interop.UIContextGuids80.SolutionExists)] ///Able to run code on solution startup
Add these class values variables:
private DTE2 applicationObject;
private BuildEvents buildEvents;
private DebuggerEvents debugEvents;
then the following code can run:
protected override void Initialize()
{
base.Initialize();
applicationObject = (DTE2)GetService(typeof(DTE));
buildEvents = applicationObject.Events.BuildEvents;
debugEvents = applicationObject.Events.DebuggerEvents;
SetupEventHandlers();
}
And finally the code we have "all" being waiting for:
private void SetupEventHandlers()
{
//buildEvents.OnBuildDone += (scope, action) =>
//{
//};
debugEvents.OnEnterBreakMode += delegate (dbgEventReason reason, ref dbgExecutionAction action)
{
};
//var componentModel =
// GetGlobalService(typeof(SComponentModel)) as IComponentModel;
//if (componentModel == null)
//{
// Debug.WriteLine("componentModel is null");
// return;
//}
//var operationState = componentModel.GetService<IOperationState>();
//operationState.StateChanged += OperationStateOnStateChanged;
}
Sooo...
I have written a plugin, and the whole plugin works fine.
ONLY PROBLEM:
My TS3 Client crashes.
To give a context:
(Warning: That code is just poorly pasted. On GitHub, it crashes at line 270 and 285)
// Helper Function
String^ getChannelName(uint64 serverConnectionHandlerID, uint64 channelID) {
char* tmp;
if (ts3Functions.getChannelVariableAsString(serverConnectionHandlerID, channelID, CHANNEL_NAME, &tmp) == ERROR_ok) {
return marshal_as<String^>(tmp);
}
else
{
return "ERROR_GETTING_CHANNELNAME";
}
}
void assemble_a() {
List<String^>^ clients;
List<String^>^ channel;
// Some middlepart here, but I made sure it works as it should
// And the actual part where it is crashing
if (resChL == ERROR_ok) {
for (int i = 0; channelListPtr[i]; ++i) {
String^ a = getChannelName(schid, channelListPtr[i]);
const char* b = (const char*)(Marshal::StringToHGlobalAnsi(a)).ToPointer();
ts3Functions.logMessage(b, LogLevel_DEBUG, "DEBUG_VC", schid);
if (String::IsNullOrEmpty(a) == false) {
channel->Add(a); // It crashes RIGHT at this point
}
}
}
}
So I am asking on the TS3 forum for a long time, got a lot of answers, and noone could tell me why it actually crashes, and I didn't manage to figure it out on my own either.
It does actually print the channel name [*spacer0]t but as soon as it should append it to the String List, it crashes.
It throws the message The thread has tried to write or read from a virtual address that it does not have the accesspermissions for.
I seriously have no idea what to do, trying to fix it now for over 2 weeks.
For full context: GitHub Sourcecode
Sorry if this question MIGHT be a little out of topic here (Is it? I don't know...) but I don't really know what to do with that problem anymore...
EDIT:
Errormessage from try/catch is:
System.NullReferebceException: The Objectreference was not set to the Objectinstance, occured in tsapi.assembleGrammar()
List<String^>^ channel;
...
channel->Add(a);
channel is null. You need to initialize it with something, probably gcnew List<String^>(). I'm not sure why you're getting an access denied message instead of NullReferenceException.
Other issues
Make sure you're handling all the unmanaged strings properly. For example, does getChannelVariableAsString require a call to explicitly free the buffer? Be absolutely sure to call FreeHGlobal to free the memory that StringToHGlobalAnsi allocated for you.