Введение
Библиотека классов, предоставляемых NET-Framework, позволяет
разработчику осуществить тесную интеграцию своего приложения как с
существующими COM-объектами, так и создать новые, следуя определенным
соглашениям. Дополнительные трудности могут возникнуть при
использовании приватных объектов, экспортируемых во внешние библиотеки
приложений, созданных сторонними производителями. Такие библиотеки часто
называются AddIn-компонентами. Здесь мы рассмотрим процесс создания
внешней библиотеки для системы 1С:Предприятие версий 7.5-7.7 средствами
NET и реализацию страницы свойств COM-объекта.
Создание внешней компоненты 1С
1С:Предприятие подключает внешнюю компоненту двумя способами: по
указанному ProgID через CreateInstance, либо по имени файла компоненты.
В последнем случае 1С пытается выполнить DllRegisterServer для
саморегистрации файла через rgs-скрипт, затем ищется строка с ID=100 из
ресурсов компоненты, содержащая ProgID объекта. Ясно, что при написании
CLR-кода последняя возможность отпадает, поскольку у NET-компонент
совсем другой принцип регистрации и другой метод хранения ресурсов.
Поэтому из 1С:Предприятия необходимо использовать только метод
ПодключитьВнешнююКомпоненту(ProgID), где ProgID должен иметь вид
AddIn.xxx, что нам может помочь достичь атрибут ProgIdAttribute.
Ключевым для работы внешней компоненты является интерфейс IInitDone:
interface IInitDone : IUnknown
{
[helpstring("method Init")] HRESULT Init([in] IDispatch *pConnection);
[helpstring("method Done")] HRESULT Done();
[helpstring("method GetInfo")] HRESULT GetInfo([in,out]
SAFEARRAY (VARIANT) *pInfo);
};
Его должен реализовывать объект внешней компоненты. Кроме того,
библиотека типов, поставляемая фирмой 1С, содержит ряд других
интерфейсов для расширения встроенного языка и других целей,
рассматривать которые мы не будем. Конечно можно воспользоваться готовым
tlb-файлом и средствами NET создать COM callable wrapper (проще говоря
добавить ссылку на COM-библиотеку из проекта), но тогда следует
согласится с тем, что множество параметров типа VARIANT будут
преобразованы в малоудобные System.Array или Object, в то время как из
документации известна их более строгая типизация. Наш интерфейс в NET
будет выглядеть так:
<Guid("AB634001-F13D-11d0-A459-004095E1DAEA"),
_ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface IInitDone
Sub Init(<MarshalAs(UnmanagedType.IDispatch)> ByVal pConnection As Object)
Sub Done()
Sub GetInfo(ByRef pInfo() As Object)
End Interface
Реализующий его объект компоненты:
<ComVisible(True), ProgId("AddIn.V7SQLClient"), Guid("ххх")> _
Public Class V7AddIn
Implements IInitDone
Private Sub Init(<MarshalAs(UnmanagedType.IDispatch)>
ByVal pConnection As Object) Implements IInitDone.Init
Try
ContainerCoordinator.V7Object = pConnection
Catch
Throw New COMException("Unknown V7 object context", COMError.S_FALSE)
End Try
End Sub
Private Sub Done() Implements IInitDone.Done
ContainerCoordinator.V7Object = Nothing
End Sub
Private Sub GetInfo(ByRef pInfo() As Object) Implements IInitDone.GetInfo
pInfo.SetValue("1000", 0)
End Sub
End Class
Страницы свойств
Внешняя компонента 1С:Предприятия как и любой COM-объект может иметь
страницу свойств. Для ее реализации средствами NET необходимо
реализовать в объекте интерфейс ISpecifyPropertyPages:
<Guid("B196B28B-BAB4-101A-B69C-00AA00341D07"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface ISpecifyPropertyPages
Sub GetPages(<Out()> ByRef pPages As CAUUID)
End Interface
´--------------------------------------------------------------------------
<StructLayout(LayoutKind.Sequential)> _
Public Structure CAUUID
Public cElems As Integer
Public pElems As IntPtr
End Structure
Метод GetPages возвращает GUID страницы свойств:
Public Sub GetPages(ByRef pPages As CAUUID) Implements ISpecifyPropertyPages.GetPages
pPages.cElems = 1
pPages.pElems = Marshal.AllocCoTaskMem(Marshal.SizeOf(GetType(Guid)))
Marshal.StructureToPtr(New Guid("601F0360-AAF2-4b89-A3BA-CB26967519F3"),
pPages.pElems, False)
End Sub
Страницу свойств будет выводить класс, реализующий стандартный
интерфейс IPropertyPage, декларацию которого опустим:
<ComVisible(True), Guid("601F0360-AAF2-4b89-A3BA-CB26967519F3")> _
Public Class V7AddInPropertyPage
Implements IPropertyPage
Public Sub New()
pPage = New PropPageControl()
bDirty = False
End Sub
Public Sub GetPageInfo(ByRef pPageInfo As propPageInfo)
Implements IPropertyPage.GetPageInfo
Try
pPageInfo.cb = Marshal.SizeOf(pPageInfo)
pPageInfo.pszTitle = "Внешние данные"
pPageInfo.cx = 300
pPageInfo.cy = 250
Catch
Throw New COMException("Invalid PageInfo structure", COMError.E_POINTER)
End Try
End Sub
Private m_PageSite As IPropertyPageSite
Private WithEvents pPage As PropPageControl
Private bDirty As Boolean
Private Sub pPage_DirtyChanged() Handles pPage.DirtyChanged
bDirty = True
m_PageSite.OnStatusChange(1)
End Sub
End Class
Класс PropPageControl не обязательно должен быть видимым извне
(public). Он наследуется от System.Windows.Forms.UserControl и
соответственно доступен для редактирования из Visual Studio Designer.
Если взаимодействие с объектом IPropertyPageSite осуществляется через
события, как в примере, то нельзя использовать делегаты.