OBJECT Codeunit 50000 Assembly Resolver | |
{ | |
OBJECT-PROPERTIES | |
{ | |
Date=24-12-15; | |
Time=09:03:26; | |
Modified=Yes; | |
Version List=; | |
} | |
PROPERTIES | |
{ | |
OnRun=VAR | |
Asm@1010 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Reflection.Assembly"; | |
AsmClient@1000 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Reflection.Assembly" RUNONCLIENT; | |
Object@1001 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Object"; | |
ObjectClient@1100498002 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Object" RUNONCLIENT; | |
IsLoaded@1100498003 : Boolean; | |
NetEnv@1100498001 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Environment"; | |
NetEnvClient@1100498004 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Environment" RUNONCLIENT; | |
BEGIN | |
IF NOT IsServerSideLoaded THEN BEGIN | |
IF RetrieveResolverAssembly(Asm) THEN BEGIN | |
Object := Asm.GetType('Myapp.Nav.AssemblyResolver.Resolver').GetProperty('SingleInstanceObject').GetValue(0); | |
AddAssembliesNst(Object); | |
END; | |
END; | |
IF IsWindowsClient() THEN BEGIN | |
IF NOT IsClientSideLoaded THEN BEGIN | |
IF RetrieveResolverAssembly(AsmClient) THEN BEGIN | |
ObjectClient := AsmClient.GetType('Myapp.Nav.AssemblyResolver.Resolver').GetProperty('SingleInstanceObject').GetValue(0); | |
AddAssembliesRtc(ObjectClient); | |
END; | |
END; | |
END; | |
END; | |
} | |
CODE | |
{ | |
[TryFunction] | |
LOCAL PROCEDURE IsServerSideLoaded@1100498001(); | |
VAR | |
Tester@1100498000 : DotNet "'Myapp.Nav.AssemblyResolver, Version=9.0.0.0, Culture=neutral, PublicKeyToken=58e0876c5da5c121'.Myapp.Nav.AssemblyResolver.Tester"; | |
BEGIN | |
//If assembly with the resolver is not yet loaded, this call will fail | |
Tester := Tester.Tester; | |
END; | |
[TryFunction] | |
LOCAL PROCEDURE IsClientSideLoaded@1100498006(); | |
VAR | |
Tester@1100498000 : DotNet "'Myapp.Nav.AssemblyResolver, Version=9.0.0.0, Culture=neutral, PublicKeyToken=58e0876c5da5c121'.Myapp.Nav.AssemblyResolver.Tester" RUNONCLIENT; | |
BEGIN | |
//If assembly with the resolver is not yet loaded, this call will fail | |
Tester := Tester.Tester; | |
//This will also succeed when assemblies are in the add-ins folder of the development environment, which is fine | |
END; | |
LOCAL PROCEDURE AddAssembliesNst@6(Resolver@1100498000 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Object"); | |
VAR | |
Asmbl@1000 : Record 11096450; | |
BEGIN | |
WITH Asmbl DO BEGIN | |
SETFILTER(Tier,'%1|%2',Tier::Both,Tier::Server); | |
IF FINDSET() THEN | |
REPEAT | |
AddAssembly(Asmbl,Resolver,Tier::Server); | |
UNTIL NEXT = 0; | |
END; | |
END; | |
LOCAL PROCEDURE AddAssembliesRtc@1100498000(Resolver@1100498000 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Object"); | |
VAR | |
Asmbl@1000 : Record 11096450; | |
BEGIN | |
WITH Asmbl DO BEGIN | |
SETFILTER(Tier,'%1|%2',Tier::Both,Tier::Client); | |
IF FINDSET() THEN | |
REPEAT | |
AddAssembly(Asmbl,Resolver,Tier::Client); | |
UNTIL NEXT = 0; | |
END; | |
END; | |
LOCAL PROCEDURE AddAssembly@7(Asmbl@1000 : Record 11096450;VAR Resolver@1007 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Object";TargetTier@1003 : Integer); | |
VAR | |
Asm@1004 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Reflection.Assembly"; | |
Bytes@1008 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Array"; | |
MemStr@1002 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.MemoryStream"; | |
MethodInfo@1005 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Reflection.MethodInfo"; | |
MethodInfoClient@1010 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Reflection.MethodInfo" RUNONCLIENT; | |
Params@1006 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Array"; | |
ParamsClient@1009 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Array" RUNONCLIENT; | |
Object@1011 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Object"; | |
InStr@1001 : InStream; | |
BEGIN | |
LoadAssemblyBytes(Asmbl,Bytes); | |
WITH Asmbl DO BEGIN | |
CASE TargetTier OF | |
Tier::Server: | |
BEGIN | |
MethodInfo := Resolver.GetType().GetMethod('AddAssembly'); | |
Params := Params.CreateInstance(GETDOTNETTYPE(Object),2); | |
Params.SetValue("Assembly Name",0); | |
Params.SetValue(Bytes,1); | |
MethodInfo.Invoke(Resolver,Params); | |
END; | |
Tier::Client: | |
BEGIN | |
IF NOT IsWindowsClient() THEN | |
EXIT; | |
MethodInfoClient := Resolver.GetType().GetMethod('AddAssembly'); | |
ParamsClient := ParamsClient.CreateInstance(GETDOTNETTYPE(Object),2); | |
ParamsClient.SetValue("Assembly Name",0); | |
ParamsClient.SetValue(Bytes,1); | |
MethodInfoClient.Invoke(Resolver,ParamsClient); | |
END; | |
END; | |
END; | |
END; | |
LOCAL PROCEDURE IsWindowsClient@4() : Boolean; | |
BEGIN | |
EXIT(CURRENTCLIENTTYPE = CLIENTTYPE::Windows); | |
END; | |
LOCAL PROCEDURE LoadAssemblyBytes@65(Asmbl@1000 : Record 11096450;VAR Bytes@1001 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Array"); | |
VAR | |
MemStr@1004 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.MemoryStream"; | |
InStr@1003 : InStream; | |
Byte@1002 : Byte; | |
BEGIN | |
WITH Asmbl DO BEGIN | |
CALCFIELDS(Assembly); | |
Assembly.CREATEINSTREAM(InStr); | |
MemStr := MemStr.MemoryStream(); | |
COPYSTREAM(MemStr,InStr); | |
Bytes := MemStr.ToArray(); | |
END; | |
END; | |
LOCAL PROCEDURE RetrieveResolverAssembly@60(VAR Asm@1000 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Reflection.Assembly") : Boolean; | |
VAR | |
Asmbl@1001 : Record 11096450; | |
Bytes@1100498000 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Array"; | |
BEGIN | |
Asmbl.SETRANGE("Assembly Name" , 'Myapp.Nav.AssemblyResolver, Version=9.0.0.0, Culture=neutral, PublicKeyToken=58e0876c5da5c121'); | |
IF Asmbl.FINDFIRST THEN BEGIN | |
LoadAssemblyBytes(Asmbl,Bytes); | |
Asm := Asm.Load(Bytes); | |
EXIT(TRUE); | |
END; | |
EXIT(FALSE); | |
END; | |
BEGIN | |
{ | |
} | |
END. | |
} | |
} | |
using System; | |
using System.Collections.Generic; | |
using System.Reflection; | |
namespace Myapp.Nav.AssemblyResolver | |
{ | |
public class Resolver | |
{ | |
public static Resolver SingleInstance { get; private set; } = new Resolver(); | |
public static Object SingleInstanceObject { get { return SingleInstance; } } | |
private readonly Dictionary<string, byte[]> _assemblies = new Dictionary<string, byte[]>(); | |
public void AddAssembly(string name, byte[] asm) | |
{ | |
if (_assemblies.ContainsKey(name)) | |
_assemblies.Remove(name); | |
_assemblies.Add(name, asm); | |
} | |
public bool IsLoaded { get { return _assemblies.Count > 0; } } | |
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) | |
{ | |
return _assemblies.ContainsKey(args.Name) ? Assembly.Load(_assemblies[args.Name]) : null; | |
} | |
private Resolver() | |
{ | |
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; | |
} | |
} | |
/// <summary> | |
/// Dummy class used in NAV to test if assembly is loaded | |
/// </summary> | |
public class Tester | |
{ | |
} | |
} |
HI JaapM
How do you solve the compile time problem? My understanding is that the actual DLL has to be at least in Add-ins folder of the client or server. For instance when I import a FOB in NAV2015 and the DLL is not there yet... the NAV object is not compiled and cannot be used until I compile it.
mitrov, see here for how it works without being anywhere in the add-ins:
http://vjeko.com/sorting-out-the-dll-hell-part-3-the-code
I am actually working on a fix for the memory leak issues and I'll post an updated to my solution today or tomorrow.
Thanks Vjeko!
If I understand your solution correctly you solve the compile time problem with the function CompileResolverAssembly for your ResolverAssembly.
But for my assemblies I would need to do the same thing... minifying my assembly c# code and write a CompileMyAssembly function... correct?
And because I'm using 3rd party assemblies as well... this is not really an option for me. So I have to deploy my assemblies including 3rd party assemblies at least once to the addins-folder to make sure that the nav objects compile on the customers side. .. right?
Hi JaapM
Thanks for this gist! When I copy your c# to VS2010 or VS2012 I get:

Are you working in VS2015?
Update: Yep... this doesn't happen in VS2015