I have created a simple control add-in for Dynamics NAV/BC and I'm having some trouble with it.
I have done exactly the same (as far as I can tell) in this add-in, as I have done in previous add-ins I have created of the same type, which have worked just fine.
The problem is that when the add-in assembly is added to a page (personally I don't know much about this part, I'm just watching as it is being done), the events and methods of the add-in are supposed to appear (stub code should be generated), however this is not the case... Nothing happens.
When they try with a similar assembly (same framework, same kind of interface, same version of 'Microsoft.Dynamics.Framework.UI.Extensibility' assembly - in short a very much similar assembly) the code for the methods and events is generated as expected.
So my question is, obviously, why is it working for one assembly and not the another, when they are next to identical in the C# code?
Here is the code for the assembly not working.
using Microsoft.Dynamics.Framework.UI.Extensibility;
namespace Dynamics.NAV.CR
{
[ControlAddInExport("Dynamics.NAV.CR")]
public interface ICRAddIn
{
[ApplicationVisible]
event ApplicationEventHandler AddInReady;
[ApplicationVisible]
event DataEventHandler ButtonClicked;
[ApplicationVisible]
event DataEventHandler ElementHtmlRetrieved;
[ApplicationVisible]
event DataEventHandler ElementTextRetrieved;
[ApplicationVisible]
void setElementHtml(string control_id, string html);
[ApplicationVisible]
void setElementText(string control_id, string text);
[ApplicationVisible]
void setElementCss(string control_id, string css);
[ApplicationVisible]
void addElementCssCls(string control_id, string cls);
[ApplicationVisible]
void removeElementCssCls(string control_id, string cls);
[ApplicationVisible]
void setElementProp(string control_id, string prop_name, string prop_value);
}
}
What am I missing?
The person deploying the assembly says he has made sure it's 'unblocked', so that shouldn't be the issue.
I forgot to add the code for the custom event handler - here it is.
namespace Dynamics.NAV.CR
{
public delegate void DataEventHandler(object data);
}
Ok, it turns out that the apparent reason that things wasn't working out for me was due to the Dynamics NAV/BC UI extensibility assembly Microsoft.Dynamics.Framework.UI.Extensibility being of a different version than the target system, or at least that is what I believe was the reason.
I have tried many different things to tweak the project in order to make it work and it seems that changing this assembly did the trick.
I can't be a 100% sure though, since I have not been deploying my assembly to NAV personally and so I don't know whether or not the appropriate amount of service restarts, cache clearings and what ever else of odd required actions has taken place.
So until proof of something else being the cause to my problems emerge, I will assume that it was an assembly version mismatch that was the cause.
Related
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 want to circunscribe this question to the specific context of WPF aplications.
The accepted answer says
"You should only to catch exceptions that you can actually do something about"
and also
"Note that if you're simply trying to catch any unhandled exceptions that might occur for the purposes of logging or error reporting, you should be using the AppDomain.UnhandledException event"
Well, all would be good and well EXCEPT (pun intended) that I had a very serious problem while deploying a WPF application which would crash right at application startup, with the dreaded IOException error in PresentationFramework.
I tried the Application.Current.DispatcherUnhandledException, but the crash apparently happended outside its grasp. I tried Windbg but the error messages were still elusive.
Then I followed advice from this post (changing app.xaml Build Action property from "Application Definition" to "Page" and putting Main() function inside App.xaml.cs itself), putting a try/catch with a MessageBox (it could be a logging call, whatever), and the message displayed immediately led me to the solution.
So the question is:
Considering WPF has its own esoterical bugs, is there any actual problem in putting Main() function body inside a try/catch?
Here is my current code:
public partial class App : System.Windows.Application {
/// <summary>
/// Application Entry Point.
/// </summary>
[System.STAThreadAttribute()]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static void Main() {
try {
var v3d = new App();
v3d.InitializeComponent();
var shellview = new ShellView();
v3d.Run(shellview);
} catch (Exception e) {
MessageBox.Show(e.ToString());
}
}
And for the record, I was getting "Cannot locate resource 'app.xaml'" caused by some culture mismatch, the problem happened in only one of two similar machines apparently because one OS (Win7) is English and other is Portuguese. I solved it with this answer.
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.
How do I use the word editor in a WPF application? Is it possible using windows forms hosting in WPF only? Is there another way to accomplish that?
I found AvalonEdit but it does not have features that I need. So using this way, my problem may not be solved.
Also there is some stuffs out there to host a windows forms control in WPF, but it could not be my answer.
I want to understand that is there a way to use word editor in a native way in a wpf app?
Will all APIs be available in that solution?
Thanks in advance.
You can host MS Word (2007/2010 and probably other versions) from within a WebBrowser control, this works in WinForms and should work in WPF too. A .NET API is provided for automating Word, documented here. The required interop assemblies ship with Office 2010, so deployment is a lot simpler than previous Office versions.
See this Microsoft Support article for more details on hosting Word within a WebBrowser control. The Screenshot below shows Word embedded within a host Winforms application.
Note that this only works reliably for a single hosted instance of Word, so you can't show 2 Word documents side by side in the same application. Also, the Ribbon can sometimes go missing - but Word hasn't ever caused the application to crash.
Administrative rights are required to make the required registry updates as there are potential security issues. One easy method to make the registry updates is to write a script, but the following (revised/untested) code shows how this can be done in c# for Word, Excel and PowerPoint:
using System.Security.AccessControl;
private Dictionary<string,uint> OfficeBrowserRegKeys()
{
string[] officeRegKeyArray = new string[]
{
#"SOFTWARE\Classes\Word.Document.12",
#"SOFTWARE\Classes\Word.DocumentMacroEnabled.12",
#"SOFTWARE\Classes\Excel.Sheet.12",
#"SOFTWARE\Classes\Excel.SheetMacroEnabled.12",
#"SOFTWARE\Classes\Excel.SheetBinaryMacroEnabled.12",
#"SOFTWARE\Classes\PowerPoint.Show.12",
#"SOFTWARE\Classes\PowerPoint.ShowMacroEnabled.12",
#"SOFTWARE\Classes\PowerPoint.SlideShow.12",
#"SOFTWARE\Classes\PowerPoint.SlideShowMacroEnabled.12"
};
Dictionary<string,uint> officeRegKeys = new Dictionary<string, uint>();
uint wrdVal = 0x80000024;
uint excelVal = 0x80000A00;
uint powerPtVal = 0x800000A0;
foreach(string keyName in officeRegKeyArray)
{
if (keyName.Contains("Word"))
{
officeRegKeys.Add(keyName, wrdVal);
}
else if (keyName.Contains("Excel"))
{
officeRegKeys.Add(keyName, excelVal);
}
else
{
officeRegKeys.Add(keyName, powerPtVal);
}
}
return officeRegKeys;
}
private void setNewOfficeKeys()
{
uint editFlag = 0x00010000;
Dictionary<string,uint> officeRegKeys = OfficeBrowserRegKeys();
foreach (KeyValuePair<string, uint> kvp in officeRegKeys)
{
try
{
RegistryKey rKey = Registry.LocalMachine.OpenSubKey(kvp.Key,
RegistryKeyPermissionCheck.ReadWriteSubTree,
System.Security.AccessControl.RegistryRights.SetValue);
rKey.SetValue("BrowserFlags", unchecked((int)kvp.Value),
RegistryValueKind.DWord);
rKey.SetValue("EditFlags", unchecked((int)editFlag),
RegistryValueKind.DWord);
}
catch (Exception e) { string msg = e.Message; }
}
}
Well, Word proper isn't technically designed to be hosted by another app, whether it's WPF, WINFORMS or anything else.
You CAN use api tricks (like SetParent) to move the Main Word window into a WPF hosted window. I've done it before, but it's pretty tricky business and it's very easy to miss things that cause GPFs (both in Word and your app).
Is there any reason why it needs to be "Word in your app"? Why not write a little word addin and then launch Word from your app when necessary. then the Addin can communicate with your app, or your DB or whatever as necessary from within Word.
Users may find that to be a more usable approach in any case.
I've encountered a problem with the XBAP Script Interop feature that was added in WPF 4. It involves a combination of the following:
Accessing members of a script object from .NET
Running .NET code in a callback invoked from JavaScript
Running in Partial trust
This seems to be a "pick any two" scenario... If I try and do all three of those things, I get a SecurityException.
For example, combining 1 and 3 is easy. I can put this into my hosting web page's script:
function ReturnSomething()
{
return { Foo: "Hello", Bar: 42 };
}
And then in, say, a button click handler in my WPF code behind, I can do this:
dynamic script = BrowserInteropHelper.HostScript;
if (script != null)
{
dynamic result = script.ReturnSomething();
string foo = result.Foo;
int bar = result.Bar;
// go on to do something useful with foo and bar...
}
That works fine, even in a partial trust deployment. (I'm using the default ClickOnce security settings offered by the WPF Browser Application template in Visual Studio 2010, which debugs the XBAP as though it were running in the Internet zone.) So far, so good.
I can also combine 2 and 3. To make my .NET method callable from JavaScript, sadly we can't just pass a delegate, we have to do this:
[ComVisible(true)]
public class CallbackClass
{
public string MyMethod(int arg)
{
return "Value: " + arg;
}
}
and then I can declare a JavaScript method that looks like this:
function CallMethod(obj)
{
var result = obj.MyMethod(42);
var myElement = document.getElementById("myElement");
myElement.innerText = "Result: " + result;
}
and now in, say, a WPF button click handler, I can do this:
script.CallMethod(new CallbackClass());
So my WPF code calls (via BrowserInteropHelper.HostScript) my JavaScript CallMethod function, which in turn calls my .NET code back - specifically, it calls the MyMethod method exposed by my CallbackClass. (Or I could mark the callback method as a default method with a [DispId(0)] attribute, which would let me simplify the JavaScript code - the script could treat the argument itself as a method. Either approach yields the same results.)
The MyMethod callback is successfully called. I can see in the debugger that the argument passed from JavaScript (42) is getting through correctly (having been properly coerced to an int). And when my method returns, the string that it returns ends up in my HTML UI thanks to the rest of the CallMethod function.
Great - so we can do 2 and 3.
But what about combining all three? I want to modify my callback class so that it can work with script objects just like the one returned by my first snippet, the ReturnSomething function. We know that it's perfectly possible to work with such objects because that first example succeded. So you'd think I could do this:
[ComVisible(true)]
public class CallbackClass
{
public string MyMethod(dynamic arg)
{
return "Foo: " + arg.Foo + ", Bar: " + arg.Bar;
}
}
and then modify my JavaScript to look like this:
function CallMethod(obj)
{
var result = obj.MyMethod({ Foo: "Hello", Bar: 42 });
var myElement = document.getElementById("myElement");
myElement.innerText = "Result: " + result;
}
and then call the method from my WPF button click handler as before:
script.CallMethod(new CallbackClass());
this successfully calls the JavaScript CallMethod function, which successfully calls back the MyMethod C# method, but when that method attempts to retrieve the arg.Foo property, I get a SecurityException with a message of RequestFailed. Here's the call stack:
at System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
at System.Security.CodeAccessSecurityEngine.Check(PermissionSet permSet, StackCrawlMark& stackMark)
at System.Security.PermissionSet.Demand()
at System.Dynamic.ComBinder.TryBindGetMember(GetMemberBinder binder, DynamicMetaObject instance, DynamicMetaObject& result, Boolean delayInvocation)
at Microsoft.CSharp.RuntimeBinder.CSharpGetMemberBinder.FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
at System.Dynamic.DynamicMetaObject.BindGetMember(GetMemberBinder binder)
at System.Dynamic.GetMemberBinder.Bind(DynamicMetaObject target, DynamicMetaObject[] args)
at System.Dynamic.DynamicMetaObjectBinder.Bind(Object[] args, ReadOnlyCollection`1 parameters, LabelTarget returnLabel)
at System.Runtime.CompilerServices.CallSiteBinder.BindCore[T](CallSite`1 site, Object[] args)
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at XBapDemo.CallbackClass.MyMethod(Object arg)
That's the whole trace as reported by the exception. And above CallbackClass.MyMethod, Visual Studio is showing two lots of [Native to Managed Transition] and an [AppDomain Transition] - so that's the whole of the stack. (Apparently we're on a different thread now. This callback is happening on what the Threads panel describes as a Worker Thread - I can see that the Main Thread is still sat inside my WPF button click handler, waiting for the call to the JavaScript CallMethod function to return.)
Apparently the problem is that the DLR has ended up wrapping the JavaScript object in the ComBinder which demands full trust. But in the earlier case where I called a JavaScript method via HostScript and it returned me an object, the HostScript wrapped it in a System.Windows.Interop.DynamicScriptObject for me.
The DynamicScriptObject class is specific to WPFs XBAP script interop - it's not part of the usual DLR types, and it's defined in PresentationFramework.dll. As far as I can tell, one of the jobs it does is to make it possible to use C#'s dynamic keyword to access JavaScript properties without needing full trust, even though those properties are being accessed through COM interop (which usually requires full trust) under the covers.
As far as I can tell, the problem is that you only get these DynamicScriptObject wrappers for objects that are returned from other DynamicScriptObject instances (such as HostScript). With callbacks, that wrapping doesn't seem to occur. In my callback, I'm getting the sort of dynamic wrapper C# would normally give me in plain old COM interop scenarios, at which point, it demands that I have full trust.
Running it with full trust works fine - that would be the "1 and 2" combination from the list above. But I don't want to have full trust. (I want 1, 2, and 3.) And outside of callback situations, I can access JavaScript object members just fine. It seems inconsistent that I can access a JavaScript object just fine most of the time, but accessing an identical object in a callback is forbidden.
Is there a way around this? Or am I doomed to run my code in full trust if I want to do anything interesting in a callback?
I haven't done XBAP in a while, but I am curious if it is the dynamic type that could be causing the issue. Try changing the dynamic parameter to type object and see if it will work.
[ComVisible(true)]
public class CallbackClass
{
public string MyMethod(object arg)
{
return "Arg is: " + arg.ToString();
}
}