EPLAN-API

Alles rund um die API in EPLAN

EPLAN API: Preview in WPF

Da es nicht ganz so einfach ist in WPF eine Vorschau im Offline Programm zu implementieren, habe ich das mal in eine Klasse gepackt.

Mir ist aufgefallen dass der Speicher hochläuft und habe hier auf EPLAN getippt… Nach Rücksprache mit dem API-Support wurde mir gesagt dass es nicht der Speicher von EPLAN ist, sondern der Applikation, bzw. WPF.

Speicher

Ich habe hier mal die Generierung der Bitmap in Verdacht gezogen und siehe da, bekannter Bug.
Hier mal im Vergleich:

Speicher2

Auf GitHub findet ihr auch eine Demo-Applikation dazu.

Preview

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Eplan.EplApi.DataModel;
using System.Runtime.InteropServices;
using Eplan.EplApi.DataModel.MasterData;
using Eplan.EplApi.HEServices;

namespace Suplanus.Sepla.Gui
{
	public class Preview
	{
		private readonly Border _border;
		private readonly DrawingService _drawingService;
		private readonly Project _project;

		/// <summary>
		/// Init Preview object for WPF
		/// </summary>
		/// <param name="border"></param>
		/// <param name="projectFile"></param>
		public Preview(Border border, string projectFile)
		{
			var projectManager = new ProjectManager();
			projectManager.LockProjectByDefault = false;
			_project = projectManager.OpenProject(projectFile, ProjectManager.OpenMode.Exclusive);

			_drawingService = new DrawingService();
			_drawingService.DrawConnections = true;

			_border = border;
		}

		/// <summary>
		/// Display a file
		/// </summary>
		/// <param name="path">Full filename</param>
		/// <param name="previewType">Type of file</param>
		public void Display(string path, PreviewType previewType)
		{
			switch (previewType)
			{
				case PreviewType.WindowMacro:
					WindowMacro windowMacro = new WindowMacro();
					windowMacro.Open(path, _project);
					_drawingService.CreateDisplayList(windowMacro);
					DrawEplan();
					windowMacro.Dispose();
					break;

				case PreviewType.SymbolMacro:
					SymbolMacro symbolMacro = new SymbolMacro();
					symbolMacro.Open(path, _project);
					_drawingService.CreateDisplayList(symbolMacro);
					DrawEplan();
					symbolMacro.Dispose();
					break;

				case PreviewType.PageMacro:
					PageMacro pageMacro = new PageMacro();
					pageMacro.Open(path, _project);
					_drawingService.CreateDisplayList(pageMacro.Pages);
					DrawEplan();
					pageMacro.Dispose();
					break;

				default:
					throw new ArgumentOutOfRangeException(nameof(previewType), previewType, null);
			}

		}

		/// <summary>
		/// Draw EPLAN files
		/// </summary>
		private void DrawEplan()
		{
			int width = Convert.ToInt16(_border.ActualWidth);
			int height = Convert.ToInt16(_border.ActualHeight);

			if (width > 0 && height > 0)
			{
				System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(width, height);
				System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap);
				System.Drawing.Rectangle rectangle = new System.Drawing.Rectangle(0, 0, width, height);
				PaintEventArgs paintEventArgs = new PaintEventArgs(graphics, rectangle);

				_drawingService.DrawDisplayList(paintEventArgs);

				IntPtr hBitmap = bitmap.GetHbitmap();
				BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(hBitmap,
					IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());

				_border.Background = new ImageBrush(bitmapSource);

				bitmap.Dispose();
				graphics.Dispose();
				paintEventArgs.Dispose();
				DeleteObject(hBitmap);
			}
			else
			{
				_border.Background = null;
			}

		}

		/// <summary>
		/// Memory Leak: http://stackoverflow.com/questions/1546091/wpf-createbitmapsourcefromhbitmap-memory-leak
		/// </summary>
		/// <param name="hObject"></param>
		/// <returns></returns>
		[DllImport("gdi32.dll")]
		public static extern bool DeleteObject(IntPtr hObject);
	}

	
	/// <summary>
	/// Filetype to preview
	/// </summary>
	public enum PreviewType
	{
		WindowMacro,
		SymbolMacro,
		PageMacro
	}
}
Von |2017-11-09T11:23:52+01:002016-02-03|EPLAN, EPLAN-API|

EPLAN-API: Offline Applikation starten

Mit der Offline Applikation hat man man vielen Problemen zu kämpfen, angefangen beim Start…

Ich hab auf GitHub mal Methoden bereitgestellt um das zu vereinfach.

 

Verwendung WPF

var eplanOffline = new EplanOffline();
eplanOffline.StartWpf(this);
if (!eplanOffline.IsRunning)
{
	throw new NotImplementedException();
}
eplanOffline.Close();

Verwendung Windows Forms

var eplanOffline = new EplanOffline();
eplanOffline.StartWindowsForms(this);
if (!eplanOffline.IsRunning)
{
	throw new NotImplementedException();
}
eplanOffline.Close();

 

Ich habe bewusst darauf verzichtet dass alles im Konstruktor zu machen, da sonst WindowsForms in WPF und anders herum hinzugefügt werden muss.

Hier mal der Code dazu (Starter ist zum registrieren der Lizenz/DLLs):

using System.Collections.Generic;
using System.IO;
using System.Linq;
using Eplan.EplApi.Starter;

namespace Suplanus.Sepla.Application
{
    /// <summary>
    /// EPLAN Starter Helper: No other EPLAN-Namespaces are allowd
    /// </summary>
    public class Starter
    {
        public static string GetBinPathLastVersion()
        {
            List<EplanData> eplanVersions = new List<EplanData>();

            List<EplanData> eplanVersions32Bit = new List<EplanData>();
            new EplanFinder().GetInstalledEplanVersions(ref eplanVersions32Bit);
            eplanVersions.AddRange(eplanVersions32Bit);

            List<EplanData> eplanVersions64Bit = new List<EplanData>();
            new EplanFinder().GetInstalledEplanVersions(ref eplanVersions64Bit, true);
            eplanVersions.AddRange(eplanVersions64Bit);

            eplanVersions = new List<EplanData>(eplanVersions
                .Where(obj => obj.EplanVariant.Equals("Electric P8"))
                .OrderBy(obj => obj.EplanVersion));

            EplanData eplanData = eplanVersions.LastOrDefault();

            var binPath = Path.GetDirectoryName(eplanData.EplanPath);

            AssemblyResolver resolver = new AssemblyResolver();
            resolver.SetEplanBinPath(binPath);
            resolver.PinToEplan();

            return binPath;
        }
    }
}

 

using System;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Interop;
using Eplan.EplApi.System;
using Suplanus.Sepla.Gui;

namespace Suplanus.Sepla.Application
{
    public class EplanOffline
    {
        public EplApplication Application;

	    public Preview Preview;

		/// <summary>
		/// Starts EPLAN with the last version of Electric P8 and attach to (WPF) window
		/// </summary>
		/// <param name="window"></param>
		public void StartWpf(Window window)
	    {
			IntPtr handle = new WindowInteropHelper(window).Handle;
			string binPath = Starter.GetBinPathLastVersion();
			Start(handle,binPath);
		}

		/// <summary>
		/// Starts EPLAN with the last version of Electric P8 and attach to (WF) form
		/// </summary>
		/// <param name="form"></param>
		public void StartWindowsForms(Form form)
		{
			IntPtr handle = form.Handle;
			string binPath = Starter.GetBinPathLastVersion();
			Start(handle, binPath);
		}

		/// <summary>
		/// Starts EPLAN with the given version of program variant and attach to (WPF) window
		/// </summary>
		/// <param name="window"></param>
		/// <param name="binPath"></param>
		public void StartWpf(Window window, string binPath)
		{
			IntPtr handle = new WindowInteropHelper(window).Handle;
			Start(handle, binPath);
		}

		/// <summary>
		/// Returns if the EPLAN-Application is running
		/// </summary>
		public bool IsRunning
        {
            get { return Application != null; }
        }

	    

	    /// <summary>
		/// Starts the application
		/// </summary>
		/// <param name="handle"></param>
		/// <param name="binPath"></param>
        private void Start(IntPtr handle, string binPath)
        {
            if (Application == null)
            {
                try
                {
                    EplApplication eplApplication = new EplApplication();
                    eplApplication.EplanBinFolder = binPath;
                    eplApplication.ResetQuietMode();
                    eplApplication.SetMainFrame(handle);
                    eplApplication.Init("", true, true);
                    Application = eplApplication;
                }
                catch
                {
                    Application = null;
                } 
            }
        }

		/// <summary>
		/// Release all objects
		/// </summary>
        public void Close()
        {
            if (Application != null)
            {
                Application.Exit();
                Application = null;
            }
        }
    }
}

In diesem Code wird immer die aktuellste Version von EPLAN Electric P8 verwendet. Ich bau irgendwann noch Methoden zur Auswahl von der Lizenz / Version.

Von |2017-11-09T11:23:59+01:002016-01-28|EPLAN, EPLAN-API|

Suplanus Eplan Api… Sepla

Da ich demnächst mehr mit der EPLAN API arbeiten darf, hab ich die Entscheidung getroffen einen Teil davon Open Source zu stellen.

Es handelt sich um eine Standard Library um gewisse Abläufe zu vereinfachen.
Der Name ist nicht gerade kreativ gewählt, aber das Kind braucht ja einen Namen :)
Würde mich freuen wenn ihr auch daran mitarbeitet! Man findet alles auf GitHub

Als erstes möchte ich gleich die Implementierung einer Offline-Application vorstellen.
Es ist nicht ganz einfach ein Programm mit EPLAN Anbindung zu schreiben. Der Aufruf mit Sepla erfolgt wie folgt:

string binPath = Starter.GetBinPath();
IntPtr handle = new WindowInteropHelper(this).Handle;
EplanOffline eplanOffline = new EplanOffline();
eplanOffline.Start(handle, binPath);
if (!eplanOffline.IsRunning)
{
    throw new NotImplementedException();
}

Geschlossen wird das ganze dann über:

eplanOffline.Close();

 

Im Hintergrund arbeiten zwei Klassen, damit eine Trennung gewährleistet ist. Denn der Resolver muss erst die Assemblies laden.

Starter.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Eplan.EplApi.Starter;

namespace Suplanus.Sepla.Application
{
    /// <summary>
    /// EPLAN Starter Helper: No other EPLAN-Namespaces are allowd
    /// </summary>
    public class Starter
    {
        public static string GetBinPath()
        {
            List<EplanData> eplanVersions = new List<EplanData>();

            List<EplanData> eplanVersions32bit = new List<EplanData>();
            new EplanFinder().GetInstalledEplanVersions(ref eplanVersions32bit);
            eplanVersions.AddRange(eplanVersions32bit);

            List<EplanData> eplanVersions64bit = new List<EplanData>();
            new EplanFinder().GetInstalledEplanVersions(ref eplanVersions64bit, true);
            eplanVersions.AddRange(eplanVersions64bit);

            eplanVersions = new List<EplanData>(eplanVersions
                .Where(obj => obj.EplanVariant.Equals("Electric P8"))
                .OrderBy(obj => obj.EplanVersion));

            EplanData eplanData = eplanVersions.LastOrDefault();

            var binPath = Path.GetDirectoryName(eplanData.EplanPath);

            AssemblyResolver resolver = new AssemblyResolver();
            resolver.SetEplanBinPath(binPath);
            resolver.PinToEplan();

            return binPath;
        }
    }
}

 

EplanOffline.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Eplan.EplApi.Starter;
using Eplan.EplApi.System;

namespace Suplanus.Sepla.Application
{
    public class EplanOffline
    {
        public EplApplication Application;

        public bool IsRunning
        {
            get { return Application != null; }
        }

        public void Start(IntPtr handle, string binPath)
        {
            if (Application == null)
            {
                try
                {
                    EplApplication eplApplication = new EplApplication();
                    eplApplication.EplanBinFolder = binPath;
                    eplApplication.ResetQuietMode();
                    eplApplication.SetMainFrame(handle);
                    eplApplication.Init("", true, true);
                    Application = eplApplication;
                }
                catch
                {
                    Application = null;
                } 
            }
        }

        public void Close()
        {
            if (Application != null)
            {
                Application.Exit();
                Application = null;
            }
        }
    }
}

Suplanus.Sepla auf GitHub

Von |2017-11-09T11:24:05+01:002015-11-19|EPLAN, EPLAN-API|

Postbuild Befehlszeile ApiAddin

Ich muss selber immer nachschauen oder kopier mir es, darum schreib ich es hier mal auf :^)

Bei einem EPLAN ApiAddin ist es praktisch wenn die fertige DLL gleich ins Zielverzeichnis kopiert wird. In Visual Studio kann man das per Postbuild-Befehlszeile automatisieren:

COPY /Y "$(TargetDir)$(ProjectName).dll" "C:\Program Files (x86)\EPLAN\Platform\2.4.4\Bin\$(ProjectName).dll"

2015-04-28_09-46-33

Kurz zur Erklärung:

  • /Y: Unterdrückt die Bestätigungsaufforderung vor dem Überschreiben vorhandener Zieldateien.
  • $(TargetDir): Eingestellte Ausgabeverzeichnis
  • $(ProjectName): Projektname (sollte dem der DLL entsprechen)
Von |2017-11-09T11:24:11+01:002015-04-28|EPLAN, EPLAN-API|

Automatischer Action-Name ApiAddin

Da ich derzeit viel mit der EPLAN-API machen darf, hab ich mir mal Gedanken gemacht wie man das ewige Action deklarieren automatisieren kann.

Die Deklaration funktioniert ein bisschen anders als im Scripting und somit kann man auch auf die Methoden- / bzw. Klassennamen zugreifen.

Mit dieser Lösung wird immer der Klassenname als Action-Name verwendet, was für mich immer zutreffend ist.
In diesem Beispiel wäre der Action-Name MyAction:

using Eplan.EplApi.ApplicationFramework;
using System;
using System.Reflection;

namespace Suplanus.EplAddin.Examples
{
    class MyAction : IEplAction
    {
        public bool OnRegister(ref string Name, ref int Ordinal)
        {
            Name = MethodBase.GetCurrentMethod().DeclaringType.Name; // Get name from class
            Ordinal = 20;
            return true;
        }

        public bool Execute(ActionCallingContext oActionCallingContext)
        {
            throw new NotImplementedException();
        }

        public void GetActionProperties(ref ActionProperties actionProperties)
        {
            throw new NotImplementedException();
        }
    }
}
Von |2017-11-09T11:24:17+01:002015-04-14|EPLAN, EPLAN-API|
Nach oben