chore: initial skeleton + KOMPAS connection spike (S1/S2)
Зафиксирован фундамент проекта MCP-сервера КОМПАС-3D: - docs/: ARCHITECTURE.md, IMPLEMENTATION_PLAN.md, presentation.html - tools/kdoc.py: навигатор по справке SDK - libs/kompas-interop/: вендорские interop-сборки КОМПАС из SDK - src/Kompas.Mcp.ConnectSpike: net8.0-windows консоль, доказывает работу COM-интеропа и attach/launch подключения к КОМПАС (IApplication, версия 24.0) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AssemblyName>kompas-connect-spike</AssemblyName>
|
||||
<RootNamespace>Kompas.Mcp.ConnectSpike</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Вендорские interop-сборки КОМПАС (из SDK Samples/Common). -->
|
||||
<ItemGroup>
|
||||
<Reference Include="KompasAPI7">
|
||||
<HintPath>$(KompasInteropDir)\KompasAPI7.dll</HintPath>
|
||||
<Private>true</Private>
|
||||
<EmbedInteropTypes>false</EmbedInteropTypes>
|
||||
</Reference>
|
||||
<Reference Include="Kompas6API5">
|
||||
<HintPath>$(KompasInteropDir)\Kompas6API5.dll</HintPath>
|
||||
<Private>true</Private>
|
||||
<EmbedInteropTypes>false</EmbedInteropTypes>
|
||||
</Reference>
|
||||
<Reference Include="Kompas6Constants">
|
||||
<HintPath>$(KompasInteropDir)\Kompas6Constants.dll</HintPath>
|
||||
<Private>true</Private>
|
||||
<EmbedInteropTypes>false</EmbedInteropTypes>
|
||||
</Reference>
|
||||
<Reference Include="Kompas6Constants3D">
|
||||
<HintPath>$(KompasInteropDir)\Kompas6Constants3D.dll</HintPath>
|
||||
<Private>true</Private>
|
||||
<EmbedInteropTypes>false</EmbedInteropTypes>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,137 @@
|
||||
// Спайк S1+S2: проверка interop-сборок и подключения к КОМПАС-3D.
|
||||
//
|
||||
// Доказывает, что:
|
||||
// * вендорские interop-сборки (KompasAPI7 / Kompas6API5) собираются и загружаются под net8.0-windows;
|
||||
// * клиент умеет attach к запущенному КОМПАС и launch нового экземпляра;
|
||||
// * получает IApplication (API7), делает окно видимым и читает версию.
|
||||
//
|
||||
// Внимание: Marshal.GetActiveObject отсутствует в .NET 5+, поэтому attach делается
|
||||
// через нативные CLSIDFromProgID + GetActiveObject (oleaut32).
|
||||
//
|
||||
// Логи -> stderr, итог -> stdout. Код возврата 0 = успех.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using Kompas6API5;
|
||||
using KompasAPI7;
|
||||
|
||||
namespace Kompas.Mcp.ConnectSpike;
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal static class Program
|
||||
{
|
||||
// Порядок попыток подключения. На v24 Home проверено: есть .7 и .5, нет LT.
|
||||
private static readonly string[] ProgIds =
|
||||
{
|
||||
"KOMPAS.Application.7", // прямой API7
|
||||
"KOMPAS.Application.5", // API5 -> ksGetApplication7()
|
||||
"KOMPASLT.Application.5", // запасной (LT-инсталляции)
|
||||
};
|
||||
|
||||
private static int Main()
|
||||
{
|
||||
Log("=== KOMPAS connect spike ===");
|
||||
bool launchedByUs = false;
|
||||
IApplication? app = null;
|
||||
|
||||
// 1) attach к уже запущенному экземпляру
|
||||
foreach (var id in ProgIds)
|
||||
{
|
||||
var root = AttachByProgId(id);
|
||||
if (root is null) continue;
|
||||
app = AsApplication(root);
|
||||
if (app != null) { Log($"attached: {id}"); break; }
|
||||
}
|
||||
|
||||
// 2) запустить новый экземпляр
|
||||
if (app is null)
|
||||
{
|
||||
foreach (var id in ProgIds)
|
||||
{
|
||||
var root = CreateByProgId(id);
|
||||
if (root is null) continue;
|
||||
app = AsApplication(root);
|
||||
if (app != null) { launchedByUs = true; Log($"launched: {id}"); break; }
|
||||
}
|
||||
}
|
||||
|
||||
if (app is null)
|
||||
{
|
||||
Log("РЕЗУЛЬТАТ: подключиться к КОМПАС не удалось.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
app.Visible = true;
|
||||
Console.WriteLine("CONNECTED");
|
||||
ReportVersion(app);
|
||||
|
||||
Log(launchedByUs
|
||||
? "Экземпляр запущен этим процессом (в реальном сервере здесь решаем про Quit при выходе)."
|
||||
: "Подключились к уже запущенному экземпляру (Quit не вызываем).");
|
||||
Log("Готово. Окно КОМПАС должно быть видимым.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Привести COM-корень к IApplication: API7 — прямой каст, API5 — через ksGetApplication7().</summary>
|
||||
private static IApplication? AsApplication(object root)
|
||||
{
|
||||
if (root is IApplication direct)
|
||||
return direct;
|
||||
|
||||
if (root is KompasObject ko)
|
||||
{
|
||||
try { return ko.ksGetApplication7() as IApplication; }
|
||||
catch (Exception ex) { Log($"ksGetApplication7 failed: {ex.Message}"); }
|
||||
}
|
||||
|
||||
try { return (IApplication)root; }
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
private static void ReportVersion(IApplication app)
|
||||
{
|
||||
try { Console.WriteLine($"BuildNumber: {app.BuildNumber}"); }
|
||||
catch (Exception ex) { Log($"BuildNumber failed: {ex.Message}"); }
|
||||
|
||||
try
|
||||
{
|
||||
int major = 0, minor = 0, release = 0, build = 0;
|
||||
app.GetSystemVersion(out major, out minor, out release, out build);
|
||||
Console.WriteLine($"Version: {major}.{minor}.{release} (build {build})");
|
||||
}
|
||||
catch (Exception ex) { Log($"GetSystemVersion failed: {ex.Message}"); }
|
||||
}
|
||||
|
||||
private static object? AttachByProgId(string progId)
|
||||
{
|
||||
try
|
||||
{
|
||||
CLSIDFromProgID(progId, out Guid clsid);
|
||||
GetActiveObject(ref clsid, IntPtr.Zero, out object obj);
|
||||
return obj;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null; // не запущен / не зарегистрирован
|
||||
}
|
||||
}
|
||||
|
||||
private static object? CreateByProgId(string progId)
|
||||
{
|
||||
var type = Type.GetTypeFromProgID(progId, throwOnError: false);
|
||||
if (type is null) return null;
|
||||
try { return Activator.CreateInstance(type); }
|
||||
catch (Exception ex) { Log($"CreateInstance({progId}) failed: {ex.Message}"); return null; }
|
||||
}
|
||||
|
||||
[DllImport("ole32.dll", PreserveSig = false)]
|
||||
private static extern void CLSIDFromProgID(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string progId, out Guid clsid);
|
||||
|
||||
[DllImport("oleaut32.dll", PreserveSig = false)]
|
||||
private static extern void GetActiveObject(
|
||||
ref Guid rclsid, IntPtr pvReserved,
|
||||
[MarshalAs(UnmanagedType.IUnknown)] out object ppunk);
|
||||
|
||||
private static void Log(string msg) => Console.Error.WriteLine(msg);
|
||||
}
|
||||
Reference in New Issue
Block a user