Создание поставщика типа для AutoCAD .Net Api.

Автор Тема: Создание поставщика типа для AutoCAD .Net Api.  (Прочитано 4675 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн Дима_Автор темы

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
   Поставщик типа (Type Provider) — средство языка F# для автоматического представления данных в виде типов. То есть программисту дается возможность создать библиотеку класса, который на этапе компиляции, на основе данных абревиатуры типа, создает заданные свойства и методы будующего класса. Поставщик типа по сути можно разделить на две части — работающию во время компиляции (и соответственно во время IntelliSense), и непосредственно код метода класса. Данный механиз реализован пока только в F# (я пологаю, что в C# он так-же рано или поздно появится, но пока его нет. т.к. отсутствует необходимый для этого механизм метапрограмирования — цитирование кода). Так как, часть кода, работает во время компиляции, то это ставит крест на большинстве возможностей использования AutoCAD .Net API (ведь компиляция происходит, не из-под автокада), но теоритически получить большинство необходимых свойств (не сами значения — которые на этапе компиляции не нужны — они будут полученны, через Net Api – во время выполнения, а просто имена), можно, например, через COM — хотя откровенно говоря это сомнительное приемущество для разработчика на каждой компиляции запускать автокад, чтоб получить необходимые свойства. Но, например, свойства dwg файла — Custom Properties (по крайней мере в текущих версиях) можно получить (недокументированно) простым «двоичным» чтением, чем мы и воспользуемся в примере.
   В примере ниже код поставщика типа предоставляющий доступ к CustomProperties Database. Формат вызова следующий:
Код - C# [Выбрать]
  1. new DwgProv<"путь к образцовому dwg файлу доступному на этапе компиляции">(Database)
то есть во время компиляции библиотека читает CustomProperties из файла образца и создает свойства с типом String Option (опциональный тип — то есть либо строка, либо ничего) который будет содержать соответствующее значение пользовательского свойства Database (полученного уже естественно во время выполнения). Для программиста это будет выглядеть, что доступ к пользовательским свойствам будет аналогичен как и к «постоянным»:

Код поставщика типа:
Код - C# [Выбрать]
  1. module DwgProvider
  2. #nowarn "25"
  3. open System
  4. open System.IO
  5. open System.Text
  6. open Autodesk.AutoCAD.ApplicationServices
  7. open Autodesk.AutoCAD.DatabaseServices
  8. open Autodesk.AutoCAD.EditorInput
  9. open Samples.FSharp.ProvidedTypes
  10. open Microsoft.FSharp.Core.CompilerServices
  11.  
  12. let GetDwgProp path= //функция "бинарного" получения пользовательских свойств dwg файла
  13.   let br=new BinaryReader(File.OpenRead(path))
  14.   let rec PropVal=function
  15.     |a::b::c->(a,b)::PropVal c
  16.     |_->[]
  17.   let ReadProps count=
  18.     {1..count}|>Seq.map (fun _ -> let str=br.ReadBytes(int32(br.ReadInt16())*2)
  19.                                             |>Encoding.Unicode.GetString
  20.                                   str.Substring(0,str.Length-1))
  21.               |>Seq.toList
  22.   br.BaseStream.Seek(32L,SeekOrigin.Begin)|>ignore
  23.   br.BaseStream.Seek(br.ReadInt32()|>int64,SeekOrigin.Begin)|>ignore
  24.   let sp=ReadProps 8
  25.   br.BaseStream.Seek(24L,SeekOrigin.Current)|>ignore
  26.   sp,ReadProps(int32(br.ReadInt16())*2)|>PropVal
  27.  
  28. let DbProperty (db:obj)= //функция получение пользовательских свойств Database через .Net Api
  29.   let cp=(db:?>Database).SummaryInfo.CustomProperties //аргумент приведен к Object чтоб не требовалась сборка AutoCAD во время компиляции
  30.   Seq.unfold (function |true->Some((cp.Entry.Key:?>string,cp.Entry.Value:?>string),cp.MoveNext())
  31.                        |false->None)
  32.              (cp.MoveNext())
  33.  
  34. [<TypeProvider>] //объявление поставщика типа
  35. type DwgPr() as this =
  36.     inherit TypeProviderForNamespaces()
  37.     let asm,ns = System.Reflection.Assembly.GetExecutingAssembly(),"DwgProvider"
  38.     let IniTy = ProvidedTypeDefinition(asm, ns, "DwgProv", None)
  39.     do IniTy.DefineStaticParameters(
  40.          [ProvidedStaticParameter("path", typeof<string>)],
  41.           fun tyName [|:? string as path|] ->
  42.            let ty = ProvidedTypeDefinition(asm, ns, tyName, None)
  43.            ProvidedConstructor([ProvidedParameter("Database",typeof<obj>)],
  44.                                InvokeCode=(fun [db]-> <@@ (%%db:obj)|>DbProperty @@>)) //цитированный код конструтора (работает во время выполнения)
  45.              |>ty.AddMember
  46.            path|>GetDwgProp|>snd // создаем свойства типа на основе шаблона path (во время компиляции)
  47.              |>Seq.map (fun (key,_)->
  48.                 ProvidedProperty(key,typeof<string option>,
  49.                                  GetterCode=fun [arg] -> //цитированный код получения свойства работающий во время выполнения
  50.                                    <@@ (%%arg:obj):?>(string*string) seq
  51.                                         |>Seq.tryPick (function |k,v when k=key->Some(v)
  52.                                                                 |_->None) @@>))
  53.              |>Seq.toList|>ty.AddMembers
  54.            ty)
  55.        this.AddNamespace(ns, [IniTy])
  56. [<TypeProviderAssembly>]
  57. do()// запускаем поставщик
  58.  

Код примера использования — выводит в ком. строку свойство «Заказчик»:
Код - C# [Выбрать]
  1. module Test
  2. open Autodesk.AutoCAD.Runtime
  3. open Autodesk.AutoCAD.DatabaseServices
  4. open Autodesk.AutoCAD.Geometry
  5. open Autodesk.AutoCAD.EditorInput
  6. open System.Collections.Generic
  7. open DwgProvider
  8.  
  9. let Init()=
  10.   let doc=Application.DocumentManager.MdiActiveDocument
  11.   doc,doc.Editor,doc.Database,doc.TransactionManager.StartTransaction
  12.  
  13. [<CommandMethod "Test">]
  14. let Test()=
  15.   let doc,ed,db,trf=Init()
  16.   let cp=new DwgProv<"d:/lib/test.dwg">(db)
  17.   cp.Заказчик|>function
  18.     |Some(x)->x|>ed.WriteMessage
  19.     |None->ed.WriteMessage "Property not present\n"

p.s. Поставщики типа «полноценно» появились в F# 3.0, сответственно .Net Framework не моложе 4.0 — то есть скомпилировать можно только начиная с AutoCAD 2013.

Оффлайн Александр Пекшев aka Modis

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
сответственно .Net Framework не моложе 4.0 — то есть скомпилировать можно только начиная с AutoCAD 2013
Автокад версии 2010 отлично работает с библиотеками, написанными на Net framework 4.0. Даже 4.5. Для этого нужно редактировать файл acad.exe.config