/
ExternalLibrary.cs
276 lines (258 loc) · 11.6 KB
/
ExternalLibrary.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
// Copyright (c) Govert van Drimmelen. All rights reserved.
// Excel-DNA is licensed under the zlib license. See LICENSE.txt for details.
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using ExcelDna.Logging;
namespace ExcelDna.Integration
{
// TODO: Allow Com References (via TlbImp?)/Exported Libraries
// DOCUMENT When loading ExternalLibraries, we check first the path given in the Path attribute:
// if there is no such file, we try to find a file with the right name in the same
// directory as the .xll.
// We load files with .dna extension as Dna Libraries
[Serializable]
[XmlType(AnonymousType = true)]
public class ExternalLibrary
{
private string _Path;
[XmlAttribute]
public string Path
{
get { return _Path; }
set { _Path = value; }
}
private string _TypeLibPath;
[XmlAttribute]
public string TypeLibPath
{
get { return _TypeLibPath; }
set { _TypeLibPath = value; }
}
private bool _ComServer;
[XmlAttribute]
public bool ComServer
{
get { return _ComServer; }
set { _ComServer = value; }
}
private bool _Pack = false;
[XmlAttribute]
public bool Pack
{
get { return _Pack; }
set { _Pack = value; }
}
private bool _LoadFromBytes = false;
[XmlAttribute]
public bool LoadFromBytes
{
get { return _LoadFromBytes; }
set { _LoadFromBytes = value; }
}
private bool _ExplicitExports = false;
[XmlAttribute]
public bool ExplicitExports
{
get { return _ExplicitExports; }
set { _ExplicitExports = value; }
}
private bool _ExplicitRegistration = false;
[XmlAttribute]
public bool ExplicitRegistration
{
get { return _ExplicitRegistration; }
set { _ExplicitRegistration = value; }
}
private bool _UseVersionAsOutputVersion = false;
[XmlAttribute]
public bool UseVersionAsOutputVersion
{
get { return _UseVersionAsOutputVersion; }
set { _UseVersionAsOutputVersion = value; }
}
internal List<ExportedAssembly> GetAssemblies(string pathResolveRoot, DnaLibrary dnaLibrary)
{
List<ExportedAssembly> list = new List<ExportedAssembly>();
try
{
string realPath = Path;
if (Path.StartsWith("packed:"))
{
// The ExternalLibrary is packed.
// We'll have to load it from resources.
string resourceName = Path.Substring(7);
if (Path.EndsWith(".DNA", StringComparison.OrdinalIgnoreCase))
{
byte[] dnaContent = ExcelIntegration.GetDnaFileBytes(resourceName);
DnaLibrary lib = DnaLibrary.LoadFrom(dnaContent, pathResolveRoot);
if (lib == null)
{
Logger.Initialization.Error("External library could not be registered - Path: {0}\r\n - Packed DnaLibrary could not be loaded", Path);
return list;
}
return lib.GetAssemblies(pathResolveRoot);
}
else
{
// DOCUMENT: TypeLibPath which is a resource in a library is denoted as fileName.dll\4
// For packed assemblies, we set TypeLibPath="packed:2"
string typeLibPath = null;
if (!string.IsNullOrEmpty(TypeLibPath) && TypeLibPath.StartsWith("packed:"))
{
typeLibPath = DnaLibrary.XllPath + @"\" + TypeLibPath.Substring(7);
}
// It would be nice to check here whether the assembly is loaded already.
// But because of the name mangling in the packing we can't easily check.
// So we make the following assumptions:
// 1. Packed assemblies won't also be loadable from files (else they might be loaded twice)
// 2. ExternalLibrary loads will happen before reference loads via AssemblyResolve.
// Under these assumptions we should not have assemblies loaded more than once,
// even if not checking here.
byte[] rawAssembly = ExcelIntegration.GetAssemblyBytes(resourceName);
Assembly assembly = Assembly.Load(rawAssembly);
list.Add(new ExportedAssembly(assembly, ExplicitExports, ExplicitRegistration, ComServer, false, typeLibPath, dnaLibrary));
return list;
}
}
if (Uri.IsWellFormedUriString(Path, UriKind.Absolute))
{
// Here is support for loading ExternalLibraries from http.
Uri uri = new Uri(Path, UriKind.Absolute);
if (uri.IsUnc)
{
realPath = uri.LocalPath;
// Will continue to load later with the regular file load part below...
}
else
{
string scheme = uri.Scheme.ToLowerInvariant();
if (scheme != "http" && scheme != "file" && scheme != "https")
{
Logger.Initialization.Error("The ExternalLibrary path {0} is not a valid Uri scheme.", Path);
return list;
}
else
{
if (uri.AbsolutePath.EndsWith("dna", StringComparison.InvariantCultureIgnoreCase))
{
DnaLibrary lib = DnaLibrary.LoadFrom(uri);
if (lib == null)
{
Logger.Initialization.Error("External library could not be registered - Path: {0} - DnaLibrary could not be loaded" + Path);
return list;
}
// CONSIDER: Should we add a resolve story for .dna files at Uris?
return lib.GetAssemblies(null); // No explicit resolve path
}
else
{
// Load as a regular assembly - TypeLib not supported.
Assembly assembly = Assembly.LoadFrom(Path);
list.Add(new ExportedAssembly(assembly, ExplicitExports, ExplicitRegistration, ComServer, false, null, dnaLibrary));
return list;
}
}
}
}
// Keep trying with the current value of realPath.
string resolvedPath = DnaLibrary.ResolvePath(realPath, pathResolveRoot);
if (resolvedPath == null)
{
Logger.Initialization.Error("External library could not be registered - Path: {0} - The library could not be found at this location" + Path);
return list;
}
if (System.IO.Path.GetExtension(resolvedPath).Equals(".DNA", StringComparison.OrdinalIgnoreCase))
{
// Load as a DnaLibrary
DnaLibrary lib = DnaLibrary.LoadFrom(resolvedPath);
if (lib == null)
{
Logger.Initialization.Error("External library could not be registered - Path: {0} - DnaLibrary could not be loaded" + Path);
return list;
}
string pathResolveRelative = System.IO.Path.GetDirectoryName(resolvedPath);
return lib.GetAssemblies(pathResolveRelative);
}
else
{
Assembly assembly;
// Load as a regular assembly
// First check if it is already loaded (e.g. as a reference from another assembly)
// DOCUMENT: Some cases might still have assemblies loaded more than once.
// E.g. for an assembly that is both ExternalLibrary and references from another assembly,
// having the assembly LoadFromBytes and in the file system would load it twice,
// because LoadFromBytes here happens before the .NET loaders assembly resolution.
string assemblyName = System.IO.Path.GetFileNameWithoutExtension(resolvedPath);
assembly = GetAssemblyIfLoaded(assemblyName);
if (assembly == null)
{
// Really have to load it.
if (LoadFromBytes)
{
// We need to be careful here to not re-load the assembly if it had already been loaded,
// e.g. as a dependency of an assembly loaded earlier.
// In that case we won't be able to have the library 'LoadFromBytes'.
byte[] bytes = File.ReadAllBytes(resolvedPath);
string pdbPath = System.IO.Path.ChangeExtension(resolvedPath, "pdb");
if (File.Exists(pdbPath))
{
byte[] pdbBytes = File.ReadAllBytes(pdbPath);
assembly = Assembly.Load(bytes, pdbBytes);
}
else
{
assembly = Assembly.Load(bytes);
}
}
else
{
assembly = Assembly.LoadFrom(resolvedPath);
}
}
string resolvedTypeLibPath = null;
if (!string.IsNullOrEmpty(TypeLibPath))
{
resolvedTypeLibPath = DnaLibrary.ResolvePath(TypeLibPath, pathResolveRoot); // null is unresolved
if (resolvedTypeLibPath == null)
{
resolvedTypeLibPath = DnaLibrary.ResolvePath(TypeLibPath, System.IO.Path.GetDirectoryName(resolvedPath));
}
}
else
{
// Check for .tlb with same name next to resolvedPath
string tlbCheck = System.IO.Path.ChangeExtension(resolvedPath, "tlb");
if (System.IO.File.Exists(tlbCheck))
{
resolvedTypeLibPath = tlbCheck;
}
}
list.Add(new ExportedAssembly(assembly, ExplicitExports, ExplicitRegistration, ComServer, false, resolvedTypeLibPath, dnaLibrary));
return list;
}
}
catch (Exception e)
{
// Assembly could not be loaded.
Logger.Initialization.Error(e, "External library could not be registered - Path: {0}", Path);
return list;
}
}
// Similar copy to this method lives in ExcelDna.Loader - AssemblyManager.cs
// But here we don't deal with .resources assemblies
private static Assembly GetAssemblyIfLoaded(string assemblyName)
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly loadedAssembly in assemblies)
{
AssemblyName loadedAssemblyName = loadedAssembly.GetName();
if (string.Equals(assemblyName, loadedAssemblyName.Name, StringComparison.OrdinalIgnoreCase))
return loadedAssembly;
}
return null;
}
}
}