Debug .NET OutOfMemoryException with windbg - c#

I need help to debug a OutOfMemoryException in a .net dll that convert rtf text in raw text or in html.
Here is the code for conversion, (http://matthewmanela.com/blog/converting-rtf-to-html/)
public string ConvertRtfToHtml(string rtfText)
{
if (rtfText.Equals("")) return "";
try
{
var thread = new Thread(ConvertRtfInSTAThread);
var threadData = new ConvertRtfThreadData { RtfText = rtfText };
thread.SetApartmentState(ApartmentState.STA);
thread.Start(threadData);
thread.Join();
return threadData.HtmlText;
}
catch (Exception e)
{
GestionErreurConv.EnregistrerErreur("Convert", "ConvertRtfToHtml", e.Message);
return rtfText;
}
}
private void ConvertRtfInSTAThread(object rtf)
{
try
{
var threadData = (ConvertRtfThreadData)rtf;
var converter = new RtfToHtmlConverter();
threadData.HtmlText = converter.ConvertRtfToHtml(threadData.RtfText);
}
catch (Exception e)
{
GestionErreurConv.EnregistrerErreur("Convert", "ConvertRtfToHtml", e.Message);
}
}
public class RtfToHtmlConverter
{
private const string FlowDocumentFormat = "<FlowDocument>{0}</FlowDocument>";
public string ConvertRtfToHtml(string rtfText)
{
var xamlText = string.Format(FlowDocumentFormat, ConvertRtfToXaml(rtfText));
var converter = new HtmlFromXamlConverter();
return converter.ConvertXamlToHtml(xamlText, false);
}
private string ConvertRtfToXaml(string rtfText)
{
string returnString;
try
{
var richTextBox = new RichTextBox
{
UndoLimit = 0,
IsUndoEnabled = false
};
var textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
//Create a MemoryStream of the Rtf content
using (var rtfMemoryStream = new MemoryStream())
{
using (var rtfStreamWriter = new StreamWriter(rtfMemoryStream))
{
rtfStreamWriter.Write(rtfText);
rtfStreamWriter.Flush();
rtfMemoryStream.Seek(0, SeekOrigin.Begin);
//Load the MemoryStream into TextRange ranging from start to end of RichTextBox.
textRange.Load(rtfMemoryStream, DataFormats.Rtf);
}
}
using (var rtfMemoryStream = new MemoryStream())
{
textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
textRange.Save(rtfMemoryStream, DataFormats.Xaml);
rtfMemoryStream.Seek(0, SeekOrigin.Begin);
using (var rtfStreamReader = new StreamReader(rtfMemoryStream)) {
returnString = rtfStreamReader.ReadToEnd();
}
}
// Libération mémoire
GC.Collect();
GC.WaitForPendingFinalizers();
return returnString;
}
catch (Exception)
{
// Libération mémoire
GC.Collect();
GC.WaitForPendingFinalizers();
return rtfText;
}
}
}
/// <summary>
/// HtmlToXamlConverter is a static class that takes an HTML string
/// and converts it into XAML
/// </summary>
public class HtmlFromXamlConverter
{
#region Public Methods
/// <summary>
/// Main entry point for Xaml-to-Html converter.
/// Converts a xaml string into html string.
/// </summary>
/// <param name="xamlString">
/// Xaml strinng to convert.
/// </param>
/// <returns>
/// Html string produced from a source xaml.
/// </returns>
public string ConvertXamlToHtml(string xamlString, bool asFullDocument)
{
var htmlStringBuilder = new StringBuilder(100);
using (var xamlReader = new XmlTextReader(new StringReader(xamlString)))
using (var htmlWriter = new XmlTextWriter(new StringWriter(htmlStringBuilder)))
{
if (!WriteFlowDocument(xamlReader, htmlWriter, asFullDocument))
{
return "";
}
return htmlStringBuilder.ToString();
}
}
#endregion Public Methods
// ---------------------------------------------------------------------
//
// Private Methods
//
// ---------------------------------------------------------------------
#region Private Methods
/// <summary>
/// Processes a root level element of XAML (normally it's FlowDocument element).
/// </summary>
/// <param name="xamlReader">
/// XmlTextReader for a source xaml.
/// </param>
/// <param name="htmlWriter">
/// XmlTextWriter producing resulting html
/// </param>
private bool WriteFlowDocument(XmlTextReader xamlReader, XmlTextWriter htmlWriter, bool asFullDocument)
{
if (!ReadNextToken(xamlReader))
{
// Xaml content is empty - nothing to convert
return false;
}
if (xamlReader.NodeType != XmlNodeType.Element || xamlReader.Name != "FlowDocument")
{
// Root FlowDocument elemet is missing
return false;
}
// Create a buffer StringBuilder for collecting css properties for inline STYLE attributes
// on every element level (it will be re-initialized on every level).
var inlineStyle = new StringBuilder();
if (asFullDocument)
{
htmlWriter.WriteStartElement("HTML");
htmlWriter.WriteStartElement("BODY");
}
WriteFormattingProperties(xamlReader, htmlWriter, inlineStyle);
WriteElementContent(xamlReader, htmlWriter, inlineStyle);
if (asFullDocument)
{
htmlWriter.WriteEndElement();
htmlWriter.WriteEndElement();
}
return true;
}
/// <summary>
/// Reads attributes of the current xaml element and converts
/// them into appropriate html attributes or css styles.
/// </summary>
/// <param name="xamlReader">
/// XmlTextReader which is expected to be at XmlNodeType.Element
/// (opening element tag) position.
/// The reader will remain at the same level after function complete.
/// </param>
/// <param name="htmlWriter">
/// XmlTextWriter for output html, which is expected to be in
/// after WriteStartElement state.
/// </param>
/// <param name="inlineStyle">
/// String builder for collecting css properties for inline STYLE attribute.
/// </param>
private void WriteFormattingProperties(XmlTextReader xamlReader, XmlTextWriter htmlWriter, StringBuilder inlineStyle)
{
// Clear string builder for the inline style
inlineStyle.Remove(0, inlineStyle.Length);
if (!xamlReader.HasAttributes)
{
return;
}
bool borderSet = false;
while (xamlReader.MoveToNextAttribute())
{
string css = null;
switch (xamlReader.Name)
{
// Character fomatting properties
// ------------------------------
case "Background":
css = "background-color:" + ParseXamlColor(xamlReader.Value) + ";";
break;
case "FontFamily":
css = "font-family:" + xamlReader.Value + ";";
break;
case "FontStyle":
css = "font-style:" + xamlReader.Value.ToLower() + ";";
break;
case "FontWeight":
css = "font-weight:" + xamlReader.Value.ToLower() + ";";
break;
case "FontStretch":
break;
case "FontSize":
css = "font-size:" + xamlReader.Value + "px;";
break;
case "Foreground":
css = "color:" + ParseXamlColor(xamlReader.Value) + ";";
break;
case "TextDecorations":
if (xamlReader.Value.ToLower() == "strikethrough")
css = "text-decoration:line-through;";
else
css = "text-decoration:underline;";
break;
case "TextEffects":
break;
case "Emphasis":
break;
case "StandardLigatures":
break;
case "Variants":
break;
case "Capitals":
break;
case "Fraction":
break;
// Paragraph formatting properties
// -------------------------------
case "Padding":
css = "padding:" + ParseXamlThickness(xamlReader.Value) + ";";
break;
case "Margin":
css = "margin:" + ParseXamlThickness(xamlReader.Value) + ";";
break;
case "BorderThickness":
css = "border-width:" + ParseXamlThickness(xamlReader.Value) + ";";
borderSet = true;
break;
case "BorderBrush":
css = "border-color:" + ParseXamlColor(xamlReader.Value) + ";";
borderSet = true;
break;
case "LineHeight":
break;
case "TextIndent":
css = "text-indent:" + xamlReader.Value + ";";
break;
case "TextAlignment":
css = "text-align:" + xamlReader.Value + ";";
break;
case "IsKeptTogether":
break;
case "IsKeptWithNext":
break;
case "ColumnBreakBefore":
break;
case "PageBreakBefore":
break;
case "FlowDirection":
break;
// Table attributes
// ----------------
case "Width":
css = "width:" + xamlReader.Value + ";";
break;
case "ColumnSpan":
htmlWriter.WriteAttributeString("COLSPAN", xamlReader.Value);
break;
case "RowSpan":
htmlWriter.WriteAttributeString("ROWSPAN", xamlReader.Value);
break;
// Hyperlink Attributes
case "NavigateUri":
htmlWriter.WriteAttributeString("HREF", xamlReader.Value);
break;
case "TargetName":
htmlWriter.WriteAttributeString("TARGET", xamlReader.Value);
break;
}
if (css != null)
{
inlineStyle.Append(css);
}
}
if (borderSet)
{
inlineStyle.Append("border-style:solid;mso-element:para-border-div;");
}
// Return the xamlReader back to element level
xamlReader.MoveToElement();
}
private string ParseXamlColor(string color)
{
if (color.StartsWith("#"))
{
// Remove transparancy value
color = "#" + color.Substring(3);
}
return color;
}
private string ParseXamlThickness(string thickness)
{
string[] values = thickness.Split(',');
for (int i = 0; i < values.Length; i++)
{
if (double.TryParse(values[i], out double value))
{
values[i] = Math.Ceiling(value).ToString();
}
else
{
values[i] = "1";
}
}
switch (values.Length)
{
case 1:
return thickness;
case 2:
return values[1] + " " + values[0];
case 4:
return values[1] + " " + values[2] + " " + values[3] + " " + values[0];
default:
return values[0];
}
}
/// <summary>
/// Reads a content of current xaml element, converts it
/// </summary>
/// <param name="xamlReader">
/// XmlTextReader which is expected to be at XmlNodeType.Element
/// (opening element tag) position.
/// </param>
/// <param name="htmlWriter">
/// May be null, in which case we are skipping the xaml element;
/// witout producing any output to html.
/// </param>
/// <param name="inlineStyle">
/// StringBuilder used for collecting css properties for inline STYLE attribute.
/// </param>
private void WriteElementContent(XmlTextReader xamlReader, XmlTextWriter htmlWriter, StringBuilder inlineStyle)
{
bool elementContentStarted = false;
if (xamlReader.IsEmptyElement)
{
if (htmlWriter != null && !elementContentStarted && inlineStyle.Length > 0)
{
// Output STYLE attribute and clear inlineStyle buffer.
htmlWriter.WriteAttributeString("STYLE", inlineStyle.ToString());
inlineStyle.Remove(0, inlineStyle.Length);
}
elementContentStarted = true;
}
else
{
while (ReadNextToken(xamlReader) && xamlReader.NodeType != XmlNodeType.EndElement)
{
switch (xamlReader.NodeType)
{
case XmlNodeType.Element:
if (xamlReader.Name.Contains("."))
{
AddComplexProperty(xamlReader, inlineStyle);
}
else
{
if (htmlWriter != null && !elementContentStarted && inlineStyle.Length > 0)
{
// Output STYLE attribute and clear inlineStyle buffer.
htmlWriter.WriteAttributeString("STYLE", inlineStyle.ToString());
inlineStyle.Remove(0, inlineStyle.Length);
}
elementContentStarted = true;
WriteElement(xamlReader, htmlWriter, inlineStyle);
}
Debug.Assert(xamlReader.NodeType == XmlNodeType.EndElement || xamlReader.NodeType == XmlNodeType.Element && xamlReader.IsEmptyElement);
break;
case XmlNodeType.Comment:
if (htmlWriter != null)
{
if (!elementContentStarted && inlineStyle.Length > 0)
{
htmlWriter.WriteAttributeString("STYLE", inlineStyle.ToString());
}
htmlWriter.WriteComment(xamlReader.Value);
}
elementContentStarted = true;
break;
case XmlNodeType.CDATA:
case XmlNodeType.Text:
case XmlNodeType.SignificantWhitespace:
if (htmlWriter != null)
{
if (!elementContentStarted && inlineStyle.Length > 0)
{
htmlWriter.WriteAttributeString("STYLE", inlineStyle.ToString());
}
htmlWriter.WriteString(xamlReader.Value);
}
elementContentStarted = true;
break;
}
}
}
}
/// <summary>
/// Conberts an element notation of complex property into
/// </summary>
/// <param name="xamlReader">
/// On entry this XmlTextReader must be on Element start tag;
/// on exit - on EndElement tag.
/// </param>
/// <param name="inlineStyle">
/// StringBuilder containing a value for STYLE attribute.
/// </param>
private void AddComplexProperty(XmlTextReader xamlReader, StringBuilder inlineStyle)
{
if (inlineStyle != null && xamlReader.Name.EndsWith(".TextDecorations"))
{
inlineStyle.Append("text-decoration:underline;");
}
// Skip the element representing the complex property
WriteElementContent(xamlReader, /*htmlWriter:*/null, /*inlineStyle:*/null);
}
/// <summary>
/// Converts a xaml element into an appropriate html element.
/// </summary>
/// <param name="xamlReader">
/// On entry this XmlTextReader must be on Element start tag;
/// on exit - on EndElement tag.
/// </param>
/// <param name="htmlWriter">
/// May be null, in which case we are skipping xaml content
/// without producing any html output
/// </param>
/// <param name="inlineStyle">
/// StringBuilder used for collecting css properties for inline STYLE attributes on every level.
/// </param>
private void WriteElement(XmlTextReader xamlReader, XmlTextWriter htmlWriter, StringBuilder inlineStyle)
{
if (htmlWriter == null)
{
// Skipping mode; recurse into the xaml element without any output
WriteElementContent(xamlReader, /*htmlWriter:*/null, null);
}
else
{
string htmlElementName;
switch (xamlReader.Name)
{
case "Run" :
case "Span":
case "InlineUIContainer":
htmlElementName = "SPAN";
break;
case "Bold":
htmlElementName = "B";
break;
case "Italic" :
htmlElementName = "I";
break;
case "Paragraph" :
htmlElementName = "P";
break;
case "BlockUIContainer":
case "Section":
htmlElementName = "DIV";
break;
case "Table":
htmlElementName = "TABLE";
break;
case "TableColumn":
htmlElementName = "COL";
break;
case "TableRowGroup" :
htmlElementName = "TBODY";
break;
case "TableRow" :
htmlElementName = "TR";
break;
case "TableCell" :
htmlElementName = "TD";
break;
case "List" :
string marker = xamlReader.GetAttribute("MarkerStyle");
if (marker == null || marker == "None" || marker == "Disc" || marker == "Circle" || marker == "Square" || marker == "Box")
{
htmlElementName = "UL";
}
else
{
htmlElementName = "OL";
}
break;
case "ListItem" :
htmlElementName = "LI";
break;
case "Hyperlink":
htmlElementName = "A";
break;
default :
htmlElementName = null; // Ignore the element
break;
}
if (htmlWriter != null && htmlElementName != null)
{
htmlWriter.WriteStartElement(htmlElementName);
WriteFormattingProperties(xamlReader, htmlWriter, inlineStyle);
WriteElementContent(xamlReader, htmlWriter, inlineStyle);
htmlWriter.WriteEndElement();
}
else
{
// Skip this unrecognized xaml element
WriteElementContent(xamlReader, /*htmlWriter:*/null, null);
}
}
}
// Reader advance helpers
// ----------------------
/// <summary>
/// Reads several items from xamlReader skipping all non-significant stuff.
/// </summary>
/// <param name="xamlReader">
/// XmlTextReader from tokens are being read.
/// </param>
/// <returns>
/// True if new token is available; false if end of stream reached.
/// </returns>
private bool ReadNextToken(XmlReader xamlReader)
{
while (xamlReader.Read())
{
switch (xamlReader.NodeType)
{
case XmlNodeType.Element:
case XmlNodeType.EndElement:
case XmlNodeType.None:
case XmlNodeType.CDATA:
case XmlNodeType.Text:
case XmlNodeType.SignificantWhitespace:
return true;
case XmlNodeType.Whitespace:
if (xamlReader.XmlSpace == XmlSpace.Preserve)
{
return true;
}
// ignore insignificant whitespace
break;
case XmlNodeType.EndEntity:
case XmlNodeType.EntityReference:
// Implement entity reading
//xamlReader.ResolveEntity();
//xamlReader.Read();
//ReadChildNodes( parent, parentBaseUri, xamlReader, positionInfo);
break; // for now we ignore entities as insignificant stuff
case XmlNodeType.Comment:
return true;
case XmlNodeType.ProcessingInstruction:
case XmlNodeType.DocumentType:
case XmlNodeType.XmlDeclaration:
default:
// Ignorable stuff
break;
}
}
return false;
}
#endregion Private Methods
}
}
This dll is used by a Windows Service and ConvertRtfToHtml method is called many times.
This is windbg informations :
0:016> !sos.clrstack
OS Thread Id: 0x220c (16)
Child SP IP Call Site
127beb0c 755bc232 [GCFrame: 127beb0c]
127bebcc 755bc232 [HelperMethodFrame_2OBJ: 127bebcc] System.Environment.GetResourceFromDefault(System.String)
127bec50 10fa493c System.Environment.GetResourceString(System.String, System.Object[])
127bec60 10fa48af System.Exception.get_Message()
127bec70 069077d9 *** WARNING: Unable to verify checksum for Convertisseur.dll
SQWebContributeur.Convertisseur.Convert.ConvertRtfInSTAThread(System.Object) [D:\SOLU-QIQ\Projets SVN\DLL Maison\Convertisseur\Convertisseur\Convert.cs # 268]
127bed94 069052d4 System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
127beda0 063e2c17 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
127bee10 063e2177 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
127bee24 06905162 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
127bee3c 069050e3 System.Threading.ThreadHelper.ThreadStart(System.Object)
127bef80 730eebf6 [GCFrame: 127bef80]
127bf164 730eebf6 [DebuggerU2MCatchHandlerFrame: 127bf164]
0:016> !pe -nested
Exception object: 019bbfc0
Exception type: System.Runtime.InteropServices.COMException
Message: Espace insuffisant pour traiter cette commande. (Exception de HRESULT : 0x80070008)
InnerException: <none>
StackTrace (generated):
SP IP Function
00000000 00000001 UNKNOWN!System.Environment.GetResourceFromDefault(System.String)+0x2
127BEC50 10FA493C UNKNOWN!System.Environment.GetResourceString(System.String, System.Object[])+0xc
127BEC60 10FA48AF UNKNOWN!System.Exception.get_Message()+0x4f
127BEC70 069077D9 Convertisseur_ae70000!SQWebContributeur.Convertisseur.Convert.ConvertRtfInSTAThread(System.Object)+0xe9
127BED94 069052D4 UNKNOWN!System.Threading.ThreadHelper.ThreadStart_Context(System.Object)+0x9c
127BEDA0 063E2C17 UNKNOWN!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+0x107
127BEE10 063E2177 UNKNOWN!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+0x17
127BEE24 06905162 UNKNOWN!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)+0x3a
127BEE3C 069050E3 UNKNOWN!System.Threading.ThreadHelper.ThreadStart(System.Object)+0x4b
StackTraceString: <none>
HResult: 80070008
Nested exception -------------------------------------------------------------
Exception object: 019b9dc4
Exception type: System.OutOfMemoryException
Message: <none>
InnerException: <none>
StackTrace (generated):
SP IP Function
00000000 00000001 UNKNOWN!System.GC._WaitForPendingFinalizers()+0x2
127BEB68 10FA11DF UNKNOWN!System.GC.WaitForPendingFinalizers()+0x4f
127BEB98 0C55631D Convertisseur_ae70000!SQWebContributeur.ClassesHelp.RtfToHtmlConverter.ConvertRtfToXaml(System.String)+0x385
127BED00 069078C9 Convertisseur_ae70000!SQWebContributeur.ClassesHelp.RtfToHtmlConverter.ConvertRtfToHtml(System.String)+0x51
127BED38 0690779C Convertisseur_ae70000!SQWebContributeur.Convertisseur.Convert.ConvertRtfInSTAThread(System.Object)+0xac
StackTraceString: <none>
HResult: 8007000e
!eeheap -gc command show that 4 Mo are used by garbage collector :
0:016> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x019b9db8
generation 1 starts at 0x019b9cec
generation 2 starts at 0x01861000
ephemeral segment allocation context: none
segment begin allocated size
01860000 01861000 01bec808 0x38b808(3717128)
Large object heap starts at 0x02861000
segment begin allocated size
02860000 02861000 028ba260 0x59260(365152)
Total Size: Size: 0x3e4a68 (4082280) bytes.
------------------------------
GC Heap Size: Size: 0x3e4a68 (4082280) bytes.
!dumpheap -stat command shows that only 2 Mo are free :
00e6a430 865 2382762 Free
Here is perfmon data :
I don't know what to do to resolve this exception. I try to add GC.Collect() to force GC without any effect.
The VM have 8 Go of physical memory and on another VM with 4 Go, exception not occurs. I don't know how can I resolve this exception.
Thanks for your help

First of all, you did a good job extracting all the inner exceptions in order to figure out that this crash is caused by a OOM exception. Not all developers have that skill.
!eeheap -gc command show that 4 Mo are used by garbage collector
That's correct - and a strong indicator that garbage collecting on its own will not really help. Even if it could free the 4 MB, you have gained almost nothing.
(But: more on that later)
!dumpheap -stat command shows that only 2 Mo are free
While this statement is not wrong, it's also not complete.
a) there are 2 MB free, but those 2 MB are split into 865 different regions. So it might still be impossible to allocate a single 2 MB block
b) these 2 MB are free from .NET point of view. If .NET does not have enough memory free, it will request more memory from the operating system. That request may then succeed or fail, depending on how much memory the OS is available to give.
With that knowledge in mind, you need to ask
why wasn't the operating system able to give .NET more memory?
The reason likely is: because it has given all memory away already. In a 32 bit process, that's 2 GB, 3 GB or 4 GB, depending on configurations and settings (mainly Large Address Aware). That's not much, and especially, it's not available as a contiguous block. In many cases, you only have 700 MB.
Where could the OS have given the memory? To COM objects in your case (because we have a COM exception, but that may be misleading). And these COM objects seem to be native (otherwise they would allocate managed memory). Looking at .NET memory will not help then.
However, there is one exception: if your .NET code is the reason that COM objects are not freed, then your .NET code is indirectly responsible for the native memory leak. So, what you should be looking for is the amount of RCW objects. If you have a lot of them, you need to somehow get rid of them.
If that's not the reason, maybe your RTF is simply too large and does not fit into the largest region of free memory.
I have once made up a graph for tackling down OOM exceptions that tells you where to start.
With !address -summary you take a look at the point of view of the operating system.
You likely have a small <unknown> value, since the .NET usage is small.
If Heap has a large value, the memory is gone via the Windows Heap Manager (e.g. C++) and you have a native leak (possibly caused by COM objects not being released).
You can also have a look at the section "largest region by size", where you'll find a value for Free. That's the maximum value someone can get with a single request. Maybe it's not enough to fit your data.

Related

.NET MAUI app on Windows platform getting System.IO.FileNotFoundException' in System.Private.CoreLib.dll

I'm working on a .NET MAUI project in the context of MVVM architecture and I created two .txt files (15.txt and 19.txt) inside directory Resources\Raw. I set the files' Build action to MauiAsset. Each of
these two files contain topology for a matrix of color-coded fields.
What were I expecting: to read the text file's content without obtaining System.IO.FileNotFoundException' in System.Private.CoreLib.dll and to be able to use (invoke) the reader method more than once in a successfull manner.
I experience the following behaviour: in case the mentioned exception is not checked under Debug -> Windows -> Exception Setings -> Common Language Runtime Exceptions then the textfile's content is successfully obtained via the reader method as it's evident by looking at the graphical changes on one of my ContentPages (a matrix of fields appears with plenty of differently colored fields), however at the moment of its invokation an System.IO.FileNotFoundException' in System.Private.CoreLib.dllemerges as part of the logs (alerting message is absent).
However, if the aforementioned exception is checked under Debug -> Windows -> Exception Setings -> Common Language Runtime Exceptions
then my application does not even start succesfully.
Inside class GameViewModel:
/// <summary>
/// event handler method for choosing 15 as tablesize and reading appropriate table
/// </summary>
private async void GameViewModel_ButtonSmallTableClick(object? sender, System.EventArgs e)
{
await Navigation.PushAsync(new GamePage
{
BindingContext = _gameViewModel
});
Stream fileStream = await FileSystem.Current.OpenAppPackageFileAsync("15.txt");
await _gameModel.LoadGameAsync(fileStream);
_gameViewModel.GameTableNumRows = 15;
_gameViewModel.GameTableNumCols = 15;
_gameViewModel.GenerateFields();
}
/// <summary>
/// event handler method for choosing 19 as tablesize and reading appropriate table
/// </summary>
private async void GameViewModel_ButtonLargeTableClick(object? sender, System.EventArgs e)
{
await Navigation.PushAsync(new GamePage
{
BindingContext = _gameViewModel
});
Stream fileStream = await FileSystem.Current.OpenAppPackageFileAsync("19.txt");
await _gameModel.LoadGameAsync(fileStream);
_gameViewModel.GameTableNumRows = 19;
_gameViewModel.GameTableNumCols = 19;
_gameViewModel.GenerateFields();
}
Here's the method definition inside class GameModel:
/// <summary>
/// Loading colored matrix.
/// </summary>
/// <param name="fileStream">Type of Stream.</param>
public async Task LoadGameAsync(System.IO.Stream fileStream)
{
if (_dataAccess == null)
throw new InvalidOperationException("No data access is provided.");
var loadResult = await _dataAccess.LoadAsync(fileStream);
_gameTable = loadResult.Item1;
_player = loadResult.Item2;
_guards = loadResult.Item3;
IsWon = false;
}
Finally, here's the persistence-related method:
/// <summary>
/// Loading file
/// </summary>
/// <param name="path">Filepath.</param>
/// <returns>Gametable, Player, list of Guards.</returns>
public async Task<(UInt16[,], Player, List<Guard>)> LoadAsync(String path)
{
try
{
using (StreamReader reader = new StreamReader(path))
{
String[] gameFieldTypes;
String line = await reader.ReadLineAsync() ?? String.Empty;
Int32 tableSize = Int32.Parse(line);
UInt16[,] table = new UInt16[tableSize, tableSize];
Player player = new Player();
List<Guard> guards = new List<Guard>();
for (Int32 i = 0; i < tableSize; i++)
{
line = await reader.ReadLineAsync() ?? String.Empty;
gameFieldTypes = line.Split(' ');
for (Int32 j = 0; j < tableSize; j++)
{
if (gameFieldTypes[j] == "Empty")
{
table[j, i] = 0;
}
if (gameFieldTypes[j] == "Wall")
{
table[j, i] = 1;
}
if (gameFieldTypes[j] == "Guard")
{
table[j, i] = 2;
Guard guard = new Guard();
guard.X = j;
guard.Y = i;
guards.Add(guard);
}
if (gameFieldTypes[j] == "Player")
{
table[j, i] = 3;
player.X = j;
player.Y = i;
}
if (gameFieldTypes[j] == "Exit")
{
table[j, i] = 4;
}
}
}
return (table, player, guards);
}
}
catch
{
throw new GameDataException();
}
}
EDIT:
Here's the emerging exception: System.IO.FileNotFoundException' in System.Private.CoreLib.dll
Could not find file 'C:\WINDOWS\system32\SuspendedGame'.'
Line throwing exception inside method LoadAsync():
using (StreamReader reader = new StreamReader(path))
And here's the class within which the phrase "SuspendedGame" appears.
public class StoredGameBrowserModel
{
private IStore _store; // persistence
/// <summary>
/// Event of container being changed.
/// </summary>
public event EventHandler? StoreChanged;
public StoredGameBrowserModel(IStore store)
{
_store = store;
StoredGames = new List<StoredGameModel>();
}
/// <summary>
/// Query of the list of stored colored-field matrices.
/// </summary>
public List<StoredGameModel> StoredGames { get; private set; }
/// <summary>
/// Updating stored resource files.
/// </summary>
public async Task UpdateAsync()
{
if (_store == null)
return;
StoredGames.Clear();
// loading saved files
foreach (String name in await _store.GetFilesAsync())
{
if (name == "SuspendedGame") // we do not want to load this particular saved file
continue;
StoredGames.Add(new StoredGameModel
{
Name = name,
Modified = await _store.GetModifiedTimeAsync(name)
});
}
// arranging elements according to their date
StoredGames = StoredGames.OrderByDescending(item => item.Modified).ToList();
OnSavesChanged();
}
private void OnSavesChanged()
{
StoreChanged?.Invoke(this, EventArgs.Empty);
}
}
Solution:
inserting the following code at the beginning of the method body of LoadAsync():
if (!String.IsNullOrEmpty(_basePath))
path = Path.Combine(_basePath, path);
Reasoning:
the prefix library path was missing from the content of the method parameter.

Remove html tags from MainBody

Have an issue here where I try to remove all html tags from this line of EPiServer code
#(Html.PropertyFor(m => m.MainBody)
Because this is suppose to be inside a <a>example code here</a>
Whats a good way to solve this when running EPi Server?
First, it is bad practice using XhtmlString this way, that being said we don't always get to choose.
I'm using this which is a modified version of Rob Volk's extension method.
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
public static class HtmlStringExtensions
{
/// <summary>
/// Truncates a string containing HTML to a number of text characters, keeping whole words.
/// The result contains HTML and any tags left open are closed.
/// by Rob Volk with modifications
/// http://robvolk.com/truncate-html-string-c-extension-method/
/// </summary>
/// <param name="html"></param>
/// <param name="maxCharacters"></param>
/// <param name="trailingText"></param>
/// <returns></returns>
public static string TruncateHtmlString(this string html, int maxCharacters, string trailingText)
{
if (string.IsNullOrEmpty(html))
return html;
// find the spot to truncate
// count the text characters and ignore tags
var textCount = 0;
var charCount = 0;
var ignore = false;
var newString = string.Empty;
foreach (char c in html)
{
newString += c;
charCount++;
if (c == '<')
{
ignore = true;
}
else if (!ignore)
{
textCount++;
}
if (c == '>')
{
ignore = false;
}
// stop once we hit the limit
if (textCount >= maxCharacters)
{
break;
}
}
// Truncate the html and keep whole words only
var trunc = new StringBuilder(newString);
//var trunc = new StringBuilder(html.TruncateWords(charCount));
// keep track of open tags and close any tags left open
var tags = new Stack<string>();
var matches = Regex.Matches(trunc.ToString(), // trunc.ToString()
#"<((?<tag>[^\s/>]+)|/(?<closeTag>[^\s>]+)).*?(?<selfClose>/)?\s*>",
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Multiline);
foreach (Match match in matches)
{
if (match.Success)
{
var tag = match.Groups["tag"].Value;
var closeTag = match.Groups["closeTag"].Value;
// push to stack if open tag and ignore it if it is self-closing, i.e. <br />
if (!string.IsNullOrEmpty(tag) && string.IsNullOrEmpty(match.Groups["selfClose"].Value))
tags.Push(tag);
// pop from stack if close tag
else if (!string.IsNullOrEmpty(closeTag))
{
// pop the tag to close it.. find the matching opening tag
// ignore any unclosed tags
while (tags.Pop() != closeTag && tags.Count > 0)
{ }
}
}
}
if (html.Length > charCount)
// add the trailing text
trunc.Append(trailingText);
// pop the rest off the stack to close remainder of tags
while (tags.Count > 0)
{
trunc.Append("</");
trunc.Append(tags.Pop());
trunc.Append('>');
}
return trunc.ToString();
}
/// <summary>
/// Truncates a string containing HTML to a number of text characters, keeping whole words.
/// The result contains HTML and any tags left open are closed.
/// </summary>
/// <param name="html"></param>
/// <param name="maxCharacters"></param>
/// <returns></returns>
public static string TruncateHtmlString(this string html, int maxCharacters)
{
return html.TruncateHtmlString(maxCharacters, null);
}
/// <summary>
/// Strips all HTML tags from a string
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string StripHtml(this string html)
{
if (string.IsNullOrEmpty(html))
return html;
return Regex.Replace(html, #"<(.|\n)*?>", string.Empty);
}
}
Implement using the ToHtmlString() from EPiServer.Core
In example
// #using EPiServer.Core
#(Html.PropertyFor(m => m.MainBody.ToHtmlString().TruncateHtmlString(160, "..."))
Why don't you use string backed by TextArea?
[UIHint(UIHint.Textarea)]
[Display(Name = "Main Body")]
public virtual string MainBody { get; set; }
What you trying to do using XhtmlString is not a best practise and it could have so many negative effects on your rendering.

C# + CoDeSys Automation Platform SDK -> PLC's variable = VarRefState.NotMonitoredYet

I'm trying to read the variable of the PLC.
In the Automation Platform, I have a plugin who start a my test (that I've write in C#).
When I execute the plugin for the first time, it always give me the same error. But if a execute it again it's good.
I use a List<IOnlineVarRef6> vars to read my variable. My error is that my vars's State is NotMonitoredYet.
Exemple :
private bool CompareValues(TestCase test, List<IOnlineVarRef6> vars)
{
// Stop here the first time
if (vars.First().State == VarRefState.NotMonitoredYet)
return false;
// Execute well the other times
List<Variable> initialVars = test.initialVariables;
for (int i = 0; i < vars.Count(); i++)
{
object initialValue = initialVars.Single(v => v.Name == vars[i].Expression.ToString()).Value;
if (!vars[i].Value.Equals(initialValue))
return false;
}
return true;
}
I think the problem is in the method who get my variable :
/// <summary>
/// Create a variable watch for each of the specified variable in the list
/// </summary>
/// <example>
/// Variable should include object hierarchy up to Device
/// GPX.Diesel_Control.CTRL.PB_CRANK1
/// </example>
public List<IOnlineVarRef6> CreateVariableWatch(List<string> vars)
{
IVarRef2 varref;
IOnlineVarRef6 iov;
IOnlineApplication17 onlineapp = (IOnlineApplication17)onlineMgr.GetApplication(SystemInstances.Engine.Projects.PrimaryProject.ActiveApplication);
List<IOnlineVarRef6> lstVarRef = new List<IOnlineVarRef6>();
foreach (string var in vars)
{
varref = (IVarRef2)SystemInstances.LanguageModelMgr.GetVarReference(var);
iov = (IOnlineVarRef6)onlineMgr.CreateWatch(varref);
lstVarRef.Add(iov);
}
return lstVarRef;
}
I have a method that wait before calling the CompareValues() and it retries 3 times and it wait before trying again :
public void SetIsTestPassed(TestCase test)
{
Thread.Sleep(test.delayedStart);
int retries = test.retries;
do
{
List<IOnlineVarRef6> vars = SetCurrentVars(test);
test.IsTestPassed = CompareValues(test, vars);
if (test.retryDelay > 0 && !test.IsTestPassed)
Thread.Sleep(test.retryDelay);
retries--;
} while (retries != 0 && !test.IsTestPassed);
}
private List<IOnlineVarRef6> SetCurrentVars(TestCase test)
{
OnlineManagerHelper OnlineMgr = new OnlineManagerHelper(true);
return OnlineMgr.CreateVariableWatch(
test.initialVariables
.Select(v => Settings.Default.InstancePath + v.Name)
.ToList());
}

C# WMEncoder Error C00D002E

I wrote a Windows Service Code to convert AVI to WMV file.
I'm using XviD, and Windows Media 9, in a Windows Vista.
The service starts converting, and stops before completing the job, with a WMENC_ENCODER_STOPPED status, but the file was not fully converted. And the resulting file stays locked to this application forever, until I stop the service. And when I do so, that resulting file is automatically deleted.
Can anyone, please, help? It is important !!
EDIT: This program works constantly, executing conversion tasks. If a file fails to convert as described above, and I use the function GC.WaitForPendingFinalizers(), I still have the locking problem. But the service goes on, and pick another task. Succeeding or failing this new task, when I use GC.WaitForPendingFinalizers(), the first one is unlocked, and automatically deleted.
public WMENC_ENCODER_STATE EncoderStatus
{
get { return _encoderStatus; }
set { _encoderStatus = value; }
}
IWMEncFile2 File;
IWMEncAudioSource[] SrcAud;
IWMEncVideoSource2[] SrcVid;
IWMEncSourceGroup2[] SrcGrp;
WMEncoder glbEncoder = new WMEncoder();
WMEncProfile2[] Pro = null;
IWMEncSourceGroupCollection SrcGrpColl = null;
public WMVEnconder(ConvertGroup[] sourceGroup, string targetFile, string profilefile)
{
glbEncoder.OnStateChange += new _IWMEncoderEvents_OnStateChangeEventHandler(Encoder_OnStateChange);
glbEncoder.OnError +=new _IWMEncoderEvents_OnErrorEventHandler(Encoder_OnError);
// Creates source group.
SrcGrpColl = glbEncoder.SourceGroupCollection;
int[] intProVBRModeAudio = new int[sourceGroup.Length];
int[] intProVBRModeVideo = new int[sourceGroup.Length];
Int32[] intProContentType = new int[sourceGroup.Length];
Pro = new WMEncProfile2[sourceGroup.Length];
for (int i = 0; i < Pro.Length; i++)
{
Pro[i] = new WMEncProfile2();
Pro[i].LoadFromMemory(profilefile);
intProContentType[i] = Pro[i].ContentType;
intProVBRModeAudio[i] = (int)Pro[i].get_VBRMode(WMENC_SOURCE_TYPE.WMENC_AUDIO, 0);
intProVBRModeVideo[i] = (int)Pro[i].get_VBRMode(WMENC_SOURCE_TYPE.WMENC_VIDEO, 0);
}
SrcGrp = new IWMEncSourceGroup2[sourceGroup.Length];
for (int i = 0; i < SrcGrp.Length; i++)
SrcGrp[i] = (IWMEncSourceGroup2)SrcGrpColl.Add(i.ToString());
SrcAud = new IWMEncAudioSource[sourceGroup.Length];
SrcVid = new IWMEncVideoSource2[sourceGroup.Length];
for (int i = 0; i < sourceGroup.Length; i++)
{
if (intProContentType[i] == 1)
{
SrcAud[i] = (WMEncoderLib.IWMEncAudioSource)SrcGrp[i].AddSource(WMENC_SOURCE_TYPE.WMENC_AUDIO);
SrcAud[i].SetInput(sourceGroup[i].AudioFile, "", "");
SrcAud[i].MarkIn = sourceGroup[i].AudioMarkin;
SrcAud[i].MarkOut = sourceGroup[i].AudioMarkout;
}
else if (intProContentType[i] == 16)
{
SrcVid[i] = (WMEncoderLib.IWMEncVideoSource2)SrcGrp[i].AddSource(WMENC_SOURCE_TYPE.WMENC_VIDEO);
SrcVid[i].SetInput(sourceGroup[i].VideoFile, "", "");
SrcVid[i].MarkIn = sourceGroup[i].VideoMarkin;
SrcVid[i].MarkOut = sourceGroup[i].VideoMarkout;
}
else if (intProContentType[i] == 17)
{
SrcAud[i] = (WMEncoderLib.IWMEncAudioSource)SrcGrp[i].AddSource(WMENC_SOURCE_TYPE.WMENC_AUDIO);
SrcAud[i].SetInput(sourceGroup[i].AudioFile, "", "");
SrcVid[i] = (WMEncoderLib.IWMEncVideoSource2)SrcGrp[i].AddSource(WMENC_SOURCE_TYPE.WMENC_VIDEO);
SrcVid[i].SetInput(sourceGroup[i].VideoFile, "", "");
SrcAud[i].MarkIn = sourceGroup[i].AudioMarkin;
SrcAud[i].MarkOut = sourceGroup[i].AudioMarkout;
SrcVid[i].MarkIn = sourceGroup[i].VideoMarkin;
SrcVid[i].MarkOut = sourceGroup[i].VideoMarkout;
}
else
throw new Exception("erro - não suporta este tipo de profile");
SrcGrp[i].set_Profile(Pro[i]);
if (i + 1 < sourceGroup.Length)
SrcGrp[i].SetAutoRollover(-1, (i + 1).ToString());
}
File = (IWMEncFile2)glbEncoder.File;
File.LocalFileName = targetFile;
glbEncoder.PrepareToEncode(true);
}
/// <summary>
/// Inicia os processos do codificador.
/// </summary>
public void Start()
{
glbEncoder.Start();
}
/// <summary>
/// Pára os processos do codificador.
/// </summary>
public void Stop()
{
glbEncoder.Stop();
}
/// <summary>
/// Elimina as configurações atuais.
/// </summary>
public void Reset()
{
glbEncoder.Reset();
}
/// <summary>
/// Evento deflagrado pela mudança no estado do codificador.
/// </summary>
/// <param name="enumState">Enumerador indicador do estado do codificador.</param>
private void Encoder_OnStateChange(WMEncoderLib.WMENC_ENCODER_STATE enumState)
{
_encoderStatus = enumState;
string strRunState = "";
switch (enumState)
{
case WMENC_ENCODER_STATE.WMENC_ENCODER_STARTING :
strRunState = "Encoder Starting";
break;
case WMENC_ENCODER_STATE.WMENC_ENCODER_RUNNING:
strRunState = "Encoder Running";
break;
case WMENC_ENCODER_STATE.WMENC_ENCODER_END_PREPROCESS:
strRunState = "Encoder End Preprocess";
break;
case WMENC_ENCODER_STATE.WMENC_ENCODER_PAUSING:
strRunState = "Encoder Pausing";
break;
case WMENC_ENCODER_STATE.WMENC_ENCODER_PAUSED:
strRunState = "Encoder Paused";
break;
case WMENC_ENCODER_STATE.WMENC_ENCODER_STOPPING:
strRunState = "Encoder Stopping";
break;
case WMENC_ENCODER_STATE.WMENC_ENCODER_STOPPED:
strRunState = "Encoder Stopped";
break;
}
_strEncoderStatus = strRunState;
}
/// <summary>
/// Evento deflagrado pela ocorrência de um erro durante o processo de codificação.
/// </summary>
/// <param name="hr">Valor numérico do erro que ocorreu.</param>
private void Encoder_OnError(int hr)
{
string errorReceived = "";
switch (hr)
{
case -1074600792:
errorReceived = "The starting time must be greater than zero and less than the ending time.";
break;
case -1074600793:
errorReceived = "The ending time must be greater than the starting time and less than the file duration.";
break;
case -1074600804:
errorReceived = "The video capture device is in use and cannot be opened.";
break;
case -1074600808:
errorReceived = "The video capture driver returned an unrecoverable error.";
break;
case -1074600809:
errorReceived = "The video capture device did not start.";
break;
case -1074600813:
errorReceived = "The video capture window was not created.";
break;
case -1074600820:
errorReceived = "The specified operation is not allowed when the file is being archived.";
break;
case -1074600825:
errorReceived = "The archive file name is not properly specified.";
break;
case -1074600831:
errorReceived = "The specified operation is not allowed when the encoder engine is not running.";
break;
case -1074600833:
errorReceived = "Inverse telecine cannot be specified when the frame rate does not equal 30 frames per second.";
break;
case -1074600834:
errorReceived = "Internal problems are preventing the preview or postview.";
break;
case -1074600835:
errorReceived = "One or more required codecs cannot be found.";
break;
case -1074600840:
errorReceived = "The display size or color setting has changed since the encoding session was defined.";
break;
}
try
{
if (Directory.Exists(#"C:\MediaDNA_V2\Data\Conversion\Exception"))
{
MediaDNAException mdnaException = new MediaDNAException(Modulo.CONVERSION, 0, "C_110018", TipoErro.INFORMACAO, new Exception(errorReceived),
ErrorMessageConstants.C_110018, new object[] { hr.ToString("X") });
ExceptionManager.RegisterException(mdnaException, #"C:\MediaDNA_V2\Data\Conversion\Exception");
}
}
catch { }
}
#region IDisposable Members
/// <summary> Release everything. </summary>
public void Dispose()
{
glbEncoder.Stop();
glbEncoder.Reset();
GC.SuppressFinalize(this);
CloseInterfaces();
}
#endregion
/// <summary>
/// Fecha as interfaces utilizadas pelo codificador.
/// Obs: O processador precisa estar "STOPPED" para executar este processo.
/// </summary>
private void CloseInterfaces()
{
if (glbEncoder != null)
{
if (File != null)
Marshal.FinalReleaseComObject(File);
if (Pro != null)
{
for (int i = 0; i < Pro.Length; i++)
Marshal.FinalReleaseComObject(Pro[i]);
Pro = null;
}
if (SrcVid != null)
{
for (int i = 0; i < SrcVid.Length; i++)
Marshal.FinalReleaseComObject(SrcVid[i]);
SrcVid = null;
}
if (SrcAud != null)
{
for (int i = 0; i < SrcAud.Length; i++)
Marshal.FinalReleaseComObject(SrcAud[i]);
SrcAud = null;
}
if (SrcGrpColl != null)
Marshal.FinalReleaseComObject(SrcGrpColl);
if (SrcGrpColl != null)
Marshal.FinalReleaseComObject(SrcGrpColl);
if (glbEncoder != null)
Marshal.FinalReleaseComObject(glbEncoder);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
I attached my function which I converted wav file to wma.
Make sure that you do encoder.Stop(); and encoder.Reset(); and off-course release all your com object.
I'm pretty sure that once you will released your COM object your issue will be fixed.
// Create a WMEncoder object.
WMEncoder encoder = new WMEncoder();
// Retrieve the source group collection.
IWMEncSourceGroupCollection srcGrpColl = encoder.SourceGroupCollection;
// Add a source group to the collection.
IWMEncSourceGroup srcGrp = srcGrpColl.Add("SG_1");
// Add a video and audio source to the source group.
IWMEncSource srcAud = srcGrp.AddSource(WMENC_SOURCE_TYPE.WMENC_AUDIO);
srcAud.SetInput(wavFileName, "", "");
// Specify a file object in which to save encoded content.
IWMEncFile file = encoder.File;
file.LocalFileName = wmaFileName;
// Create a profile collection object from the WMEncoder object.
encoder.ProfileCollection.ProfileDirectory =
string.Format("{0}Profiles", Request.PhysicalApplicationPath);
encoder.ProfileCollection.Refresh();
IWMEncProfileCollection proColl = encoder.ProfileCollection;
// Create a profile object
IEnumerator profEnum = proColl.GetEnumerator();
IWMEncProfile profile = null; ;
IWMEncProfile2 newProfile = null;
while (profEnum.MoveNext())
{
profile = (IWMEncProfile)profEnum.Current;
if (profile.Name == "WavToWma")
{
// Load profile
newProfile = new WMEncProfile2();
newProfile.LoadFromIWMProfile(profile);
// Specify this profile object as the profile to use in source group.
srcGrp.set_Profile(newProfile);
}
}
// Start the encoding process.
// Wait until the encoding process stops before exiting the application.
encoder.PrepareToEncode(true);
encoder.Start();
while (encoder.RunState != WMENC_ENCODER_STATE.WMENC_ENCODER_STOPPED)
{
Thread.Sleep(500);
}
encoder.Stop();
encoder.Reset();
try
{
#region Release com objects
if (file != null)
Marshal.FinalReleaseComObject(file);
if (profile != null)
Marshal.FinalReleaseComObject(profile);
if (newProfile != null)
Marshal.FinalReleaseComObject(newProfile);
if (srcAud != null)
Marshal.FinalReleaseComObject(srcAud);
if (srcGrp != null)
Marshal.FinalReleaseComObject(srcGrp);
if (srcGrpColl != null)
Marshal.FinalReleaseComObject(srcGrpColl);
if (proColl != null)
Marshal.FinalReleaseComObject(proColl);
if (encoder != null)
Marshal.FinalReleaseComObject(encoder);
// GC collect is explicitly called because of a memory leak issue of WMEncoder.
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
#endregion
}
catch { }
}

escaping tricky string to CSV format

I have to create a CSV file from webservice output and the CSV file uses quoted strings with comma separator. I cannot change the format...
So if I have a string it becomes a "string"...
If the value has quotes already they are replaced with double quotes.
For example a str"ing becomes "str""ing"...
However, lately my import has been failing because of the following
original input string is: "","word1,word2,..."
every single quote is replaced by double resulting in: """",""word1,word2,...""
then its prefixed and suffixed with quote before written to CVS file: """"",""word1,word2,..."""
As you can see the final result is this:
""""",""word1,word2,..."""
which breaks my import (is sees it as another field)...
I think the issue is appereance of "," in the original input string.
Is there a CVS escape sequence for this scenario?
Update
The reason why above breaks is due to BCP mapping file (BCP utility is used to load CSV file into SQL db) which has terminator defined as "," . So instead of seeing 1 field it sees 2...But I cannot change the mapping file...
I use this code and it has always worked:
/// <summary>
/// Turn a string into a CSV cell output
/// </summary>
/// <param name="str">String to output</param>
/// <returns>The CSV cell formatted string</returns>
public static string StringToCSVCell(string str)
{
bool mustQuote = (str.Contains(",") || str.Contains("\"") || str.Contains("\r") || str.Contains("\n"));
if (mustQuote)
{
StringBuilder sb = new StringBuilder();
sb.Append("\"");
foreach (char nextChar in str)
{
sb.Append(nextChar);
if (nextChar == '"')
sb.Append("\"");
}
sb.Append("\"");
return sb.ToString();
}
return str;
}
Based on Ed Bayiates' answer:
/// <summary>
/// Turn a string into a CSV cell output
/// </summary>
/// <param name="value">String to output</param>
/// <returns>The CSV cell formatted string</returns>
private string ConvertToCsvCell(string value)
{
var mustQuote = value.Any(x => x == ',' || x == '\"' || x == '\r' || x == '\n');
if (!mustQuote)
{
return value;
}
value = value.Replace("\"", "\"\"");
return string.Format("\"{0}\"", value);
}
My penny thought:
String[] lines = new String[] { "\"\",\"word\",word,word2,1,34,5,2,\"details\"" };
for (int j = 0; j < lines.Length; j++)
{
String[] fields=lines[j].Split(',');
for (int i =0; i<fields.Length; i++)
{
if (fields[i].StartsWith("\"") && fields[i].EndsWith("\""))
{
char[] tmp = new char[fields[i].Length-2];
fields[i].CopyTo(1,tmp,0,fields[i].Length-2);
fields[i] =tmp.ToString();
fields[i] = "\""+fields[i].Replace("\"","\"\"")+"\"";
}
else
fields[i] = fields[i].Replace("\"","\"\"");
}
lines[j]=String.Join(",",fields);
}
Based on contribution of "Ed Bayiates" here's an helpful class to buid csv document:
/// <summary>
/// helpful class to build csv document
/// </summary>
public class CsvBuilder
{
/// <summary>
/// create the csv builder
/// </summary>
public CsvBuilder(char csvSeparator)
{
m_csvSeparator = csvSeparator;
}
/// <summary>
/// append a cell
/// </summary>
public void appendCell(string strCellValue)
{
if (m_nCurrentColumnIndex > 0) m_strBuilder.Append(m_csvSeparator);
bool mustQuote = (strCellValue.Contains(m_csvSeparator)
|| strCellValue.Contains('\"')
|| strCellValue.Contains('\r')
|| strCellValue.Contains('\n'));
if (mustQuote)
{
m_strBuilder.Append('\"');
foreach (char nextChar in strCellValue)
{
m_strBuilder.Append(nextChar);
if (nextChar == '"') m_strBuilder.Append('\"');
}
m_strBuilder.Append('\"');
}
else
{
m_strBuilder.Append(strCellValue);
}
m_nCurrentColumnIndex++;
}
/// <summary>
/// end of line, new line
/// </summary>
public void appendNewLine()
{
m_strBuilder.Append(Environment.NewLine);
m_nCurrentColumnIndex = 0;
}
/// <summary>
/// Create the CSV file
/// </summary>
/// <param name="path"></param>
public void save(string path )
{
File.WriteAllText(path, ToString());
}
public override string ToString()
{
return m_strBuilder.ToString();
}
private StringBuilder m_strBuilder = new StringBuilder();
private char m_csvSeparator;
private int m_nCurrentColumnIndex = 0;
}
How to use it:
void exportAsCsv( string strFileName )
{
CsvBuilder csvStringBuilder = new CsvBuilder(';');
csvStringBuilder.appendCell("#Header col 1 : Name");
csvStringBuilder.appendCell("col 2 : Value");
csvStringBuilder.appendNewLine();
foreach (Data data in m_dataSet)
{
csvStringBuilder.appendCell(data.getName());
csvStringBuilder.appendCell(data.getValue());
csvStringBuilder.appendNewLine();
}
csvStringBuilder.save(strFileName);
}
the first step in parsing this is removing the extra added " 's around your string. Once you do this, you should be able to deal with the embedded " as well as the ,'s.
After much deliberation, it was decided that import utility format was needed to be fixed. The escaping of the string was correct (as users indicated) but the format file that import utility used was incorrect and was causing it to break import.
Thanks all and special thanks to #dbt (up vote)

Categories