how to release object from thid party dll? - c#

i'm developing a program to convert RTF to html
i'm using the DLLs found here
http://www.codeproject.com/KB/recipes/RtfConverter.aspx?fid=1458864&df=90&mpp=25&noise=3&sort=Position&view=Quick&select=3427424&fr=1#xx0xx
this dll saves a jpg file from html to a specific folder,
when i run the program, it cinverts the rtf for the first time and saves the images to the folder perfectly
but when i try to convert it again i hace this error
"error a generic error occured in GDI+"
i think this dll use SaveImage method and to avoid this you must release the Image object you created but i can't modify the DLL,
is there is any way to release the object i've created from this dll?
this is my code
RtfVisualImageAdapter imageAdapter = new RtfVisualImageAdapter(
#Application.StartupPath + "\\Program Data\\temp\\{0}{1}",
System.Drawing.Imaging.ImageFormat.Jpeg);
RtfImageConvertSettings imageConvertSettings =
new RtfImageConvertSettings(imageAdapter);
RtfImageConverter imageConverter = new RtfImageConverter(imageConvertSettings);
try
{
IRtfDocument rtfDocument = RtfInterpreterTool.BuildDoc(
ConversionText, imageConverter);
RtfHtmlConverter htmlConverter = new RtfHtmlConverter(rtfDocument);
htmlConverter.Settings.ConvertVisualHyperlinks = true;
htmlConverter.Settings.UseNonBreakingSpaces = true;
this.richTextBoxPrintCtrl2.Text = htmlConverter.Convert();
}
catch (Exception exception)
{
MessageBox.Show(this, "Error " + exception.Message, this.Text,
MessageBoxButtons.OK, MessageBoxIcon.Error);
}

The code is sloppy, it doesn't call the Dispose() method on the bitmap after saving it. That keeps a lock on the file, GDI+ uses a memory-mapped file to avoid putting pressure on the paging file. Important because bitmaps can be quite large. Trying to save to the same file again fails because of the lock. GDI+ exception messages are notoriously sloppy as well.
I think the bug is located in Interpreter\Converter\Image\RtfImageConverter.cs, SaveImage() method. The "convertedImage" bitmap doesn't get disposed. Note that the Graphics object in that same method doesn't get disposed either. Fix it by wrapping them with the using statement.
Run this code through FxCop to catch similar mistakes. And ask yourself if you really want to maintain code like this.

If something implements IDisposable, you can call its Dispose() method. Objects are eligible for garbage collection as soon as they go out of scope so you might also try calling GC.Collect() after there are no more references to the object you want "released."

As Max sez. Or better use the using construct. NEVER call GC.Collect unless you are dead sure by doing so you'll free a few GB or RAM!

Since you have the source code you could examine it and figure out where it keeps a reference and make sure it's released.
If you cannot figure out where to do this you could load the code up in a separate AppDomain, and execute your code there. When you are finished you can unload the AppDomain, and your application will release any objects. Then recreate the AppDomain for the next run.
But I would try to spend some time figuring out the real issue before using AppDomains.
And another thing. Do you get the GDI error when you execute the same file twice, or two different files in succession? It could be that it fails to load the image of the second file and gives you the error.

Related

G1ANT - disposing of unmanaged code in C# macros

I am enjoying using G1ANT's "macros" capability to call unmanaged code, but the unmanaged objects are of course not being automatically garbage collected absent code to do it.
My request is specifically for best practices in disposing of unmanaged code in these G1ANT C# macros, not for disposing of the same in C# generally, and it is not a request to fix the code below, which runs as is just fine.
If I were coding in C# using Visual Studio, I would likely use a System.Runtime.InteropServices.SafeHandle class, override the Finalize method, or use one of the other approaches in common use (see also this post on disposing of unmanaged objects in C#).
But none of these approaches appear to be a good fit for G1ANT macros per se, at least with my novice experience of them.
For illustration purposes I'm referring to this G1ANT code, but WITHOUT the last line in the macro (ahk.Reset()), because it runs fine with that line, more than once. (I'm painfully aware that there must be a much better example, but as I'm new to G1ANT, this is the only thing I have so far.) What I'm after is C# code that works in G1ANT when there is no explicit disposal of the unmanaged object:
addon core version 4.100.19170.929
addon language version 4.100.19170.929
-dialog ♥macrodlls
♥macrodlls = System.dll,System.Drawing.dll,System.Windows.Forms.dll,AutoHotkey.Interop.dll,System.Runtime.InteropServices.dll
-dialog ♥macrodlls
♥macronamespaces = System,AutoHotkey.Interop,System.Windows.Forms
⊂
var ahk = AutoHotkeyEngine.Instance;
//Load a library or exec scripts in a file
ahk.LoadFile("functions.ahk");
//execute a specific function (found in functions.ahk), with 2 parameters
ahk.ExecFunction("MyFunction", "Hello", "World");
string sayHelloFunction = "SayHello(name) \r\n { \r\n MsgBox, Hello %name% \r\n return \r\n }";
ahk.ExecRaw(sayHelloFunction);
//execute's newly made function\
ahk.ExecRaw(#"SayHello(""Mario"") ");
var add5Results = ahk.ExecFunction("Add5", "5");
MessageBox.Show("ExecFunction: Result of 5 with Add5 func is" + add5Results);
addon core version 4.100.19170.929
addon language version 4.100.19170.929
-dialog ♥macrodlls
♥macrodlls = System.dll,System.Drawing.dll,System.Windows.Forms.dll,AutoHotkey.Interop.dll,System.Runtime.InteropServices.dll,System.Reflection.dll,Microsoft.CSharp.dll
-dialog ♥macrodlls
♥macronamespaces = System,AutoHotkey.Interop,System.Windows.Forms,System.Reflection
⊂
var ahk = AutoHotkeyEngine.Instance;
//Load a library or exec scripts in a file
ahk.LoadFile("functions.ahk");
//execute a specific function (found in functions.ahk), with 2 parameters
ahk.ExecFunction("MyFunction", "Hello", "World");
string sayHelloFunction = "SayHello(name) \r\n { \r\n MsgBox, Hello %name% \r\n return \r\n }";
ahk.ExecRaw(sayHelloFunction);
//executes new function
ahk.ExecRaw(#"SayHello(""Mario"") ");
var add5Results = ahk.ExecFunction("Add5", "5");
MessageBox.Show("ExecFunction: Result of 5 with Add5 func is" + add5Results);
ahk.Reset();
⊃
⊃
It's taken nearly verbatim from the AutoHotkey.Interop github page.
Without the last line in the macro ('ahk.Reset()), the code runs perfectly the first time through, but on the second run G1ANT still sees the included AutoHotkey file, and warns of duplicate function definitions, but continues and still functions properly. The as-far-as-I-can-tell-undocumented AutoHotkey.Interop command Reset() takes care of the garbage collection problem by calling
public void Terminate()
{
AutoHotkeyDll.ahkTerminate(1000);
}
public void Reset() {
Terminate();
AutoHotkeyDll.ahkReload();
AutoHotkeyDll.ahktextdll("", "", "");
}
Thus, the AutoHotkeyEngine instance itself appears to be garbage collected, even without the ahk.Reset();, but the AutoHotkey script it loads into an object is not.
Stopping the G1ANT.Robot application and restarting, then reloading the script above (as mentioned, without the line ahk.Reset();), works just fine, but once again only for a single run.
Edit: The given answer's advice on treatment of singletons is what I will use henceforth when loading of AutoHotkey function scripts and the DLL itself. It seems prudent and good practice to check to see if the DLL or function file have been loaded, whether problems exist or not. "An ounce of prevention", etc. In addition, I have forked the AutoHotkey.Interop repo here, adding a boolean check to see if the AutoHotkeyEngine instance is ready.
Best regards,
burque505
You use AutoHotkeyEngine.Instance, so I guess it's a singleton. It will stay loaded in memory as long as the corresponding dll is kept there, and the latter is loaded and lives as long as the its domain lives. The macro app domain (the place where script stuff is placed) currently lives as long as Robot's app domain, so in fact your singleton instance lives as long as Robot.
Either:
don't use singleton,
or reset it right after obtaining the instance (kinda what you already did),
or treat it as a singleton that has life span longer than your app. In this case after obtaining singleton instance do a check if your functions file has been already loaded and only load it if it wasn't done already.

Can't delete file from server straight after upload it

I've created fileUpload Controller that posts a file to a server.
myControllerName.PostedFile.SaveAs(saveLocation);
In case I'm trying to delete the file from the server immediately after the upload finishes, it returns me an exception:
[IOException: The process cannot access the file 'C:\RestOFMyFilePath' because it is being used by another process.]
So I've used:
GC.Collect();
GC.WaitForPendingFinalizers();
I tried to put those lines before and after the functions in my code to locate the specific function that locks the resource. If I put it before the problem, it still occurs, and when it was after it fixes it.
Narrowed it down to
somefunctionName(FileUpload myControllerName)
Tried to dispose (actively) the resources by
iconUploadController.PostedFile.InputStream.Dispose();
iconUploadController.Dispose();
and I still get that error.
Another solution I've tried is to create proxy class for fileUpload
FileUpload.cs:
public class FileUploadProxy : IDisposable
{
public FileUpload fileUploadController;
public void Dispose()
{
fileUploadController.Dispose();
}
}
So I could use "using" (releases the resources straight after exit the scope) - still didn't work.
Ok, The problem was that inside the "somefunctionName(FileUpload myControllerName)" I had another call to the same file which I've missed:
System.Drawing.Image objImage = System.Drawing.Image.FromFile(filePathOnDisk)
The solution:
using (System.Drawing.Image objImage = System.Drawing.Image.FromFile(filePathOnDisk))
{
//Operation on the objImage.
}

Does VBA support Task.Wait() from a C# class import?

I wrote a C# class which connects to Dropbox and lets you upload, download, delete and generate link files.
It's working with a Windows Forms but I have to access it from VBA (Microsoft Access). The problem comes when it goes to task.Wait(). I've ""debugged" this throwing Exceptions and after that, doesn't go through.
public DropBox()
{
//Empty constructor because VBA doesn't support constructors with args
}
public void Connect(string tokenUser)
{
try
{
dropbox = new DropboxClient(tokenUser);
var taskInicio = Task.Run(async () => await dropbox.Users.GetCurrentAccountAsync());
//throw new Exception("Arriving?"); //ARRIVES
taskInicio.Wait();
throw new Exception("Arriving?"); //Throws "one or more errors"
}
catch (AggregateException ex)
when (ex.InnerException is BadInputException
|| ex.InnerException is AuthException)
{
throw new Exception("Incorrect Token or without access", ex.InnerException);
}
}
On VBA
Option Compare Database
Private Sub btActivar_Click()
Call test
End Sub
Public Function test()
Dim objDrop As CloudFiles.DropBox
Set objDrop = New CloudFiles.DropBox
MsgBox (objDrop.HolaMundo)
objDrop.Connect("TokenLongChicken")
'objDrop.DeleteFile("https://www.dropbox.com/s...?dl=0")
End Function
The "One or more errors produced" sounds like it comes from the "mscorlib" or so...
Any ideas? This is getting quite messy :/
Thanks.
VBA does have Application.Wait, I think you can give it a try. I have a code to wait for an IE connection which you can use as example:
Do While IE.Busy ' Need to wait until the page has loaded
Application.Wait (Now + TimeValue("00:00:01")) ' Wait one second
Loop
Tell me if it helps you.
I was having headaches thinking that it could be something related with the Tasks, asyncs, awaits... and it was something related with the Newtonsoft json library.
I updated the library through the NuGet to the 9.0 version and everything worked fine on Windows Forms, but looks like something is wrong when I use it through the .TLB because when I escaped the exception I got at some point in my deletion method, it said the Newtonsoft Json 7.0.0.0 library was missing (it was at the same directory anyway).
I finally removed that Newtonsoft Json version I was using and the Dropbox API, downloaded box again but I declined applying any updates. I couldn't even try to apply a downgrade or so.
Good ending, but I don't really get why did it search for the 7.0.0.0 when I was using the 9.x on my Windows Form project, which works and exported the .DLL and the .TLB.
Thanks to everybody.
EDIT: And yes, I guess this answers the question: VBA supports Task.Wait (at least coming from a C# .dll import)

Unknown exception at declaration and initalisation of a Class

The main Problem is completely different, please skip to the Edit
I have an exception of an unknown type which doesn't even get thrown properly. Following Code provides the Context:
MMDataAccess.InitDemoDB();
MMDataAccess.InitInternalDB();
MMDataAccess.InitMaintDB();
try
{
SQLiteToDBLib sqltdbl = new SQLiteToDBLib();
sqltdbl.WriteToSQLite();
}
catch(Exception ex)
{
string message = ex.Message;
}
These are the very first lines of my first Activity in my app. The first 3 lines belong to my very own implementation of an in-memory database and are behaving nicely. The problem rises with the next two lines inside the try-block. The declaration and initalistation of the sqltdbl variable never happens. The constructor of SQLiteToDBLib looks like this:
public SQLiteToDBLib()
{
msc = new MSConnection();
}
The MSConnection class doesn't even have a constructor (except for the default one of course).
As you can see i've tried to catch any exceptions, but without success. everything i can figure out is, that a exception is thrown because of the debugger going into the catch section while ignoring everything that has to do with "ex". Without breakpoints everything seems fine. Just without the call to WriteToSQLite which should create a .sqlite file on the external Memory.
What can I do to resolve this error? Is there anything i can catch except the default Exception?
Edit:
After some testing with commented code something interresting happened. I could step into commented code. Well not exactly the commented code, but the code that was there before my changes. Visual Studio somehow shows me the things, that are changed in the file, but is compiling the old code. Up to now i tried to rebuild, clean and build the project in various combinations, unload and reload the project, Restart Visual Studio and restart Windows. Nothing has changed so far. I Will now proceed to create a new .cs File With the exact same Code. I'm working with VS 2013 Community
add static constructor to your SQLiteToDBLib class and perform all static objects initialization in it:
static SQLiteToDBLib()
{
// initialize static members here
}
If this doesn't give you a clue, try enabling CLRE exceptions-break in visual-studio:
DEBUG
Exceptions
Check the 'Common Language Runtime Exceptions' option (under the 'Thrown' column)
Press OK
Restart your app and try again

Executable fails with weird exception

I am using ILMerge and Quartz.NET in a C# .NET 4.0 Windows Service application. The app runs fine without using ILMerge, but now that we're nearing shipping release, I wanted to combine all DLLs into a single executable.
Problem is, that ILMerge seems to work fine, but when I run the combined executable, it throws this exception:
Unhandled Exception: Quartz.SchedulerException: ThreadPool type 'Quartz.Simpl.SimpleThreadPool' could not be instantiated. ---> System.InvalidCastException: Unable to cast object of type 'Quartz.Simpl.SimpleThreadPool' to type 'Quartz.Spi.IThreadPool'.
at Quartz.Util.ObjectUtils.InstantiateType[T](Type type) in :line 0
at Quartz.Impl.StdSchedulerFactory.Instantiate() in :line 0
--- End of inner exception stack trace ---
at Quartz.Impl.StdSchedulerFactory.Instantiate() in :line 0
at Quartz.Impl.StdSchedulerFactory.GetScheduler() in :line 0
Does anyone have any idea why this is? I have been wasting over 4 hours already and I can't figure it out. If I don't combine with ILMerge, then everything runs fine (with the Quartz.dll and Common.Logging.dll in the same directory).
I'm sure someone must have tried packaging Quartz.net up like this before, any ideas?
Disclaimer: I don't know Quartz.NET at all, although I spent some time struggling with ILMerge. When I finally understood its limitations... I stopped using it.
ILMerge'd application tends to have problems with everything which contains the word "reflection".
I can guess (I've never used Quartz.NET) that some classes are resolved using reflection and driven by configuration files.
Class is not only identified by its name (with namespace) but also by assembly it is coming from (unfortunatelly it doesn't get displayed in exception message).
So, let's assume you had (before ILMerging) two assemblies A (for you Application) and Q (for Quartz.NET).
Assembly 'A' was referencing assembly 'Q' and was using a class 'Q:QClass' which was implementing 'Q:QIntf'.
After merging, those classes became 'A:QClass' and 'A:QIntf' (they were moved from assembly Q to A) and all the references in code has been replaced to use those (completely) new classes/interfaces, so "A:QClass" is implementing "A:QIntf" now.
But, it did not change any config files/embedded strings which may still reference "Q:QClass".
So when application is reading those not-updated config files it still loads "Q:QClass" (why it CAN find it is a different question, maybe you left assembly 'Q' in current folder or maybe it is in GAC - see 1).
Anyway, "Q:QClass" DOES NOT implement "A:QIntf", it still implements "Q:QIntf" even if they are binary identical - so you can't cast 'Q:QClass' to 'A:QIntf'.
The not-ideal-but-working solution is to "embed" assemblies instead of "merging" them. I wrote a open-source tool which does it (embedding instead of merging) but it is not related to this question. So if you decide to embed just ask me.
You can test it by removing (hiding, whatever works for you) every single instance of Q.dll on your PC. If I'm right, the exception should say now 'FileNotFound'.
You could try creating your own ISchedulerFactory and avoid using reflection to load all of your types.
The StdSchedulerFactory uses this code to creat a threadpool. It's where your error is happening and would be the place to start looking at making changes:
Type tpType = loadHelper.LoadType(cfg.GetStringProperty(PropertyThreadPoolType)) ?? typeof(SimpleThreadPool);
try
{
tp = ObjectUtils.InstantiateType<IThreadPool>(tpType);
}
catch (Exception e)
{
initException = new SchedulerException("ThreadPool type '{0}' could not be instantiated.".FormatInvariant(tpType), e);
throw initException;
}
The ObjectUtils.InstantiateType method that is called is this one, and the last line is the one throwing your exception:
public static T InstantiateType<T>(Type type)
{
if (type == null)
{
throw new ArgumentNullException("type", "Cannot instantiate null");
}
ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
if (ci == null)
{
throw new ArgumentException("Cannot instantiate type which has no empty constructor", type.Name);
}
return (T) ci.Invoke(new object[0]);
}
Right after this section in the factory, datasources are loaded using the same pattern and then the jobs themselves are also loaded dynamically which means you'd also have to write your own JobFactory. Since Quartz.Net loads a bunch of bits and pieces dynamically at runtime going down this road means you might end up rewriting a fair amount of things.

Categories