Sparx EA .feap projects images saved as blank - c#

In addin for Sparx EA I use this code to get pictures and assign to entity. Then I use images from entities, to, as example, save at some folders or insert in word report etc (from this answer)
/// <summary>
/// Access to diagram image without using clipboard
/// </summary>
/// <param name="projectInterface">Ea Sparx interface</param>
/// <param name="eaDiagramGuid">Guid of the diagramm</param>
/// <returns></returns>
public static Image GetDiagramImage(this Project projectInterface, Guid eaDiagramGuid, ApplicationLogger _logger)
{
Image diagramImage;
try
{
var diagramByGuid = projectInterface.GUIDtoXML(eaDiagramGuid.ToString("B"));
string tempFilename = string.Format("{0}{1}{2}", Path.GetTempPath(), Guid.NewGuid().ToString(), ".png");
bool imageToFileSuccess = projectInterface.PutDiagramImageToFile(diagramByGuid, tempFilename, FileExtensionByName);
if (imageToFileSuccess)
{
using (var imageStream = new MemoryStream(File.ReadAllBytes(tempFilename)))
{
diagramImage = Image.FromStream(imageStream);
}
File.Delete(tempFilename);
}
else
{
throw new Exception(string.Format("Image to file exprot fail {0}", projectInterface.GetLastError()));
}
}
catch (Exception e)
{
throw;
}
return diagramImage;
}
The problem is - it works if project I work with saved as .eap file.
If it's .feap file, which, as I believe means that it works with Firebird database (instead of Access), all saved/exproted to report images are blank, like this down below
Why does it happens and is there workaround?
UPD
It works if I use projectInterface.PutDiagramImageOnClipboard instead but I don't wont to use clipboard at all
UPD 2
After some experiments today at the morning (at my timezone, gmt+3, it's still morning) I found out that the problem was with GUIDs register!
After I decided to apply .toUpper() here
var diagramByGuid = projectInterface.GUIDtoXML(eaDiagramGuid.ToString("B").ToUpper());
it started work fine!
Strange thing thou that if project is *.EAP type everything works even when guid is not in upper register!
UPD3
Well, unfortunately, I was wrong. Some pictures are still blank. But somehow that changes got impact on diagrams, I keep testing this stuff.
And some of the pictures are appeared twice or in wrong place.
But it's kinda interesting (if I could say so) behaviour.
UPD 4
I was wrong in my UPD2 part! GUID can contain down register symbols as well as upper ones.
So, first I removed that part.
What I done next - I started to pass GUID directly from diagram, so signature changed like that
public static Image GetDiagramImage(this Project projectInterface, string eaDiagramGuid, ApplicationLogger _logger)
and eaDiagramGuid should be passed right from EA.Diagram object.
When we parse it as Guid by Guid.Parse(eaDiagramGuid) it convert everything in lowercase like here
so, thats why I got the problem.
But for some reason it was not appeared in *.EAP type of projects!
Also it strange that register matters in that case, really. Seems like GUID in common and GUID in sparx ea are different things!

Okay, as I founded out here, the thing is, in case of *.FEAP all items GUIDs are surprisingly case sensetive.
So, my mistake was to store item GUID as Guid type - when we use Guid.Parse(myGuidString) function and then we converting it back to string - all symbols are appers to be in lowercase.
Also, I got my answer from support (they were surprisingly fast, I really like that)
Hello Danil,
Running over the Stack Overflow - it sounds like it's effectively
answered. The core point is that the Feap files are case sensitive.
To be technical, the collation used for our Firebird databases is case
sensitive.
So, in the Automation script you do need to adhere to the case.
So, I just change things to work directly with GUID string from project item like
Image diagramImage = _eaProjectInterface.GetDiagramImage(diagram.DiagramGUID, _logger);
and function now look like this
public static Image GetDiagramImage(this Project projectInterface, string eaDiagramGuid, ApplicationLogger _logger)
{
Image diagramImage;
try
{
var diagramByGuid = projectInterface.GUIDtoXML(eaDiagramGuid);
string tempFilename = string.Format("{0}{1}{2}", Path.GetTempPath(), Guid.NewGuid().ToString(), ".png");
bool imageToFileSuccess = projectInterface.PutDiagramImageToFile(diagramByGuid, tempFilename, FileExtensionByName);
if (imageToFileSuccess)
{
using (var imageStream = new MemoryStream(File.ReadAllBytes(tempFilename)))
{
diagramImage = Image.FromStream(imageStream);
}
File.Delete(tempFilename);
}
else
{
throw new Exception(string.Format("Image to file exprot fail {0}", projectInterface.GetLastError()));
}
}
catch (Exception e)
{
throw;
}
return diagramImage;
}
So, this means that Sparx EA *.FEAP project GUIDs are NOT really GUIDs but just string-keys (as I assume).
Be careful when your work with them :-)

Related

Xcode Cocoa - Drag Drop from NSTableView to Finder

I want my MacOS App to be able to Drag a item from NSTableView to an other Application like Logic Pro X, Finder, etc.
The items in this TableViews are classes I created which are representing Files on my HD.
public class AudioFile
{
#region Computed Propoperties
public string Filename { get; set; } = "";
public string Filepath { get; set; } = "";
#endregion
public AudioFile()
{
}
public AudioFile(string filename, string filepath)
{
this.Filename = filename;
this.Filepath = filepath;
}
}
Unfortunately I can't find a solution for Swift or Objective-C which I could translate to C# (Xamarin). Does anyone know one or has some code that could help here?
Thanks for your help!
I know nothing about C#, but you asked for a solution in Swift or Objective-C. That I can help with! The below is Swift 4.
First of all, make sure your ViewController is the table view's data source:
class ViewController: NSViewController, NSTableViewDataSource
You will also need to make that connection either in code or in IB.
You then need to set your table view as a dragging source. Choose the operation you want, usually either .move or .copy:
tableView.setDraggingSourceOperationMask(.move, forLocal: false)
This example assumes that you're using an ArrayController to manage the content of the tableView. You really should be, it makes a host of things easier. Also, this example is for dragging multiple files. (It will work for a single file, but there are other approaches if you only ever want to drag one.)
In your ViewController class, implement this method:
func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
var filePaths = [String]()
// Swift 4 hack--the FilenamesPboardType is missing
let NSFilenamesPboardTypeTemp = NSPasteboard.PasteboardType("NSFilenamesPboardType")
pboard.addTypes([NSFilenamesPboardTypeTemp], owner: nil)
if let audioFiles = audioFilesArrayController.arrangedObjects as? [AudioFile] {
for i in rowIndexes {
filePaths.append(audioFiles[i].Filepath)
}
}
pboard.setPropertyList(filePaths, forType: NSFilenamesPboardTypeTemp)
return true
}
You can learn more about the NSFilenamesPboardTypeTemp hack here.
And that's it! Recompile and you should be able to move one or more of your files by dragging them to a Finder window. Simple. :-)

How to create transform for .msi file using c#

I'm trying to create a transform for .msi file in C#. Here is my code:
public static void CreateTransforms(string original_MSI, string backup_MSI, string MSTpath, string query)
{
File.Copy(original_MSI, backup_MSI, true);
using (var origDatabase = new Microsoft.Deployment.WindowsInstaller.Database(original_MSI, DatabaseOpenMode.ReadOnly))
{
using (var database = new Microsoft.Deployment.WindowsInstaller.Database(backup_MSI, DatabaseOpenMode.Direct))
{
//database.Execute("Update `Property` Set `Property`.`Value` = 'Test' WHERE `Property`.`Property` = 'ProductName'");
database.Execute(query);
database.GenerateTransform(origDatabase, MSTpath);
database.CreateTransformSummaryInfo(origDatabase, MSTpath, TransformErrors.None, TransformValidations.None);
}
}
}
I got the following error : "This installation package could not be opened. Contact the application vendor to verify that this is a valid Windows Installer package." in the step create transform summary info. I used "Microsoft.Deployment.WindowsInstaller.dll" library. Any help would be great.
A quick read of this static method looked correct so I created a console app out of it. It works fine for me on my machine. I would look at your calling method and make sure the data being passed is correct. I get really nervous any time I have a method that takes 4 strings as arguments as that leaves a lot to desire in terms of type safety.
when CreateTransforms start, it open database and it does not close it ...
you must commit and close the database before apply a new transform!
database.GenerateTransform(origDatabase, TRANSFORM);
database.CreateTransformSummaryInfo(origDatabase, TRANSFORM, TransformErrors.None, TransformValidations.None);
database.Commit();
database.Close();

Delving into the world of XML (Windows Phone) Error I dont understand (The ' ' character, hexadecimal value 0x20, cannot be included in a name.)

So I am starting to learn how to use XML data within a app and decided to use some free data to do this however I cannot for the life of me get it working this is my code so far. (I have done a few apps with static data before but hey apps are designed to use the web right? :p)
public partial class MainPage : PhoneApplicationPage
{
List<XmlItem> xmlItems = new List<XmlItem>();
// Constructor
public MainPage()
{
InitializeComponent();
LoadXmlItems("http://hatrafficinfo.dft.gov.uk/feeds/datex/England/CurrentRoadworks/content.xml");
test();
}
public void test()
{
foreach (XmlItem item in xmlItems)
{
testing.Text = item.Title;
}
}
public void LoadXmlItems(string xmlUrl)
{
WebClient client = new WebClient();
client.OpenReadCompleted += (sender, e) =>
{
if (e.Error != null)
return;
Stream str = e.Result;
XDocument xdoc = XDocument.Load(str);
***xmlItems = (from item in xdoc.Descendants("situation id")
select new XmlItem()
{
Title = item.Element("impactOnTraffic").Value,
Description = item.Element("trafficRestrictionType").Value
}).ToList();***
// close
str.Close();
// add results to the list
xmlItems.Clear();
foreach (XmlItem item in xmlItems)
{
xmlItems.Add(item);
}
};
client.OpenReadAsync(new Uri(xmlUrl, UriKind.Absolute));
}
}
I am basically trying to learn how to do this at the moment as I am intrigued how to actually do it (I know there are many ways but ATM this way seems the easiest) I just don't get what the error is ATM. (The bit in * is where it says the error is)
I also know the display function ATM is not great (As it will only show the last item) but for testing this will do for now.
To some this may seem easy, as a learner its not so easy for me just yet.
The error in picture form:
(It seems I cant post images :/)
Thanks in advance for the help
Edit:
Answer below fixed the error :D
However still nothing is coming up. I "think" it's because of the XML layout and the amount of descendants it has (Cant work out what I need to do being a noob at XML and pulling it from the web as a data source)
Maybe I am starting too complicated :/
Still any help/tips on how to pull some elements from the feed (As there all in Descendants) correctly and store them would be great :D
Edit2:
I have it working (In a crude way) but still :D
Thanks Adam Maras!
The last issue was the double listing. (Adding it to a list, to then add it to another list was causing a null exception) Just using the 1 list within the method solved this issue, (Probably not the best way of doing it but it works for now) and allowed for me to add the results to a listbox until I spend some time working out how to use ListBox.ItemTemplate & DataTemplate to make it look more appealing. (Seems easy enough I say now...)
Thanks Again!!!
from item in xdoc.Descendants("situation id")
// ^
XML tag names can't contain spaces. Looking at the XML, you probably just want "situation" to match the <situation> elements.
After looking at your edit and further reviewing the XML, I figured out what the problem is. If you look at the root element of the document:
<d2LogicalModel xmlns="http://datex2.eu/schema/1_0/1_0" modelBaseVersion="1.0">
You'll see that it has a default namespace applied. The easiest solution to your problem will be to first get the namespsace from the root element:
var ns = xdoc.Root.Name.Namespace;
And then apply it wherever you're using a string to identify an element or attribute name:
from item in xdoc.Descendants(ns + "situation")
// ...
item.Element(ns + "impactOnTraffic").Value
item.Element(ns + "trafficRestrictionType").Value
One more thing: <impactOnTraffic> and <trafficRestrictionType> aren't direct children of the <situation> element, so you'll need to change that code as well:
Title = items.Descendants(ns + "impactOnTraffic").Single().Value,
Description = item.Descendants(ns + "trafficRestrictionType").Single().Value

How can I persist data for an Excel Ribbon Addin in Excel

I have created an Excel ribbon addin which needs to persist user selections between Excel sessions. Using custom XML Parts seems to be the best option for this. However I can't get this to work without getting COMExceptions.
The MSDN docs are just not very useful (http://msdn.microsoft.com/en-us/library/bb608612.aspx). Can someone give me an example of making this work in an Excel Ribbon addin?
There are three different methods I know of:
Custom XML parts
For an application-level add in, this is my preferred method of storing any application data that needs to be persisted in a saved xls file without ever being visible to the user.
http://msdn.microsoft.com/en-us/library/bb608612.aspx
Cached Data Islands
This only works for document-level add ins. You will get exceptions if you try to use it in an application-level add in.
http://blogs.msdn.com/b/eric_carter/archive/2004/04/23/119294.aspx
Hidden worksheet
Using VSTO, you can create invisible worksheets that cannot be seen by users. This works well, but leads to a lot of awkward coding to convert your data to fit in an excel sheet.
Update (2014):
So in the end the usage of Custom XML parts turned out to be a performance issue so my application had to be changed back to use hidden worksheets. Apparently, once the XML reaches a certain size, Excel becomes very sluggish. My add-in had Custom Parts reach thousands of nodes, and the larger the XML grew, the slower everything in Excel became. For example, simply clicking on any cell would incur a very noticeable delay.
If you want to store any kind of metadata from an application level add-in with a specific document, you can serialise data to some kind of string (base64, xml etc.), and save it in a "very hidden" sheet. A worksheet with visibility set to "very hidden" will only be accessable via the programming API, so even if the user uncovers hidden sheets they will still not be able to access it, or indeed even know that it is there.
// create sheet for this save
workbook.Sheets.Add();
newSettingsWorksheet = workbook.ActiveSheet;
newSettingsWorksheet.Name = hiddenSheetName;
newSettingsWorksheet.Visible = Excel.XlSheetVisibility.xlSheetVeryHidden;
One important thing to note, if you are storing a string longer than 32767 characters (the max number of characters that fit in a cell) then you will have to cut it into chunks and spread it across several cells.
Regarding the COM exceptions you are experiencing, you should be aware that Excel can throw a COM exception for ANY request that touches a COM object (e.g. a worksheet, a cell, or anything belonging to Excel) at ANY time if it is busy with another request (e.g. user is typing, it is recalculating formulae). Exceptions you can expect are:
HRESULT: 0x800AC472 (ignore)
HRESULT: 0x8000101A (retry later)
I guess the Excel app developers do this so an add-in can't make Excel itself look bad / unresponsive.
You should use the registry to store bits of information such as user preferences and history that need to persist after the application is shut down or that needs to be shared between multiple instances.
The user's hive (HKEY_CURRENT_USER) will never have permission problems. Just refer to the .NET Registry Class: http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.aspx
Consider using custom properties. Each Excel sheet maintains, behind the scene, lists of properties that are easily used by the programmer. For instance, I've used custom properties to "remember" what items in ribbon drop down lists were chosen for a particular sheet; when the sheet changes, pull up the custom property for that sheet to find out what drop down items were picked when it was last active.
Custom properties persist with each sheet and the document.
using System;
using Microsoft.Office.Interop.Excel;
public partial class CustPropExample
{
/// <summary>
/// delete and then store the custom property by passed key and value
/// </summary>
bool bExcelCustProp_Replace(Worksheet wkSheet,
string custPropKey,
string custPropVal)
{
if (!ExcelCustProp_DeleteByKey(wkSheet, custPropKey))
return (false);
if (!ExcelCustProp_Add(wkSheet, custPropKey, custPropVal))
return (false);
return (true);
}
/// <summary>
/// return the custom property value of passed key
/// </summary>
string ExcelCustProp_Get(Worksheet wkSheet,
string key)
{
try
{
for (int i = 1; i <= wkSheet.CustomProperties.Count; i++) // NOTE: 1-based !!!!!!!!
{
if (wkSheet.CustomProperties.get_Item(i).Name == key)
return (wkSheet.CustomProperties.get_Item(i).Value);
}
}
catch (Exception ex)
{
ShowErrorMsg("Error with getting cust prop; key [" + key + "], exc: " + ex.Message, false);
}
return (string.Empty);
}
/// <summary>
/// add cust prop
/// </summary>
bool ExcelCustProp_Add(Worksheet wkSheet,
string key,
string custPropVal)
{
try
{
wkSheet.CustomProperties.Add(key, custPropVal);
}
catch (Exception ex)
{
return(ShowErrorMsg("Error in adding cust prop: " + ex.Message, false));
}
return (true);
}
/// <summary>
/// if passed key exists, delete it
/// </summary>
bool ExcelCustProp_DeleteByKey(Worksheet wkSheet,
string key)
{
try
{
for (int i = 1; i <= wkSheet.CustomProperties.Count; i++) // NOTE: 1-based !!!!!!!!
{
if (wkSheet.CustomProperties.Item[i].Name == key)
{
wkSheet.CustomProperties.Item[i].Delete();
break;
}
}
}
catch (Exception ex)
{
return(ShowErrorMsg("Error deleting cust prop (key='" + key + "') - " + ex.Message, false));
}
return (true);
}
/// <summary>
/// stub for error handling
/// </summary>
bool ShowErrorMsg(string msg,
bool retval)
{
System.Windows.Forms.MessageBox.Show(msg);
return (retval);
}
}

Add an attachment to Bugzilla using XML-RPC in VBA

I am currently developing an Excel macro which allows creating Bugs in a Bugzilla instance.
After some trial and error this now turns out to work fine.
I wanted to enhance the client so that it's also possible to add screenshots to the newly created bug.
The environment I'm using is a little bit tricky:
I have to use MS Excel for my task.
As Excel does not understand XML-RPC, I downloaded an interface DLL (CookComputing.XmlRpcV2.dll from xml-rpc.net) which makes the XML-RPC interface accessible from .NET.
Then I created an additional DLL which can be called from Excel macros (using COM interop).
As already mentioned, this is working fine for tasks like browsing or adding new bugs.
But when adding an attachment to the bug, the image must be converted into a base64 data type. Although this seems to work fine and although the creation of the screenshot seems to succeed, the image seems to be corrupted and cannot be displayed.
Here's what I do to add the image:
The Bugzilla add_attachment method accepts a struct as input:
http://www.bugzilla.org/docs/4.0/en/html/api/Bugzilla/WebService/Bug.html#add_attachment.
This type was defined in C# and is visible also in VBA.
This is the struct definition:
[ClassInterface(ClassInterfaceType.AutoDual)]
public class TAttachmentInputData
{
public string[] ids;
public string data; // base64-encoded data
public string file_name;
public string summary;
public string content_type;
public string comment;
public bool is_patch;
public bool is_private;
public void addId(int id)
{
ids = new string[1];
ids[0] = id.ToString();
}
public void addData(string strData)
{
try
{
byte[] encData_byte = new byte[strData.Length];
encData_byte = System.Text.Encoding.ASCII.GetBytes(strData);
string encodedData = Convert.ToBase64String(encData_byte);
data = new Byte[System.Text.Encoding.ASCII.GetBytes(encodedData).Length];
data = System.Text.Encoding.ASCII.GetBytes(encodedData);
}
catch (Exception e)
{
throw new Exception("Error in base64Encode" + e.Message);
}
}
This is the part in my macro where I would like to add the attachment:
Dim attachmentsStruct As New TAttachmentInputData
fname = attachmentFileName
attachmentsStruct.file_name = GetFilenameFromPath(fname)
attachmentsStruct.is_patch = False
attachmentsStruct.is_private = False
'multiple other definitions
Open fname For Binary As #1
attachmentsStruct.addData (Input(LOF(1), #1))
Close #1
attachmentsStruct.file_name = GetFilenameFromPath(fname)
Call BugzillaClass.add_attachment(attachmentsStruct)
Where BugzillaClass it the interface exposed from my DLL to Excel VBA.
The method add_attachment refers to the XML-RPC method add_attachment.
I assume that my problem is the conversion from the binary file into base64.
This is done using the addData method in my C# DLL.
Is the conversion done correctly there?
Any idea why the images are corrupted?
I think the issue is that you are reading in binary data in the macro, but the addData method is expecting a string. Try declaring the parameter in addData as byte[].

Categories