Click here to Skip to main content
15,881,092 members
Please Sign up or sign in to vote.
1.67/5 (2 votes)
Thanks for reply.

and Sorry If my Q? was not clear enough. Let me try again.

I really didn't wanted to give the code at first because I didn't wanted to make your reply bias to the code. My first requirement is, to know what is the best approach to tackle this problem.

I have a C++ host Exe project and a C# plugin type project and I want a communication way in between them. So I tried to add a C++\CLI layer in between them.

Moreover I am not the owner of EXE project so I can't make any change in it. C# project is more like a plugin type of project. Here the code is called by EXE not by C# i.e. EXE loads the C++ dll from the path given in a specific txt file and then calls a specific method. And I can make a little bit change in C# project but not a big one like change the project type.

So in simple words, to start my Execution I have to create an object of a specific class from C# but as the flow given. EXE starts the calling of functionality.

If you think this is the best approach, I can provide the code, though I haven't written much of the code as I wanted to complete the calling flow.

Problem 1)
So as said before, I first tried to add only C++\CLI layer in between but it didn't work, which I don't know why? as host exe calling C++ dll properly, and my C++ dll calling the C++\CLI dll properly. but when I extern the same function from C++\CLI as done from C++, host exe is not able to call it.

Problem 2)
Moreover I am able to create a Manage object inside the C++\CLI for the class defined in the same project but when I tried same with the class defined in C# dll I am getting following exception.

I am getting following exception :-

Message = "Could not load file or assembly 'MY_C_Sharp.dll, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified."

The C++ , C# dll and it all dependency are in the same folder.
Posted
Updated 5-Apr-13 3:55am
v4
Comments
[no name] 5-Apr-13 5:50am    
"tell where I may be doing something wrong", probably not. We cannot see your code, your project or read your mind. How can we tell what you did wrong when you do not show us what it is that you have done?
saurabh saini 5-Apr-13 6:10am    
Thanks for reply.

and Sorry If my Q? was not clear enough. Let me try again.

I really didn't wanted to give the code at first because I didn't wanted to make your reply bias to the code. My first requirement is, to know what is the best approach to tackle this problem.

I am not the owner of EXE project so I can't make any change in it. C# project is more like a plugin type of project. So I want a communication way in between them. Here the code is called by EXE not by C# i.e. EXE loads the dll from the path given in a specific txt file and then calls a specific method. And I can make a little bit change in C# project but not a big one like change the project type.

So in simple words, to start my Execution I have to create an object of a specific class from C# but as the flow given. EXE starts the calling of functionality.

If you think this is the best approach, I can provide the code, though I haven't written much of the code as I wanted to complete the calling flow.

But here, I first tried to add only C++\CLI layer in between but it didn't work, which I don't know why? as host exe calling C++ dll properly, and my C++ dll calling the C++\CLI dll properly. but when I extern the same function from C++\CLI as done from C++, host exe is not able to call it.

Moreover I am able to create a Manage object inside the C++\CLI for the class defined in the same project but when I tried same with the class defined in C# dll I am getting following exception.

I am getting following exception :-

Message = "Could not load file or assembly '<my name="" dll="">, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified."

The C# dll is in the same folder.
Sergey Alexandrovich Kryukov 5-Apr-13 9:40am    
Even you picture you referenced is not informative. All you need to do is to create complete code samples of DLL and the application, minimal code manifesting the problem, nothing else. Your words and the picture do not really explain the problem. For example, when you say "call DLL". There is no such call: "DLL" is not a method, function, procedure, subroutine, property or operator...
—SA
saurabh saini 5-Apr-13 9:58am    
I have updated the Q?
Sergey Alexandrovich Kryukov 5-Apr-13 14:07pm    
From the first glance, your plug-in approach looks totally useless. You cannot effectively use application assemblies (EXEs) as plug-ins, at least not if they are not specifically developed in a special way. If you don't have source code of some assemblies; and if they are not developed to your specs, you cannot makes them plug-ins. One exclusion is those plug-ins are based on simple console-only text interface. Then you can easily redirect it's input and output and use it in your host process. It would work, but such design is lame.

Plug-in architectures is not a secret, but they don't look like you are trying to envision. If you want to continue this discussion, you should define your architecture, or your requirements, or goals 100% strictly. I think your text could be understood in ambiguous ways. (So, I'm sorry if I did not get your description correctly.)

—SA

Does the CREO is in the same folder as C++, C++/CLI and C#? The EXE will search DLLs on it own path or an explicit path but if the first DLL is loaded with a path, I don't think that subsequent DLLs will be found in that directory.

So if you have DirectoryA\App.Exe, DirectoryB\Cpp.DLL, DirectoryB\CppCli.DLL and DirectoryB\Csharp.DLL, I think it won't work if the DirectoryB is not in the application path as the current directory that is used for loading is DirectoryA and not directoryB.

Also does C# DLL has dependencies on DLLs other than those from the framwork itself?

The first thing to try would be to move all DLLs to the EXE directory.

An alternative might be to specify the (application) path so that extra directories would be searched.

Another possibility might be to merge all DLLs. I never try it.

In .NET you can hook assembly loading if you don't want to follows usual loading rules. Or you can explicitly load an assembly.

Do you have any reason why you are using so many DLLs? Maybe you should have a single DLL (either C++/CLI or C#) and have a simpler system.
 
Share this answer
 
Comments
saurabh saini 8-Apr-13 3:16am    
Thanks for the reply.
Let's assume I have directory c:\X in which I have all my plug in dlls (C++\CLI and C# and their dependency) and I have CREO executable in C:\Y . And from command prompt I Change directory to C:\X and execute the command "C:\Y\CREO.exe" so that my working directory will be C:\X. So according to you where should I have placed my dlls, in C:\X or C:\Y ...

yes C# dll has dependencies other than those from the framework but all those dll are in the same folder as of my first C# dll (note:- other dependency dlls are all also in C#)

I didn't get what do you mean by "merge all dlls".

What is a hook assembly loading can you give me a pointer or some link to follow.

Yes I have to use that many dll though I don't want to. So I will tell you the scenario if you have any better approach you tell me. As you can see CREO is a native application and a third party software so we can't change it. Now they have given a way to write a plug in i.e. We have to have our plug in dll in native and have to export a specific function which CREO will call on runtime to execute our plug in code. And Our whole plug in was developed in C# before we have planed to integrate is with CREO so we can't change it either so we want a layer between then to communicate. We started we C++\CLI and exported that specific function from it. but CREO was not able to call it so added one more layer of pure native C++ layer in between CREO and C++\CLI. Now Calling going fine till C# i.e. CREO is able to call C++ dll and C++ dll is calling C++\CLI. So my first problem is why my exported function form C++\CLI is not getting called from CREO as I am able to call it from any other C++ Exe. One more thing CREO was compiled by Cygwin and all my dll are complied by VS2010 will it cause any problem.
Philippe Mori 8-Apr-13 8:52am    
See http://msdn.microsoft.com/en-us/library/zwdd50tz(v=vs.100).aspx for information on how to resolve assembly loading.

For the exact way DLLs are loaded, you can either search official documentation or do some experiment.

Although changing the directory can give some hints, you cannot rely much on that as the application itself might change it for examaple. You should start by trying to copy everything in C:\Y directory and see if that works.

By merging DLLs, I mean combine them so that you have a single DLL. You can uses Google if you need more information as I have never done it myself but I know that for .NET application is is possible to merge assembly.
Babak Mahmoudi 8-Apr-13 4:10am    
Why CREO cannot use C++\CLI, is probably because the exported functions have dual entries (i.e they have both managed and native). Try exporting pure native entries with '#pragma managed' directive. I'm sure you can eliminate the C++ dll in your solution.
saurabh saini 8-Apr-13 5:26am    
Thanks for reply.
Can you provide an example. That will be a great help.
I don't know if I got the problem well, so assuming that:
1 )CREO is a native application that can utilize native DLL plugins (i.e plugins that export know functions that will be used by CREO.
2 ) You want to implement the plug in using C#.

I think your approach of a C++\CLI approach is quite acceptable. In this case you would export the required functions (those that are called by CREO), as native C++ methods within which you then call the managed entries from these methods. But you should carefully consider:
1) The C++\CLI layer should obey the bitness(64 or 32) of CREO.
2) The C# layer can be "Any CPU" or with exact same bitness.
3) The C# dll should be in same directory of CREO, or in Assembly Cache, or you should provide an assembly locator functionality. This is what I guess is the root of your problem if anything else is OK.
 
Share this answer
 
Comments
saurabh saini 8-Apr-13 2:52am    
Thanks for the reply.
Yes you understood the problem correctly
for the 2) consideration I built my C# layer in "Any CPU" mode is that ok.
For the 3) consideration I want to know about "in same directory of CREO"
i.e. Let's assume I have directory c:\X in which I have all my plug in dlls (C++\CLI and C# and their dependency) and I have CREO executable in C:\Y . And from command prompt I Change directory to C:\X and execute the command "C:\Y\CREO.exe" so that my working directory will be C:\X. So according to you where should I have placed my dlls, in C:\X or C:\Y ...

One more thing CREO was compiled by Cygwin and all my dll are complied by VS2010 will it cause any problem.
Babak Mahmoudi 8-Apr-13 4:02am    
DLLs are loaded from the directory where the main executable is loaded, not the working directory, so I think you should place the DLLs in C:\Y, where CREO.exe is located or use some other way to influence the DLL seach, such as locating dlls in assembly cache. (See http://msdn.microsoft.com/en-us/library/windows/desktop/ms682586(v=vs.85).aspx#search_order_for_desktop_applications for more details on Dll Search Mechanism)

I dont think Cygwin has any influence in the problem.
By the way, there is an ILMERGE tool that can merge two assemblies in one. It's a great tool, but I really wonder if it can Merge a mixed (C++/CLI) assembly with a pure one. Nevertheless there's still a way to merge the C# layer with the C++ layer (http://blogs.msdn.com/b/texblog/archive/2007/04/05/linking-native-c-into-c-applications.aspx), this way you would only have a single DLL, which can be registered as a plugin and assuming all other refrenced assemblies are system assemblies and located in the Assembly Cache you'll get a neat solution with less dependencies.
saurabh saini 8-Apr-13 4:49am    
Thanks a lot It worked. I was stuck here for more than 3 week.
Babak Mahmoudi 10-Apr-13 1:12am    
Ok, did you removed C++ dll? I still think that you can remove this layer.
saurabh saini 10-Apr-13 4:30am    
Yes I have removed the C++ Layer. That helped. But I am not able to add my dlls path in the search paths dynamically that's it automatically use Debug dlls from debug folder and release dlls from release folder. Right now I have to copy all dlls in exe folder. I have tried "SetDllDirectory" it doesn't work. how can I add my DLL paths in search path. From the code or as a post build command.
// CodeProjectSample.h

If I'm still right, you have managed to export an entry that is now
correctly called by the main application.
The remaining problem is that you still need to manually copy all DLLs to the destination folder.
I believe that if you had added the C# layer as refrence to CLI code, when you complile the code,
the required DLLS (Debug or Release) will be copied to same folder where CLI dll is located.
Now you setup the main app to load your DLL in the destination folder where all other dlls are located, when you
start using the code in C# layer, in fact you are ordering CLR to load the C# dll which will fail, since it is not
located in the main app folder.
It could be solved using an AssemblyResolveHandler or adding path to private search path of AppDomain. But it is
very important that these actions be done before any attempt to call the C# layer. Therefore if you put the
C# call method in the main function, since before executing this function, CLR need to load the requested DLL, you
have no chance to do any fixups.

Following code is a sample for above consideration


*/

#pragma once


using namespace System;
using namespace System::Reflection;

namespace CodeProjectSample {

	public ref class Helper
	{
	public:
		// TODO: Add your methods for this class here.
		static int DoWork()
		{
			//CShrapDLL::MyClass^ inst = gcnew MyClass();
			//return inst->DoWork();
			return 0;
		}
		static String^ GetMyDllPath()
		{
			String^ myLocation = Helper::typeid->Assembly->Location;
			return System::IO::Path::GetDirectoryName(myLocation);

		}
		static Helper()
		{
			// Following line should resolve the problem by adding 
			// the current dll path to assembly search path.
			AppDomain::CurrentDomain->AppendPrivatePath(GetMyDllPath());
			// Even if above fails, adding an AssemblyResolveHandler will do that.
			AppDomain::CurrentDomain->AssemblyResolve += gcnew ResolveEventHandler(&CurrentDomain_AssemblyResolve);
		}
		static Assembly^ CurrentDomain_AssemblyResolve(System::Object^ sender,System::ResolveEventArgs^ args)
		{
			// You may manually load the assembly here by correcting the path
			String^ CorrectedPath = args->Name; // Change it to some correctin action.
			return Assembly::Load(CorrectedPath);
		}
	};
}



// This is the exorted entry. '#pragma comment' is only the way i like to export entries
// you may use whatever method you prefer.
// just note that the function doesnot directly call the C# code, so that
// CLR can start executing before it tries to load any additional (C#) layer dlls.
// The static constructor of the Helper class does initialize the necessary fix ups.
// After that we can now safely call DoWork, that will call the C# layer.

#pragma comment(linker,"/EXPORT:PluginEntry=_PluginEntry@0");
extern "C" int __stdcall PluginEntry()
{
	CodeProjectSample::Helper::DoWork();
	return 0;
}
 
Share this answer
 
Comments
saurabh saini 10-Apr-13 8:30am    
VS giving a warning that AppendPrivatePath is Obsolete.
I tried AppDomain.CurrentDomain.SetupInformation.PrivateBinPath = GetMyDllPath()
But it didn't work.
moreover in CurrentDomain_AssemblyResolve it went in infinite loop because it was not able to load the Assembly.
what I did there is "String^ CorrectedPath = GetMyDllPath() + "\\" + args->Name;"
can you tell me how to edit that name
saurabh saini 10-Apr-13 8:53am    
I did
String^ CorrectedPath = GetMyDllPath() + "\\" + args->Name->Split(',')[0]+".dll";
return Assembly::LoadFrom(CorrectedPath);
in CurrentDomain_AssemblyResolve
it worked.
But I have Q? what will happen if dll is not in the folder it will go in an infinite loop.
can you tell me how to avoid that.
Babak Mahmoudi 10-Apr-13 9:09am    
Sorry for my mistake, yes you should use LoadFrom. The simplest way to avoid the infinite loop is to save args.name in a field, now when the event is fired you check this field if it contains exactly the same one it means that you are calling yourself now you may try another location or simply return null.
By the way I hoped that AppenPrivatePath would work though I had not used it before myself.
saurabh saini 10-Apr-13 15:31pm    
AppenPrivatePath didn't work so I googled it & I found following blog but I didn't understand much and didn't get <probing> config file etc. Hoping you can and help me in implementing the alternative.
http://blogs.msdn.com/b/dotnet/archive/2009/05/14/why-is-appdomain-appendprivatepath-obsolete.aspx
though every thing I googled about AppenPrivatePath, people there end up telling about AssemblyResolve event. So I think it is good.
Babak Mahmoudi 10-Apr-13 23:29pm    
OK, so lets accept it as a solution and move on. I'm glad that it worked after all.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900