I am having trouble locating an issue with my xml files how do I locate a Bad signature (0x01549FF9) at position 0x001D4DA8 - xlsx

While attempting to troubleshoot a use case I get a "Bad signature (0x01549FF9) at position 0x001D4DA8" exception after trying to open the workbook. What is the best way to find what part of the xlsx xml is causing this issue?
Opening the file in Excel and saving it resolves the issue and I can see from diffs that Excel must have found and corrected "errors" but there are too many to definitively figure out which one is the issue.
[TestMethod]
public void FileSaving_FileOpensAfterMultipleEdits_FunctioningFile()
{
var template = #"C:\xxxxx\test.xlsx";
var outputSmokeTest = Path.Combine(testDocumentsRootDirectory, #"Results\CorruptTest.xlsx");
smokeTestWorkbook = new Workbook(key);
if (File.Exists(outputSmokeTest))
File.Delete(outputSmokeTest);
smokeTestWorkbook.Open(template);
smokeTestWorkbook.SaveAs(outputSmokeTest);
}
/// <summary>
/// Opens a reference to the workbook
/// </summary>
/// <returns></returns>
public void Open(string workbookPath)
{
var loggerString = "Open";
if (File.Exists(workbookPath))
{
try
{
logger.Track(loggerString);
excelPackage = new ExcelPackage(new FileInfo(workbookPath));
}
catch (Exception ex)
{
logger.TrackException(loggerString, ex);
}
}
}
the new ExcelPackage line is what causes the exception.

Related

Is there any memory leak or some bugs in SQL Server CLR?

There are my code, this method thrown exception usually when multiple thread invoke it, sometimes, it throw threadAbortException, some times throw out of memory or the memory is collect by SQL Server, and there are another class which is used to lock a List, it is stored the accessed client information, the SQL Server usually to clear it or release the CLR assembly, I don't know why. Can you help me if you have the CLR experience, thank you. My code as bellow:
private static bool QueryResultSet(string value)
{
if(string.IsNullOrEmpty(value))
{
return false;
}
int returnValue;
bool result;
if (!Int32.TryParse(value, out returnValue))
{
return false;
}
result = true;
try
{
using (SqlDataReader dr = SqlHelper.ExecuteReader(SqlHelper.connectionString,
CommandType.Text,
string.Format("SELECT {0} AS Result", returnValue)))
{
SqlContext.Pipe.Send(dr);
}
}
catch (Exception ex)
{
_logger.Error(ex);
result = false;
}
return result;
}
When using SqlHelper.ExecuteReader(conectionString...) then by default you have memory leak. The ExecuteReader() method when using connection string instead of connection is faulted by design. It can't be used correctly since the Connection is made inside the method, not outside, therefore after using the reader you can't close the connection manually. Therefore, for start, create the connection inside using() statement, then pass it to the SqlHelper.ExecuteReader(_theConnection..) method.
Here is that same function from the ApplicationBlocks.Data source:
/// <summary>
/// Execute a SqlCommand (that returns a resultset) against the database specified in the connection string
/// using the provided parameters.
/// </summary>
/// <remarks>
/// e.g.:
/// SqlDataReader dr = ExecuteReader(connString, CommandType.StoredProcedure, "GetOrders", new SqlParameter("#prodid", 24));
/// </remarks>
/// <param name="ConnectionString">a valid connection string for a SqlConnection</param>
/// <param name="commandType">the CommandType (stored procedure, text, etc.)</param>
/// <param name="commandText">the stored procedure name or T-SQL command</param>
/// <param name="commandParameters">an array of SqlParamters used to execute the command</param>
/// <returns>a SqlDataReader containing the resultset generated by the command</returns>
public static SqlDataReader ExecuteReader(string ConnectionString, CommandType commandType, string commandText, params SqlParameter[] commandParameters)
{
//create & open a SqlConnection
SqlConnection cn = new SqlConnection(ConnectionString);
cn.Open();
try
{
//call the private overload that takes an internally owned connection in place of the connection string
return ExecuteReader(cn, null, commandType, commandText, commandParameters,SqlConnectionOwnership.Internal);
}
catch
{
//if we fail to return the SqlDatReader, we need to close the connection ourselves
cn.Close();
throw;
}
}
As we can see from the exact function above, they are creating the connection inside that function, then they are creating the reader and returning it.
... The connection is never closed.
If we use this method then the connection pool will eventually overflow, since this function by default is producing leakage. Therefore, do not use this function by passing the connection string. Instead you open a connection outside the, pass it to the SqlHelper.ExecuteReader(), do stuff with the reader, close the reader and then close the connection.
See the modified code of your function:
private static bool QueryResultSet(string value)
{
if(string.IsNullOrEmpty(value))
{
return false;
}
int returnValue;
bool result;
if (!Int32.TryParse(value, out returnValue))
{
return false;
}
result = true;
using (SqlConnection connection = new SqlConnection(SqlHelper.connectionString))
{
try
{
using (SqlDataReader dr = SqlHelper.ExecuteReader(connection,
CommandType.Text,
string.Format("SELECT {0} AS Result", returnValue)))
{
SqlContext.Pipe.Send(dr);
}
}
catch (Exception ex)
{
_logger.Error(ex);
result = false;
}
finally
{
// TODO: Here you can check if connection state is open before closing it
conneciton.Close();
}
}
return result;
}

Dynamic CSV load in SQL Server

Ok so I have been searching the internet for a solution but have not yet come up with anything yet
What I have is a CSV - this CSV could have any number of unknown columns
e.g.
Col 1, Col 2, Col 3
I have used BULK INSERT #temp FROM ... to insert from a CSV but this relies on me having a table before hand to load into - This is where the problem arises - I don’t know my table structure before loading the CSV
Is there a way to dynamically create the table, based on the CSV, on the fly to load the data into?
Thanks
Rob
I was faced with the same tasks many many times. What I ended up doing is writing a simple c# script for the load. I can admit, each time I had to change the script a little bit, because each time the requirements were different, the CSV file had specific peculiarities, etc. This means that my code most likely won't work for you straight away, but I hope that it can help you a lot.
The main C# file is program.cs. Here is its source:
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
namespace CsvToSql
{
class Program
{
static string server = #"localhost";
static string database = #"test";
static bool hasHeaders = false;
static string fieldLength = "max";
static string fieldPattern = "[%fieldName%] [nvarchar](%fieldLength%) NULL,\n";
static string createTablePattern =
#"
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[%tableName%]') AND type in (N'U'))
DROP TABLE [dbo].[%tableName%]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[%tableName%](
%fields%
) ON [PRIMARY]
";
static string commandScriptPattern =
#"sqlcmd -S %server% -E -d %database% -i %tableScriptName%
bcp %database%.dbo.%tableName% in %headrelsessFileName% -c -t^^ -r \n -T -S %server%
";
private static void Main(string[] args)
{
server = System.Configuration.ConfigurationSettings.AppSettings["server"] ?? server;
database = System.Configuration.ConfigurationSettings.AppSettings["database"] ?? database;
hasHeaders = System.Configuration.ConfigurationSettings.AppSettings["hasHeaders"] == "true";
fieldLength = System.Configuration.ConfigurationSettings.AppSettings["fieldLength"] ?? fieldLength;
string[] fileNames = Directory.GetFiles(".", "*.csv");
foreach (string fileName in fileNames)
{
Console.WriteLine("Processing {0}", fileName);
Process(fileName);
}
WriteExecuteAllFile(fileNames);
WriteCleanUpFile(fileNames);
}
private static void Process(string fileName)
{
string[] fieldNames = ReadHeaders(fileName);
ProduceTableScript(fileName, fieldNames);
ProduceCommandScript(fileName);
}
private static void WriteExecuteAllFile(string[] fileNames)
{
StringBuilder sb = new StringBuilder();
foreach (string fileName in fileNames)
{
sb.Append("call ");
sb.AppendLine(GetCommandScriptName(fileName));
}
SaveStringToFile(sb.ToString(), "_all.cmd");
}
private static void WriteCleanUpFile(string[] fileNames)
{
StringBuilder sb = new StringBuilder();
foreach (string fileName in fileNames)
{
sb.Append("del ");
sb.AppendLine(GetCommandScriptName(fileName));
sb.Append("del ");
sb.AppendLine(GetHeaderlessFileName(fileName));
sb.Append("del ");
sb.AppendLine(GetTableScriptName(fileName));
}
sb.AppendLine("del _all.cmd");
sb.AppendLine("del _cleanup.cmd");
SaveStringToFile(sb.ToString(), "_cleanup.cmd");
}
private static string[] ReadHeaders(string fileName)
{
using (FileStream fs = File.OpenRead(fileName))
using (StreamReader sr = new StreamReader(fs))
{
if (hasHeaders)
{
string[] result = ParseQutationLineToList(sr.ReadLine());
ProduceHeaderlessFile(sr, fs.Name);
return result;
}
else
{
string s = sr.ReadLine();
string[] fields = ParseQutationLineToList(s);
fs.Seek(0, SeekOrigin.Begin);
sr.DiscardBufferedData();
string[] result = new string[fields.Length];
for (int i = 0; i < fields.Length; i++)
{
result[i] = "F" + (i + 1).ToString();
}
ProduceHeaderlessFile(sr, fs.Name);
return result;
}
}
}
private static void ProduceTableScript(string fileName, string[] fieldNames)
{
string tableName = GetTableName(fileName);
string fields = fieldNames.Aggregate("", (s, i) => s + fieldPattern.Replace("%fieldName%", i).Replace("%fieldLength%", fieldLength));
string table = createTablePattern.Replace("%fields%", fields).Replace("%tableName%", tableName);
SaveStringToFile(table, GetTableScriptName(fileName));
}
private static void ProduceCommandScript(string fileName)
{
string content = commandScriptPattern;
content = content.Replace("%server%", server);
content = content.Replace("%database%", database);
content = content.Replace("%tableName%", GetTableName(fileName));
content = content.Replace("%tableScriptName%", GetTableScriptName(fileName));
content = content.Replace("%headrelsessFileName%", GetHeaderlessFileName(fileName));
SaveStringToFile(content, GetCommandScriptName(fileName));
}
private static void ProduceHeaderlessFile(StreamReader sr, string basefileName)
{
string headerlessFileName = GetHeaderlessFileName(basefileName);
if (File.Exists(headerlessFileName))
{
return;
}
int counter = 0;
using(FileStream fs = File.Open(headerlessFileName, FileMode.Create, FileAccess.Write, FileShare.Read))
using (StreamWriter sw = new StreamWriter(fs))
{
while(!sr.EndOfStream)
{
//sw.WriteLine(sr.ReadLine().Replace("\"", ""));
sw.WriteLine(ParseLine(sr.ReadLine()));
counter++;
}
sw.Flush();
fs.Flush();
}
Console.WriteLine("Written {0} records to {1}", counter, headerlessFileName);
}
private static string ParseLine(string s)
{
if (s.TrimStart(' ', '\t').StartsWith("\""))
{
return ParseQutationLine(s);
}
return s.Replace(',', '^');
}
// Some tables has the default field terminator (comma) inside them
// this is why we have to parse
private static string ParseQutationLine(string s)
{
string[] fields = ParseQutationLineToList(s);
StringBuilder sb = new StringBuilder();
foreach (string field in fields)
{
sb.Append(field.Trim('"'));
sb.Append('^');
if (field.IndexOf('^') >= 0)
{
throw new ApplicationException("String contains separator character. " + s);
}
}
return sb.ToString().Substring(0, sb.Length - 1);
}
private static string[] ParseQutationLineToList(string s)
{
JouniHeikniemi.Tools.Strings.CsvReader cr = new JouniHeikniemi.Tools.Strings.CsvReader();
ArrayList result = new ArrayList();
cr.ParseCsvFields(result, s);
return (string[])result.ToArray(typeof(string));
}
private static void SaveStringToFile(string s, string fileName)
{
using (FileStream fs = File.Open(fileName, FileMode.Create, FileAccess.Write, FileShare.Read))
using (StreamWriter sw = new StreamWriter(fs))
{
sw.Write(s);
sw.Flush();
fs.Flush();
}
}
private static string GetTableName(string fileName)
{
return "_" + Path.GetFileNameWithoutExtension(fileName).Replace('.', '_');
}
private static string GetHeaderlessFileName(string fileName)
{
return Path.ChangeExtension(fileName, "inp");
}
private static string GetTableScriptName(string fileName)
{
return Path.ChangeExtension(fileName, "tbl");
}
private static string GetCommandScriptName(string fileName)
{
return Path.ChangeExtension(fileName, "cmd");
}
}
}
This file use a library that I found in internet for parsing CSV file. Note, that I saw valid CSV files, that this library failed to parse. The text for CsvReader.cs file follows:
using System;
using System.Collections;
using System.IO;
using System.Text;
namespace JouniHeikniemi.Tools.Strings {
/// <summary>
/// A data-reader style interface for reading Csv (and otherwise-char-separated) files.
/// </summary>
public class CsvReader : IDisposable {
#region Private variables
private Stream stream;
private StreamReader reader;
private char separator;
#endregion
#region Constructors
public CsvReader() { separator = ','; }
/// <summary>
/// Creates a new Csv reader for the given stream.
/// </summary>
/// <param name="s">The stream to read the CSV from.</param>
public CsvReader(Stream s) : this(s, null, ',') { }
/// <summary>
/// Creates a new reader for the given stream and separator.
/// </summary>
/// <param name="s">The stream to read the separator from.</param>
/// <param name="separator">The field separator character</param>
public CsvReader(Stream s, char separator) : this(s, null, separator) { }
/// <summary>
/// Creates a new Csv reader for the given stream and encoding.
/// </summary>
/// <param name="s">The stream to read the CSV from.</param>
/// <param name="enc">The encoding used.</param>
public CsvReader(Stream s, Encoding enc) : this(s, enc, ',') { }
/// <summary>
/// Creates a new reader for the given stream, encoding and separator character.
/// </summary>
/// <param name="s">The stream to read the data from.</param>
/// <param name="enc">The encoding used.</param>
/// <param name="separator">The separator character between the fields</param>
public CsvReader(Stream s, Encoding enc, char separator) {
this.separator = separator;
this.stream = s;
if (!s.CanRead) {
throw new CsvReaderException("Could not read the given data stream!");
}
reader = (enc != null) ? new StreamReader(s, enc) : new StreamReader(s);
}
/// <summary>
/// Creates a new Csv reader for the given text file path.
/// </summary>
/// <param name="filename">The name of the file to be read.</param>
public CsvReader(string filename) : this(filename, null, ',') { }
/// <summary>
/// Creates a new reader for the given text file path and separator character.
/// </summary>
/// <param name="filename">The name of the file to be read.</param>
/// <param name="separator">The field separator character</param>
public CsvReader(string filename, char separator) : this(filename, null, separator) { }
/// <summary>
/// Creates a new Csv reader for the given text file path and encoding.
/// </summary>
/// <param name="filename">The name of the file to be read.</param>
/// <param name="enc">The encoding used.</param>
public CsvReader(string filename, Encoding enc)
: this(filename, enc, ',') { }
/// <summary>
/// Creates a new reader for the given text file path, encoding and field separator.
/// </summary>
/// <param name="filename">The name of the file to be read.</param>
/// <param name="enc">The encoding used.</param>
/// <param name="separator">The field separator character.</param>
public CsvReader(string filename, Encoding enc, char separator)
: this(new FileStream(filename, FileMode.Open), enc, separator) { }
#endregion
#region Properties
/// <summary>
/// The separator character for the fields. Comma for normal CSV.
/// </summary>
public char Separator {
get { return separator; }
set { separator = value; }
}
#endregion
#region Parsing
/// <summary>
/// Returns the fields for the next row of data (or null if at eof)
/// </summary>
/// <returns>A string array of fields or null if at the end of file.</returns>
public string[] GetCsvLine() {
string data = reader.ReadLine();
if (data == null) return null;
if (data.Length == 0) return new string[0];
ArrayList result = new ArrayList();
ParseCsvFields(result, data);
return (string[])result.ToArray(typeof(string));
}
// Parses the fields and pushes the fields into the result arraylist
public void ParseCsvFields(ArrayList result, string data) {
int pos = -1;
while (pos < data.Length)
result.Add(ParseCsvField(data, ref pos));
}
// Parses the field at the given position of the data, modified pos to match
// the first unparsed position and returns the parsed field
private string ParseCsvField(string data, ref int startSeparatorPosition) {
if (startSeparatorPosition == data.Length-1) {
startSeparatorPosition++;
// The last field is empty
return "";
}
int fromPos = startSeparatorPosition + 1;
// Determine if this is a quoted field
if (data[fromPos] == '"') {
// If we're at the end of the string, let's consider this a field that
// only contains the quote
if (fromPos == data.Length-1) {
fromPos++;
return "\"";
}
// Otherwise, return a string of appropriate length with double quotes collapsed
// Note that FSQ returns data.Length if no single quote was found
int nextSingleQuote = FindSingleQuote(data, fromPos+1);
startSeparatorPosition = nextSingleQuote+1;
return data.Substring(fromPos+1, nextSingleQuote-fromPos-1).Replace("\"\"", "\"");
}
// The field ends in the next separator or EOL
int nextSeparator = data.IndexOf(separator, fromPos);
if (nextSeparator == -1) {
startSeparatorPosition = data.Length;
return data.Substring(fromPos);
}
else {
startSeparatorPosition = nextSeparator;
return data.Substring(fromPos, nextSeparator - fromPos);
}
}
// Returns the index of the next single quote mark in the string
// (starting from startFrom)
private static int FindSingleQuote(string data, int startFrom) {
int i = startFrom-1;
while (++i < data.Length)
if (data[i] == '"') {
// If this is a double quote, bypass the chars
if (i < data.Length-1 && data[i+1] == '"') {
i++;
continue;
}
else
return i;
}
// If no quote found, return the end value of i (data.Length)
return i;
}
#endregion
/// <summary>
/// Disposes the reader. The underlying stream is closed.
/// </summary>
public void Dispose() {
// Closing the reader closes the underlying stream, too
if (reader != null) reader.Close();
else if (stream != null)
stream.Close(); // In case we failed before the reader was constructed
GC.SuppressFinalize(this);
}
}
/// <summary>
/// Exception class for CsvReader exceptions.
/// </summary>
[Serializable]
public class CsvReaderException : ApplicationException {
/// <summary>
/// Constructs a new CsvReaderException.
/// </summary>
public CsvReaderException() : this("The CSV Reader encountered an error.") { }
/// <summary>
/// Constructs a new exception with the given message.
/// </summary>
/// <param name="message">The exception message.</param>
public CsvReaderException(string message) : base(message) { }
/// <summary>
/// Constructs a new exception with the given message and the inner exception.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="inner">Inner exception that caused this issue.</param>
public CsvReaderException(string message, Exception inner) : base(message, inner) { }
/// <summary>
/// Constructs a new exception with the given serialization information.
/// </summary>
/// <param name="info"></param>
/// <param name="context"></param>
protected CsvReaderException(System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
: base(info, context) { }
}
}
I also have a config file CsvToSql.exe.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="server" value="localhost"/>
<add key="database" value="test"/>
<!-- If your csv files have header, set this to true to generate field names from headers-->
<!-- Otherwise set it to false to generate names F1, F2, F3, etc.-->
<add key="hasHeaders" value="false"/>
<!-- This is the lenght of nvarchar field created can be a number or 'max'-->
<add key="fieldLength" value="500"/>
</appSettings>
</configuration>
And a script that compiles the whole lot build.cmd:
%systemroot%\Microsoft.NET\Framework\v3.5\csc.exe /out:CsvToSql.exe Program.cs CsvReader.cs
This is how I use it:
Run build.cmd to compile CsvToSql.exe
Edit CsvToSql.exe.config to fit your case
Put csv files in the same folder the executable and the config file
Run CsvToSql.exe
The executable does not connect to the database. Instead it produces a number of files:
*.tbl files are table definitions, *.inp files are input files for bcp command line utility, *.cmd files are files that run table creation scripts and bcp command line utility. _all.cmd that runs *.cmd for all tables and _cleanup.cmd that deletes all the files that CsvToSql.exe generates
Run _all.cmd file
Go to your SQL and look at what has been produced. Make changes to the script and / or config, rinse and repeat
There are a lot of assumtions that this script makes, and also a lot of stuff that is hardcoded. This is what I usaully quickly change each new time I need to load a set of CSV into SQL.
Good luck and if you have any questions please don't hesitate to ask.
The script requires .NET 3.5
If there is no extra-special about data I'm loading, I'm usually up and running with this script in 15 minutes. If there are troubles, twicking might take longer.
CSV parsing is non-trivial (taking into account text qualifiers, values that contain linebreaks, qualifier escape mechanisms, etc). There are several .Net libraries out there that do all this stuff for you (eg http://www.codeproject.com/KB/database/CsvReader.aspx), so I would think it would be easier to use a different technology, eg powershell, or SQL CLR, to make use of an existing library - rather that trying to roll your own CSV parser in T-SQL...
Huh, just found this nice and simple solution on an old forum post (http://forums.databasejournal.com/showthread.php?t=47966):
select *
from OpenRowset('MSDASQL', 'Driver={Microsoft Text Driver (*.txt; *.csv)}; DefaultDir=D:\;',
'select * from test1.csv')
Unfortunately, it doesn't work on recent windows versions where the text driver isn't installed by default...

WPF: fast way to apply formatting to RichTextBox

I'm trying to display basic syntax highlighting in a WPF RichTextBox. It mostly works, but the rendering performance is awful.
First I naively tried:
/// <summary>
/// Main event handler for syntax highlighting.
/// </summary>
private void XmlChanged(object sender, TextChangedEventArgs e)
{
VM.Dirty = true;
if (VM.Pretty)
{
var range = new TextRange(XmlView.Document.ContentStart, XmlView.Document.ContentEnd);
Render(range.Text);
}
}
/// <summary>
/// Entry point for programmatically resetting the textbox contents
/// </summary>
private void Render(string text)
{
XmlView.TextChanged -= this.XmlChanged;
if (VM.Pretty)
{
var tokens = tokenizer.Tokenize(text);
Format(XmlView.Document, tokens);
}
XmlView.TextChanged += this.XmlChanged;
}
private void Format(FlowDocument doc, List<Token> tokens)
{
var start = doc.ContentStart;
foreach (var token in tokens)
{
TextRange range = new TextRange(start.GetPositionAtOffset(token.StartPosition, LogicalDirection.Forward),
start.GetPositionAtOffset(token.EndPosition, LogicalDirection.Forward));
range.ApplyPropertyValue(TextElement.ForegroundProperty, m_syntaxColors[token.Type]);
}
}
Testing on a 2KB document with just over 100 tokens, it took 1-2 seconds to redraw after every keystroke; clearly not acceptable. Profiling showed that my tokenizer was orders of magnitude faster than the Format() function. So I tried some double-buffering:
private void Render(string text)
{
XmlView.TextChanged -= this.XmlChanged;
// create new doc offscreen
var doc = new FlowDocument();
var range = new TextRange(doc.ContentStart, doc.ContentEnd);
range.Text = text;
if (VM.Pretty)
{
var tokens = tokenizer.Tokenize(text);
Format(doc, tokens);
}
// copy to active buffer
var stream = new MemoryStream(65536);
range.Save(stream, DataFormats.XamlPackage);
var activeRange = new TextRange(XmlView.Document.ContentStart, XmlView.Document.ContentEnd);
activeRange.Load(stream, DataFormats.XamlPackage);
XmlView.TextChanged += this.XmlChanged;
}
Benchmarks show that Format() runs slightly faster rendering offscreen, but the perceived performance is even worse now!
What's the right way to go about this?
I'd try taking as much object instantiation out of the method/loop as possible and pass in references instead. You're calling new quite a few times per loop per keystroke.

WinForms Interop, Drag & Drop from WinForms -> WPF

I'm trying to drag data from the Winforms portion of my application on a WPF controls that's contained inside an "ElementHost". And it crashes when I try doing so.
Trying the same thing but from Winforms to Winforms works fine. (See example code below)
I need help on making this work... have any clues what I'm doing wrong?
Thanks!
Example:
In the sample code below, I'm just trying to drag a custom MyContainerClass object created when initating the drag on the label control on a 1) System.Windows.Forms.TextBox (Winforms) and 2) System.Windows.TextBox (WPF, added to an ElementHost).
Case 1) works fine but case 2) is crashing when trying to retrieve the drop data using GetData(). GetDataPresent("WindowsFormsApplication1.MyContainerClass") returns "true" so In theory, I should be able to retrive my drop data of that type like in Winforms.
Here is the stack trace of the crash:
"Error HRESULT E_FAIL has been returned from a call to a COM component" with the following stack trace:
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Windows.Forms.DataObject.GetDataIntoOleStructs(FORMATETC& formatetc, STGMEDIUM& medium)
at System.Windows.Forms.DataObject.System.Runtime.InteropServices.ComTypes.IDataObject.GetDataHere(FORMATETC& formatetc, STGMEDIUM& medium)
at System.Windows.Forms.DataObject.System.Runtime.InteropServices.ComTypes.IDataObject.GetData(FORMATETC& formatetc, STGMEDIUM& medium)
at System.Windows.DataObject.OleConverter.GetDataInner(FORMATETC& formatetc, STGMEDIUM& medium)
at System.Windows.DataObject.OleConverter.GetDataFromOleHGLOBAL(String format, DVASPECT aspect, Int32 index)
at System.Windows.DataObject.OleConverter.GetDataFromBoundOleDataObject(String format, DVASPECT aspect, Int32 index)
at System.Windows.DataObject.OleConverter.GetData(String format, Boolean autoConvert, DVASPECT aspect, Int32 index)
at System.Windows.DataObject.OleConverter.GetData(String format, Boolean autoConvert)
at System.Windows.DataObject.GetData(String format, Boolean autoConvert)
at System.Windows.DataObject.GetData(String format)
at WindowsFormsApplication1.Form1.textBox_PreviewDragEnter(Object sender, DragEventArgs e) in WindowsFormsApplication1\WindowsFormsApplication1\Form1.cs:line 48
Here is some code:
// -- Add an ElementHost to your form --
// -- Add a label to your form --
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
System.Windows.Controls.TextBox textBox = new System.Windows.Controls.TextBox();
textBox.Text = "WPF TextBox";
textBox.AllowDrop = true;
elementHost2.Child = textBox;
textBox.PreviewDragEnter += new System.Windows.DragEventHandler(textBox_PreviewDragEnter);
System.Windows.Forms.TextBox wfTextBox = new System.Windows.Forms.TextBox();
wfTextBox.Text = "Winforms TextBox";
wfTextBox.AllowDrop = true;
wfTextBox.DragEnter += new DragEventHandler(wfTextBox_DragEnter);
Controls.Add(wfTextBox);
}
void wfTextBox_DragEnter(object sender, DragEventArgs e)
{
bool dataPresent = e.Data.GetDataPresent("WindowsFormsApplication1.MyContainerClass");
// NO CRASH here!
object data = e.Data.GetData("WindowsFormsApplication1.MyContainerClass");
}
void textBox_PreviewDragEnter(object sender, System.Windows.DragEventArgs e)
{
bool dataPresent = e.Data.GetDataPresent("WindowsFormsApplication1.MyContainerClass");
// Crash appens here!!
// {"Error HRESULT E_FAIL has been returned from a call to a COM component."}
object data = e.Data.GetData("WindowsFormsApplication1.MyContainerClass");
}
private void label1_MouseDown(object sender, MouseEventArgs e)
{
label1.DoDragDrop(new MyContainerClass(label1.Text), DragDropEffects.Copy);
}
}
public class MyContainerClass
{
public object Data { get; set; }
public MyContainerClass(object data)
{
Data = data;
}
}
#Pedery & jmayor: Thanks for the suggestions guys! (see my findings below)
After quite a few experimentation, trials and errors, and a bit of "Reflector'ing", I managed to figure out exactly why I was receiving the cryptic error message "Error HRESULT E_FAIL has been returned from a call to a COM component".
It was due to the fact that when dragging data WPF <-> Winforms in a same app, that data has to be Serializable!
I've checked how difficult it would be to transform all of our classes to "Serializable" and I would have a been a real pain for a couple of reasons... one, we would need to practically make all of classes serializable and two, some of these classes have references to Controls! And Controls aren't serializable. So a major refactoring would have been needed.
So... since we wanted to pass any object of any class to drag from/to WPF inside the same application, I decided to create a wrapper class, with the Serializable attribute and implementing ISerializable. I would have 1 contructor with 1 parameter of type "object" which would be the actual drag data. That wrapper, when serializing/de-serializing, would serialize not the object itself... but rather the IntPtr to the object (which we can do since we only want that functionnality inside our 1 instance only application.) See code sample below:
[Serializable]
public class DataContainer : ISerializable
{
public object Data { get; set; }
public DataContainer(object data)
{
Data = data;
}
// Deserialization constructor
protected DataContainer(SerializationInfo info, StreamingContext context)
{
IntPtr address = (IntPtr)info.GetValue("dataAddress", typeof(IntPtr));
GCHandle handle = GCHandle.FromIntPtr(address);
Data = handle.Target;
handle.Free();
}
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
GCHandle handle = GCHandle.Alloc(Data);
IntPtr address = GCHandle.ToIntPtr(handle);
info.AddValue("dataAddress", address);
}
#endregion
}
To keep the IDataObject functionnality, I created the following DataObject wrapper:
public class DataObject : IDataObject
{
System.Collections.Hashtable _Data = new System.Collections.Hashtable();
public DataObject() { }
public DataObject(object data)
{
SetData(data);
}
public DataObject(string format, object data)
{
SetData(format, data);
}
#region IDataObject Members
public object GetData(Type format)
{
return _Data[format.FullName];
}
public bool GetDataPresent(Type format)
{
return _Data.ContainsKey(format.FullName);
}
public string[] GetFormats()
{
string[] strArray = new string[_Data.Keys.Count];
_Data.Keys.CopyTo(strArray, 0);
return strArray;
}
public string[] GetFormats(bool autoConvert)
{
return GetFormats();
}
private void SetData(object data, string format)
{
object obj = new DataContainer(data);
if (string.IsNullOrEmpty(format))
{
// Create a dummy DataObject object to retrieve all possible formats.
// Ex.: For a System.String type, GetFormats returns 3 formats:
// "System.String", "UnicodeText" and "Text"
System.Windows.Forms.DataObject dataObject = new System.Windows.Forms.DataObject(data);
foreach (string fmt in dataObject.GetFormats())
{
_Data[fmt] = obj;
}
}
else
{
_Data[format] = obj;
}
}
public void SetData(object data)
{
SetData(data, null);
}
#endregion
}
And we are using the above classes like this:
myControl.DoDragDrop(new MyNamespace.DataObject(myNonSerializableObject));
// in the drop event for example
e.Data.GetData(typeof(myNonSerializableClass));
I know I know... it's not very pretty... but it's doing what we wanted. We also created a dragdrop helper class which masks the DataObject creation and has templated GetData functions to retrieve the data without any cast... a bit like:
myNonSerializableClass newObj = DragDropHelper.GetData<myNonSerializableClass>(e.Data);
So thanks again for the replies! You guys gave me good ideas where to look at for possible solutions!
-Oli
I had a "similar" issue some time ago so I can at least tell you what I found out.
It seems .Net is resorting to OLE remoting when drag/drop operations are performed in but the simplest of cases. For some reason GetDataPresent will in these cases be successful and GetData will fail. This is furthermore mystified by the fact that there are several versions of the IDataObject in the .Net framework.
Windows Forms defaults to System.Windows.Forms.IDataObject. However, in your case you could try to give System.Runtime.InteropServices.ComTypes.IDataObject a shot instead. You can also check out my discussion here.
Hope this helps.
Seems wonderfull at first sight. I tried it but got some errors on implementations.
I began to correct some errors when I decided to look for something a little bit more simplier, that do not have pointers (humm I don't like that, particularly with carbage collection, but I have no idea if it could have real impact) and that do not use Interop.
I come up with that. It works for me and I hope it will work for anybody else. It is only intended to be used for local drag drop (inside the same app).
How to use to drag:
DragDrop.DoDragDrop(listBoxOfAvailableScopes, new DragDropLocal(GetSelectedSimulResultScopes()),
DragDropEffects.Copy);
How to use to drop (get):
DragDropLocal dragDropLocal = (DragDropLocal)e.Data.GetData(typeof(DragDropLocal));
SimulResultScopes simulResultScopes = (SimulResultScopes)dragDropLocal.GetObject();
Code:
namespace Util
{
[Serializable]
public class DragDropLocal
{
private static readonly Dictionary<Guid, object> _dictOfDragDropLocalKeyToDragDropSource = new Dictionary<Guid, object>();
private Guid _guid = Guid.NewGuid();
public DragDropLocal(object objToDrag)
{
_dictOfDragDropLocalKeyToDragDropSource.Add(_guid, objToDrag);
}
public object GetObject()
{
object obj;
_dictOfDragDropLocalKeyToDragDropSource.TryGetValue(_guid, out obj);
return obj;
}
~DragDropLocal()
{
_dictOfDragDropLocalKeyToDragDropSource.Remove(_guid);
}
}
}
Maybe the events are in the opposite way. The PreviewDragEnter should be related with the WPFTextBox. Also watch out the DragEventArgs class. There is one in System.Windows.Form ( Windows Form version) and the one under System.Windows( for WPF version).

Create a thumbnail and then convert to byte array

I'm having a heck of a time with creating thumbnails and then converting them into a byte array. I've tried three methods, and all 3 times I get an error.
The first was using Bitmap.GetThumbnailImage, which I have used in the past and then saved directly into Response.OutputStream
The second was using System.Drawing.Graphics with DrawImage(). Still no go.
The third was just to create a new bitmap, pass in the old bitmap, and set the new size. Same error.
Value cannot be null.
Parameter name: encoder
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: encoder
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[ArgumentNullException: Value cannot be null.
Parameter name: encoder]
System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams) +615244
Here is the code for my method. Maybe someone will see what I'm doing wrong. In case you aren't sure about GetThumbSize, it's simply a method that takes in the image size and the maximum thumb size and then computes an actual size to preserve the aspect ratio.
public static System.Drawing.Image.GetThumbnailImageAbort thumbnailCallback = new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback);
public static bool ThumbnailCallback()
{
return false;
}
/// <summary>
///
/// </summary>
/// <param name="image"></param>
/// <param name="size"></param>
/// <remarks>
/// This method will throw a AccessViolationException when the machine OS running the server code is windows 7.
/// </remarks>
/// <returns></returns>
public static byte[] CreateThumbnail(byte[] imageData, Size size)
{
using (MemoryStream inStream = new MemoryStream())
{
inStream.Write(imageData, 0, imageData.Length);
using (System.Drawing.Image image = Bitmap.FromStream(inStream))
{
Size thumbSize = GetThumbSize(new Size(image.Width, image.Height), size);
//do not make image bigger
if (thumbSize.Equals(image.Size) || (image.Width < size.Width || image.Height < size.Height))
{
//if no shrinking is ocurring, return the original bytes
return imageData;
}
else
{
using (System.Drawing.Image thumb = image.GetThumbnailImage(thumbSize.Width, thumbSize.Height, thumbnailCallback, IntPtr.Zero))
{
using (MemoryStream outStream = new MemoryStream())
{
thumb.Save(outStream, thumb.RawFormat);
return outStream.ToArray();
}
}
}
}
}
}
This line is throwing the error:
thumb.Save(outStream, thumb.RawFormat);
Any ideas? Thanks for the help!
I think the problem may be the original image's encoding. IIRC, Save(stream, format) results in a call to Save(stream, encoder, params), with the encoder being taken from the format; which in your case is the original format of the image.
According to the Community Content for the Save method, some formats will not translate well into an appropriate encoder.
I would suggest you specify the encoder yourself, using some standard format like PNG.
Try:
thumb.Save(outStream, ImageFormat.Png, null); // optionally add encoder parameters here, like quality or luminescence
If what you are trying to do is save it in a Raw format you can try the following, as in my case it works when the original image format is a supported one:
try
{
thumb.Save(outStream, img.RawFormat);
}
catch (System.ArgumentNullException)
{
thumb.Save(outStream, ImageFormat.Png);
}

Resources