1C中是否可能不遵守外部组件的技术? 或如何祝贺使用1C的同事?

这里有个主意是要以某种新颖的方式祝贺我们的总会计师,例如,借助她最喜欢的1C程序? 但是如何?

经过一番思考,该想法开始用于背景祝贺,即在1C77-1C82上常规配置的客户区域中的背景图像,或在1C82托管窗体以及在所有情况下对于1C83的外部窗口中的背景图像。 如图所示,在其上显示所需的消息并提供指向祝贺视频的链接。

祝贺1C

第一部分-结果


显然,这个想法并不新鲜。 因此,在2011年, Aleksey Fedorov又名ALF提出了基于FormEx.dll类似解决方案 。 早在2008年就提出了有关如何实现这一目标的问题。

一次,我们还使用此组件将背景图像加载到1C77中。 但是,下载大的bmp文件(以及其他不能使用的文件)的速度很慢(因此,使用了铺有瓷砖的小图片),因此希望编写自己的外部组件(VK),它将仅下载必要的图像,仅此而已,除非还有什么可以作为实验的试验场。

编写了这样的组件(也仅用于bmp文件,必要时使用平铺)。 在那里使用了WinAPI LoadImage()函数。 该dll与FormEx.dll没有冲突,它很简单,足够快并且可以使用很长时间。

所有这些都很棒,但是现在是扩展其功能的时候了,这里需要一种不同的方法。

在本文中,我们不会解决创建多媒体文件的问题。 这不是我们的专长。 我们只限于为1C编程外部组件的一些细微差别。

1C77


由于1C平台的版本可能不同,因此可以有多种解决方案。 在我们的案例中,这些是1C77上的配置(图1)。

图1. 1C77上的测试配置中的祝贺图片
图 1.祝贺您在1C77上的测试配置中的图像。

此处的视频虽然是自己的,但其创作思想是从安娜·希亚诺娃(Anna Shiyanova)那里获得的,昵称为“特例” 。 这个女孩有才华,可以被模仿,但是很难完全重复这种风格。 在这种情况下,我只想要至少一些创造力。

如果其中一位同事已经厌倦了看着别人的祝贺,那么他们可以用“ Alt + I ”重载图片(图2-3)。

图2.在“文件/选择背景”菜单中或通过“ Alt + I”选择另一个背景图像
图 2.在“文件/选择背景”菜单中或通过“ Alt + I”选择其他背景图像。

并且同时查看有关“ Alt + L ”所使用的模块的信息(图3)。

图3.重载的背景图片以及有关程序的信息(“帮助/关于LionExt32.dll模块”或“ Alt + L”)
图 3.重载背景图像以及有关程序的信息(“帮助/关于LionExt32.dll模块”或“ Alt + L”)。

1C82常规形式


自然,大多数现在都面向G8(1C8x)。 但是,只有在8.2版及更低版本中的普通格式下,才可以在1C中使用背景图像,并且,如果您不使用任何以“桌面”模式开始的处理,这将完全覆盖我们的背景(图4)。

图4.普通表格1C82中测试配置中的祝贺图片
图 4.在常规配置1C82上的测试配置中显示祝贺图像。

请注意,链接到图。 4表示不是我们的视频。 显示它们只是为了测试。

在普通形式中,1C82不再提供标准的菜单访问方式,因为它不是系统性的(如“七个”而是“拥有的”)(尽管可以创建系统,但是为什么我们需要两个主菜单?)。 但是,可以使用热键。 同样,在我们的组件中,“ Alt + I”将调用一个对话框,如图2所示,并加载另一个背景(图5)。

图5.以“厚” 1C82形式重载背景图像
图 5.以“厚” 1C82格式重载背景图像。

同样,您可以通过按“ Alt + L”键来获取有关模块的信息,如图2所示。 3。

1C82托管表格


对于1C82中的托管表单,您仍然可以在第七个嵌套级别中找到所需的窗口,例如“ V8FormElement ”并在其上进行绘制,但是以某种方式,它并不有趣。

对我们来说,从这些考虑中可以得出结论,创建一个带有祝贺消息的外部窗口(图6)比处理每个个案要容易得多。 通过“ Esc ”,“ Ctrl + F4 ”,“ Alt + F4 ”或单击“ 叉号 ”,可以更精确地关闭窗口本身。

图6.托管表单1C82上的测试配置中的祝贺图像
图 6.托管表单1C82上测试配置中的祝贺图像。

此外,最小化的窗口(图7)可以再次扩大。

图7.托管表单1C82上的外部窗口的最小化图像
图 7.托管表单1C82上的外部窗口的最小化图像。

外窗的尺寸和相对位置可以更改,此处一切都与往常一样(请参见图6和图10中的外窗放大图)。 请注意,仅当外部窗口处于活动状态时,热键才起作用。

1C83常规形式


在1C83中,根本没有更多的子窗口,这些子窗口可以用作dll中1C版本的标准。 此外,“厚”表单是框架窗口(图8),托管表单是无框架的(图9)。 也就是说,所有不是框架的东西都可以重绘。 框架也可以重绘,但只能作为系统元素。
图8.框架窗口以“厚”形式1C83图9.托管形式1C83的无框窗口
图 8.以“厚”形式的框架窗口1C83。图 9.受控形式1C83的无框窗口。
在这里,我们使用动态库创建了一个测试窗口,并将其从属于1C主窗口。 在图中可以看出行为上的差异。

1C83托管表格


在1C83的情况下,就像在托管表格1C82中一样,我们不是在背景下而是在一个单独的窗口中(图11中显示了其原型)来表示祝贺。 8-9。 结果,所需的组件( LionExt32.dllLionExt64.dll )将给出以下结果(图10-12)。

图10.常规表格1C83的外部窗口中的背景图像
图 10.常规表格1C83的外部窗口中的背景图像。

图11.托管表单1C83,发行版14,64位版本的外部窗口中的背景图像
图 11.受管表格1C83,版本14,64位版本的外部窗口中的背景图像。

图12.托管表单1C83,版本15,64位版本的外部窗口中的背景图像
图 12.托管表单1C83,版本15,64位版本的外部窗口中的背景图像。

初步发现


该组件实际上已在实践中使用(图1),总会计师感到满意,一切都进行得非常好。 一路走来,事实证明,用户喜欢选择自己的背景图片,在这种情况下,需要处理“七个”图片。 对于G8,我们的组件已适应将来的储备,但应将其视为演示版本。

这里的兴趣在于该组件不需要符合从1C创建外部组件的技术 。 也许会出现其他想法来扩展其功能。 例如,对于完全受支持的配置,您不想在没有特殊需要的情况下更改1C代码。 在这种情况下,可以选择将任意dll外部加载到地址空间1C中。 但这是另一篇文章的主题。

在技​​术创新中,使用了锁来通过1C平台卸载我们的组件(因为它不符合VK格式)。 另外,由于Windows操作系统阻止为从属窗口创建此类菜单,因此另一个技巧使得可以将本地菜单分配给子窗口。 因此,您在任何地方都不会在同一MDI (多文档界面)中看到本地菜单。 他被命令面板,工具栏和上下文菜单取代。 还有一些时间来更新窗口。 有时会发生UpdateWindow()InvalidateRect()都无法正常工作的情况。 但是在这种情况下,有几个功能是成功的:

ShowWindow(hWnd, SW_HIDE); ShowWindow(hWnd, SW_SHOW); 

还应注意,我们的组件可能与其他组件发生冲突,例如与1C77的FormEx.dll冲突。 在这种情况下,它需要最后加载。

顺便提一下,请注意,如果您在1C-8.3.14及更高版本中创建配置,则不会以任何常规方式加载组件。 但是,如果数据库是在1C的早期版本中创建的,并且在最新版本中打开,则加载我们的VK不会有问题。 这再次暗示需要创建一个外部引导程序。

该项目使用WinAPI GDI +子系统。 使用它,您可以显示各种格式的图片: bmp,jpg,gif,png,tif等。 组件以相同的顺序尝试从当前配置的本地Pics目录中加载第一个可用的Main。*文件。 如果找不到这些文件,则使用来自组件资源的简单背景图像。 在图。 图13显示了版本15的64位1C83的通常形式的背景图像。进行更改后,s语的外部窗口已放大,并且Main1.png文件中的另一张图像(已平铺)已添加到其背景中。

图13.常规格式64位1C83版本15的默认墙纸
图 13.版本64的64位1C83常规格式的默认背景图像。此外,还添加了Main1.png文件中另一张图像的图像,该图像放置为“平铺”。

在不同的位模式下,组件的操作没有区别。

还应注意,我们的组件将主1C窗口及其MDI客户端(如果有)子类化。 显然,这最后一次加载时(在1C77中)与FormEx.dll发生冲突。

第二部分-技术


该项目本身可以在以下链接中找到:


如果在配置文件中将字符串“ v120 ”替换为“ v100 ”,将“ ToolsVersion =“ 12.0”替换为“ ToolsVersion =“ 4.0” ,则可以轻松地将C ++项目用于版本10

1C32位和64位版本的代码相同,并且可以同时进行编译。

版本1C77在外部组件中由非零的GetMenu ()函数handle决定 ,而版本1C83在主窗口中不存在子窗口,而主窗口中的子窗口由GetForegroundWindow()函数确定。

关于为1C创建外部组件的技术


在1C公司的ITS光盘上以及在Internet上,可以轻松地找到有关使用不同的编程语言创建VC和相应模板的信息。 但是,在1C77时代,这些模式满足了“不仅每个人”。

如果您查看一些广泛使用的组件,尤其是1C77,将发现他们的作者经常使用特殊的编程方法来扩展其设计的功能。

最早的此类外部组件之一可能是“针对1C的RAINBOW ADDIN 2000:企业7.7” 。 也许最重要的是,尽管遵循VK格式,但比官方VK技术所允许的对“七个”肠子的渗透更深。 这是由于收到的,很可能是非标准的方法,其他广为人知的项目中使用的1C77库文件的标头(* .h文件)而实现的。

确实,如果诸如LoadExternalComponent()ConnectExternalComponent()这样的1C函数允许您将外部dll嵌入到自己的地址空间中(首先,满足VK技术格式),那么为什么用户程序不屈从于诱惑并尝试访问隐藏在其中的其他它们,目标平台的程序和其他对象? Rainbow.dll组件已成功演示了此方法。

后来,组件1C版本7.7的其他作者采用了类似的机制。 特别值得注意的是“七个” 1C ++。Dll的组件及其FormEx.dll的特例

但是,用于1C77外部组件设计的非平凡方法并没有就此结束。 显然,有人应该说:“为什么我们需要铁匠? 我们不需要铁匠!” 在这里,“铁匠”是指MicroSoft的COM技术,在某种意义上,其后是“七个”的VK技术。 不,嗯,真的,如果直接下载VK,为什么需要注册中心? 这对于可以与Internet一起使用的Web浏览器可能是有意义的,但是对于本地操作,使用注册表显然是多余的。 至少,这不应该是先决条件。 此外,要编辑注册表,您需要管理权限。

请注意,1C非常喜欢这项技术(至少在将1C移植到Linux之前)。 我们对待她很酷。 COM使用ActiveX组件很方便,这很自然,因为ActiveX组件最初是为Internet开发的。

但是,在最新版本中,1C添加了使用本机API技术的功能,从而消除了对注册表的需求。 原则上,这是我们需要的,除了该技术不适用于“七个”之外,并且对于某些人来说仍然有用。

但是,当您不想为VK使用一堆样板代码时,有时会出现相对简单的任务,建议仅从外部组件一侧使用1C。 举例来说,在我们的示例中,在客户区域或必要时在单独的窗口中显示祝贺图像,配置1C。

换句话说,如果我们不打算在1C和VK之间直接交换数据,那么我们将为1C的外部组件的更简单,更通用的版本感到满意。 由于缺少样板代码,因此此处可以简化。

为1C创建VK的替代技术


由于用于1C的VK是COM服务器的一种特殊情况(在Native API技术之前),因此有些VK开发人员说:“ COM-不!” 亚历山大·奥雷夫科夫Alexander Orefkov )在这个方向上的活动特别引人注目。 它的组件“ 1sqlite.dll ”,“ TurboMD.dll ”以及其他可能的组件不使用“完全”一词中的COM。 Yoksel组件(“ SpreadSheet.dll ”)也沿此路径开发。

但是,如何从1C77加载VK加载程序呢? 毕竟,他们甚至没有尝试在此模仿某种COM。 确实,如果我们试图将由MS VC ++向导生成的一些标准dll直截了当地滑入LoadExternalComponent()函数,那么我们将感到无聊。

在“七个”中,我们收到如下消息:
从<完整路径\组件名称> .dll组件创建对象时发生错误(缺少CLSID)

在“厚” 32位客户端中的“八”消息将类似。 相同的dll将引起类似的咒骂(图15):
调用上下文方法时出错(加载外部组件):加载外部组件时出错

那么,提到的库如何解决这个问题呢? 通过研究Orefkov和Yoksel程序的文本,我们最终得出结论,资源文件(* .rc或* .rc2)中的以下“ 魔术线 ”是“怪罪”:

 STRINGTABLE DISCARDABLE BEGIN 100 "\0sd" // 1sqlite.dll 100 "\0tmd" // TurboMD.dll 100 "\0f" // SpreadSheet.dll END 

即 毫无疑问,在程序资源中,有一行带有标识符100和一些字符串值,其第一个字符为零。 您可以尝试使用此类字符串的变体,但是字符串“ \ 0L ”对我来说很好。 因此,我们创建一个资源文件并编写如下代码:

 STRINGTABLE DISCARDABLE BEGIN 100 "\0L" //    1     ! END 

我们将此文件连接到由MS C ++向导生成的最简单的dll项目,添加代码:

 BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { switch(dwReason) { case DLL_PROCESS_ATTACH: MessageBox(NULL, ",  DllMain()!", "", MB_OK); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: break; } // switch(dwReason) return TRUE; } // DllMain() 

并观察(图14)。

图14.在1C82中使用最简单的“ VK”
图 14.在1C82中使用最简单的“ VK”。

在资源文件中没有“魔术线”的情况下,我们的dll在显示MessageBox之后立即以1C的诅咒卸载(图15)。

图15.在1C82中加载常规dll时出错
图 15.在1C82中加载常规dll时出错。

也就是说,这些线路确实对外部1C组件的加载器产生了神奇的影响。

似乎第一条“魔术线”是由Alexei Fedorov(ALF)在他的旧文章中描述的,但是不再有指向它的链接,并且作者没有看到它的重新出版。 此外, 亚历山大·奥雷夫科夫Alexander Orefkov)频繁地使用它们,显然,从他的投稿中,作者是约克瑟尔 。 因此,我们将讨论Fedorov-Orefkov“魔术”线 。 它们的含义是通过功能LoadExternalComponent()阻止非标准(从1C角度来看)dll文件的卸载 。 而且,如我们所见,该技术不仅适用于1C77,而且适用于“厚” 1C82形式。

但是,在托管形式1C82和所有版本的1C83中,此功能已被完全破坏(另一个加载程序也已出现-ConnectExternalComponent() )。

因此,在1C的现代版本中,您需要寻找Fedorov-Orefkov的“魔术”线的其他简单替代方法。

这样的替代方案很容易提供。 重点很简单。 如果尝试使用指定的协议(例如,请求组件的版本)访问1C加载器时引发异常,则1C加载器将卸载该组件。 自然,我们没有这种类型的东西,它是卸载自定义dll的基础。 但是,如果仍在某处使用此VK,则系统可以忽略操作系统对1C卸载此动态库的要求。 代替删除本身,系统仅减少所需模块的使用计数器。 并且物理删除不早于此计数器被重置。 因此,我们的任务是人为地增加此计数器。

为此,您可以在DLL_THREAD_ATTACH部分再次调用我们的dll函数WinAPI LoadLibrary()

 BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { switch(dwReason) { case DLL_PROCESS_ATTACH: { WCHAR szDllName[_MAX_PATH] = {0}; //     dll GetModuleFileName(hModule, szDllName, _MAX_PATH); //MessageBox(NULL, szDllName, L"Info", MB_OK); //    dll (     183), //      DLL_PROCESS_ATTACH HMODULE hDll = LoadLibrary(szDllName); break; } // case DLL_PROCESS_ATTACH case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: break; } // switch(dwReason) return TRUE; } // DllMain() 

仅此而已! 问题已解决。 调用相同的动态库将使它的使用计数器增加1,而卸载(具有对DLL_THREAD_DETACH部分的初步访问权)将减少1。 我们总共有2-1 = 1> 0 ,因此,操作系统将不会卸载我们的dll。 而且,重要的是,不会发生DLL_PROCESS_ATTACH部分的重新初始化。

顺便说一下,从中可以看到1C如何处理其最新版本中的类似技巧(显然,它已经在1C-8.3.14及更高版本中创建的配置中做到了这一点)。 它可以将LoadLibraryEx()函数与阻止初始化段DLL_PROCESS_ATTACH的执行的参数一起使用,此后立即调用必要的导出函数。 而且,的确,如果您查看Native API的VK示例代码,则可以看到不需要调用初始化代码,因为初始化代码必须以VK格式为空。

对于使用COM技术的示例,很明显在那里需要执行初始化部分DLL_PROCESS_ATTACH ,因此,在不太新的1C版本中,更确切地说,在1C-8.3.13及更低版本中进行的配置中,1C加载程序适合我们:

 (, , .COM); 

在这里可以删除最后一个参数,因为默认情况下它是隐含的。 同时,它们可以在任何更高版本中正常打开。 在版本1C83中,以前的引导加载程序LoadExternalComponent(组件地址)不再适合我们(分别,Fedorov-Orefkov的“魔术线”在此处不起作用)。

如前所述,在一般情况下,可以使用外部引导程序解决问题。 或者,很自然地在某种程度上观察1C外部组件的技术。

还应注意,我们在文件版本为1C的文件中使用不同的位深度进行了实验。 要下载我们的组件,您可能需要在配置中将“ 同步呼叫使用模式 ”属性设置为“ 使用 ”。

还应该理解,使用这种技术需要您自担风险,请事先对测试配置或工作副本进行实验,以避免在主程序中出现潜在问题。

从09/11/2019更新


原来,我徒劳地担心:“在版本1C-8.3.14及更高版本中,不再使用“完全”一词来执行外部组件中的初始化部分。”

事实证明,仅需要处理ConnectExternalComponent()函数中的返回消息。 此外,无论我们指定哪种类型的组件: COMNative API

因此,您可以在1C的所有当前可用版本中创建配置,我们的组件应该在任何地方都可以正常工作,并且创建外部引导加载程序将是相关的,除非您不想更改配置,这种情况将得到完全支持。

在这方面,虽然1C82和1C83的测试配置之间的区别不再是根本的区别,但它们的测试配置中的代码略有更改。

同时,我们指出1C公司可以轻松地阻止任何VK中初始化代码的执行,至少对于诸如Native API之类的外部组件而言,显然仍然有效,因为从它们的模板来看,这是没有必要的。 到目前为止,对于VK类型的COM仍存在这种需求,但是有什么阻止它摆脱的呢? 同时,让我们看看他们是否会考虑此信息?

Source: https://habr.com/ru/post/zh-CN466713/


All Articles