WPF开发,将引用的dll在编译时指向单独的文件夹。
1、参考文章1
系列文章
- C#开发奇技淫巧一:调试windows系统服务
- C#开发奇技淫巧二:根据dll文件加载C++或者Delphi插件
- C#开发奇技淫巧三:把dll放在不同的目录让你的程序更整洁
程序目录的整理
想必C#的开发者都遇到过这个问题,引用的dll都放在根目录下,随着项目的日益增大,根目录下充满了各种各样的dll,非常的不美观。
如果能够把dll按照想要的目录来存放,那么系统就美观多了,以下是我常用的程序各文件的分布:
- 【3rdLibs】
- NLog.dll
- Newtonsoft.Json.dll
- ……
- 【MyLibs】
- 【Resources】
- 【Images】
- Excecutable.exe
- Excecuteble.exe.config
网上有很多的文章述说这个,比如使用Assembly.Load,但是没有说明在程序中怎么使用,也没有给出具体的代码。这里我结合自己多年的实践经验,再把整个流程和方法详细叙述一遍,以便各位看官有个具体的体会。
系统搜索dll的目录以及顺序
CLR解析一个程序集会在一个根目录内进行搜索,整个探索过程又称Probing,这个根目录很显然就是当前包含当前程序集的目录。
AppDomainSetup这个类存储着探索目录的信息,其成员包括:ApplicationBase
、PrivateBinPath
。
程序搜索dll的顺序如下(区分强名称签名的和没有强名称签名的程序集):
没有做强名称签名的程序集:
- 程序的根目录
- 根目录下面,与被引用程序集同名的子目录
- 根目录下面被明确定义为私有目录的子目录
- 在目录中查找的时候,如果dll查找不到,则会尝试查找同名的exe
- 如果程序集带有区域性,而不是语言中立的,则还会尝试查找以语言区域命名的子目录
具有强名称签名的程序集:
- 全局程序集缓存
- 如果有定义codebase,则以codebase定义为准,如果codebase指定的路径找不到,则直接报告错误
- 程序的根目录
- 根目录下面,与被引用程序集同名的子目录
- 根目录下面被明确定义为私有目录的子目录
- 在目录中查找的时候,如果dll查找不到,则会尝试查找同名的exe
- 如果程序集带有区域性,而不是语言中立的,则还会尝试查找以语言区域命名的子目录
如何让程序识别不同目录下的dll?
我们看到,上面的顺序无论是否有强名称签名看,都提到了一个名词**“私有目录”**
方法一:配置App.config文件的privatePath
——【推荐】
这是最简单的方法,当然也有一定的局限性,就是没法对dll做控制,另外,无法解决第三方
DllImprt
中引入的程序集不在根目录下的问题,不过无论怎么说,这个都基本解决了问题。配置如下,多个目录用;分隔
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="3rdLib;MyLibs;SubFolder\Sub.dll"/>
</assemblyBinding>
</runtime>
方法二:订阅程序集解析事件AssemblyResolve
在代码中解析
应用程序集域中支持在程序集解析时的处理:
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
。通过这个事件,我们可以在程序集解析时,根据不同的程序集做不用的处理,比如加载x86的程序集还是64位的程序集,当然也就可以指定程序集目录了
这也正是
Assembly.Load
和Assembly.LoadFrom
等方法的用武之地。
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
AssemblyName assemblyName = new AssemblyName(args.Name);
return Assembly.LoadFrom(Path.Combine(baseDirectory, "3rdLibs"));
}
方法三:在加载使用到dll的代码之前设置重置当前环境的目录
这个方法就是通过
Environment.CurrentDirectory=customPath
,这样,在调用dll方法时,因为目录已经切换到了
这是一个取巧的方法,不是很实用,要来回切换程序集目录,但是在某些情况下非常好用
如何处理[dllImport]
中的程序集的加载
自己写dllImport
如果是自己写,那么久好控制了,可以直接指定相对的目录
DllImport(3rdLibs\NLog.dll)
。不过这种方法不一定可靠,在某些系统硬是加载不了,如果使用了dllImport还是,推荐下面的另外一种方法。
引用的C#的插件又使用了dllImport
这是很多文章都没有提及的:
因为无法更改路径,那么只能够使用上述特殊的方法,更改当前程序的路径
当然,还有更省事一点的做法,就是在系统环境中,增加一条记录,指向要加载的dll的所在目录。因为C++的代码中,Windows目录和Windows\System32目录以及环境变量设定的目录都是搜索路径之一。
这里提供怎么从C#中修改系统环境变量的代码:
static void AddEnvironmentPaths(IEnumerable<string> paths)
{
var path = new[] { Environment.GetEnvironmentVariable("PATH") ?? string.Empty };
string newPath = string.Join(Path.PathSeparator.ToString(), path.Concat(paths));
Environment.SetEnvironmentVariable("PATH", newPath);
}
参考文章
2、参考文章2
设置生成的DLL引用路径文件夹
C#把dll分别放在指定的文件夹的方法步骤
https://www.zhangshengrong.com/p/RmNP8RP4Nk/
嗯,大家的解决方案可能会有许多dll,这样不美观,而且也麻烦。
很多小白都不知道如何将这些dll放到如自己程序的bin文件夹下。
本渣今天来试着将dll复制到指定的文件夹下~
比如我之前做的一个Winform使用WebKit内核。然后webkit的dll和文件夹就一大堆全堆积在debug目录下。
这也太乱了吧!!!!!
于是我们便打算将这些文件塞入一个叫bin的文件夹内。
只让我的winform程序能被用户一眼看到,让其他麻烦的dll藏在bin就好了owo。
第一步:在程序内新建App.config文件,如果有就跳过
第二步:打开
然后加入这段代码
这段代码的PrvatPatch所指的是你的dll托管目录
<runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath="bin"/> </assemblyBinding> </runtime>
PrivatePath="xxx"可以指定多个文件夹,比如这样写
PrivatePath="233;666;555"这相当于指定233文件夹、666文件夹、555文件夹均是你的dll文件夹。
也可以这样:PrivatePath="2333/4444"相当于指定2333文件夹下的4444文件夹为你的托管目录。
然后在和你应用程序同级目录(xxxx.config)下新建你指定的文件夹的名字~
嗯,弄完后把你所需要托管的dll都扔到这个文件夹里awa。
别着急,我们还有最后一步~
把你所有引用的DLL,包括NuGet的dll。除了系统以外的dll。
把这些引用属性里的"复制本地"按钮 给改成False。
这样程序就不会去复制dll到调试目录下而会老老实实地搜你指定给它的文件夹里有没有这些dll。
如果有,那就直接调用。如果没有,会抛出没找到文件的异常,这时你需要排查有没有放这个文件,文件名字对不对。
同解决方案多项目,引用同DLL文件,同一存放项目DLL引用文件夹
配合解决方案文件夹新建DLL文件夹,索引指向同一文件夹引用
1、在解决方案资源管理器中右键单击您的解决方案。悬停
单击"添加",然后选择"新解决方案文件夹"。这不是一个文件夹
并且不指向任何物理文件夹。这是一个抽象
用于在解决方案中对项目进行分组的结构,但我们将使用它
为了别的
称其为"引用的程序集"。
2、接下来,再次右键单击您的解决方案,然后单击"打开文件夹
在Windows资源管理器中"。在弹出的Windows资源管理器窗口中
添加一个新的"物理文件夹"(该文件夹将位于下面
解决方案的根文件夹)。
3、您可以将其称为"引用的程序集",以便解决方案文件夹和物理文件夹共享相同的名称,以避免混淆。
抓住所有潜伏在所有DLL中的DLL(即
埋在AjaxControlToolkit.dll或DocumentFormat.OpenXml.dll中
程序文件)并复制(请勿拖拉-将原件放在原处
是)它们进入Windows资源管理器中的"引用的程序集"文件夹
(不在解决方案中)。
4、现在,您可以从"引用的程序集"中拖动那些复制的dll。
Windows资源管理器中的"物理"文件夹放入"参考程序集"解决方案文件夹中
在Visual Studio的解决方案资源管理器中。
遍历解决方案中的每个项目并删除所有引用
到您将从"参考"中引用的程序集
程序集"文件夹。
5、重新添加引用,但这一次(在项目下)
右键单击"参考",单击"添加参考…“,选择
在"浏览"标签上,点击带有绿色箭头的文件夹图标(例如"向上”
将鼠标悬停在"一级"上,双击"参考"
程序集"文件夹,选择您要引用的所有dll,
点击"确定"。
6、你完成了。现在,您的解决方案知道了这些dll以及在哪里可以找到它们,当您通过SVN,TFS或其他方式签入代码时,它将把它们推上并复制下来,以供其他人使用。
如果您使用的是TortiseSVN,并且还使用Visual Studio的AnkhSVN插件,则无需执行任何操作。它研究您的解决方案文件(就像TFS一样)以找到该解决方案引用的文件。在这种情况下,它将获取"引用的程序集"解决方案文件夹所引用的新dll,并在提交它们之前自动添加源代码控制跟踪(即,如果像我一样从Visual Studio中的解决方案资源管理器中提交解决方案更改, )。
3、参考文章3
WPF:
在App.xaml.cs文件中加入,如下代码即可指定引用Libs文件下的dll:
public App()
{
AppDomain.CurrentDomain.SetData("PRIVATE_BINPATH", "Libs;");
AppDomain.CurrentDomain.SetData("BINPATH_PROBE_ONLY", "Libs;");
var method = typeof(AppDomainSetup).GetMethod("UpdateContextProperty", BindingFlags.NonPublic | BindingFlags.Static);
var funsion = typeof(AppDomain).GetMethod("GetFusionContext", BindingFlags.NonPublic | BindingFlags.Instance);
method.Invoke(null, new object[] { funsion.Invoke(AppDomain.CurrentDomain, null), "PRIVATE_BINPATH", "Libs;" });
}
WinFrom:
在Program.cs中加入,如下代码即可指定引用Libs文件下的dll:
static Program()
{
AppDomain.CurrentDomain.SetData("PRIVATE_BINPATH", "Libs;");
AppDomain.CurrentDomain.SetData("BINPATH_PROBE_ONLY", "Libs;");
var method = typeof(AppDomainSetup).GetMethod("UpdateContextProperty", BindingFlags.NonPublic | BindingFlags.Static);
var funsion = typeof(AppDomain).GetMethod("GetFusionContext", BindingFlags.NonPublic | BindingFlags.Instance);
method.Invoke(null, new object[] { funsion.Invoke(AppDomain.CurrentDomain, null), "PRIVATE_BINPATH", "Libs;" });
}
或者:
通过配置App.config
<configuration>
<runtime>
<!--xmlns是必需的特性。指定程序集绑定所需的 XML 命名空间。 使用字符串“urn: 架构-microsoft-com:asm.v1”作为值。-->
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<publisherPolicy apply="yes"/> <!--指定运行时是否使用发布者策略-->
<!--指定加载程序集时公共语言运行时搜索的子目录, 其中privatePath是相对于*.exe.config文件的相对路径,多个文件夹以分号分隔。-->
<probing privatePath="Libs;"/>
</assemblyBinding>
</runtime>
</configuration>
原文地址:https://blog.csdn.net/qq_43307934/article/details/143500996
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!