These lines of code allow to run a C++ Winform application:
#include "MyForm.h"
using namespace System;
using namespace System::Windows::Forms;
[STAThread]
int main(array<String^>^ args)
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
MyProject::MyForm form;
Application::Run(%form);
}
Could you explain me what is that [STAThread]?
And what about this:
Application::Run(%form);
What does that % symbol mean?
From MSDN
STAThreadAttribute indicates that the COM threading model for the
application is single-threaded apartment. This attribute must be
present on the entry point of any application that uses Windows Forms;
if it is omitted, the Windows components might not work correctly. If
the attribute is not present, the application uses the multithreaded
apartment model, which is not supported for Windows Forms.
It is equivalent of CoInitialize for C++
And % is Tracking Reference.
It is like reference & but in managed code.
Related
Looking for a way to use shinyorg/shiny nuget packages in cross-platform projects built on the UNO platform.
Facing some challenges beyond my (limited) skills for iOS development, I'm specifically looking for how to integrate Shiny.Core into the solution iOS project.
More precisely, I'm looking for where to put this initialization override:
public override void PerformFetch(UIApplication application, Action<UIBackgroundFetchResult> completionHandler)
=> this.ShinyPerformFetch(completionHandler);
Since when I try adding this in the Main.cs (Application class) of the iOS project, I can't find where to start...
The Main.cs class from the iOS project contains a static Main method (which is the main entry point of the app) in which a call to UIApplication.Main(args, null, typeof(App)); is made.
UIApplication being in fact UIKit.UIApplication
Following this guide https://github.com/shinyorg/shiny/tree/master/src/Shiny.Core where it's said:
* Add the following as the first line in your AppDelegate.cs - FinishedLaunching method
using Shiny;
this.ShinyFinishedLaunching(new YourStartup());
** IOS JOBS **
If you plan to use jobs in iOS, please do the following:
1. Add this to your AppDelegate.cs
public override void PerformFetch(UIApplication application, Action<UIBackgroundFetchResult> completionHandler)
=> this.ShinyPerformFetch(completionHandler);
On iOS, the AppDelegate is actually the App class in your app, created from the default Uno Platform templates.
Windows.UI.Xaml.Application inherits from UIApplicationDelegate and provides a way declare this:
#if __IOS__
public override void PerformFetch(UIApplication application, Action<UIBackgroundFetchResult> completionHandler)
=> this.ShinyPerformFetch(completionHandler);
#endif
in order for the code for the other platforms to ignore this iOS-specific code
How can I solve the problem:
I am very new to C/C++ so I do not know what info I have to give in order to describe my problem properly. All that I was doing is trying to incorporate Windows Forms following step by step after the guy here. He got everything working and I do not even though I did exactly the same stuff as he did.
Here is the code with the error:
using namespace System;
using namespace System::Windows::Forms;
[STAThreadAttribute]
void Main(array<String^>^ args) {
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
Project1::MyForm form;
Application::Run(%form);
}
You should really save yourself a lot of pain and use C# for your GUI and only use C++/CLI to wrap native unmanaged C++ code within your C# GUI.
Anyway, Project1 is not a namespace so just drop it. Try this:
using namespace System;
using namespace System::Windows::Forms;
[STAThreadAttribute]
void Main(array<String^>^ args) {
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
MyForm form;
Application::Run(%form);
}
Use Calculator instead of Project1.
Calculator::MyForm form;
Application::Run(%form)
I'm trying to follow along with Jonathan Worthington's airport announcement example in An Event-driven and Reactive Future
It compiles.
The problem: SayGateChange is never called. I'm new to Rx. I must be leaving something out. What I have here is his code as exactly as I could transcribe it. Sadly, there is no source available online.
AddGateChange is supposed to push a new item onto EventStreams.GateChanged, which in turn is supposed to be watched by Announcer.Announcements, which is supposed to be watched by SayGateChange.
I'm in Windows forms, not WPF, if that makes a difference.
I will gladly put it into a console app or LinqPad if that will make it work.
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading;
public class frmAnnouncements
{
Announcer _Announcer = new Announcer();
ObservableCollection<string> Announcements = new ObservableCollection<string>();
private void frmRx_Load(System.Object sender, System.EventArgs e)
{
PopulateAnnouncements();
AddGateChange();
}
private void AddGateChange()
{
EventStreams.GateChanged.OnNext(new GateChanged {
Destination = "DAL",
FlightCode = 1503
});
}
private void PopulateAnnouncements()
{
_Announcer.Announcements.ObserveOnDispatcher().Subscribe(SayGateChange);
}
private void SayGateChange(string Message)
{
Interaction.MsgBox(Message);
}
public class GateChanged
{
public string FlightCode;
public string Destination;
}
public class EventStreams
{
public static Subject<GateChanged> GateChanged = new Subject<GateChanged>();
}
public class Announcer
{
public Announcer()
{
this.Announcements = EventStreams.GateChanged.Select(e => string.Format("gate change {0} to {1} ", e.FlightCode, e.Destination));
}
public IObservable<string> Announcements;
}
public frmAnnouncements()
{
Load += frmRx_Load;
}
}
As #Enigmativity stated, using ObserveOnDispatcher() is a problem - although without looking at Interaction.MsgBox its hard to be 100% certain it's the whole story - I guess it may be in the video, but it's rather long and I didn't watch it all.
The use of ObservableOnDispatcher() suggests you have pulled in the wrong nuget package for Rx:
For WPF applications, use rx-xaml (deprecated synonym rx-wpf), which provides the extension method ObserveOnDispatcher()
For Winforms applications, use rx-winforms, which provides the extension method overload ObserveOn(Control)
Both Winforms and WPF have a similar design where the user interface runs on a dedicated thread. In Winforms this is known as the "UI Thread" and in WPF as the "Dispatcher". Although the concept is very similar, the implementation is quite different.
ObserveOnDispatcher in WPF will cause the observer notifications OnXXX to be invoked on the dispatcher thread.
In WinForms, where you use ObserveOn(this), the this will generally be the form itself. For any WinForms control, this will locate the control's SynchronizationContext and Post OnXXX notifications to that.
Both overloads are smart in that invocations are direct if you happen to be on the correct Dispatcher thread or UI thread already.
I do seem to remember that WinForms is a lot more tolerant of updating UI off the UI thread - although this problem occurs in WPF too. This isn't a good thing, since it can lead to unpredictable results that are hard to debug. I note that the WinForms MessageBox.Show method, for example, doesn't care which thread it is invoked on since it creates it's own window. In general, use of some form of ObserveOn/ObserveOnDispatcher is always recommended in UI scenarios.
For this reason, it's a good idea to understand how these work in detail. For this, and to learn about the related SubscribeOn, have a look at this question.
I am surprised that you didn't get an informative InvalidOperationException stating that "The current thread has no Dispatcher associated with it." I can only think some other part of your code is swallowing exceptions, or you are using WPF code in your app as well and a Dispatcher had been created associated with the Winforms UI thread. That code behind Interaction.MsgBox is probably to blame for swallowing an error. Either way, I suggest removing rx-xaml to avoid confusion.
I followed this tutorial to combine a few DLL's into my EXE.
http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application
The way I understand this works is:
- it starts by telling the compiler to embed (as embedded resources) each and every DLL that have their Local Copy set to True.
That part is working fine. It apparently doesn't "add" them as resources to my project (figure 1 in the tutorial kind of says otherwise), but I can tell that the size of my EXE is correct.
FYI, my program uses WPFtoolkit, in my case, that's 2 DLL's:
system.windows.controls.datavisualization.toolkit.dll
WPFToolkit.dll
Then, I set the Build Action of my App.xaml to Page, and made a program.cs file which I added to my project.
this is my project.cs:
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Media.TextFormatting;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Shell;
using System.IO;
using System.Reflection;
using System.Globalization;
namespace Swf_perium {
public class Program {
//[System.Diagnostics.DebuggerNonUserCodeAttribute()]
//[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
[STAThreadAttribute]
public static void Main() {
Swft_perium.App app = new Swf_perium.App();
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
app.InitializeComponent();
app.Run();
}
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
AssemblyName assemblyName = new AssemblyName(args.Name);
string path = assemblyName.Name + ".dll";
Console.WriteLine(path);
if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
{
path = String.Format(#"{0}\{1}", assemblyName.CultureInfo, path);
}
using (Stream stream = executingAssembly.GetManifestResourceStream(path))
{
if (stream == null)
return null;
byte[] assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
return Assembly.Load(assemblyRawBytes);
}
}
}
}
After I build the project, I run it off VS2013, no problem, since both DLL's have their local copy set to true. If I go in my debug folder, take both DLL's out and run the EXE off windows explorer, then the program instantly crashes because it can find the DLL's.
What this tutorial should allow me to do is being able to run that EXE by itself without the DLL's, so yeah, it doesn't work.
I added a console writeline of the path that are being read by the OnResolveAssembly method of my program.cs. And here's what I get:
4 times the same path:
"Swf_perium.resources.dll"
Obviously, when it gets to the Stream, it's null and the method then returns null.
I am trying to understand where these paths are coming from? I don't understand why 4? And why this path?
Has anyone ever tried this technique? Comments on the blog show pretty good success rate.
Does anyone have an idea?
I made several mistakes to get to this stage, but at this point I don't see what I am doing wrong.
Thanks
Steve
EDIT: following HB's guidance, here's what I did:
I took the MSBuild target "mod" out.
Set both references' copy local to FALSE.
Added both DLL as embedded resources manually. They're both into the "Resources" directory at the root of the project.
I set App.xaml build action back to "ApplicationDefinition".
And I excluded my program.cs out of the project.
and added this code to App.xaml.cs:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Reflection;
namespace Swf_perium
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var execAssembly = Assembly.GetExecutingAssembly();
string resourceName = execAssembly.FullName.Split(',').First() + ".Resources." + new AssemblyName(args.Name).Name + ".dll";
Console.WriteLine(resourceName);
using (var stream = execAssembly.GetManifestResourceStream(resourceName))
{
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
}
}
now, the console prints out of the 2 DLL's filename, but not the other.. I am guessing that's why it's still not working..
that's where I'm at.
edit:
The DLL that doesn't show is not called by my code directly. it's a dependence from the first DLL. I took that second DLL out of references and resources.. If I set copy local to true for the first DLL (which my program actually uses), building the project generates both DLL at the root - in this case with both dlls generated the program works, funny thing is if I delete that second DLL, the program still works. So the problem isn't that second DLL but the first one.
the error I have (which I've had all along no matter what technique I use) is that my XAML is calling that namespace and can't find it!
edit:
Ok, well it still doesn't work. I've brought my program.cs back into the solution, set it as the entry point. And added the code suggested by HB into it.
I made sure that the assemblyresolve is done on the first line of the main so that's it's done before any wpf is done. I even added a 5s sleep just to make sure that the dll was loaded before any wpf happens. Still no go.
Either the dependence to the second DLL is what's causing a problem (?) or maybe the way I import the namespace in my XAML is incorrect. Do I need to specify that this namespace is embedded? and where it's located - i.e. its path?.
thanks
Perhaps look at Costura where it will do all the hard work of embedding assemblies for you.
Don't know your project structure but i usually add a directory for the assemblies to the root of the project and then add the dlls to that directory as embedded resource. I also then turn off the local copy of the references to make sure that it works.
Here is the code i use in my App.xaml.cs:
static App()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var execAssembly = Assembly.GetExecutingAssembly();
string resourceName = execAssembly.FullName.Split(',').First() + ".ReferencedAssemblies." +
new AssemblyName(args.Name).Name + ".dll";
using (var stream = execAssembly.GetManifestResourceStream(resourceName))
{
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
Simply replace the ".ReferencedAssemblies." string according to the directory you placed the dlls in.
(Using the static constructor of the class makes sure that the event is hooked up before any code that potentially accesses referenced assemblies is executed, in your code i would move the hook to the first line of Main, that may already solve your problem.)
Can you create a .NET 4 version of your app for testing was the bosses' innocent question - sure!
But after I changed our 27 projects in our Winforms application to .NET 4, and recompiled, when launching the app, I get
System.TypeLoadException was unhandled
Message=Inheritance security rules violated while overriding member:
'MyCustomORM.GetObjectData(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)'.
Security accessibility of the overriding method must match the security accessibility of the method being overriden.
Hmmm.....
MyCustomORM does indeed implement the ISerializable interface and thus has this method
[Serializable]
public abstract class MyCustomORM: IMyCustomORM, ISerializable, ICloneable, ISecurable
{
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
// do stuff here.......
}
}
and I also have two classes that derive from Exception that override the GetObjectData method.
But what could be wrong here?? Googling around I found some additional attributes to stick onto my method and namespace - so I did:
[assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution = true)]
namespace MyApplication.ORM
{
[Serializable]
public abstract class MyCustomORM: IMyCustomORM, ISerializable, ICloneable, ISecurable
{
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
// do stuff here.......
}
}
}
but that doesn't change anything.....
The exception happens even before my first line of code in my static Main() method is reached....
I've combed through the project and removed any references to old .NET 1.1 libraries (yes, the app is that old.....) and replaced them with their .NET 4 counterparts (mostly log4net). Still no luck....
Any ideas??
Is the assembly in which the MyCustomORM class resides marked with SecurityTransparentAttribute? If so, the problem stems from changes in the security transparency model between .NET 3.5 and .NET 4.0. For your testing scenario, you may wish to simply opt into using the older transparency mechanism. To do so, add the following assembly-level attribute:
[assembly: SecurityRules(SecurityRuleSet.Level1)]
For more information on the differences between the Level1 and Level2 transparency models, see http://blogs.msdn.com/b/shawnfa/archive/2009/11/12/differences-between-the-security-rule-sets.aspx.
I know this is pretty old, but I ran into this issue with one of my assemblies recently. It only occurred on some machines and was very difficult to determine what was causing it. I didn't just want to put security rule adjustments in, so after much searching I ran across the SecAnnotate tool that is included with Visual Studio.
Using SecAnnotate to Identify Transparency Violations
Using the tool I was able to determine that one of my assemblies was referencing an older version of a dll which contained some security attributes which were causing the problem. Updating the reference fixed the issue.
The SecAnnotate tool seems like a great way to identify any violations that you may have accidentally overlooked or didn't know about.
Hope this helps someone.