I have a DLL that I need to access methods from.
In most cases like this I just use [DllImport] to access methods from unmanaged assemblies, but the problem with that in this situation is that it requires the path to the DLL at instantiation time, so a constant string.
This particular DLL is one that gets installed with my application and I can't guarantee where it will be after the program is installed (I'd rather not put it somewhere static like %SystemRoot%).
So is there a way in C# that I can declare and use a method from a DLL at runtime with a variable path?
Any ideas or suggestions would be greatly appreciated!
This is a bit of hack, but since you say that you can find the path to the dll at runtime, why not copy it to your current working directory before you use any of the functions? That way, the dll will exist next to your exe and will be found by LoadLibrary. No need for any additional path in your DllImport.
The only other way to use a method from a dynamic path is to do this:
1) Do the necessary P/Invoke signatures for LoadLibrary & GetProcAddress
2) Load the library from the desired path (LoadLibrary)
3) Find the desired function (GetProcAddress)
4) Cast the pointer to a delegate Marshal.GetDelegateForFunctionPointer
5) Invoke it.
Of course, you will need to declare a delegate for each function you want to "import" in this way since you have to cast the pointer to a delegate.
Don't use a path at all. Windows uses a default method of searching for DLLs when trying to dynamically or statically load a function from it.
The exact search logic is documented at MSDN in the docs for LoadLibrary - basically, if the DLL is just used by your app, put in the same folder as your application during the install and don't worry about it. If it's a commonly used DLL, put it somewhere in the folder structure searched by LoadLibrary() and it'll get found.
I had a similar situation. I use DLLs from a SDK that is installed on the machine. I get the directory location of the DLLs from that SDKs registry key. I set the DLL location on the executing users PATH variable (only temporary modification). Basically it allows you to set a dynamic path for the DLL you want to invoke, so it don't have to be from registry. Mind that the PATH var is the last place Windows looks for DLLs. But on the other hand, it does not change the other places Windows looks for DLLs.
Example:
API i want to call, on the DLL:
[DllImport("My.DLL")]
private static extern IntPtr ApiCall(int param);
Get the registry key (you need using Microsoft.Win32;):
private static string GetRegistryKeyPath() {
string environmentPath = null;
using (var rk = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\SOMENNAME"))
{
if (rk != null)
{
environmentPath = rk.GetValue("Path(or whatever your key is)").ToString();
}
if (string.IsNullOrEmpty(environmentPath))
{
Log.Warn(
string.Format("Path not found in Windows registry, using key: {0}. Will default to {1}",
#"SOFTWARE\SOMETHING", #"C:\DefaultPath"));
environmentPath = #"C:\DefaultPath";
}
}
return environmentPath;
}
Add the path of the DLL on the PATH var (Concat() is found in Linq):
void UpdatePath(IEnumerable<string> paths){
var path = new[] { Environment.GetEnvironmentVariable("PATH") ?? "" };
path = path.Concat(paths);
string modified = string.Join(Path.PathSeparator.ToString(), path);
Environment.SetEnvironmentVariable("PATH", modified);
}
Start Using the API call:
var sdkPathToAdd = GetRegistryKeyPath();
IList<string> paths = new List<string>
{
Path.Combine(sdkPathToAdd),
Path.Combine("c:\anotherPath")
};
UpdatePath(paths);
//Start using
ApiCall(int numberOfEyes);
Related
I'm building a utility that uses Microsoft's DACPAC libraries. For the purpose of this tool, I want to embed all requisite libraries in the executable. It appears that when I execute DacServices.GenerateDeployScript() it's trying to use the Microsoft.SqlServer.Dac.Extensions library. The library is also embedded, but perhaps isn't being resolved with my EventHandler the way other DLLs are. My EventHandler is like this:
private static Assembly ResolveEventHandler(Object sender, ResolveEventArgs args)
{
//Debugger.Break();
String dllName = new AssemblyName(args.Name).Name + ".dll";
var assem = Assembly.GetExecutingAssembly();
String resourceName = assem.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));
if (resourceName == null) return null;
using (var stream = assem.GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
This works for resolving other items, but I believe that the likely issue is that the Microsoft.SqlServer.Dac namespace is making an execution time call to the .Extensions namespace and isn't able to resolve the namespace or the methods in it. I could be wrong, but I'm not sure what else could be the cause.
The calls to methods and classes in .Dac itself are being handled fine, so I know the EventHandler is working properly. I'm not really sure what to do and would appreciate any guidance. I've tried using Microsoft.SqlServer.Dac.Extenions at the top of the .cs file, but since I don't directly call anything in that namespace, it's grey and probably is ignored by the compiler.
Thanks!
Update:
I made a call to the .Extensions namespace in the code to force it to be read into memory prior to the failing call, though it appears that it already was. I set a breakpoint where the resolver kicks off. Just prior to it failing, it's trying to resolve .resource for each DLL, e.g. Microsoft.SqlServer.Dac.resource and Microsoft.SqlServer.TransactSql.ScriptDom.resource - all for DLLs embedded in the executable. The resolver doesn't see anything because there are no .resource files in the project, so nothing compiled into the manifest. Aren't these supposed to just be resident in memory while a DLL is being utilized? When the DLLs are all present in the same directory as the .exe, it functions fine, and also doesn't create temporary .resource files in the directory, so I'm unsure what I'm looking to resolve.
Update 2:
Using a PDB of the DAC libraries, it appears the failing line is:
IOperation operation = DacServices.CreateDeploymentArtifactGenerationOperation(OperationResources.GenerateDeployScriptCaption, (ErrorManager errorManager) => this.CreatePackageToDatabaseDeployment(package.PackageSource, targetDatabaseName, dacDeployOption, errorManager), (IDeploymentController controller, DeploymentPlan plan, ErrorManager errorManager) => DacServices.WriteDeploymentScript(streamWriter, controller, plan, errorManager), cancellationToken1, dacLoggingContext);
And the resulting exceptions are:
The extension type Microsoft.SqlServer.Dac.Deployment.Internal.InternalDeploymentPlanExecutor could not be instantiated.
and
The extension type Microsoft.SqlServer.Dac.Deployment.Internal.InternalDeploymentPlanModifier could not be instantiated.
Suppose I have a solution which contains 4 projects, A, A_UnitTests, B, and B_UnitTests.
Project A has a data file, which is added as a link to A_UnitTests and set to copy to the output directory. When unit tests are run or when the code is executed in production, the path to that file is correctly identified using the following code snippet:
public static string GetFullPath(string relativePath)
{
string retVal = string.Empty;
if (System.Web.HttpContext.Current == null)
{
string locationBeforeShadowCopy = typeof(A.SomeClassInA).Assembly.CodeBase;
UriBuilder uri = new UriBuilder(locationBeforeShadowCopy);
string locationWithoutUriPrefixes = Uri.UnescapeDataString(uri.Path);
string dir = Path.GetDirectoryName(locationWithoutUriPrefixes);
retVal = Path.Combine(dir, relativePath);
}
else
{
// stuff that doesn't matter
}
return retVal;
}
However, I have a new testcase in B_UnitTests which attempts to use this code path to find the file location. However, even though I call typeof(A.SomeClassInA).Assembly.CodeBase, it is being called from B_UnitTests, using its referenced DLLs. This means the path return is the B_UnitTests output directory + a relative path. So it doesn't find the data file.
Without resorting to hard coded settings and build scripts, what could I use to specify the correct path?
Update (clarification)
The real issue is with typeForClassInA.Assembly.CodeBase returning the path of the executing assembly rather than A itself. It seems very wrong to provide a type that comes from some assembly but instead of returning the original assembly location, it returns the path to the executing assembly which happens to have a reference to it.
If there is a reference to 'typeForClassInA', then its assembly will be being copied into the output directory of B_UnitTests. So when you ask for CodeBase of that type's assembly from a test in B_UnitTests, it is (correctly) pointing at the version of assembly A in the B_UnitTests output folder, because that's where it's being loaded from.
I admit that I avoid using Shadow Copy to avoid exactly these kinds of problems of locating resources which are alongside the assembly, since ShadowCopy doesn't understand that they are needed, and they don't get shadow copied.
Another thing which can help is to build all the projects into the same output folder by changing all the project output folders to be "..\bin". For example, this would mean that A_UnitTests would not need the link to the resource file (once shadow copy is off).
I have a method similar to the one you've shown which goes "up" from the assembly's location (which for me is the shared bin folder) to the solution's location; and my relative paths are 'rooted' at that folder.
If that all sounds too complex, you could just use the same approach that A_UnitTests did, of including a link to it from B_UnitTests.
There is an application that checks for activation using DLL Check function. Check returns 1 if application is activated and 0 otherwise. I create simple application and DLL containing function MyCheck (which always returns 1) with the same signature and detoured Check function with my version using MS detours lib for function hooking. Obviously it works and the application is successfully cracked, so I need to avoid it.
I tried to call Check function directly (by specifying exact address), without even using GetProcAddress, but looks like detours lib is modifying the function body itself, not export table.
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate bool CheckFunctionDelegate();
static void Main(string[] args)
{
ProcessModule module = Process.GetCurrentProcess().Modules
.Cast<ProcessModule>()
.First(m => m.ModuleName == "licensing_check.dll");
IntPtr procedurePtr = IntPtr.Add(module.BaseAddress, 0x00003FF0);
// Calling validation function by pointer
CheckFunctionDelegate checkFunction = (CheckFunctionDelegate)
Marshal.GetDelegateForFunctionPointer(procedurePtr, typeof(CheckFunctionDelegate));
if (checkFunction())
{
// do some stuff
}
}
}
Then I tried to read function body and I see that after detour MD5 checksum differs from the original one. So I'm trying to read entire contents of DLL in memory and check it to confirm that DLL contents are not changed, but it doesn't work either. It throws AccessViolationException.
Process.EnterDebugMode();
ProcessModule module = Process.GetCurrentProcess().MainModule;
byte[] data = new byte[module.ModuleMemorySize];
Marshal.Copy(module.BaseAddress, data, 0, module.ModuleMemorySize);
I used MainModule here, but it gives the same error for each module in Process.GetCurrentProcess().Modules collection.
I would appreciate any help on this, I'm not necessarily expecting to solve it in one of the ways I describe, any good solution is acceptable.
Thanks.
okay, here is the question. I have two projects one is C# Console and other is Class library.
I am accessing/calling Class library method from the console app.
There is a folder called Files within the class library project.
I need to get the path of the Class library's files folder but whenever I use
System.IO.Directory.GetCurrentDirectory();
and
Environment.CurrentDirectory;
it is giving me path of the Console project which I am using to call the method.
Above methods are giving me path like
C:\\ConsolePro\\bin\\Debug
but I need the path of Class library project
C:\\ClassLibPro\\bin\\Debug
Please advise
Once the code is compiled and running, 'Project Path' has no meaning. All you can determine are the file locations of the compiled assemblies. And you can only do what you are asking if your Console project references the built 'class library' DLL directly, rather than via a Project Reference.
Then, you can make use of Reflection to get Assembly paths like;
string path = Assembly.GetAssembly(typeof (SomeClassInOtherProject)).Location;
You should be able to use Directory.GetParent(Directory.GetCurrentDirectory()) a few times to get higher level directories and then add the path of the lib directory to the end of that.
I believe the problem is:
Since the Console project has the DLL file reference it is using DLL to call any methods.
At this time it is returning the class library projct's DLL location which is located in console project's bin directory and it doesn't know about the physical location of class library project.
so essentially it is returning the same project path. I will have to move both projects in same directory in order to solve this issue.
If you loading the class library from another assembly.
string Path = System.Reflection.Assembly.GetAssembly(typeof({LibraryClassName})).Location;
string PathToClassLibPro = Path.GetDirectoryName( Path);
Replace {LibraryClassName} with the class name of your library.
I hope I understand u corretly:
Path.GetDirectoryName(typeof(Foo.MyFooClass).Assembly.Location);
I would recommend one of two options.
If the files are small include them in the class library and stream them to a temp location when needed
Other option is to copy the files during the build to the output directory and use them that way. In cases of multiple shared projects it is best to have a common bin folder that you copy assemblies to and run from that location.
Despite i cant find a good solution i use this trick :
as long as you want to come back to your ideal path u should add Directory.GetParent() instead of ...
Directory.GetParent(...(Directory.GetParent(Directory.GetCurrentDirectory()).ToString()...).ToString()
I use the following approach to get the current project path at runtime:
public static class ProjectInfo {
public static string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
public static string projectPath = appDirectory.Substring(0, appDirectory.IndexOf("\\bin"));
}
I had this exact issue as well where I couldn't access the file in my namespace's bin/debug folder. My solution was to manipulate the string using Split() then construct a new string which is the absolute path to the json file I have in my namespace.
private static string GetFilePath()
{
const char Escape = '\\'; //can't have '\' by itself, it'll throw the "Newline in constant" error
string directory = Environment.CurrentDirectory;
string[] pathOccurences = directory.Split(Escape);
string pathToReturn = pathOccurences[0] + Escape; //prevents index out of bounds in upcoming loop
for(int i = 1; i < pathOccurences.Length; i++)
{
if (pathOccurences[i] != pathOccurences[i - 1]) //the project file name and the namespace file name are the same
pathToReturn += pathOccurences[i] + Escape;
else
pathToReturn += typeof(thisClass).Namespace + Escape; //In the one occurrence of the duplicate substring, I replace it with my class Namespace name
}
return pathToReturn + "yourFile.json";
}
I personally don't like this solution, but it was the only answer I could think of.
I am using .net Primary Interoperability Assembly for Excel in my code.
But, the application can be run on machine which doesn't have .net PIA for Excel installed.
I want to give an error message if it is not installed
Even though I am checking in GAC, to see if PIA is installed and only if it is present I am using Microsoft.Office.Interop.Excel related code. I am getting an error.
My problem is - I am getting error which is - Unhandled Exception - can't find could not load file or assembly Microsoft.Office.Interop.Excel
Any solution?
Thanks in advance!
I've used this Building block to load the x86 / x64 versions of hunspell in may NHunspell wrapper. maybe it is a good starting point for your own dynamic loader:
// Initialze the dynamic marshall Infrastructure to call the 32Bit (x86) or the 64Bit (x64) Dll respectively
SYSTEM_INFO info = new SYSTEM_INFO();
GetSystemInfo( ref info );
// Load the correct DLL according to the processor architecture
switch( info.wProcessorArchitecture )
{
case PROCESSOR_ARCHITECTURE.Intel:
string pathx86 = AppDomain.CurrentDomain.BaseDirectory;
if (!pathx86.EndsWith("\\"))
pathx86 += "\\";
pathx86 += Resources.HunspellX86DllName;
dllHandle = LoadLibrary(pathx86);
if (dllHandle == IntPtr.Zero)
throw new DllNotFoundException(string.Format(Resources.HunspellX86DllNotFoundMessage, pathx86));
break;
case PROCESSOR_ARCHITECTURE.Amd64:
string pathx64 = AppDomain.CurrentDomain.BaseDirectory;
if (!pathx64.EndsWith("\\"))
pathx64 += "\\";
pathx64 += Resources.HunspellX64DllName;
dllHandle = LoadLibrary(pathx64);
if (dllHandle == IntPtr.Zero)
throw new DllNotFoundException(string.Format(Resources.HunspellX64DllNotFoundMessage, pathx64));
break;
look at this delegate declarations:
internal delegate bool HunspellSpellDelegate(IntPtr handle, [MarshalAs(UnmanagedType.LPWStr)] string word);
And how to bind a library function to it
HunspellSpell = (HunspellSpellDelegate)GetDelegate("HunspellSpell", typeof(HunspellSpellDelegate));
I think this should work for you too, but you have to declare the complete interop code. You can check out the Nhunspell code to get a working sample of this technique:
NHunspell Web Site
I guess System.Type.GetTypeFromProgID("Excel.Application",true); method will throw exception if Excel is not present. You can use a try catch block and verify.
You shouldn't make static reference to Excel.Interop library. Instead of this, you should look for it in GAC, and load it in runtime. It's rather complicated, and there are no managed API to do this (by default).
Try to use this: fusion.dll API wrapper