Bitness agnostic ReadDWG

The following source code sample demonstrate how to load DWG/DXF files from a .NET application compiled against AnyCPU. It includes the operating system bitness detection, dynamic loading of the Eyeshot x86 or x64 DLL from a separate folder and the instantiation of the ReadAutodesk class using reflection.

BitnessAgnosticReadDWG.zip

Was this article helpful?
3 out of 3 found this helpful
Have more questions? Submit a request

Comments

5 comments
  • Our project is referencing other controls that have x86 and x64 DLLs and we use several methods from these controls. We searched a more type-safe and ‘friendly’ approach and found a very clever solution developed by Yurik on stack exchange. http://stackoverflow.com/a/9951658

    In a nutshell, before anything we subscribe to the AppDomain.CurrentDomain.AssemblyResolve event (which is raised when the application is looking for a DLL) and intercept the call and replace the requested DLL with the platform specific version that we simply put in x86 and x64 subfolders of the application. For in-depth details, please see the original solution on stack exchange.

    The big difference from Alberto’s approach is that you reference the x86 version of the DLL in your Visual Studio project. So you can use the ReadAutodesk class, like any other without invoking methods and constructors using reflection.

    I’ve updated Alberto’s sample with this technique (see attached project).

    Some additional information:

    • You might want to check the Post-build event to set your path to Eyeshot bin folder.
    • After you’ve referenced the x86 control in your project, make sure you change its ‘Copy Local’ property to False.
    • Since you will reference an x86 library in your AnyCPU project, you will get a ‘mismatch between the processor architecture’ compile warning is visual studio. To safely ignore this warning add this PropertyGroup to your project file. (Thanks to Derik for the solution)

     

    http://stackoverflow.com/a/12672514

     

    <PropertyGroup>

    <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>

    </PropertyGroup>

     

    Good luck with your projects.

    Alex

  • VB.NET code

     

    Protected Overrides Sub OnLoad(e As EventArgs)

    Dim appPath As String = Path.GetDirectoryName(Application.ExecutablePath)
    Dim pathDll As String

    ' checks Operating System bitness
    If Environment.Is64BitProcess Then
    pathDll = appPath & Convert.ToString("\x64\devDept.Eyeshot.Control.x64.dll")
    Else
    pathDll = appPath & Convert.ToString("\x86\devDept.Eyeshot.Control.x86.dll")
    End If

    If Not File.Exists(pathDll) Then
    Return
    End If

    Dim foundType As Type = Nothing
    Dim assembly__1 As Assembly = Assembly.LoadFrom(pathDll)
    Dim types As Type() = assembly__1.GetExportedTypes()

    If types.Length > 0 Then
    For Each type As Type In types
    If type.Name.Equals("ReadAutodesk", StringComparison.OrdinalIgnoreCase) Then
    foundType = type
    Exit For
    End If
    Next
    End If

    If foundType IsNot Nothing Then
    ' invokes the constructor
    Dim constructor As ConstructorInfo = foundType.GetConstructor(New Type() {GetType(String), GetType(String), GetType(Boolean), GetType(Boolean)})
    If constructor IsNot Nothing Then
    ' parameters are: fileName, password, fixErrors, skipHatches
    Dim reader As Object = constructor.Invoke(New Object() {"app8.dwg", Nothing, False, False})

    ' invokes the DoWork() method
    Dim mi As MethodInfo = foundType.GetMethod("DoWork", New Type() {})
    mi.Invoke(reader, New Object() {})

    ' invokes the AddToScene() method
    mi = foundType.GetMethod("AddToScene", New Type() {GetType(ViewportLayout)})
    mi.Invoke(reader, New Object() {viewportLayout1})
    Else
    MessageBox.Show("Unable to get the ReadAutodesk constructor.")
    End If
    Else
    MessageBox.Show("Unable to get the ReadAutodesk class.")
    End If

    ' fits the drawing in the viewport
    viewportLayout1.ZoomFit()

    MyBase.OnLoad(e)
    End Sub

     

  • Working on a larger WPF solutions (10+ individual projects), I need DWG read in two of these project, and HiddenLinesViewOnFileAutodesk two other placed.

    The solution described in this thread, using reflection, works fine; but to avoid writing the same code in four projects, I wrote a little wrapper class, to take care of the work.

    Please find attached project, where devDept's BitnessAgnosticReadDWG_WPF is modified, using my wrapper class.

    In order to keep size down, the content of bin/debug/X86 and bin/debug/x64 folders are not included, please copy from your own installation, remember to replace dll's i root, if you use a different version/build)

    This sample is build with EyeShot NURBS edition build 250.

    Feel free to use this code as is, or modify/extend in anyway you like.

    Regards

    Flemming

  • Thanks Alex. Your solution worked for our product that was redesigned to build using AnyCPU.  

    As the x86 assembly is referenced by the built assembly, but missing (Copy Local=false), the MultiPlatformDllLoader class is looking for 'devDept.Eyeshot.Control.x86.dll'.  When installed on a 64-bit OS this this caused the software to fail as it could not find 'devDept.Eyeshot.Control.x86.dll' not (not installed) when the actual installed library was 'devDept.Eyeshot.Control.x64.dll'. 

    As a quick workaround for this we added a check for this specific file in MultiPlatformDllLoader after the code getting the assembly name:

    switch (assemblyName)
    {
    case "devDept.Eyeshot.Control.x86.dll":
    {
    if(Environment.Is64BitProcess)
    assemblyName = "devDept.Eyeshot.Control.x64.dll";
    }
    break;
    }

    This system also highlighted another issue that we had not previously identified.  When we changed to AnyCPU we found that ours was still running as 32-bit as the build setting of the project has a 'Prefer 32-bit' option that was checked.

    Hope this helps others.

  • Hi Richard,

    I’m glad the solution worked for your product. Since I posted this, I had similar problems as you.

    The 'Prefer 32-bit' option tricked me too. Since then, while debugging, in order to ensure I’m running in 64 bits, I added something like this in my ViewportLayout descendant:

            protected override void DrawOverlay(DrawSceneParams drawSceneParams)
            {
                base.DrawOverlay(drawSceneParams);
     
    #if DEBUG
                DrawDiagnosticInformation();
    #endif
            }

            private void DrawDiagnosticInformation()
            {
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.AppendLine($"{nameof(Environment.Is64BitProcess)}: {Environment.Is64BitProcess}");
                DrawText(10, Height - 10, stringBuilder.ToString(), DefaultFont, Color.Black, ContentAlignment.TopLeft);
            }

     

    Since we use many libraries that require platform specific version, we use a string,string dictionary in the assembly loader. This way we can easily add ‘name pairs’ of DLLs that don’t have the same name in X86 vs X64. Below is our latest code for the assembly loader.

    HTH

    Alex

    // Thanks to Yurik on http://stackoverflow.com/a/9951658

        public static class MultiPlatformAssemblyLoader
        {
            private const string X86 = "x86";
            private const string X64 = "x64";
            private const string LibraryExtension = ".DLL";

            private static readonly Dictionary<string, string> Assemblies = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) {
                {"devDept.Eyeshot.Control.x86.v9.dll", "devDept.Eyeshot.Control.x64.v9.dll"},
            };
     
            private static bool isEnabled;

            public static bool Enable
            {
                get { return isEnabled; }
                set
                {
                    lock (typeof (MultiPlatformAssemblyLoader))
                    {
                        if (isEnabled != value)

                        {
                            if (value)
                            {
                                AppDomain.CurrentDomain.AssemblyResolve += Resolver;
                            }
                            else
                            {
                                AppDomain.CurrentDomain.AssemblyResolve -= Resolver;
                            }

                            isEnabled = value;
                        }
                    }
                }
            }

            private static Assembly Resolver(object sender, ResolveEventArgs args)
            {
                string assemblyName = args.Name.Split(new[] {','}, 2)[0] + LibraryExtension;

                if (Assemblies.ContainsKey(assemblyName) && Environment.Is64BitProcess)
                {
                    assemblyName = Assemblies[assemblyName];
                }

                string platformSpecificPath = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, Environment.Is64BitProcess ? X64 : X86, assemblyName);

                return File.Exists(platformSpecificPath) ? Assembly.LoadFile(platformSpecificPath) : null;
            }
        }

     

Please sign in to leave a comment.