1 // MGW (Мохов Геннадий Владимирович) 2017 - ВК для 1С 8.3
2 //
3 // dmd -ofmgw1c_vk1.dll mgw1c_vk1 terminal asc1251 -L/IMPLIB -shared -release
4 
5 module cd1;
6 
7 import core.sys.windows.windows;
8 import core.sys.windows.dll;
9 import core.runtime;     // Загрузка DLL Для Win
10 import std..string;
11 import core.stdc.wchar_ : wcscmp, wmemcpy;
12 import std.stdio;
13 
14 /*----- Фичи, на которые ушло 5 дней тестирования алгоритма 1С при загрузке и регистрации ВК -----
15 * 1) - 1С функция "ПодключитьВнешнююКомпоненту(путьdll.XXX.типвк)" имеет три параметра. Самый примечательный средний
16 *      он определяет и средний параметр 1С функции "Новый(AddIn.XXX.расширение)", который ОДНАЗНАЧНО должен
17 *      совпадать с средним параметром функции "ПодключитьВнешнююКомпоненту(путьdll.XXX.типвк)". Причем, что 
18 *      содержит это поле не важно, ВК об этом ни чего не знает.
19 * 2) - Заставить ВК создать несколько объектов возможно, если для ВК функции GetClassNames("obj1|obj2|obj3") <-- для примера
20 *      в строке параметра через разделитель ихперечислить. При этом 1С последовательно будет вызывать GetClassObject
21 *      для каждого объекта в этом списке. Поскольку первый и второй параметр в Новый() у нас уже предопределен, то
22 *      как то различить объекты можно только по третьему параметру. По этому третий параметр будет одно из
23 *      значений из "obj1|obj2|obj3". Это значит, что для каждого вызова функции ВК RegisterExtensionAs() нам
24 *      надо помнить, что записать мы должны имя объекта (obj1, obj2 или obj3)
25 * 3) - Объекты создаются сразу все в момент вызова "ПодключитьВнешнююКомпоненту()" и "Новый()" ни чего не создаёт,
26 *      НО! если будет дан вызов ещё одного "Новый()", то будет создан ещё один новый экземпляр объекта.
27 *
28 * ---------------------------------------------------*/
29 
30 
31 // ________________________________________________
32 // Алиасы для упращения записи работы с указателями
33 alias p   = void*;
34 alias ppi = immutable(p)*;
35 // ________________________________________________
36 // Вспомогательные функции. Forth фукция @
37 p dog(T)(T pz) { return *cast(p*)pz; }
38 /*
39 void msgbox(wstring msg, wstring zag) {
40 	char[256] msgbuf;		char[256] zagbuf;	
41 	wsprintf(cast(wchar*)cbuf, cast( const(wchar)*)"wstrLen = %d"w, tvar.VarEnum.vtRecWideString.wstrLen );	
42 	MessageBoxW(null, cast(wchar*)msgbuf, zagbuf, 0);
43 }
44 */
45 // ______________________________________________________________
46 // Выделение и освобождение памяти средствами 1С для внутренних нужд
47 // IMemoryManager --> C++ Interface representing memory manager.
48 // ______________________________________________________________
49 // Реализация интерфейса класса С++. Смысл: компилятору D рассказывается, как вызывать
50 // методы С++ класса. Однако сам класс уже создан внутри 1С (С++) и нам в D будет передан
51 // указатель на этот класс. По этому, что написано в теле самих методов (пустые) не важно,
52 // т.к. они реально не выполняются.
53 extern (C++) class CMemoryManager /* :IMemoryManager */ {
54 	extern (Windows) {
55 		// C++ деструктор, всегда первый
56 		// ----------------------------------
57 		void Destroy() {}
58 		// Выделить память указанного размера
59 		// ----------------------------------
60 		// @InOut pMemory - двойной указатель на переменную, которая будет содержать
61 		// указатель на блок памяти или null если была ошибка
62 		// @In ulCountByte - размер запрашиваемого блока
63 		// @return результат операции
64 		bool AllocMemory(p* pMemory, uint ulCountByte) { return true; }
65 		// Освободить память 
66 		// ----------------------------------
67 		// @In pMemory - двойной указатель на переменную содержащию
68 		// указатель на блок памяти
69 		void FreeMemory (p* pMemory) {}
70 	}
71 }
72 
73 // Это входные параметы для функции "GetInterface()" которая может возвратить указатель на дополнительные
74 // интерфейсы 1С новых версий, что позволит задействовать их возможности
75 enum InterfacesEnum  {    eIMsgBox = 0,    eIPlatformInfo    }
76 enum AddInQuestionDialogModeEnum { eAQDModeOK, eAQDModeOKCancel, eAQDModeAbortRetryIgnore, eAQDModeYesNoCancel, eAQDModeYesNo, eAQDModeRetryCancel }
77 enum AddInDlgRetCodesEnum  {
78     eADlgRetCodeTimeout 	= -1,
79     eADlgRetCodeOK 			=  1,
80     eADlgRetCodeCancel,
81     eADlgRetCodeAbort,
82     eADlgRetCodeRetry,
83     eADlgRetCodeIgnore,
84     eADlgRetCodeYes,
85     eADlgRetCodeNo,
86 }
87 	
88 // ______________________________________________________________
89 /* Представляет интерфейс для взаимодействия ВК и 1С Платформой
90  *
91  */
92 /// Base interface for object components.
93 extern (C++) class CAddInDefBase {
94 	extern (Windows) @nogc @system {
95 		// C++ деструктор, всегда первый
96 		// ----------------------------------
97 		void Destroy() {}
98 		
99 		/// добавить error сообщение
100 		/**
101 		 *  @param wcode - error code
102 		 *  @param source - source of error
103 		 *  @param descr - description of error
104 		 *  @param scode - error code (HRESULT)
105 		 *  @return the result of
106 		 */
107 		bool AddError(ushort wcode, immutable(wchar)* source, immutable(wchar)* descr, int scode) { return true; }
108 
109 		/// Reads a property value
110 		/**
111 		 *  @param wszPropName -property name
112 		 *  @param pVal - value being returned
113 		 *  @param pErrCode - error code (if any error occured)
114 		 *  @param errDescriptor - error description (if any error occured)
115 		 *  @return the result of read.
116 		 */
117 		bool Read(wchar* wszPropName, TVariant* pVal, long* pErrCode, wchar** errDescriptor) { return true; }
118 		/// Writes a property value
119 		/**
120 		 *  @param wszPropName - property name
121 		 *  @param pVar - new property value
122 		 *  @return the result of write.
123 		 */
124 		bool Write(wchar* wszPropName, TVariant* pVar) { return true; }
125 
126 		///Registers profile components
127 		/**
128 		 *  @param wszProfileName - profile name
129 		 *  @return the result of
130 		 */
131 		bool RegisterProfileAs(wchar* wszProfileName) { return true; }
132 
133 		/// Changes the depth of event buffer
134 		/**
135 		 *  @param lDepth - new depth of event buffer
136 		 *  @return the result of
137 		 */
138 		bool SetEventBufferDepth(long lDepth) { return true; }
139 		/// Returns the depth of event buffer
140 		/**
141 		 *  @return the depth of event buffer
142 		 */
143 		long GetEventBufferDepth() { return true; }
144 		/// Registers external event
145 		/**
146 		 *  @param wszSource - source of event
147 		 *  @param wszMessage - event message
148 		 *  @param wszData - message parameters
149 		 *  @return the result of
150 		 */
151 		bool ExternalEvent( immutable(wchar)* wszSource,  immutable(wchar)* wszMessage,  immutable(wchar)* wszData) { return true; }
152 		/// Clears event buffer
153 		/**
154 		 */
155 		void CleanEventBuffer() { }
156 
157 		// Вывести строку в StatusLine обычного приложения (не управляемые формы)
158 		// ----------------------------------------------------------------------
159 		// @param wszStatusLine - строка для отображения, может быть обычной "Строка для отображения"w.ptr
160 		// @return результат операции
161 		bool SetStatusLine(wchar* wszStatusLine) { return true; }
162 		
163 		/// Resets the status line contents
164 		// --------------------------------
165 		// @return the result of
166 		void ResetStatusLine() { }
167 	}
168 }
169 
170 extern (C++) class CAddInDefBaseEx  :  CAddInDefBase {
171 	extern (Windows) @nogc @system {
172 		// void Destroy2() {}
173 		// ----------------------------------
174 		// Тут есть странность. В примере С++ тут первым должен быть деструктор, но в реальности
175 		// его нет!!! Получается, что компилятор как то по другому распеделяет память, либо
176 		// пример устарел. Как бы там не было, но закоментировав деструктор удалось вызвать GetInterface() ...
177 		// ----------------------------------
178 		// void Destroy2() {}
179 		// p GetInterface(InterfacesEnum iface) { return null; }
180 		
181 		p GetInterface(InterfacesEnum iface) { return null; }
182 	}
183 }
184 
185 extern (C++) class CIMsgBox {
186 	extern (Windows) @nogc @system {
187 		// void	DestroyMsgBox() {}
188 		// @disable this();
189 		bool 	Confirm(immutable(wchar)* queryText, TVariant* retVal) { return true; }
190 		bool 	Alert(immutable(wchar)* queryText) { return true; }
191 	}
192 }
193 
194 alias t_Alert = extern (Windows) @nogc bool function(immutable(wchar)*);
195 
196 // ================= MGW1 =================
197 p m_iConnect;
198 p m_iMsg;
199 extern (C++) class CMgw1CIInitDoneBase {				// для CMgw1
200 	extern (Windows)  @nogc @system {
201 		void Destroy() {
202 			// MessageBoxA(null, "--V40-- Destructor CMgw1CIInitDoneBase()".ptr, "aution".ptr, 0);
203 		}
204 		bool Init(p pConnection) {
205 			debug {
206 				MessageBoxA(null, "--V411-- Init()".ptr, "aution".ptr, 0);
207 			}
208 			mgw1.ptrCPPobjIConnect = pConnection; 
209 			mgw1.st_iConnect = cast(CAddInDefBase*)&(mgw1.ptrCPPobjIConnect);
210 			return pConnection !is null;
211 		}
212 		bool setMemManager(p mem) {
213 			debug {
214 				MessageBoxA(null, "--V42-- setMemManager()".ptr, "aution".ptr, 0);
215 			}
216 			// Для D нужно отодвинуть указатель, т.к. D класс "дальше" C++ класса
217 			mgw1.ptrCPPobjIMemory = mem; 
218 			mgw1.st_iMemory = cast(CMemoryManager*)&(mgw1.ptrCPPobjIMemory);
219 			return mem !is null;
220 		}
221 		int GetInfo() { 
222 			debug {
223 				MessageBoxA(null, "--V43-- GetInfo()".ptr, "aution".ptr, 0);	
224 			}
225 			return 2000; 	
226 		}
227 		void	Done()    { 
228 			debug {
229 				MessageBoxA(null, "--V44-- Done()".ptr, "aution".ptr, 0);	
230 			}
231 		}
232 	}
233 }
234 
235 wstring prNameExt;
236 wstring str2;
237 
238 extern (C++) class CMgw1CILanguageExtenderBase {		// для CMgw1
239 	extern (Windows) {
240 		void Destroy() {
241 			// MessageBoxA(null, "--X40-- Destructor CMgw1CILanguageExtenderBase()".ptr, "aution".ptr, 0);
242 		}
243 		// Создает строку с char_t* wide содержащей имя ВК Последнее поле после точек
244 		// Обязательно использование менеджера памяти 1С, в противном случае не работает
245 		bool	RegisterExtensionAs(wchar** wsExtensionName) {	
246 			bool rez = false;
247 			// MessageBoxW(null, "--X45-- RegisterExtensionAs()"w.ptr, prNameExt.ptr, 0);
248 			if(mgw1.st_iMemory !is null) {
249 				int iActualSize = prNameExt.length + 1;		// Длина с нулем
250 				if(mgw1.st_iMemory.AllocMemory(cast(p*)wsExtensionName, wchar.sizeof * iActualSize)) {
251 					wmemcpy(*wsExtensionName, prNameExt.ptr, iActualSize);
252 					// MessageBoxW(NULL, cast(const(wchar)*)*wsExtensionName, NULL, 1);
253 					rez = true;
254 				}
255 			}
256 			return rez;
257 		}
258 		// Возвращает количество свойств, 0 = свойств нет
259 		int	GetNProps() { 
260 			// MessageBoxA(null, "--X46-- GetNProps()".ptr, "aution".ptr, 0);	
261 			return Props.ePropLast; 
262 		};
263 		// Возвращает порядковый номер свойства, имя которого передается в параметрах
264 		int	FindProp(const(wchar)* wsPropName) { 
265 			int n = -1;  // Вдруг не найдено
266 			for(int i; i != Props.ePropLast; i++) {
267 				if( 0 == wcscmp( wsPropName, listProp[i].propNameRu.ptr )) 		{	n = i; break;	} 
268 				else {
269 					if( 0 == wcscmp( wsPropName, listProp[i].propNameEn.ptr )) 	{	n = i; break;	}
270 				}
271 			}
272 			// wstring bufwstr = format("--X47-- FindProp() n == %s"w, n);
273 			// MessageBoxW(NULL, cast(const(wchar)*)wsPropName, bufwstr.ptr, 1);
274 			return n; 
275 		};
276 		// Возвращает имя свойства по его порядковому номеру и по переданному идентификатору языка
277 		p GetPropName(int lPropNum, int lPropAlias)  { /* MessageBoxA(null, "--X48-- GetPropName()".ptr, "aution".ptr, 0);	*/ return null; };
278 		// Возвращает значение свойства с указанным порядковым номером
279 		bool	GetPropVal(const int lPropNum, TVariant* pvarPropVal)   { 
280 			bool rez = false;
281 			// MessageBoxA(null, "--X49-- GetPropVal()".ptr, "aution".ptr, 0);
282 			if(lPropNum == Props.eTest) {
283 				str2 = "[[[ " ~ str1 ~ " ]]]";
284 	
285 				wchar* t1;
286 				int iActualSize = str2.length + 1;		// Длина с нулем
287 				if(mgw1.st_iMemory.AllocMemory(cast(p*)&t1, wchar.sizeof * iActualSize)) 
288 					wmemcpy(t1, str2.ptr, iActualSize);
289 				(*pvarPropVal).vt = ENUMVAR.VTYPE_PWSTR;
290 				(*pvarPropVal).VarEnum.vtRecWideString.pwstrVal = t1;
291 				(*pvarPropVal).VarEnum.vtRecWideString.wstrLen  = str2.length;
292 				rez = true; 
293 			}
294 			if(lPropNum == Props.eIsLogConsole) {			// естьЛогКонсоль + isLogConsole
295 				(*pvarPropVal).vt = ENUMVAR.VTYPE_BOOL;
296 				(*pvarPropVal).VarEnum.bVal = f_LogConsole;
297 				// MessageBoxA(null, "--X49-- eIsLogConsole GetPropVal()".ptr, "aution".ptr, 0);
298 				rez = true; 
299 			}
300 			return rez; 
301 		};
302 		// Устанавливает значение свойства с указанным порядковым номером
303 		bool	SetPropVal(const int lPropNum, TVariant* pvarPropVal)   { 
304 			bool rez = false;
305 			// TVariant tvar = *pvarPropVal;
306 			// MessageBoxA(null, "--X50-- SetPropVal()".ptr, "aution".ptr, 0);	
307 			if(lPropNum == Props.eTest) {
308 				import std.conv : to;
309 				
310 				if( (*pvarPropVal).vt == ENUMVAR.VTYPE_PWSTR) {
311 					str1 = to!wstring((*pvarPropVal).VarEnum.vtRecWideString.pwstrVal);
312 					// mgw1.st_iConnect.ResetStatusLine();
313 					// mgw1.st_iConnect.SetStatusLine(cast(wchar*)(*pvarPropVal).VarEnum.vtRecWideString.pwstrVal);
314 					mgw1.st_iConnect.SetStatusLine(cast(wchar*)"Привет из ВК на D ..."w.ptr);
315 					// MessageBoxW(null, cast( const(wchar)*)str1.ptr, null, 0);
316 				}
317 				rez = true; 
318 			}
319 			if(lPropNum == Props.eIsLogConsole) {			// естьЛогКонсоль + isLogConsole
320 				if((*pvarPropVal).vt == ENUMVAR.VTYPE_BOOL) {
321 					f_LogConsole = (*pvarPropVal).VarEnum.bVal;
322 					consInit(f_LogConsole);
323 					rez = true;
324 				}
325 			}
326 			if(lPropNum == Props.ePrint) {					// Печать на консоль
327 				if( (*pvarPropVal).vt == ENUMVAR.VTYPE_PWSTR) {
328 					import std.conv : to;
329 					string strWs = to!string((*pvarPropVal).VarEnum.vtRecWideString.pwstrVal);
330 					consPrint(strWs);
331 				}
332 				rez = true; 
333 			}
334 			return rez; 
335 		};
336 		// Возвращает флаг флаг возможности чтения свойства с указанным порядковым номером
337 		bool	IsPropReadable(int lPropNum)    { 	return listProp[lPropNum].isRead;	};
338 		// Возвращает флаг флаг возможности записи свойства с указанным порядковым номером
339 		bool	IsPropWritable(int lPropNum)    { 	return listProp[lPropNum].isWrite;	};
340 		
341 		int	GetNMethods()   { /* MessageBoxA(null, "--X53-- GetNMethods()".ptr, "aution".ptr, 0); */	return 0; };
342 		int	FindMethod(p wsMethodName) { MessageBoxA(null, "--X54-- FindMethod()".ptr, "aution".ptr, 0);	return 0; };
343 		p	GetMethodName(int lMethodNum, int lMethodAlias)  { MessageBoxA(null, "--X55-- GetMethodName()".ptr, "aution".ptr, 0);	return null; };
344 		int		GetNParams(int lMethodNum)  { MessageBoxA(null, "--X56-- GetNParams()".ptr, "aution".ptr, 0);	return 0; };
345 		bool	GetParamDefValue(int lMethodNum, int lParamNum, p pvarParamDefValue)  { MessageBoxA(null, "--X57-- GetParamDefValue()".ptr, "aution".ptr, 0);	return 0; };   
346 		bool	HasRetVal(int lMethodNum)  { MessageBoxA(null, "--X58-- HasRetVal()".ptr, "aution".ptr, 0);	return 0; };   
347 		bool	CallAsProc(int lMethodNum, p paParams, int lSizeArray)  { MessageBoxA(null, "--X59-- CallAsProc()".ptr, "aution".ptr, 0);	return 0; };   
348 		bool	CallAsFunc(int lMethodNum, p pvarRetValue, p paParams, int lSizeArray)  { MessageBoxA(null, "--X60-- CallAsFunc()".ptr, "aution".ptr, 0);	return 0; };   
349 	}
350 }
351 
352 extern (C++) class CMgw1CILocaleBase {					// для CMgw1
353 	extern (Windows) {
354 		void Destroy() {
355 			// MessageBoxA(null, "--Z40-- Destructor CMgw1CILocaleBase()".ptr, "aution".ptr, 0);
356 		}
357 		void	SetLocale(p loc)   { /* MessageBoxA(null, "--Z61-- SetLocale ()".ptr, "aution".ptr, 0); */ };   
358 	}
359 }
360 // ================= MGW1 =================
361 
362 enum   AppCapabilities { eAppCapabilitiesInvalid = -1,    eAppCapabilities1 = 1,    eAppCapabilitiesLast = eAppCapabilities1 };
363 static AppCapabilities g_capabilities = AppCapabilities.eAppCapabilitiesInvalid;
364 //---------------------------------------------------------------------------//
365 
366 // Определяет количество и имена методов
367 struct stProp {
368 	Props		num;			// Номер свойства
369 	wstring		propNameEn;		// Имя свойства En
370 	wstring		propNameRu;		// Имя свойства Ru
371 	bool		isRead;			// Можно читать?
372 	bool		isWrite;		// Можно писать?
373 }
374 // Это перечисление нумерует свойства и автоматически задаёт размерность массива для listProp
375 enum Props {
376 	eTest,
377 	eIsLogConsole,
378 	ePrint,
379 	ePropLast
380 }
381 // Массив структур, каждая запись хранит описание Свойства
382 stProp[Props.ePropLast] listProp = [
383 	{ num: Props.eTest, propNameEn: "Test", propNameRu: "Тест", isRead: true, isWrite: true },
384 	{ num: Props.eIsLogConsole, propNameEn: "isLogConsole", propNameRu: "естьЛогКонсоль", isRead: true, isWrite: true },
385 	{ num: Props.ePrint, propNameEn: "Print", propNameRu: "Печать", isRead: false, isWrite: true }
386 ];
387 
388 // _____________________________________________________________________________________
389 // Это есть аналог С++ класса с множ наследоваеием и мы в нем задействуем ещё пару полей
390 // для собственных нужд ( 3 vtbl имеет )
391 struct CCPPmgw1 {
392 	// Обязательные поля, интерфейсы С++
393 	ppi IInitDoneBase;
394 	ppi ILanguageExtenderBase;
395 	ppi ILocaleBase;
396 	// Мои собственные свойства и методы
397 	p ptrCPPobjIMemory;						// Ссылка на объект класса С++ МенеджерПамяти
398 	CMemoryManager* st_iMemory;				// Указатель на указатель МенеджерПамяти для D
399 	p ptrCPPobjIConnect;					// Ссылка на объект класса С++ Взаимодействия с 1С
400 	CAddInDefBase* st_iConnect;				// Указатель на указатель Взаимодействия с 1С для D
401 	CMgw1CIInitDoneBase          objCInt;
402 	CMgw1CILanguageExtenderBase objCLang;
403 	CMgw1CILocaleBase      objLocaleBase;
404 	p ptrCPPobjCIMsgBox;					// Ссылка на объект класса С++ Сообщения
405 	CIMsgBox* st_iMsgBox;					// Указатель на указатель Сообщения 1С
406 } 
407 
408 CCPPmgw1* mgw1;		// Для mgwTest1
409 
410 // Внимание!
411 // =========
412 // Имя компоненты должно совпадать с именем первого класса
413 // -------------------------------------------------------
414 
415 // g_nameAddIn - строка где '|' перечислены всеимена классов
416 // 1С будет создавать экземпляры каждого класса сразу при подключении, а потом будет использовать их.
417 // Если вызвать первый раз Новый("AddIn.VK.myClass"), то будет использован уже созданный при подключении экземпляр,
418 // а если вызвать снова Новый("AddIn.VK.myClass") - то будет создан уже новый экземпляр класса. 
419 static const wstring g_nameAddIn = "LogConsole";
420 
421 // Имя расширения, последнее поле в имени
422 // wstring CMgw1 = "CMgw1";
423 wstring  str1;
424 
425 version(Windows) {
426 	import core.sys.windows.windows;  // GetProcAddress для Windows
427 }
428 
429 //---------------------------------------------------------------------------//
430 // Функция названа неправильно. Это не имя класса, а имя AddIn, т.к. классов в этом AddIn
431 // может быть несколько. Используется в 1C:
432 // ПодключитьВнешнююКомпоненту(fullNameDll, "имяAddIn", ТипВнешнейКомпоненты.Native);
433 // ОбъектВК = Новый("AddIn.имяAddIn.ИмяЗарегКласса");   
434 export extern (C) p GetClassNames() { printf("hello.../n");	return cast(p)g_nameAddIn.ptr; }
435 //---------------------------------------------------------------------------//
436 export extern (C) p GetClassObject(wchar* wsName, p* pInterface) {
437 	if(*pInterface) return null;	// Проверка на готовность 1С
438 
439 	debug {
440 		char[256] cbuf;	
441 		MessageBoxW(NULL, cast(const(wchar)*)wsName, ">>--21--GetClassObject()"w.ptr, 1);
442 	}
443 	
444 	// Создание классов разнесено специально, что бы показать, что они могут быть совршенны разные 
445 	// по структуре и размеру
446 	
447 	// Создаём класс -- mgwTest1
448 	if( 0 == wcscmp( wsName, "LogConsole"w.ptr ) ) {		// 1с запрашивает у нас класс mgwTest1
449 		// создаю основу объекта
450 		mgw1 = new CCPPmgw1;
451 		
452 		mgw1.objCInt 		= new CMgw1CIInitDoneBase();
453 		mgw1.objCLang 		= new CMgw1CILanguageExtenderBase();
454 		mgw1.objLocaleBase 	= new CMgw1CILocaleBase();
455 		
456 		// собираю объект клсса С++ в памяти
457 		mgw1.IInitDoneBase 			=       mgw1.objCInt.__vptr;
458 		mgw1.ILanguageExtenderBase 	=      mgw1.objCLang.__vptr;
459 		mgw1.ILocaleBase 			= mgw1.objLocaleBase.__vptr;
460 		
461 		// Верну в 1С указатель на созданный мной аналог C++ объекта класса
462 		*pInterface = mgw1;
463 		prNameExt = "LogConsole";
464 		debug {
465 			wsprintf(cast(wchar*)cbuf, cast( const(wchar)*)"*pInterface = %p objCInt = %p"w, *pInterface, mgw1.objCInt );
466 			MessageBoxW(null, cast(wchar*)cbuf, "", 0);
467 		}
468 		goto m1;
469 	}
470 	// В примере ВК C++ возврат int ... проще вернуть указатель, чем перекодировать его в int
471 m1:	
472 	return *pInterface;
473 }
474 //---------------------------------------------------------------------------//
475 export extern (C) AppCapabilities SetPlatformCapabilities(AppCapabilities capabilities) {
476 	debug {
477 		MessageBoxA(null, "--24--SetPlatformCapabilities()".ptr, "aution".ptr, 0);
478 	}
479 	g_capabilities = capabilities;
480 	
481 	debug {
482 		char[256] cbuf;	
483 		wsprintf(cast(wchar*)cbuf, cast( const(wchar)*)"g_capabilities = %d capabilities = %d"w, g_capabilities, capabilities );
484 		MessageBoxW(null, cast(wchar*)cbuf, "", 0);
485 	}
486     
487 	return AppCapabilities.eAppCapabilitiesLast;
488 }
489 //---------------------------------------------------------------------------//
490 export extern (C) long DestroyObject(p* pInterface)  {
491 	debug {
492 		MessageBoxW(NULL, "--22--DestroyObject()"w.ptr, NULL, 1);
493 	}
494 
495 	// Уничтожим объект С++, Убрав ссылку на объект, отдаём его на уничтожение GC
496 	if(*pInterface == mgw1) 	mgw1 = null;
497 	
498 	debug {
499 		char[256] cbuf;	
500 		wsprintf(cast(wchar*)cbuf, cast( const(wchar)*)"*pInterface = %p mgw1 = %p"w, *pInterface, mgw1 );	
501 		MessageBoxW(null, cast(wchar*)cbuf, "", 0);
502 	}
503 
504 	// Для С++ 1С тоже скажем, что уничтожили
505 	*pInterface = null;
506 	return 0;
507 }
508 //--------------- Загрузка DLL --------------------------------------------------//
509 
510 __gshared HINSTANCE g_hInst;
511 
512 extern (Windows) BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
513 {
514     switch (ulReason)
515     {
516 	case DLL_PROCESS_ATTACH:
517 	    g_hInst = hInstance;
518 	    dll_process_attach( hInstance, true );
519 	    break;
520 	case DLL_PROCESS_DETACH:
521 	    dll_process_detach( hInstance, true );
522 	    break;
523 	case DLL_THREAD_ATTACH:
524 	    dll_thread_attach( true, true );
525 	    break;
526 	case DLL_THREAD_DETACH:
527 	    dll_thread_detach( true, true );
528 	    break;
529         default:
530     }
531     return true;
532 }
533 
534 //--------------- Алиасы, константы и прочее --------------------------------------------------//
535 
536 alias TGUID = byte[16];
537 
538 struct TInterfaceVarRec { align (1):		// iface
539     p				pInterfaceVal;
540     TGUID			InterfaceID;
541 }
542 struct TStringVarRec {    align (1):		// str
543     p				pstrVal;
544     int				LongWord;	// количество байтов
545 }
546 struct TWideStringVarRec {align (1):		// wstr
547     const(wchar)*	pwstrVal;   // указатель на wstring
548     int				wstrLen;	// количество символов 
549 }
550 struct Ttm {
551 	int		tm_sec;		// секунд после минуты (с 0)
552     int		tm_min;		// митуты после часа (с 0)
553     int		tm_hour;	// часов поле дня (с 0)
554     int		tm_mday;	// дней после месяца (с 1)
555     int		tm_mon;		// месяцев после года (с 0)
556     int		tm_year;	// лет после 1900 (с 0)
557     int		tm_wday;	// дней после воскресения (с 0)
558     int		tm_yday;	// дней с начала года (с 0)
559     int		tm_isdst;	// Daylight Saving Time flag
560 }
561 union TVarEnum {
562 	byte					i8Val;				// = 1
563 	short					shortVal;			// = 2
564 	int						lVal;				// = 4
565 	int						intVal;				// = 4
566 	uint					uintVal;			// = 4
567 	long					llVal;				// = 8
568 	ubyte					ui8Val;				// = 1
569 	ushort					ushortVal;			// = 2
570 	uint					ulVal;				// = 4
571 	ulong					ullVal;				// = 8
572 	int						errCode;			// = 4
573 	int						hRes;				// = 4
574 	float					fltVal;				// = 4
575 	double					dblVal;				// = 8
576 	bool					bVal;				// = 1
577 	char					chVal;				// = 1
578 	wchar					wchVal;				// = 2
579 	double					data;				// = 8
580 	TGUID					IDVal;				// = 16
581 	p						pvarVal;			// = 4
582 	Ttm						tmVal;				// = 36
583 	TInterfaceVarRec		vtRecInterface;
584 	TStringVarRec			vtRecString;
585 	TWideStringVarRec		vtRecWideString; 
586 }
587 // Сам тип Variant, именно с ним работает 1С
588 struct TVariant {  align (1):
589 	TVarEnum	VarEnum;			// Объеденение, хранит одну актуальную позицию
590 	uint		cbElements;
591 	ushort		vt;					// Признак того, что за данные хранятся
592 }
593 // тип того, что лежит в Variant
594 enum ENUMVAR: ushort {   
595     VTYPE_EMPTY,
596     VTYPE_NULL,
597     VTYPE_I2,                   //int16_t
598     VTYPE_I4,                   //int32_t
599     VTYPE_R4,                   //float
600     VTYPE_R8,                   //double
601     VTYPE_DATE,                 //DATE (double)
602     VTYPE_TM,                   //struct tm
603     VTYPE_PSTR,                 //struct str    string
604     VTYPE_INTERFACE,            //struct iface
605     VTYPE_ERROR,                //int32_t errCode
606     VTYPE_BOOL,                 //bool
607     VTYPE_VARIANT,              //struct _tVariant *
608     VTYPE_I1,                   //int8_t
609     VTYPE_UI1,                  //uint8_t
610     VTYPE_UI2,                  //uint16_t
611     VTYPE_UI4,                  //uint32_t
612     VTYPE_I8,                   //int64_t
613     VTYPE_UI8,                  //uint64_t
614     VTYPE_INT,                  //int   Depends on architecture
615     VTYPE_UINT,                 //unsigned int  Depends on architecture
616     VTYPE_HRESULT,              //long hRes
617     VTYPE_PWSTR,                //struct wstr
618     VTYPE_BLOB,                 //means in struct str binary data contain
619     VTYPE_CLSID,                //UUID
620     VTYPE_STR_BLOB    	= 0xfff,
621     VTYPE_VECTOR   		= 0x1000,
622     VTYPE_ARRAY    		= 0x2000,
623     VTYPE_BYREF    		= 0x4000,    //Only with struct _tVariant *
624     VTYPE_RESERVED 		= 0x8000,
625     VTYPE_ILLEGAL  		= 0xffff,
626     VTYPE_ILLEGALMASKED	= 0xfff,
627     VTYPE_TYPEMASK 		= 0xfff
628 }
629 
630 // ======================= Функции расширения ======================
631 import core.sys.windows.wincon;
632 import asc1251;
633 import terminal;
634 
635 static		Terminal _terminal;			// Объект Консоль
636 bool		f_isTerminal;				// Есть консоль или нету
637 bool		f_LogConsole;				// 1C запоминает, есть ли консоль
638 
639 // Печатать строку
640 void consPrint(string s) {
641 	if(s.length == 0) return;
642 	if(s.length < 3) { writeln(toCON(s)); return; }
643 	if(s[1] == '|') {
644 		if(s[0] == 'R') _terminal.color(Color.red,    Color.DEFAULT);
645 		if(s[0] == 'G') _terminal.color(Color.green,  Color.DEFAULT);
646 		if(s[0] == 'Y') _terminal.color(Color.yellow, Color.DEFAULT);
647 		if(s[0] == 'B') _terminal.color(Color.blue,   Color.DEFAULT);
648 		writeln(toCON(s[2 .. $]));
649 		return;
650 	}
651 	_terminal.color(Color.DEFAULT, Color.DEFAULT);
652 	writeln(toCON(s));
653 }
654 // Инициализировать консоль
655 //__________________________
656 void consInit(bool sw) {
657 	// char[256] cbuf;
658 	// wsprintf(cast(wchar*)cbuf, cast( const(wchar)*)"consInit()  sw = %d, f_isTerminal = %d, f_LogConsole = %d"w.ptr, sw, f_isTerminal, f_LogConsole);	MessageBoxW(null, cast(wchar*)cbuf, "", 0);
659 	if(f_isTerminal) {
660 		if(!sw) {
661 			FreeConsole();
662 			f_isTerminal = false;
663 		}
664 	} else {
665 		if(sw) {
666 			AllocConsole();	freopen(cast(const(char*))"conout$".ptr, cast(const(char*))"w".ptr, core.stdc.stdio.stdout);
667 			_terminal = Terminal(ConsoleOutputType.cellular);
668 			_terminal.setTitle("Log console for 1C:Enterprase 8.3");
669 			f_isTerminal = true;
670 		}
671 	}
672 }
673