Convert all Solidworks files in folder to step files macro - macros

I was searching around and looking for a macro that will when run it will convert the files in the location into .stp files and I came across the below. how can i manipulate it to grab the next file in the folder and continue the next files and convert them until all the files have been converted.
Dim swApp As Object
Dim Part As Object
Dim FilePath As String
Dim sFilePath As String
Dim PathSize As Long
Dim PathNoExtention As String
Dim NewFilePath As String
Dim FileLocation As String
Dim sPath As String
Dim i As Long
Dim bRebuild As Boolean
Dim bRet As Boolean
Dim sRev As String
Dim nErrors As Long
Dim nWarnings As Long
Sub main()
Set swApp = Application.SldWorks
Set Part = swApp.ActiveDoc
FilePath = Part.GetPathName
PathSize = Strings.Len(FilePath)
sPath = Left(Part.GetPathName, InStrRev(Part.GetPathName, "\"))
sRev = Part.CustomInfo("re") 'Change Configuration Property name here
FileLocation = "C:"
PathNoExtension = Strings.Left(FilePath, PathSize - 7)
Part.SaveAs (PathNoExtension & "rev" & sRev & ".step")
End Sub

You could do this a few different ways if you're not using a VB6 Macro. If you use a .NET Macro (Visual Basic or C#), they support .NET libraries which makes this process quite simple. I've created the following Console Application in C#. You could create the same thing as a .NET Macro in SolidWorks. The important thing to add to the example you provided is the foreach statement which will iterate over all of the files in the directory and only perform the translation on SolidWorks Parts or Assemblies.
using SolidWorks.Interop.sldworks;
using System;
using System.IO;
namespace CreateStepFiles
{
class Program
{
static SldWorks swApp;
static void Main(string[] args)
{
string directoryName = GetDirectoryName();
if (!GetSolidWorks())
{
return;
}
int i = 0;
foreach (string fileName in Directory.GetFiles(directoryName))
{
if (Path.GetExtension(fileName).ToLower() == ".sldprt")
{
CreateStepFile(fileName, 1);
i += 1;
}
else if (Path.GetExtension(fileName).ToLower() == ".sldasm")
{
CreateStepFile(fileName, 2);
i += 1;
}
}
Console.WriteLine("Finished converting {0} files", i);
}
static void CreateStepFile(string fileName, int docType)
{
int errors = 0;
int warnings = 0;
ModelDoc2 swModel = swApp.OpenDoc6(fileName, docType, 1, "", ref errors, ref warnings);
string stepFile = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName), ".STEP");
swModel.Extension.SaveAs(stepFile, 0, 1, null, ref errors, ref warnings);
Console.WriteLine("Created STEP file: " + stepFile);;
swApp.CloseDoc(fileName);
}
static string GetDirectoryName()
{
Console.WriteLine("Directory to Converty");
string s = Console.ReadLine();
if (Directory.Exists(s))
{
return s;
}
Console.WriteLine("Directory does not exists, try again");
return GetDirectoryName();
}
static bool GetSolidWorks()
{
try
{
swApp = (SldWorks)Activator.CreateInstance(Type.GetTypeFromProgID("SldWorks.Application"));
if (swApp == null)
{
throw new NullReferenceException(nameof(swApp));
}
if (!swApp.Visible)
{
swApp.Visible = true;
}
Console.WriteLine("SolidWorks Loaded");
return true;
}
catch (Exception)
{
Console.WriteLine("Could not launch SolidWorks");
return false;
}
}
}
}

Related

Open OSM pbf results in Protobuf exception

Using OSMSharp I am having trouble to open a stream for a file (which I can provide on demand)
The error occurs in PBFReader (line 104)
using (var tmp = new LimitedStream(_stream, length))
{
header = _runtimeTypeModel.Deserialize(tmp, null, _blockHeaderType) as BlobHeader;
}
and states: "ProtoBuf.ProtoException: 'Invalid field in source data: 0'" which might mean different things as I have read in this SO question.
The file opens and is visualized with QGis so is not corrupt in my opinion.
Can it be that the contracts do not match? Is OsmSharp/core updated to the latest .proto files for OSM from here (although not sure if this is the real original source for the definition files).
And what might make more sense, can it be that the file I attached is generated for v2 of OSM PBF specification?
In the code at the line of the exception I see the following comment which makes me wonder:
// TODO: remove some of the v1 specific code.
// TODO: this means also to use the built-in capped streams.
// code borrowed from: http://stackoverflow.com/questions/4663298/protobuf-net-deserialize-open-street-maps
// I'm just being lazy and re-using something "close enough" here
// note that v2 has a big-endian option, but Fixed32 assumes little-endian - we
// actually need the other way around (network byte order):
// length = IntLittleEndianToBigEndian((uint)length);
BlobHeader header;
// again, v2 has capped-streams built in, but I'm deliberately
// limiting myself to v1 features
So this makes me wonder if OSM Sharp is (still) up-to-date.
My sandbox code looks like this:
using OsmSharp.Streams;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OsmSharp.Tags;
namespace OsmSharp
{
class Program
{
private const string Path = #"C:\Users\Bernoulli IT\Documents\Applications\Argaleo\Test\";
private const string FileNameAntarctica = "antarctica-latest.osm";
private const string FileNameOSPbf = "OSPbf";
private const Boolean useRegisterSource = false;
private static KeyValuePair<string, string> KeyValuePair = new KeyValuePair<string, string>("joep", "monita");
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
//string fileName = $#"{Path}\{FileNameAntarctica}.pbf";
string fileName = $#"{Path}\{FileNameOSPbf}.pbf";
string newFileName = $"{fileName.Replace(".pbf", string.Empty)}-{Guid.NewGuid().ToString().Substring(0, 4)}.pbf";
Console.WriteLine("*** Complete");
string fileNameOutput = CompleteFlow(fileName, newFileName);
Console.WriteLine("");
Console.WriteLine("*** Display");
DisplayFlow(fileNameOutput);
Console.ReadLine();
}
private static string CompleteFlow(string fileName, string newFileName)
{
// 1. Open file and convert to bytes
byte[] fileBytes = FileToBytes(fileName);
// 2. Bytes to OSM stream source (pbf)
PBFOsmStreamSource osmStreamSource;
osmStreamSource = BytesToOsmStreamSource(fileBytes);
osmStreamSource.MoveNext();
if (osmStreamSource.Current() == null)
{
osmStreamSource = FileToOsmStreamSource(fileName);
osmStreamSource.MoveNext();
if (osmStreamSource.Current() == null)
{
throw new Exception("No current in stream.");
}
}
// 3. Add custom tag
AddTag(osmStreamSource);
// 4. OSM stream source to bytes
//byte[] osmStreamSourceBytes = OsmStreamSourceToBytes(osmStreamSource);
// 5. Bytes to file
//string fileNameOutput = BytesToFile(osmStreamSourceBytes, newFileName);
OsmStreamSourceToFile(osmStreamSource, newFileName);
Console.WriteLine(newFileName);
return newFileName;
}
private static void DisplayFlow(string fileName)
{
// 1. Open file and convert to bytes
byte[] fileBytes = FileToBytes(fileName);
// 2. Bytes to OSM stream source (pbf)
BytesToOsmStreamSource(fileBytes);
}
private static byte[] FileToBytes(string fileName)
{
Console.WriteLine(fileName);
return File.ReadAllBytes(fileName);
}
private static PBFOsmStreamSource BytesToOsmStreamSource(byte[] bytes)
{
MemoryStream memoryStream = new MemoryStream(bytes);
memoryStream.Position = 0;
PBFOsmStreamSource osmStreamSource = new PBFOsmStreamSource(memoryStream);
foreach (OsmGeo element in osmStreamSource.Where(osmGeo => osmGeo.Tags.Any(tag => tag.Key.StartsWith(KeyValuePair.Key))))
{
foreach (Tag elementTag in element.Tags.Where(tag => tag.Key.StartsWith(KeyValuePair.Key)))
{
Console.WriteLine("!!!!!!!!!!!!!! Tag found while reading !!!!!!!!!!!!!!!!!!".ToUpper());
}
}
return osmStreamSource;
}
private static PBFOsmStreamSource FileToOsmStreamSource(string fileName)
{
using (FileStream fileStream = new FileInfo(fileName).OpenRead())
{
PBFOsmStreamSource osmStreamSource = new PBFOsmStreamSource(fileStream);
return osmStreamSource;
}
}
private static void AddTag(PBFOsmStreamSource osmStreamSource)
{
osmStreamSource.Reset();
OsmGeo osmGeo = null;
while (osmGeo == null)
{
osmStreamSource.MoveNext();
osmGeo = osmStreamSource.Current();
if(osmGeo?.Tags == null)
{
osmGeo = null;
}
}
osmGeo.Tags.Add("joep", "monita");
Console.WriteLine($"{osmGeo.Tags.FirstOrDefault(tag => tag.Key.StartsWith(KeyValuePair.Key)).Key} - {osmGeo.Tags.FirstOrDefault(tag => tag.Key.StartsWith(KeyValuePair.Key)).Value}");
}
private static byte[] OsmStreamSourceToBytes(PBFOsmStreamSource osmStreamSource)
{
MemoryStream memoryStream = new MemoryStream();
PBFOsmStreamTarget target = new PBFOsmStreamTarget(memoryStream, true);
osmStreamSource.Reset();
target.Initialize();
UpdateTarget(osmStreamSource, target);
target.Flush();
target.Close();
return memoryStream.ToArray();
}
private static string BytesToFile(byte[] bytes, string fileName)
{
using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
fs.Write(bytes, 0, bytes.Length);
}
return fileName;
}
private static void OsmStreamSourceToFile(PBFOsmStreamSource osmStreamSource, string fileName)
{
using (FileStream fileStream = new FileInfo(fileName).OpenWrite())
{
PBFOsmStreamTarget target = new PBFOsmStreamTarget(fileStream, true);
osmStreamSource.Reset();
target.Initialize();
UpdateTarget(osmStreamSource, target);
target.Flush();
target.Close();
}
}
private static void UpdateTarget(OsmStreamSource osmStreamSource, OsmStreamTarget osmStreamTarget)
{
if (useRegisterSource)
{
osmStreamTarget.RegisterSource(osmStreamSource, osmGeo => true);
osmStreamTarget.Pull();
}
else
{
bool isFirst = true;
foreach (OsmGeo osmGeo in osmStreamSource)
{
Tag? tag = osmGeo.Tags?.FirstOrDefault(t => t.Key == KeyValuePair.Key);
switch (osmGeo.Type)
{
case OsmGeoType.Node:
if (isFirst)
{
for (int indexer = 0; indexer < 1; indexer++)
{
(osmGeo as Node).Tags.Add(new Tag(KeyValuePair.Key + Guid.NewGuid(), KeyValuePair.Value));
}
isFirst = false;
}
osmStreamTarget.AddNode(osmGeo as Node);
break;
case OsmGeoType.Way:
osmStreamTarget.AddWay(osmGeo as Way);
break;
case OsmGeoType.Relation:
osmStreamTarget.AddRelation(osmGeo as Relation);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}
}
}
Already I posted this question on the GITHube page of OSMSharp as is linked here. Any help would be very appreciated.

Export Metadata from a Custom Tab from file properties for a list of files in sub folders

The Problem:
On my Windows file server I have around 1,000,000 SolidWorks files that have metadata in the file properties under a custom tab (see image below) that I would like to export to a single CSV file.
These files are located in sub-tree of folders, and mixed with other file types.
The Solution:
A script is needed to only target specific file types (extensions) in a sub-tree of folders, that exports the metadata from the custom tab to a CSV File, where i can then clean up and import the data into an SQL database.
I am not sure the best way to achieve this I was thinking along the lines of PowerShell, any help to get going would be greatly appreciated.
If you use a .NET language like C# or Visual Basic, you can use the SolidWorks API and the Document Manager libraries (you'll need to get a license, free with your subscription from the Customer Portal) to extract this information without opening the files. It's quite fast. As for only looking at certain files, that's straight forward with .NET IO.Path.GetExtension.
Below is a working example of what I think you're looking for.
You will need the Document Manager dll, which is on the SolidWorks API SDK, which is included in the installation media. Then you can referencene SolidWorks.Interop.swdocumentmgr.dll.
You will also need a Document Manager Serial Number which you can request through the SolidWorks Customer Portal free of charge with your SolidWorks Subscription. Once you have that number, replace the lic string value below with the entire serial number, in quotes.
To define which Custom Properties to read from the SolidWorks files, just change the list propertiesToRead to include any values you need to retrieve. These are NOT case sensitive.
You you run this, you will be prompted to enter a directory path. This is also where the Output.csv file will be created.
At the bottom is a screen-shot of sample results.
using SolidWorks.Interop.swdocumentmgr;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace WriteProperties
{
class Program
{
static ISwDMApplication4 docManager;
static List<string> propertiesToRead = new List<string>() { "Number", "Description", "Revision", "Material", "Finish", "Weight" };
const string lic = "YOU CAN GET THIS NUMBER FROM THE CUSTOMER PORTAL WITH YOUR SOLIDWORKS SUBSCRIPTION AT NO COST";
static char[] charactersToQuote = { ',', '"', '\n' };
const string QUOTE = "\"";
const string QUOTEFORMATTED = "\"\"";
static void Main(string[] args)
{
string directoryPath = GetDirectory(args);
if (string.IsNullOrEmpty(directoryPath)) return;
if (!LoadDocManager()) return;
string outputPath = Path.Combine(directoryPath, "Output.csv");
StringBuilder sb = new StringBuilder();
sb.AppendLine("File Name," + string.Join(",", propertiesToRead));
int counter = 0;
foreach (string filePath in Directory.EnumerateFiles(directoryPath, "*.sld*", SearchOption.AllDirectories))
{
SwDMDocument21 dmDocument = GetDocument(filePath);
if (dmDocument == null) continue;
WriteProperties(sb, dmDocument, filePath);
counter++;
}
File.WriteAllText(outputPath, sb.ToString());
Console.WriteLine("{0} files read and saved to {1}", counter, outputPath);
Console.ReadLine();
}
static string GetDirectory(string[] args)
{
if (args != null && args.Count() > 0 && Directory.Exists(args[0])) return args[0];
Console.WriteLine("Directory to read:");
string filePath = Console.ReadLine();
if (Directory.Exists(filePath)) return filePath;
Console.WriteLine("Directory does not exists: {0}", filePath);
return string.Empty;
}
static bool LoadDocManager()
{
if (docManager != null) return true;
try
{
SwDMClassFactory factory = new SwDMClassFactory();
if (factory == null) throw new NullReferenceException(nameof(SwDMClassFactory));
docManager = (SwDMApplication4)factory.GetApplication(lic);
if (docManager == null) throw new NullReferenceException(nameof(SwDMApplication4));
return true;
}
catch (Exception ex)
{
Console.WriteLine("Document Manager failed to load: {0}", ex.Message);
Console.ReadLine();
return false;
}
}
static SwDMDocument21 GetDocument(string filePath)
{
SwDmDocumentType documentType = GetDocType(filePath);
if (documentType == SwDmDocumentType.swDmDocumentUnknown) return null;
SwDmDocumentOpenError result = SwDmDocumentOpenError.swDmDocumentOpenErrorNone;
SwDMDocument21 dmDocument = (SwDMDocument21)docManager.GetDocument(filePath, documentType, true, out result);
if (result == SwDmDocumentOpenError.swDmDocumentOpenErrorNone || result == SwDmDocumentOpenError.swDmDocumentOpenErrorFileReadOnly) return dmDocument;
if (dmDocument != null) dmDocument.CloseDoc();
return null;
}
static SwDmDocumentType GetDocType(string filePath)
{
if (filePath.Contains("~$")) return SwDmDocumentType.swDmDocumentUnknown;
switch (Path.GetExtension(filePath).ToLower())
{
case ".sldprt": return SwDmDocumentType.swDmDocumentPart;
case ".sldasm": return SwDmDocumentType.swDmDocumentAssembly;
case ".slddrw": return SwDmDocumentType.swDmDocumentDrawing;
default: return SwDmDocumentType.swDmDocumentUnknown;
}
}
static void WriteProperties(StringBuilder sb, SwDMDocument21 dmDocument, string filePath)
{
Console.WriteLine("Reading {0}", filePath);
List<string> propertiesInFile = new List<string>();
if (dmDocument.GetCustomPropertyCount() > 0) propertiesInFile.AddRange(dmDocument.GetCustomPropertyNames());
string csvLine = filePath;
foreach (string property in propertiesToRead)
{
string propertyValue = "";
if (propertiesInFile.Any(s => string.Compare(s, property, true) == 0))
{
SwDmCustomInfoType propertyType = SwDmCustomInfoType.swDmCustomInfoText;
string resolvedValue;
propertyValue = dmDocument.GetCustomPropertyValues(property, out propertyType, out resolvedValue);
}
csvLine = csvLine + "," + FixChars(propertyValue);
}
sb.AppendLine(csvLine);
dmDocument.CloseDoc();
}
static string FixChars(string s)
{
if (s.Contains(QUOTE)) s = s.Replace(QUOTE, QUOTEFORMATTED);
if (s.IndexOfAny(charactersToQuote) > -1) s = QUOTE + s + QUOTE;
return s;
}
}
}
Here is a sample output

Writing the path of a dragdrop file to a textbox

EDIT: The code below is a class I created in order to have a drag/drop richtextbox in which dragging a file into it causes the information inside the file appear in the richtextbox. This works perfectly, I even made it so decimal characters over 128 appear as binary so I can see bitmap images to some degree.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Troy_PCL_Utility
{
public class DragDropRichTextBox : RichTextBox
{
public DragDropRichTextBox()
{
this.AllowDrop = true;
this.DragDrop += DragDropRichTextBox_DragDrop;
}
public static class BinaryFile
{
private static string[] __byteLookup = new string[300];
static BinaryFile()
{
//CHARACTERS: All Control ASCII Characters
for (int i = 0x00; i < 0x1E; i++) { __byteLookup[i] = ((char)i).ToString(); }
// Display printable ASCII characters
for (int i = 0x1E; i < 0x7E; i++) { __byteLookup[i] = ((char)i).ToString(); }
//BITMAPS: Display non-printable ASCII characters
//for (int i = 0; i <= 0x00; i++) { __byteLookup[i] = "\\" + i.ToString(); }
for (int i = 0x7E; i <= 0xFF; i++) { __byteLookup[i] = Convert.ToString(i, 2); } //As Binary
//for (int i = 0x00; i <= 0xFF; i++) { __byteLookup[i] = "\\" + i.ToString(); } //As Characters
}
public static string ReadString(string filename)
{
byte[] fileBytes = System.IO.File.ReadAllBytes(filename);
return String.Join("", (from i in fileBytes select __byteLookup[i]).ToArray());
}
}
public void DragDropRichTextBox_DragDrop(object sender, DragEventArgs e)
{
//string[] fileText = e.Data.GetData(DataFormats.FileDrop) as string[];
string[] fileText = e.Data.GetData(DataFormats.FileDrop) as string[];
if (fileText != null)
{
foreach (string name in fileText)
{
try
{
this.AppendText(BinaryFile.ReadString (name) + "\n -------- End of File -------- \n\n");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
}
}
However, in addition to dragging and dropping content into this richtextbox, I would also like textbox2 on my main form to populate with the path from which the file came from so if the user is dealing with several files they can double check to see which one they are viewing and from which directory.
So I have this class (DragDropRichTextBox : RichTextBox), and I have this other class (Main Form). I'm not sure how to accomplish this task. Should the code to copy the directory into textbox2 belong in my DragDrop class (above), is that even possible? Or should I create an event from the richtextbox in my main?
I tried clicking on the richtextbox (that derived from my DragDropRichTextBox class) and then created a drag/drop event from where I tried to make it so dragging and dropping copy the file path to textbox2, but I had no luck. Here is the code I tried. I don't feel confident in it:
private void dragDropRichTextBox1_DragDrop(object sender, DragEventArgs e)
{
string fileName = (string) e.Data.GetData(DataFormats.FileDrop, false);
textBox2.Text = fileName;
}
Does anyone have any suggestions for the new guy? Thank you for your help.

Can I drag items from Outlook into my SWT application?

Background
Our Eclipse RCP 3.6-based application lets people drag files in for storage/processing. This works fine when the files are dragged from a filesystem, but not when people drag items (messages or attachments) directly from Outlook.
This appears to be because Outlook wants to feed our application the files via a FileGroupDescriptorW and FileContents, but SWT only includes a FileTransfer type. (In a FileTransfer, only the file paths are passed, with the assumption that the receiver can locate and read them. The FileGroupDescriptorW/FileContents approach can supply files directly application-to-application without writing temporary files out to disk.)
We have tried to produce a ByteArrayTransfer subclass that could accept FileGroupDescriptorW and FileContents. Based on some examples on the Web, we were able to receive and parse the FileGroupDescriptorW, which (as the name implies) describes the files available for transfer. (See code sketch below.) But we have been unable to accept the FileContents.
This seems to be because Outlook offers the FileContents data only as TYMED_ISTREAM or TYMED_ISTORAGE, but SWT only understands how to exchange data as TYMED_HGLOBAL. Of those, it appears that TYMED_ISTORAGE would be preferable, since it's not clear how TYMED_ISTREAM could provide access to multiple files' contents.
(We also have some concerns about SWT's desire to pick and convert only a single TransferData type, given that we need to process two, but we think we could probably hack around that in Java somehow: it seems that all the TransferDatas are available at other points of the process.)
Questions
Are we on the right track here? Has anyone managed to accept FileContents in SWT yet? Is there any chance that we could process the TYMED_ISTORAGE data without leaving Java (even if by creating a fragment-based patch to, or a derived version of, SWT), or would we have to build some new native support code too?
Relevant code snippets
Sketch code that extracts file names:
// THIS IS NOT PRODUCTION-QUALITY CODE - FOR ILLUSTRATION ONLY
final Transfer transfer = new ByteArrayTransfer() {
private final String[] typeNames = new String[] { "FileGroupDescriptorW", "FileContents" };
private final int[] typeIds = new int[] { registerType(typeNames[0]), registerType(typeNames[1]) };
#Override
protected String[] getTypeNames() {
return typeNames;
}
#Override
protected int[] getTypeIds() {
return typeIds;
}
#Override
protected Object nativeToJava(TransferData transferData) {
if (!isSupportedType(transferData))
return null;
final byte[] buffer = (byte[]) super.nativeToJava(transferData);
if (buffer == null)
return null;
try {
final DataInputStream in = new DataInputStream(new ByteArrayInputStream(buffer));
long count = 0;
for (int i = 0; i < 4; i++) {
count += in.readUnsignedByte() << i;
}
for (int i = 0; i < count; i++) {
final byte[] filenameBytes = new byte[260 * 2];
in.skipBytes(72); // probable architecture assumption(s) - may be wrong outside standard 32-bit Win XP
in.read(filenameBytes);
final String fileNameIncludingTrailingNulls = new String(filenameBytes, "UTF-16LE");
int stringLength = fileNameIncludingTrailingNulls.indexOf('\0');
if (stringLength == -1)
stringLength = 260;
final String fileName = fileNameIncludingTrailingNulls.substring(0, stringLength);
System.out.println("File " + i + ": " + fileName);
}
in.close();
return buffer;
}
catch (final Exception e) {
return null;
}
}
};
In the debugger, we see that ByteArrayTransfer's isSupportedType() ultimately returns false for the FileContents because the following test is not passed (since its tymed is TYMED_ISTREAM | TYMED_ISTORAGE):
if (format.cfFormat == types[i] &&
(format.dwAspect & COM.DVASPECT_CONTENT) == COM.DVASPECT_CONTENT &&
(format.tymed & COM.TYMED_HGLOBAL) == COM.TYMED_HGLOBAL )
return true;
This excerpt from org.eclipse.swt.internal.ole.win32.COM leaves us feeling less hope for an easy solution:
public static final int TYMED_HGLOBAL = 1;
//public static final int TYMED_ISTORAGE = 8;
//public static final int TYMED_ISTREAM = 4;
Thanks.
even if
//public static final int TYMED_ISTREAM = 4;
Try below code.. it should work
package com.nagarro.jsag.poc.swtdrag;
imports ...
public class MyTransfer extends ByteArrayTransfer {
private static int BYTES_COUNT = 592;
private static int SKIP_BYTES = 72;
private final String[] typeNames = new String[] { "FileGroupDescriptorW", "FileContents" };
private final int[] typeIds = new int[] { registerType(typeNames[0]), registerType(typeNames[1]) };
#Override
protected String[] getTypeNames() {
return typeNames;
}
#Override
protected int[] getTypeIds() {
return typeIds;
}
#Override
protected Object nativeToJava(TransferData transferData) {
String[] result = null;
if (!isSupportedType(transferData) || transferData.pIDataObject == 0)
return null;
IDataObject data = new IDataObject(transferData.pIDataObject);
data.AddRef();
// Check for descriptor format type
try {
FORMATETC formatetcFD = transferData.formatetc;
STGMEDIUM stgmediumFD = new STGMEDIUM();
stgmediumFD.tymed = COM.TYMED_HGLOBAL;
transferData.result = data.GetData(formatetcFD, stgmediumFD);
if (transferData.result == COM.S_OK) {
// Check for contents format type
long hMem = stgmediumFD.unionField;
long fileDiscriptorPtr = OS.GlobalLock(hMem);
int[] fileCount = new int[1];
try {
OS.MoveMemory(fileCount, fileDiscriptorPtr, 4);
fileDiscriptorPtr += 4;
result = new String[fileCount[0]];
for (int i = 0; i < fileCount[0]; i++) {
String fileName = handleFile(fileDiscriptorPtr, data);
System.out.println("FileName : = " + fileName);
result[i] = fileName;
fileDiscriptorPtr += BYTES_COUNT;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
OS.GlobalFree(hMem);
}
}
} finally {
data.Release();
}
return result;
}
private String handleFile(long fileDiscriptorPtr, IDataObject data) throws Exception {
// GetFileName
char[] fileNameChars = new char[OS.MAX_PATH];
byte[] fileNameBytes = new byte[OS.MAX_PATH];
COM.MoveMemory(fileNameBytes, fileDiscriptorPtr, BYTES_COUNT);
// Skip some bytes.
fileNameBytes = Arrays.copyOfRange(fileNameBytes, SKIP_BYTES, fileNameBytes.length);
String fileNameIncludingTrailingNulls = new String(fileNameBytes, "UTF-16LE");
fileNameChars = fileNameIncludingTrailingNulls.toCharArray();
StringBuilder builder = new StringBuilder(OS.MAX_PATH);
for (int i = 0; fileNameChars[i] != 0 && i < fileNameChars.length; i++) {
builder.append(fileNameChars[i]);
}
String name = builder.toString();
try {
File file = saveFileContent(name, data);
if (file != null) {
System.out.println("File Saved # " + file.getAbsolutePath());
;
}
} catch (IOException e) {
System.out.println("Count not save file content");
;
}
return name;
}
private File saveFileContent(String fileName, IDataObject data) throws IOException {
File file = null;
FORMATETC formatetc = new FORMATETC();
formatetc.cfFormat = typeIds[1];
formatetc.dwAspect = COM.DVASPECT_CONTENT;
formatetc.lindex = 0;
formatetc.tymed = 4; // content.
STGMEDIUM stgmedium = new STGMEDIUM();
stgmedium.tymed = 4;
if (data.GetData(formatetc, stgmedium) == COM.S_OK) {
file = new File(fileName);
IStream iStream = new IStream(stgmedium.unionField);
iStream.AddRef();
try (FileOutputStream outputStream = new FileOutputStream(file)) {
int increment = 1024 * 4;
long pv = COM.CoTaskMemAlloc(increment);
int[] pcbWritten = new int[1];
while (iStream.Read(pv, increment, pcbWritten) == COM.S_OK && pcbWritten[0] > 0) {
byte[] buffer = new byte[pcbWritten[0]];
OS.MoveMemory(buffer, pv, pcbWritten[0]);
outputStream.write(buffer);
}
COM.CoTaskMemFree(pv);
} finally {
iStream.Release();
}
return file;
} else {
return null;
}
}
}
Have you looked at https://bugs.eclipse.org/bugs/show_bug.cgi?id=132514 ?
Attached to this bugzilla entry is an patch (against an rather old version of SWT) that might be of interest.
I had the same problem and created a small library providing a Drag'n Drop Transfer Class for JAVA SWT. It can be found here:
https://github.com/HendrikHoetker/OutlookItemTransfer
Currently it supports dropping Mail Items from Outlook to your Java SWT application and will provide a list of OutlookItems with the Filename and a byte array of the file contents.
All is pure Java and in-memory (no temp files).
Usage in your SWT java application:
if (OutlookItemTransfer.getInstance().isSupportedType(event.currentDataType)) {
Object o = OutlookItemTransfer.getInstance().nativeToJava(event.currentDataType);
if (o != null && o instanceof OutlookMessage[]) {
OutlookMessage[] outlookMessages = (OutlookMessage[])o;
for (OutlookMessage msg: outlookMessages) {
//...
}
}
}
The OutlookItem will then provide two elements: filename as String and file contents as array of byte.
From here on, one could write it to a file or further process the byte array.
To your question above:
- What you find in the file descriptor is the filename of the outlook item and a pointer to an IDataObject
- the IDataObject can be parsed and will provide an IStorage object
- The IStorageObject will be then a root container providing further sub-IStorageObjects or IStreams similar to a filesystem (directory = IStorage, file = IStream
You find those elements in the following lines of code:
Get File Contents, see OutlookItemTransfer.java, method nativeToJava:
FORMATETC format = new FORMATETC();
format.cfFormat = getTypeIds()[1];
format.dwAspect = COM.DVASPECT_CONTENT;
format.lindex = <fileIndex>;
format.ptd = 0;
format.tymed = TYMED_ISTORAGE | TYMED_ISTREAM | COM.TYMED_HGLOBAL;
STGMEDIUM medium = new STGMEDIUM();
if (data.GetData(format, medium) == COM.S_OK) {
// medium.tymed will now contain TYMED_ISTORAGE
// in medium.unionfield you will find the root IStorage
}
Read the root IStorage, see CompoundStorage, method readOutlookStorage:
// open IStorage object
IStorage storage = new IStorage(pIStorage);
storage.AddRef();
// walk through the content of the IStorage object
long[] pEnumStorage = new long[1];
if (storage.EnumElements(0, 0, 0, pEnumStorage) == COM.S_OK) {
// get storage iterator
IEnumSTATSTG enumStorage = new IEnumSTATSTG(pEnumStorage[0]);
enumStorage.AddRef();
enumStorage.Reset();
// prepare statstg structure which tells about the object found by the iterator
long pSTATSTG = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, STATSTG.sizeof);
int[] fetched = new int[1];
while (enumStorage.Next(1, pSTATSTG, fetched) == COM.S_OK && fetched[0] == 1) {
// get the description of the the object found
STATSTG statstg = new STATSTG();
COM.MoveMemory(statstg, pSTATSTG, STATSTG.sizeof);
// get the name of the object found
String name = readPWCSName(statstg);
// depending on type of object
switch (statstg.type) {
case COM.STGTY_STREAM: { // load an IStream (=File)
long[] pIStream = new long[1];
// get the pointer to the IStream
if (storage.OpenStream(name, 0, COM.STGM_DIRECT | COM.STGM_READ | COM.STGM_SHARE_EXCLUSIVE, 0, pIStream) == COM.S_OK) {
// load the IStream
}
}
case COM.STGTY_STORAGE: { // load an IStorage (=SubDirectory) - requires recursion to traverse the sub dies
}
}
}
}
// close the iterator
enumStorage.Release();
}
// close the IStorage object
storage.Release();

Excel Export in MVC for IE not allowing to open multiple windows

Here is our ExcelExport action that inherits from ActionResult:
public class ExcelResult<Model> : ActionResult
{
string _fileName;
string _viewPath;
Model _model;
ControllerContext _context;
public ExcelResult(ControllerContext context, string viewPath, string fileName, Model model)
{
this._context = context;
this._fileName = fileName;
this._viewPath = viewPath;
this._model = model;
}
protected string RenderViewToString()
{
if (!_viewPath.EndsWith(".aspx"))
{
return _viewPath;
}
using (var writer = new StringWriter())
{
var view = new WebFormView(_context, _viewPath);
var vdd = new ViewDataDictionary<Model>(_model);
var viewCxt = new ViewContext(_context, view, vdd, new TempDataDictionary(), writer);
viewCxt.View.Render(viewCxt, writer);
return writer.ToString();
}
}
void WriteFile(string content)
{
HttpContext context = HttpContext.Current;
context.Response.Clear();
context.Response.AddHeader("content-disposition", "attachment;filename=\"" + _fileName + "\"");
context.Response.Charset = "";
//context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.ContentType = "application/ms-excel";
context.Response.Write(RemoveImages(content));
context.Response.End();
}
public override void ExecuteResult(ControllerContext context)
{
string content = this.RenderViewToString();
this.WriteFile(content);
}
public static string RemoveImages(string html)
{
StringBuilder retval = new StringBuilder();
using (StringReader reader = new StringReader(html))
{
string line = string.Empty;
do
{
line = reader.ReadLine();
if (line != null)
{
if (!line.StartsWith("<img"))
{
retval.Append(line);
}
}
} while (line != null);
}
return retval.ToString();
}
}
The export works fine, but in IE only (works in FF), if you export, and choose to open the file instead of save it, and then click export again right away, it tries to open another file with the same name and therefore Excel won't let you until you close your working document.
In FF however, the name just adds an integer that increments by 1 each time you click export.
What do I have to do to achieve the same functionality in IE?
I faced the same problem and out of the box you cant do anything because that's the way IE and Excel handle this. You are also not able to identify that a file of the same name is already opened. But you can use either JavaScript or the user session to identify that the user has already loaded the export within a timespan and and change the file name on server side for this download. That worked for me after 2 days of searching and mailing.