Loading C# DLL in Delphi does not find dependent library DLL - c#

I have written a small interop DLL in C# to be used by Delphi (not COM).
I am using iTextSharp to produce PDF files.
The C# DLL is basically really simple:
namespace ClassLibrary1
{
public class Class1
{
[DllExport("Test1", CallingConvention = CallingConvention.StdCall)]
public static void Test1()
{
System.IO.FileStream fs = new FileStream(#"D:\x.pdf", FileMode.Create, FileAccess.Write, FileShare.None);
Document document = new Document();
document.SetPageSize(PageSize.A4);
PdfWriter writer = PdfWriter.GetInstance(document, fs);
document.Open();
document.Add(new Paragraph("Hello World"));
document.Close();
writer.Close();
fs.Close();
}
}
}
And Delphi host application code (which located in a different folder):
procedure TForm1.Button1Click(Sender: TObject);
type
TDLLProc = procedure; stdcall;
var
DLLModule: HMODULE;
DLLProc: TDLLProc;
begin
// SetCurrentDir('G:\Projects\Test\ClassLibrary1\bin\x86\Release');
DLLModule := LoadLibrary('G:\Projects\Test\ClassLibrary1\bin\x86\Release\ClassLibrary1.dll');
if DLLModule = 0 then RaiseLastWin32Error;
DLLProc := GetProcAddress(DLLModule, 'Test1');
if #DLLProc = nil then RaiseLastWin32Error;
DLLProc(); // external exception E0434352
end;
The problem is that calling the method raises external exception E0434352 because for some reason the C# DLL can't find itextsharp.dll.
If I copy itextsharp.dll to the Project1.exe directory all works well.
If I copy Project1.exe to the 'G:\Projects\Test\ClassLibrary1\bin\x86\Release\ all works well.
I don't want the ClassLibrary1.dll and itextsharp.dll to be placed in my EXE program directory, but use them from their own directories.
I have tried to explicitly set SetCurrentDirecory, also tried SetDllDirectory, also tried LoadLibraryEx. nothing helps.
Seems that even if I place the itextsharp.dll DLL (just for testing) in the C:\Windows directory, I get the same exception!
What can I do to solve this problem?
EDIT: A possible solution (twisted IMHO) after looking at Loading .NET Assemblies out of Seperate Folders suggested by #Peter Wolf in the comment if to use System.Reflection.Assembly.LoadFrom to load the itextsharp.dll assmbly and access each of its classes/methods/members/etc via reflection. i.e.
[DllExport("Test1", CallingConvention = CallingConvention.StdCall)]
public static void Test1()
{
var asm = System.Reflection.Assembly.LoadFrom(#"G:\Projects\Test\ClassLibrary1\bin\x86\Release\itextsharp.dll");
Type tDocument = asm.GetType("iTextSharp.text.Document", true, true);
dynamic document = Activator.CreateInstance(tDocument);
Type tPageSize = asm.GetType("iTextSharp.text.PageSize", true, true);
tPageSize.GetMethod("GetRectangle");
// ETC... ETC... ETC...
}
This actually works, However this looks insane to me!
Ruining a perfectly simple and good C# code just to be able to do that.
I just can't believe there isn't a normal way to load a C# DLL that is located in a different folder than my host Delphi application.
Is there a native/normal and straight forward way to do this without the reflection???

You can hook onto assembly resolving mechanism in your C# DLL via AssemblyResolve event. Here you get a chance to locate and load assembly that the .NET runtime is unable to locate by itself. You can install the handler e.g. in class constructor (Class1 in your case), but this might not be sufficient, if your DLL has some entry points outside of Class1. The code to install handler is:
using System;
using System.IO;
using System.Linq;
using System.Reflection;
public class Class1
{
static Class1()
{
var dllDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
if (assembly != null)
{
return assembly;
}
var fileName = args.Name.Split(',')[0] + ".dll";
var filePath = Path.Combine(dllDirectory, fileName);
if (File.Exists(filePath))
{
return Assembly.LoadFile(filePath);
}
return null;
};
}
// ...
}

Related

C# Include DLL in a Library DLL

I have the following problem:
I'm making a library with few non-windows dependecies.
I would like to make the dll in order to have all the dependecies included without need to have also the other dll together with it.
The idea is to have only the new dll with the project that it'll use it instead of having also all the dependecies that this new dll is using.
I've read about inlcluding them as resource but it appears to not work as expected, using this code (I'm calling it in a static constructor of the class that need that dependecies, all the dependecies are in a subfolder lib of the library project):
internal static void Include()
{
if (!included)
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
String resourceName = "namespaceOfTheLibrary" + ".lib." + new AssemblyName(args.Name).Name + ".dll";
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
included = true;
}
This would be very helpfull in order to keep the code updated in many projects without forget any dependecies.
Hope something it's possible.
Thanks

How to embed System.Data.SQLite.dll to my Class Library?

i'm building a class library for SQLite , when i build the project the System.Data.SQLite.dll comes as a standalone dll file and i need it to be merged with my class library in a single dll file
i added System.Data.SQLite.dll as a ressource then , add / existing item , and then adding it as an embedded resource
then i drop this static constractor in the start of my namespace
static SimpleClass()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
if (new AssemblyName(args.Name).Name.ToLowerInvariant() == "System.Data.SQLite")
{
String resourceName = "SQLlite." + new AssemblyName(args.Name).Name + ".dll";
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
else
{
return null;
}
};
}
This is giving me errors like error CS1518: Expected class, delegate, enum, interface, or struct
appreciate any help from you guys
If you want to make one assembly from two, you can try ILMerge.
This will ultimately fail. System.Data.SQLite is a PInvoke wrapper for SQLite.Interop.dll, which is an unmanaged dll, which is not resolved in .net.

why c# console command not running on java?

Can anyone tell me why this c# console command not running on Java?
I've made a C# console program as given below:
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace face
{
class Program
{
public static void Main(string[] args)
{
String path = args[0];
byte[] imageBytes = File.ReadAllBytes(path);
MemoryStream ms = new MemoryStream(imageBytes, 0, imageBytes.Length);
// Convert byte[] to Image
ms.Write(imageBytes, 0, imageBytes.Length);
Image image = Image.FromStream(ms, true);
Bitmap S1 = new Bitmap(image);
Console.WriteLine(S1.GetPixel(2, 10));
}
}
}
When I run Program.exe image.jpg, I get:
Color [A=255, R=128, G=32, B=128]
Then I created a simple Java application to run the Program.exe executable:
class project
{
public static void main(String[] args)
{
String comman="C:\\WINXP\\system32\\Program.exe Sunset.jpg";
try
{
Process process = Runtime.getRuntime().exec(comman);
System.out.println(process.getOutputStream());
} catch (Exception e)
{e.printStackTrace(System.err);}
}
}
When I try to run the Java application, I get the following error:
Program.exe has encountered a problem and needs to close. We are sorry for the inconvenience.
You probably want to change
String comman = "C:\\WINXP\\system32\\Program.exe Sunset.jpg";
to
String[] comman = { "C:\\WINXP\\system32\\Program.exe", "Sunset.jpg" };
As a comment said, it's probably because the C# program can't open the file: you should specify an absolute path for it, since getting the actual working directory to work right can be an unnecessary pain in this situation. Also it wouldn't hurt to catch exceptions in the C# program (for example for the "no arguments" and "file not found" cases).
The C# process is a child process of the java process, and killed automatically then the java VM terminates. Because the process is started asynchronously, that will happen immediately. If you're interested in the output, replace
System.out.println(process.getOutputStream());
with (for instance)
InputStream inputStream = process.getInputStream();
int c;
while ((c = inputStream.read()) >= 0) {
System.out.print((char) c);
}
if not, write
process.getInputStream().close();
process.waitFor();
Apart from what others have said, I don't think that System.out.println(process.getOutputStream()); will output what you want here - namely the output of the executed C# executable.
System.out.println() has no overload for OutputStream, the return type of Process.getOutputStream(). So the System.out.println(Object) overload will be choosen, which will call OutputStream.ToString(), which is not the output of the C# program, but (most likely, bear with me here) the fully qualified typename of the output stream instance.
Check this SO question/answer, for example, for more information.
You need to check path of exe and image file that you have provided. As I check this code and execute it then I get following output
java.io.BufferedOutputStream#c17164
Here I provide you your modified code
public class Test
{
public static void main(String[] args)
{
String path="C:\\C#Sample\\ImageDisplayDemo\\ImageDisplayDemo\\bin\\Debug\\ImageDisplayDemo.exe E:\\Users\\Public\\Pictures\\Sample Pictures\\Desert.jpg";
try
{
Process proc=Runtime.getRuntime().exec(path);
System.out.println(proc.getOutputStream());
}
catch(Exception ex)
{
}
}
}

c# .NET Loading/Unloading assembly while keeping the same session

I am rather new to c# and .NET, and trying to create an asp.net web application that dynamically loads assembly.
Initially, I've used Activator.CreateInstance to dynamically load assemblies, but it seems to lock the assembly DLL file. Because I am making frequent changes to the assembly it's become quite a pain. I also need to share the assembly with other apps, so it may become a problem later.
It appears that most people recommend creating a separate AppDomain and loading the assembly into it, then unload the appdomain after I'm done. However, my app and the assembly also depends on session context and all the session is lost once I send it over to the app domain; the assembly crashes because it cannot find session context.
Is there any way to pass on my session context to the AppDomain? Or is there any way for me to load the assembly without locking the DLL file? I've tried streaming the file as some suggested, but it still locks the DLL.
Edit: I've tried the following code as Davide Piras suggested, but the DLL file is still locked
private static T CreateInstance<T>(string fileName, string typeName)
{
if (!File.Exists(fileName)) throw new FileNotFoundException(string.Format("Cannot find assembly '{0}'.", fileName));
try
{
Assembly assembly = Assembly.LoadFrom(fileName);
if (assembly != null)
{
List<Type> assemblyTypes = assembly.GetTypes().ToList();
Type assemblyType =
assemblyTypes.FirstOrDefault(asmType => typeof(T).IsAssignableFrom(asmType));
T instance = (T) Activator.CreateInstance(assemblyType);
if (instance != null) return instance;
}
// Trouble if the above doesn't work!
throw new NullReferenceException(string.Format("Could not create type '{0}'.", typeName));
}
catch (Exception exp1)
{
throw new Exception("Cannot create instance from " + fileName + ", with type " + typeName + ": " + exp1.Message + exp1.Source, exp1.InnerException);
}
}
to load the assemblies in the same AppDomain but not lock the files after load just use the LoadFrom method in this way:
Assembly asm = Assembly.LoadFrom( “mydll.dll” );
in this way you are done and Session will be available.
there will be an issue with the debugger because symbols (*.pdb) will not get loaded so no breakpoint and no debugging available, to be able to debug you should really load the .pdb files in memory as well, for example using FileStream.
Edit: The way you can do to also load symbols and not lock the files is to use proper overload of Assembly.Load, the one that gets two byte[] one for the assembly and the other for the assembly' symbols file (.pdb file):
public static Assembly Load(
byte[] rawAssembly,
byte[] rawSymbolStore
)
in fact you should first load the bytes with a stream then call Assembly.Load and pass the byte[]
EDIT 2:
here a full example to load the assemblies in your current domain, including the symbol file and not having the files locks.
it's a full example found online, it reflect everything you need including the handling of AppDomain.AssemblyResolve...
public static void Main() {
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolver);
}
static void InstantiateMyType(AppDomain domain) {
try {
// You must supply a valid fully qualified assembly name here.
domain.CreateInstance("Assembly text name, Version, Culture, PublicKeyToken", "MyType");
} catch (Exception e) {
Console.WriteLine(e.Message);
}
}
// Loads the content of a file to a byte array.
static byte[] loadFile(string filename) {
FileStream fs = new FileStream(filename, FileMode.Open);
byte[] buffer = new byte[(int) fs.Length];
fs.Read(buffer, 0, buffer.Length);
fs.Close();
return buffer;
}
static Assembly MyResolver(object sender, ResolveEventArgs args) {
AppDomain domain = (AppDomain) sender;
// Once the files are generated, this call is
// actually no longer necessary.
EmitAssembly(domain);
byte[] rawAssembly = loadFile("temp.dll");
byte[] rawSymbolStore = loadFile("temp.pdb");
Assembly assembly = domain.Load(rawAssembly, rawSymbolStore);
return assembly;
}

Embedding one dll inside another as an embedded resource and then calling it from my code

I've got a situation where I have a DLL I'm creating that uses another third party DLL, but I would prefer to be able to build the third party DLL into my DLL instead of having to keep them both together if possible.
This with is C# and .NET 3.5.
The way I would like to do this is by storing the third party DLL as an embedded resource which I then place in the appropriate place during execution of the first DLL.
The way I originally planned to do this is by writing code to put the third party DLL in the location specified by System.Reflection.Assembly.GetExecutingAssembly().Location.ToString()
minus the last /nameOfMyAssembly.dll. I can successfully save the third party .DLL in this location (which ends up being
C:\Documents and Settings\myUserName\Local Settings\Application
Data\assembly\dl3\KXPPAX6Y.ZCY\A1MZ1499.1TR\e0115d44\91bb86eb_fe18c901
), but when I get to the part of my code requiring this DLL, it can't find it.
Does anybody have any idea as to what I need to be doing differently?
Once you've embedded the third-party assembly as a resource, add code to subscribe to the AppDomain.AssemblyResolve event of the current domain during application start-up. This event fires whenever the Fusion sub-system of the CLR fails to locate an assembly according to the probing (policies) in effect. In the event handler for AppDomain.AssemblyResolve, load the resource using Assembly.GetManifestResourceStream and feed its content as a byte array into the corresponding Assembly.Load overload. Below is how one such implementation could look like in C#:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var resName = args.Name + ".dll";
var thisAssembly = Assembly.GetExecutingAssembly();
using (var input = thisAssembly.GetManifestResourceStream(resName))
{
return input != null
? Assembly.Load(StreamToBytes(input))
: null;
}
};
where StreamToBytes could be defined as:
static byte[] StreamToBytes(Stream input)
{
var capacity = input.CanSeek ? (int) input.Length : 0;
using (var output = new MemoryStream(capacity))
{
int readLength;
var buffer = new byte[4096];
do
{
readLength = input.Read(buffer, 0, buffer.Length);
output.Write(buffer, 0, readLength);
}
while (readLength != 0);
return output.ToArray();
}
}
Finally, as a few have already mentioned, ILMerge may be another option to consider, albeit somewhat more involved.
In the end I did it almost exactly the way raboof suggested (and similar to what dgvid suggested), except with some minor changes and some omissions fixed. I chose this method because it was closest to what I was looking for in the first place and didn't require using any third party executables and such. It works great!
This is what my code ended up looking like:
EDIT: I decided to move this function to another assembly so I could reuse it in multiple files (I just pass in Assembly.GetExecutingAssembly()).
This is the updated version which allows you to pass in the assembly with the embedded dlls.
embeddedResourcePrefix is the string path to the embedded resource, it will usually be the name of the assembly followed by any folder structure containing the resource (e.g. "MyComapny.MyProduct.MyAssembly.Resources" if the dll is in a folder called Resources in the project). It also assumes that the dll has a .dll.resource extension.
public static void EnableDynamicLoadingForDlls(Assembly assemblyToLoadFrom, string embeddedResourcePrefix) {
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { // had to add =>
try {
string resName = embeddedResourcePrefix + "." + args.Name.Split(',')[0] + ".dll.resource";
using (Stream input = assemblyToLoadFrom.GetManifestResourceStream(resName)) {
return input != null
? Assembly.Load(StreamToBytes(input))
: null;
}
} catch (Exception ex) {
_log.Error("Error dynamically loading dll: " + args.Name, ex);
return null;
}
}; // Had to add colon
}
private static byte[] StreamToBytes(Stream input) {
int capacity = input.CanSeek ? (int)input.Length : 0;
using (MemoryStream output = new MemoryStream(capacity)) {
int readLength;
byte[] buffer = new byte[4096];
do {
readLength = input.Read(buffer, 0, buffer.Length); // had to change to buffer.Length
output.Write(buffer, 0, readLength);
}
while (readLength != 0);
return output.ToArray();
}
}
There's a tool called IlMerge that can accomplish this: http://research.microsoft.com/~mbarnett/ILMerge.aspx
Then you can just make a build event similar to the following.
Set Path="C:\Program Files\Microsoft\ILMerge"
ilmerge /out:$(ProjectDir)\Deploy\LevelEditor.exe $(ProjectDir)\bin\Release\release.exe $(ProjectDir)\bin\Release\InteractLib.dll $(ProjectDir)\bin\Release\SpriteLib.dll $(ProjectDir)\bin\Release\LevelLibrary.dll
I've had success doing what you are describing, but because the third-party DLL is also a .NET assembly, I never write it out to disk, I just load it from memory.
I get the embedded resource assembly as a byte array like so:
Assembly resAssembly = Assembly.LoadFile(assemblyPathName);
byte[] assemblyData;
using (Stream stream = resAssembly.GetManifestResourceStream(resourceName))
{
assemblyData = ReadBytesFromStream(stream);
stream.Close();
}
Then I load the data with Assembly.Load().
Finally, I add a handler to AppDomain.CurrentDomain.AssemblyResolve to return my loaded assembly when the type loader looks it.
See the .NET Fusion Workshop for additional details.
You can achieve this remarkably easily using Netz, a .net NET Executables Compressor & Packer.
Instead of writing the assembly to disk you can try to do Assembly.Load(byte[] rawAssembly) where you create rawAssembly from the embedded resource.

Categories