Как правило, имя нужной DLL достаточно просто выяснить во время выполнения кода. Например, в обозначенном выше коде Александра Ривилиса нужная функция хранится в файлах acdb17.dll, acdb18.dll, ac1st19.dll, ac1st20.dll, ac1st21.dll. Т.е. мы видим, что каждая библиотека в качестве суффикса использует значение Major версии приложения. Кроме того, мы видим, что начиная с версии 19 (AutoCAD 2013) функция была перенесена в др. библиотеку (т.е. изменился префикс в её имени). Зная обозначенные выше правила, можно динамически вычислить имя DLL файла, в котором находится интересующая нас функция.
Далее показываю небольшой пример того, как можно динамически вызывать функцию acedSetEnv в разных версиях AutoCAD, не создавая для неё статических обёрток. На всякий случай напоминаю, что до версии AutoCAD 2013 эта функция находилась в файле acad.exe, а начиная с AutoCAD 2013 была перенесена в accore.dll.
Код проверялся в AutoCAD 2009 и 2016.
————————————————————-
/* Utils.cs */
using System;
using System.Text;
using System.Runtime.InteropServices;
using cad = Autodesk.AutoCAD.ApplicationServices.Application;
namespace Bushman.AutoCAD.Sandbox {
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet
= CharSet.Auto)]
public delegate int acedSetEnvDelegate(string envName,
StringBuilder NewValue);
public static class Utils {
[DllImport(«kernel32.dll»)]
public static extern IntPtr LoadLibrary(string
dllToLoad);
[DllImport(«kernel32.dll»)]
public static extern IntPtr GetProcAddress(IntPtr
hModule, string procedureName);
[DllImport(«kernel32.dll»)]
public static extern bool FreeLibrary(IntPtr hModule);
const int AutoCAD_2013_Major = 19;
public static string GetDllName() {
if (cad.Version.Major < AutoCAD_2013_Major)
return «acad.exe»;
else
return «accore.dll»;
}
public static acedSetEnvDelegate acedSetEnv;
static Utils() {
acedSetEnv = GetAcedSetEnv();
}
static acedSetEnvDelegate GetAcedSetEnv() {
string dllName = Utils.GetDllName();
IntPtr pDll = Utils.LoadLibrary(dllName);
if (pDll == IntPtr.Zero) {
return null;
}
string funcName = «acedSetEnv»;
IntPtr pAddressOfFunctionToCall = Utils
.GetProcAddress(pDll, funcName);
if (pAddressOfFunctionToCall == IntPtr.Zero) {
return null;
}
acedSetEnvDelegate acedSetEnv = (
acedSetEnvDelegate) Marshal
.GetDelegateForFunctionPointer(
pAddressOfFunctionToCall,
typeof(acedSetEnvDelegate));
bool result = Utils.FreeLibrary(pDll);
return acedSetEnv;
}
}
}
————————————————————
Далее создадим тестовую команду, которая изменит значение переменной «ACAD«:
————————————————————
/* Commands.cs */
using System;
using System.Runtime.InteropServices;
using System.Text;
using cad = Autodesk.AutoCAD.ApplicationServices.Application;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
namespace Bushman.AutoCAD.Sandbox {
public sealed class Commands {
[CommandMethod(«Test», CommandFlags.Modal)]
public void Test() {
Document doc = cad.DocumentManager
.MdiActiveDocument;
if (doc == null)
return;
Editor ed = doc.Editor;
acedSetEnvDelegate acedSetEnv = Utils.acedSetEnv;
if (acedSetEnv == null) {
ed.WriteMessage(«acedSetEnv function was » +
«not found.»);
return;
}
/* For example, we’ll edit the «Support File Search
* Path» value: */
string varName = «ACAD»;
StringBuilder sb = new StringBuilder(
@»C:\abc\def»);
int theResult = acedSetEnv(varName, sb); // 5100
}
}
}
—————————————————————
Динамическое связывание и изменение системой переменной ACAD успешно происходит в обоих тестируемых версиях AutoCAD: 2009 и 2016.