How do you call a method from a UI thread? - c#

I am trying to access the Microsoft Store from a multi-threaded program. The program itself work well - no problems there, but the method
Windows.Foundation.IAsyncOperation<StorePurchaseResult> purchase = product.RequestPurchaseAsync();
throws the exception:
"Invalid window handle.\r\n\r\nThis function must be called from a UI
thread"
My code is as follows...
private void testButton_Click(object sender, EventArgs e)
{
//Dispatcher dispUI = Dispatcher.CurrentDispatcher;
//dispUI.BeginInvoke((ThreadStart)delegate ()
//{
// _ = SetStore();
//});
//Dispatcher.BeginInvoke(new Action(TestStuff));
_ = SetStore();
}
[ComImport]
[Guid("4AEEEC08-7C92-4456-A0D6-1B675C7AC005")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IInitializeWithWindow
{
void Initialize(IntPtr hwnd);
}
private async Task SetStore()
{
try
{
StoreContext theStore = StoreContext.GetDefault();
// "Unable to cast object of type 'Windows.Services.Store.StoreContext' to type 'IInitializeWithWindow'."
// IInitializeWithWindow initWindow = (IInitializeWithWindow)(object)theStore;
// initWindow.Initialize(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle);
StoreProductResult app = await theStore.GetStoreProductForCurrentAppAsync();
// This works...
StoreProduct product = app.Product;
string title = product.Title;
string price = product.Price.FormattedPrice;
// This works...
StoreAppLicense license = await theStore.GetAppLicenseAsync();
bool trial = license.IsTrial;
bool full = license.IsActive;
// "Invalid window handle.\r\n\r\nThis function must be called from a UI thread"
StorePurchaseResult result = await theStore.RequestPurchaseAsync("9NRFBVGVGW8K");
// "Invalid window handle.\r\n\r\nThis function must be called from a UI thread"
// Windows.Foundation.IAsyncOperation<StorePurchaseResult> purchase = product.RequestPurchaseAsync();
}
catch (Exception e)
{
int a = 1;
}
}
I have associated the VS2019 UWP installer project with the store, which I figure is why the other methods return the right answers without needing to cast the store object with IInitializeWithWindow (which also throws an error, but which bypassing seems to let the code work).
I figure I have to tap into the UI thread somehow - go no idea how. Various examples from all over the place don't seem to work for me. Can anyone help me?
EDIT: This is a .Net program with a UWP wrapper creating a MSIX package.

For UWP, you need to use the following bit of code to call to the UI thread:
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => {
//UI code here
});

Try following, It works for me for .net 6 wpf app
WinRT.Interop.InitializeWithWindow.Initialize(storeContext, new WindowInteropHelper(window).Handle);

Related

How can we call Invoke(Delegate) method from a Microsoft Office Add-in?

Question: How can we use Invoke(...) method in a Microsoft Office 2010-2016 VSTO project. Or, are there any work around, alternatives etc for a VSTO project?
Background: I am trying to implement a source code of a third party Windows Forms application into my Microsoft Office VSTO add-in project. The third party vendor has provided its users a sample/example as a Windows Form project with source code and has asked its users to mimic that code to their other projects (WPF, Office Solutions etc.).
I have been able to implement all parts of their Windows Forms sample app except the following line of code that is not recognized by the VS2019 in my VSTO project:
Invoke(new IsActivatedDelegate(CheckIfActivated));
Remark: delegate void IsActivatedDelegate(); is a delegate declared in their sample's Form1.cs file and CheckIfActivated() is a method declared in the same Form1.cs file.
Now, we know that the Invoke(...) method is from the Control class of System.Windows.Forms namespace. And, hence, cannot be used in a VSTO project. But there may be an alternative for a VSTO project.
UPDATE:
Following is an excerpt from the Form1.cs file of the third party's sample code that is calling Invoke(...) method:
...........
...........
void mnuActDeact_Click(object sender, EventArgs e)
{
if (isGenuine)
{
// deactivate product without deleting the product key allows the user to easily reactivate
try
{
ta.Deactivate(false);
}
catch (ThirdPartyObjectException ex)
{
MessageBox.Show("Failed to deactivate: " + ex.Message);
return;
}
isGenuine = false;
ShowTrial(true);
}
else
{
// Note: you can launch the ThirdPartyObject wizard or you can create you own interface
// launch ThirdPartyObject.exe to get the product key from the user, and activate.
Process TAProcess = new Process
{
StartInfo =
{
FileName = Path.Combine(
Path.GetDirectoryName(Application.ExecutablePath),"ThirdPartyObject.exe")
},
EnableRaisingEvents = true
};
TAProcess.Exited += p_Exited;
TAProcess.Start();
}
}
void p_Exited(object sender, EventArgs e)
{
// remove the event
((Process) sender).Exited -= p_Exited;
// the UI thread is running asynchronous to ThirdPartyObject closing that's why we can't call CheckIfActivated(); directly
Invoke(new IsActivatedDelegate(CheckIfActivated));
}
delegate void IsActivatedDelegate();
void CheckIfActivated()
{
bool isNowActivated = false;
try
{
isNowActivated = ta.IsActivated();
}
catch (ThirdPartyObjectException ex)
{
MessageBox.Show("Failed to check if activated: " + ex.Message);
return;
}
// recheck if activated
if (isNowActivated)
{
isGenuine = true;
ReEnableAppFeatures();
ShowTrial(false);
}
}
............
........
I wish you showed code for context sake... at least a few of the lines...buh I know this. if you have say
YourDelegate yd = new YourDelegate(YourMethod);
you can substitute:
yd.Invoke(new IsActivatedDelegate(CheckIfActivated));
with
yd(new IsActivatedDelegate(CheckIfActivated));

SceneManager.LoadScene doesn't work on development mobile build

I'm trying to build a Unity game on android/iOS by checking the development build option (so that I can use the profiler for debugging), but the SceneManager.LoadScene function doesn't work. I tried the scene name and index as the function parameters, but both didn't work.
It's important to note that the game works perfectly fine if I uncheck the development build option. I'm currently using Unity 2019.3.5.f1.
Edit:
I noticed that the scene loads without any problems when I run SceneManager.LoadScene from the Start() function. However, in most parts of my code, I run SceneManager.LoadScene from inside other functions, such as event handlers.
Here are a few code examples:
I run this function in Awake(). It reaches SceneManager.LoadScene("Credentials"); then stops:
private void ConfirmGooglePlayerServicesRequirements()
{
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {
var dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available)
{
// Create and hold a reference to your FirebaseApp,
// where app is a Firebase.FirebaseApp property of your application class.
app = Firebase.FirebaseApp.DefaultInstance;
InitializeFirebase();
if (!signedIn)
SceneManager.LoadScene("Credentials");
}
else
{
Debug.LogError(System.String.Format(
"Could not resolve all Firebase dependencies: {0}", dependencyStatus));
// Firebase Unity SDK is not safe to use here.
}
});
}
This is a Firebase function which is called whenever a user signs in/out. It invokes an event called signedInEvent, which is handled by a function in another script that runs SceneManager.LoadScene.
void AuthStateChanged(object sender, System.EventArgs eventArgs)
{
if (auth.CurrentUser != user)
{
signedIn = user != auth.CurrentUser && auth.CurrentUser != null;
if (!signedIn && user != null)
{
Debug.Log("Signed out " + user.UserId);
}
user = auth.CurrentUser;
if (signedIn)
{
Debug.Log("Signed in " + user.UserId);
displayName = user.DisplayName ?? "";
emailAddress = user.Email ?? "";
userId = user.UserId ?? "";
signedInEvent.Invoke(displayName, emailAddress, userId);
}
}
}
Notes:
Scenes/FirstScene is enabled. It was disabled when I took the screenshot above because I was doing some tests.
I got this error using adb (android debug bridge) a while ago when I ran the game in development mode. It might be related to this issue: FindGameObjectWithTag can only be called from the main thread
Thanks!
FindGameObjectWithTag can only be called from the main thread
This is indeed related! Now that you posted your code:
ContinueWith might get called on a background thread. Most of the Unity API may only be called from the main thread otherwise it might either show an error such as the one you mentioned or sometimes it just fails silently.
In general this is often solved by something like e.g. UnityMainThreadDispatcher.
However, specifically for Firebase there already exists an extension called ContinueWithOnMainThread in the Firebase.Extensions namespace which makes sure the callback code is actually executed in the main thread where the Unity API works.
Extension methods for System.Threading.Tasks.Task and System.Threading.Tasks.Task<T> that allow the continuation function to be executed on the main thread in Unity.
using Firebase.Extensions;
private void ConfirmGooglePlayerServicesRequirements()
{
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
var dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available)
{
// Create and hold a reference to your FirebaseApp,
// where app is a Firebase.FirebaseApp property of your application class.
app = Firebase.FirebaseApp.DefaultInstance;
InitializeFirebase();
if (!signedIn)
{
SceneManager.LoadScene("Credentials");
}
}
else
{
Debug.LogError($"Could not resolve all Firebase dependencies: {dependencyStatus}", this);
// Firebase Unity SDK is not safe to use here.
}
});
}

Call to Web API from Xamarin Android not returning results

Hi I have created a solution with 3 projects in it. My solution has a Windows console app, a Xamarin Android project and a portable class library (PCL). I have referenced my PCL in both the Console and Android app. My PCL has my Models and ViewModels and i am calling a Web API from the viewModels. Here is a sample viewModel
public class RoutineViewModel
{
public async Task<IEnumerable<Routine>> GetRoutines()
{
//Lets Get List from Routines Web API
try
{
var client = new HttpClient();
var json = await client.GetStringAsync(BroadCastApiUrls.Routines);
var results = JsonConvert.DeserializeObject<IEnumerable<Routine>>(json);
if (results.Any())
return results;
return null;
}
catch (Exception ex)
{
//Some logging code here
return null;
}
}
}
I am making via an instance of this RoutineViewModel in a Method call from a button click handler, here is the code calling the GetRoutines Method
//In the Activity class
readonly RoutineViewModel routineViewModel = new RoutineViewModel();
private async Task<bool> LoadRoutines()
{
try
{
var routines = await routineViewModel.GetRoutines();
if (routines != null)
{
routinesList = routines.ToList();
listView.Adapter = new RoutinesListAdapter(this, routinesList);
}
else
{
var builder = new AlertDialog.Builder(this);
var alert = builder.Create();
alert.SetTitle("No Results!");
// alert.SetIcon(Resource.Drawable)
alert.SetMessage("No Routines From API!");
alert.DismissEvent += alert_DismissEvent;
alert.Show();
return true;
}
}
catch (Exception ex)
{
//Some logging and clean up code
}
return false;
}
This call returns the expected results from my console app but the same method call on my android simulator returns no results. What am i missing here? its driving me nuts.
Edit
From the loadRoutines click handler the call is
bool isLoaded = await LoadRoutines();
You're probably calling Task<T>.Result or Task.Wait further up the call stack, which will work on Console apps but fail in a GUI context. I explain this behavior in more detail on my blog.
The best solution is to use await instead of Result or Wait. Note that this means you'll have to decide what your UI looks like while the routines are loading, and then update it with the results when the loading completes.

C# 2.0 Function does not work when call in a separate thread

I have a function to download a mailmessage as MSG file from DocuShare server. The function works perfectly when called from a main thread. However, when I call the function in a separate thread, the download fails. When I step in to the code, I can see that the function is being called, all the parameters are evaluated correctly and the return value is what I expect. Unfortunately, I see, no files get downloaded.
Codes:
private void btnDownloadMails_Click(object sender, EventArgs e)
{
//Thread t = new Thread(new ThreadStart(DownloadMailAsMsg));
//t.Start(); //Does not work
DownloadMailAsMsg(); // Works fine
}
void DownloadMailAsMsg()
{
DSServerMap.Server dsserver = new DSServerMap.Server();
if (!SelectMappedServer(ref dsserver, textServer.Text.ToString()))
return;
long status = 0;
dsserver.DocuShareAddress = textServer.Text;
dsserver.UserName = textUser.Text;
dsserver.Password = textPwd.Text;
status = dsserver.Logon();
if (status == 0)
{
IItemObj objParentItem;
string[] emailHan = { "MailMessage-12", "MailMessage-13", "MailMessage-31" };
foreach (string handnum in emailHan)
{
objParentItem = (IItemObj)dsserver.CreateObject(handnum);
DSGATEWAYLib.IGatewayHandler gateway = (DSGATEWAYLib.IGatewayHandler)dsserver.Open();
objParentItem.AttachGateway(gateway, true);
objParentItem.Name = #"D:\em\m_" + handnum + ".msg";
int flag = objParentItem.DSDownload(0);
}
}
}
Any ideas?
Thanks
Prakash
Maybe you need a STA thread for this. I had a similar problem once and the following solved my problem:
Thread t = new Thread((ThreadStart)delegate
{ // MAPI does only work in STA threads. Therefore an STA thread needs to be created explicitly for the SendMail call.
//...do work here
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
Maybe this will solve your problem as well.
Your thread should be a class member instead of a method variable.
When your method completes, the thread variable goes out of scope and could get cleaned up without completing.
You are trying to access Control's properties in non UI threads,
for example in lines,
dsserver.DocuShareAddress = textServer.Text;
dsserver.UserName = textUser.Text;
dsserver.Password = textPwd.Text;
you are trying to access UI Control's Text properties in different thread, which actually throws an exception.
Each of the control's values you want to access in different thread, you have to wrap it in some sort of arguements and pass it to the thread.
class MyServerParameters{
string Server;
string Username;
string Password;
}
private void btnDownloadMails_Click(object sender, EventArgs e)
{
MyServerParameters p = new MyServerParameters();
// we are still in UI thread so copy your values
// to p
p.Server = textServer.Text;
p.Username = textUser.Text;
p.Password = textPwd.Text;
Thread t = new Thread(new ParametricThreadStart(DownloadMailAsMsg));
// pass p to another thread
t.Start(p); // this will work...
}
void DownloadMailAsMsg(object mp)
{
// access p back like this...
MyServerParameters p = mp as MyServerParameters;
dsserver.DocuShareAddress = p.Server;
dsserver.UserName = p.Username;
dsserver.Password = p.Password;
Create a copy of .Text properties of the controls and reference only them in your second thread.
You'll lock your application or get an exception if you use different thread to access any of the controls.
Other way around is to use .Invoke(), but in your case you really don't need to go there.

What's wrong with my application ---- Size was 0, but I expected 46806 !

I'm a C# programmer.
Now, I'm using the ICSharpCode.SharpZipLib.dll to create a zip file in my current project. But it occurs to me that when I click the button at the SECOND TIME to execute a function to create a zip file, the application will throw an exception, friendly and seriously told me that "Size was zero, but I expected 46086".
I'm so confused that I want to know why? When I click the button at the first time, I can do it successfully without any error.
My related codes are as follows:
internal void ThreadProc()
{
try
{
ZipHelper.CreateZip(backupZipFile, Constants.HomeConstant, true);
// do other things
}
}
The CreateZip() function's realization is as follows:
public static void CreateZip(string zipFileName, string sourceDirectory, bool recurse)
{
FastZip zip = new FastZip();
if (File.Exists(zipFileName))
{
File.Delete(zipFileName);
}
zip.CreateZip(zipFileName, sourceDirectory, true, "");
}
Now, I will show you the recursive calling process:
Call method "UpdateAppAsync" in "ActiveCheckManager" class
public void UpdateAppAsync(string masterConfig)
{
this.masterConf = masterConfig;
Thread actualThread = new Thread(new ThreadStart(UpdateApp));
actualThread.IsBackground = true;
actualThread.CurrentCulture = Thread.CurrentThread.CurrentCulture;
actualThread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
actualThread.Start();
}
Call the UpdateApp function asynchronously, in the UpdateApp method, it will only call the UpdateDetail function simply.
private void UpdateDetail(string masterConfig, string category)
{
IUpdate worker = new HP.ActiveCheckLocalMode.UpdateEngine.UpdateManager();
worker.UpdateApp(masterConf);
}
The worker.UpdateApp will call UpdateDetail(string, UpdateCategory) only.
private void UpdateDetail(string masterConfig, UpdateCategory cat)
{
UpdateThread updateThread = new UpdateThread(this, cat);
updateThread.MasterConfig = masterConfig;
updateThread.ThreadProc();
}
That is the calling process. When I click the update button second time, it will throw an exception, can you help me? Thank you very much.
Has the first task thread finished before you start the second time?
I would imagine that File.Delete() and some items in the SharpZipLib to not respond nicelly to multithreadingly zip the same folder simultaneously to the same file.
Promote that " UpdateThread updateThread " as a private member of the "ActiveCheckManager" class, then check if it is already running from a previous click before creating a new thread.

Categories