{"id":68112,"date":"2011-10-04T22:25:31","date_gmt":"2011-10-04T15:25:31","guid":{"rendered":"https:\/\/bahtera.jp\/com-interface\/"},"modified":"2025-03-20T09:27:46","modified_gmt":"2025-03-20T02:27:46","slug":"com-interface","status":"publish","type":"post","link":"https:\/\/bahtera.jp\/en\/com-interface\/","title":{"rendered":"Extending Asprova Functionality Through the COM Interface"},"content":{"rendered":"<p>The production scheduler Asprova exposes a COM interface, allowing functionality to be extended through EXE files or plugins (DLLs). It retrieves project objects deployed in memory from EXE or DLL files, accessing lower-level table objects in Asprova.<br \/>\nThe Asprova main body provides Hooks to execute plugins at specific event timings. These access points, represented by plugin keys, correspond to WordPress\u2019s do_action hook or apply_filters hook.<br \/>\n\t\t\t\t<a href=\"https:\/\/bahtera.jp\/en\/indonesia-scheduler\/\" class=\"st-cardlink\" aria-label=\"Production Scheduler in Indonesia\">\r\n\t\t\t\t<div class=\"kanren st-cardbox\" >\r\n\t\t\t\t\t\t\t\t\t\t<dl class=\"clearfix\">\r\n\t\t\t\t\t\t<dt class=\"st-card-img\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"150\" height=\"150\" src=\"https:\/\/bahtera.jp\/wp-content\/uploads\/2020\/12\/1-18-150x150.png\" class=\"attachment-st_thumb150 size-st_thumb150 wp-post-image\" alt=\"\u30a4\u30f3\u30c9\u30cd\u30b7\u30a2\u306e\u751f\u7523\u30b9\u30b1\u30b8\u30e5\u30fc\u30e9\u30fc\u307e\u3068\u3081\" srcset=\"https:\/\/bahtera.jp\/wp-content\/uploads\/2020\/12\/1-18-150x150.png 150w, https:\/\/bahtera.jp\/wp-content\/uploads\/2020\/12\/1-18-100x100.png 100w\" sizes=\"(max-width: 150px) 100vw, 150px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/dt>\r\n\t\t\t\t\t\t<dd>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"st-cardbox-t\">Production Scheduler in Indonesia<\/p>\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"st-card-excerpt smanone\">\r\n\t\t\t\t\t\t\t\t\t<p>In Indonesia&#8217;s Japanese manufacturing industry, the adoption of production management systems has been increasing. However, when it comes to one of the key challenges in production management\u2014creating feasible production plans that take machine and equipment loads into account\u2014manual work using Excel remains the standard practice. As a result, the demand for production schedulers is expected to grow in the future.<\/p>\n\t\t\t\t\t\t\t\t<\/div>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p class=\"cardbox-more\">\u7d9a\u304d\u3092\u898b\u308b<\/p>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<\/dd>\r\n\t\t\t\t\t<\/dl>\r\n\t\t\t\t<\/div>\r\n\t\t\t\t<\/a>\r\n\t\t\t\t<\/p>\n<h2>What is a COM Interface?<\/h2>\n<p>Asprova has a mechanism to expose internal data and processing via COM (Component Object Model). Functionality can be added through plugins (ActiveX DLL files: function calls from Asprova) or automation clients (Standard EXE files: accessing Asprova from external programs).<br \/>\nDLL files are placed in the same folder as Asprova\u2019s EXE and are automatically loaded by Asprova at startup.<br \/>\nDLLs are registered as internal commands in Asprova, so they can be attached to user-defined menus and called from Asprova. For EXE files, an active project is obtained by specifying and opening a project file name.<\/p>\n<h2>DLL Placement Location<\/h2>\n<p>DLLs must be placed in the following locations relative to the launching EXE:<\/p>\n<div class=\"graybox\">\n<div class=\"maruno\">\n<ol>\n<li>Place the EXE and DLL in the same directory.<\/li>\n<li>Launch the EXE with the DLL\u2019s directory set as the current directory.<\/li>\n<li>Place the DLL in the system32 directory.<\/li>\n<li>Place the DLL in the Windows directory.<\/li>\n<li>Set the DLL\u2019s directory in the Path environment variable.<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<h2>Reference Settings in the Development Environment (Using Asprova Libraries from Within Plugins)<\/h2>\n<p>Reference settings are stored in the project file (.vbp file) to use the type library of an ActiveX object without a GUI in the project. Plugins (DLLs compiled from project files) access Asprova\u2019s libraries via the COM interface.<br \/>\nWith reference settings, object types can be used in Dim variable declarations, and instances can be created with the new operator.<\/p>\n<div class=\"graybox\">\n<div class=\"maruno\">\n<ol>\n<li>Asprova Project Class<br \/>\nDim project As aslib.ASBProject<\/li>\n<li>List of Objects Related to Plugin Keys Used in Extended Plugin DLLs<br \/>\nDim ArgList As ASPArgList<\/li>\n<li>Item Class<br \/>\nDim item As ASBItem<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<p>When referencing Asprova libraries from a plugin, register the following two Asprova class libraries in the development environment\u2019s reference settings:<\/p>\n<div class=\"graybox\">\n<div class=\"maruno\">\n<ol>\n<li>As 1.1 Type Library<\/li>\n<li>AsPlugInManager 1.0 Type Library<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<h2>Structure of a Plugin Project File<\/h2>\n<p style=\"text-align: left;\">For plugins, functions called by Asprova are registered in the AutoRegistration method of the ASDefault class.<\/p>\n<p style=\"text-align: left;\">If registered as a plugin in Asprova\u2019s \u201cPlugin Information,\u201d the AutoRegistration function is automatically called at startup and implemented in the project object.<\/p>\n<p><img decoding=\"async\" class=\"alignleft wp-image-6398 size-full\" title=\"projectfile\" src=\"https:\/\/bahtera.jp\/wp-content\/uploads\/projectfile.jpg\" alt=\"COM Port 2\" width=\"350\" height=\"276\" srcset=\"https:\/\/bahtera.jp\/wp-content\/uploads\/projectfile.jpg 350w, https:\/\/bahtera.jp\/wp-content\/uploads\/projectfile-300x236.jpg 300w\" sizes=\"(max-width: 350px) 100vw, 350px\" \/><\/p>\n<p style=\"text-align: left;\">For Standard EXE (automation clients), the ASDefault class is not needed.<\/p>\n<p>Plugins are registered by passing four arguments to the AddASPlugIn method of Asprova\u2019s plugin manager (plugin registration function). This essentially says, \u201cI\u2019m in a project called Factory, with a method AddOverTime in the Overtime class, so call it at the KeyHookGeneric plugin key timing.\u201d<\/p>\n<div style=\"clear: both;\"><\/div>\n<h2>Mapping Hooks and Functions to Plugin Keys (Access Points)<\/h2>\n<p>Asprova provides hooks to execute plugins at specific events.<br \/>\nThese access points, called plugin keys, represent each event. For example, KeyHookGeneric is the \u201ccommand addition\u201d access point\u2014executing a command triggers the plugin.<\/p>\n<pre><code class=\"prettyprint linenums\">Option Explicit\r\nPublic Function AutoRegistration(plugInManager As ASPPlugInManager, module As ASPModule) As Boolean\r\n    'Set the comment for this module\r\n    module.Comment = \"APS Extended Functionality PlugIns (VB)\"\r\n    'Define a variable to hold the plugin object\r\n    Dim plugIn As ASPPlugIn\r\n    ' ---------------------------------------------------------------------------------------------\r\n    'Call the plugin manager to register a new plugin. The method name is AddASPlugIn\r\n    ' 1st Param: Plugin display name\r\n    ' 2nd Param: ProgID representing the module and class where the plugin is implemented\r\n    ' 3rd Param: Plugin entry function name (call name)\r\n    ' 4th Param: Key name representing the context in which the plugin is used\r\n\r\n    '(1) Automatically calculate overtime or weekend work and set it in the calendar table\r\n    Set plugIn = plugInManager.AddASPlugIn(\"Incorporate Overtime into Regular Overtime and Saturday Shifts\", \"Factory1.OverTime\", \"AddOverTime\", ASPlugInKeyName.KeyHookGeneric)\r\n    'Set comments, etc., for this plugin\r\n\r\n    plugIn.Comment = \"Distribute overtime into 2 hours of weekday overtime and 2 Saturday shifts.\"\r\n    'Plugin call order (for cases with multiple plugins for the same plugin key)\r\n    plugIn.Order = 1\r\n    'Return True to indicate successful auto-registration\r\n    AutoRegistration = True\r\nEnd Function<\/code><\/pre>\n<p>When Asprova loads plugins at startup, it displays the \u201centry function display name\u201d (the first argument in the plugin manager call) in the internal command plugins.<br \/>\nPlugins register themselves by calling Asprova\u2019s plugin manager from within and passing four arguments.<br \/>\n<img decoding=\"async\" class=\"aligncenter wp-image-6399 \" title=\"HookGeneric\" src=\"https:\/\/bahtera.jp\/wp-content\/uploads\/HookGeneric.jpg\" alt=\"COM Port 3\" width=\"541\" height=\"538\" srcset=\"https:\/\/bahtera.jp\/wp-content\/uploads\/HookGeneric.jpg 590w, https:\/\/bahtera.jp\/wp-content\/uploads\/HookGeneric-150x150.jpg 150w, https:\/\/bahtera.jp\/wp-content\/uploads\/HookGeneric-300x298.jpg 300w, https:\/\/bahtera.jp\/wp-content\/uploads\/HookGeneric-36x36.jpg 36w, https:\/\/bahtera.jp\/wp-content\/uploads\/HookGeneric-115x115.jpg 115w\" sizes=\"(max-width: 541px) 100vw, 541px\" \/><\/p>\n<h2>Same Concept as COM Port (Communication Port)<\/h2>\n<p>By the way, COM in COM ports and COM interfaces are different, but both are standard interfaces for external communication, making them conceptually similar.<br \/>\nSoftware and hardware have standardized interfaces (ports) for exchanging data with external entities. For instance, POP and SMTP server ports or database connection ports are TCP\/IP ports.<br \/>\nStandard PCs feature interfaces like PS\/2 ports, USB ports, and LAN ports (Ethernet ports) for connecting peripherals such as keyboards.<br \/>\nIn software, OSes provide standardized methods to uniformly handle data transmission with external devices or networks, using ports to identify communication partners.<br \/>\nWindows offers \u201cCOM ports\u201d to standardize I\/O methods, allowing applications to access modems, printers, or scanners uniformly. Apps only need to know which COM port their device is connected to for I\/O handling.<br \/>\nFor example, connecting a USB barcode assigns COM13, but since PCs don\u2019t physically have 13 interfaces, COM13 is virtual.<br \/>\nIn TCP\/IP network communication, \u201cport numbers\u201d from 0 to 65,535 serve as sub-addresses under a device\u2019s or computer\u2019s IP address, enabling multiple services or simultaneous communication with multiple computers. \u201cPort\u201d alone often refers to these TCP\/IP port numbers.<\/p>\n<h2>Mapping Recordset Field Values to Crystal Report Field Definitions via TTX Files<\/h2>\n<p>Export Asprova\u2019s operation table to Access to issue production tags.<br \/>\nThough outdated, Crystal Report 9 has Parameter Fields. I thought VB would pass parameters, but tossing a Recordset auto-fetches field values, displaying reports in the free Crystal Report Viewer ActiveX component.<\/p>\n<pre><code class=\"prettyprint linenums\">    CrRep.Database.SetDataSource rs, 3, 1\r\n    CRViewer91.ReportSource = CrRep\r\n    CRViewer91.ViewReport<\/code><\/pre>\n<p>Here, the third argument, 1, represents the Table Number.<\/p>\n<pre><code class=\"prettyprint linenums\">    'Get ORDERCOD from the selected combo\r\n    result = Combo1.List(Combo1.ListIndex)\r\n\r\n    'Passing parameter from VB6 to CR using CRAXDRT\r\n    Dim CrApp As CRAXDRT.Application\r\n    Dim CrRep As CRAXDRT.Report\r\n\r\n    'Get the recordset for ORDERCOD from ORDERTBL\r\n    Dim conn As New ADODB.Connection\r\n    Dim rs As ADODB.Recordset\r\n\r\n    Dim mySQL As String\r\n\r\n    cn.Open _\r\n        \"Provider = Microsoft.Jet.OLEDB.4.0; \" &amp; _\r\n            \"Data Source = \" &amp; CurDir &amp; \"\\NNA.mdb\"\r\n\r\n    Set CrApp = New CRAXDRT.Application\r\n    Set CrRep = CrApp.OpenReport(App.Path &amp; \"\\reports\\TravelSheet.rpt\", &amp; _\r\n                    crOpenReportByTempCopy)\r\n\r\n    CrRep.DiscardSavedData\r\n\r\n    If result &lt;&gt; \"ALL\" Then\r\n        mySQL = \"SELECT * FROM OPERATIONTBL WHERE ORDERCOD='\" &amp; _\r\n            Combo1.Text &amp; \"' ORDER BY ORDERCOD\"\r\n    Else\r\n        mySQL = \"SELECT * FROM OPERATIONTBL ORDER BY ORDERCOD\"\r\n\r\n    End If\r\n    With rs\r\n        .ActiveConnection = cn\r\n        .CursorLocation = adUseServer\r\n        .CursorType = adOpenKeyset\r\n        .LockType = adLockOptimistic\r\n        .Properties(\"IRowsetIdentity\") = True\r\n\r\n        .Open mySQL\r\n    End With\r\n    CrRep.Database.SetDataSource rs, 3, 1\r\n    CRViewer91.ReportSource = CrRep\r\n    CRViewer91.ViewReport\r\n<\/code><\/pre>\n<p>However, apps using non-standard Win7 components like Crystal Report Viewer can cause issues in XP. Since crviewer9.dll causes frequent problems on older PCs, I downgraded to CR8.5 and adjusted the code as follows.<br \/>\nCR8.5 limits itself to form layout, defining fields in a TTX file. VB throws a recordset, mapping it to the form per the TTX file\u2019s field definitions.<\/p>\n<pre><code class=\"prettyprint linenums\">Dim result As String\r\n\r\n    'Get ORDERCOD from the selected combobox\r\n    result = Combo1.List(Combo1.ListIndex)\r\n\r\n    'Get the recordset for ORDERCOD from ORDERTBL\r\n    Dim conn As New ADODB.Connection\r\n    Dim rs As ADODB.Recordset\r\n\r\n    Dim mySQL As String\r\n\r\n    cn.Open _\r\n        \"Provider = Microsoft.Jet.OLEDB.4.0; \" &amp; _\r\n            \"Data Source = \" &amp; CurDir &amp; \"\\DNN.mdb\"\r\n\r\n    If result &lt;&gt; \"ALL\" Then\r\n        mySQL = \"SELECT * FROM OPERATIONTBL WHERE ORDERCOD='\" &amp; _\r\n                    Combo1.Text &amp; \"' ORDER BY ORDERCOD\"\r\n\r\n    Else\r\n        mySQL = \"SELECT * FROM OPERATIONTBL ORDER BY ORDERCOD\"\r\n\r\n    End If\r\n\r\n    rs.Open mySQL, cn, adOpenKeyset, adLockOptimistic\r\n\r\n    With rptControl\r\n        .WindowTitle = \"Travel Sheet\"\r\n        .ReportFileName = App.Path &amp; \"\\Reports\\TravelSheet.rpt\"\r\n\r\n        .SetTablePrivateData 0, 3, rs\r\n        .WindowShowPrintBtn = True\r\n        .WindowShowPrintSetupBtn = True\r\n\r\n        .RetrieveDataFiles\r\n        .WindowState = crptMaximized\r\n        .Destination = crptToWindow\r\n        .Action = 1\r\n    End With<\/code><\/pre>\n<p>Specifying the TTX file (rpt.ttx) in CR\u2019s Report Expert maps the recordset\u2019s field values to CR file fields automatically.<br \/>\n<img decoding=\"async\" class=\"aligncenter wp-image-9326 size-full\" src=\"https:\/\/bahtera.jp\/wp-content\/uploads\/reportexpert.jpg\" alt=\"reportexpert\" width=\"579\" height=\"442\" srcset=\"https:\/\/bahtera.jp\/wp-content\/uploads\/reportexpert.jpg 579w, https:\/\/bahtera.jp\/wp-content\/uploads\/reportexpert-300x229.jpg 300w, https:\/\/bahtera.jp\/wp-content\/uploads\/reportexpert-100x76.jpg 100w, https:\/\/bahtera.jp\/wp-content\/uploads\/reportexpert-320x244.jpg 320w\" sizes=\"(max-width: 579px) 100vw, 579px\" \/><br \/>\nDefining field names, attributes, and lengths in a tab-delimited TTX file may be a classic approach in Japan, but it\u2019s still active in Indonesia.<\/p>\n<pre><code class=\"prettyprint linenums\">ORDERCOD\tstring\t100\r\nPROCNO\tnumber\r\nRESOURCE\tstring\t100\r\nITEM\tstring\t100\r\nQTY\tnumber\r\nSTARTDATE\tstring\t100\r\nENDDATE\tstring\t100\r\nWORKTIME\tstring\t100\r\nPARENTITEM\tstring\t100\r\nCLASS\tnumber<\/code><\/pre>\n<h2>Accessing Jet Database Engine from VB<\/h2>\n<p>Add \u201cMicrosoft ActiveX Data Objects 2.1 Library\u201d from reference settings.<\/p>\n<pre><code class=\"prettyprint linenums\">Dim conn As New ADODB.Connection\r\nDim rs As New ADODB.Recordset\r\nconn.Open _\r\n    \"Provider = Microsoft.Jet.OLEDB.4.0; \" &amp; _\r\n\"Data Source = C:\\Users\\Toshiba\\Desktop\\ERP\\VB\\LinkWithERP.mdb\"\r\n'DELETE\r\nDim query1 As String\r\nquery1 = \"DELETE * FROM ORDER_SOURCE\"\r\nrs.Open query1, cn, adOpenKeyset, adLockOptimistic\r\n'SELECT\r\nDim query2 As String\r\nquery2 = \"SELECT * FROM ORDER_SOURCE\"\r\nrs.Open query2, cn, adOpenKeyset, adLockOptimistic\r\nDo Until rs.EOF\r\n    X1 = rs!Order_Code\r\n    X2 = rs!Order_Item\r\n    X3 = rs!Order_Qty\r\n\r\nrs.MoveNext\r\n\r\nLoop<\/code><\/pre>\n<p>ADO and ODBC are middleware for using the Jet Database Engine (MDB).<br \/>\nThe MDB file in MS-Access format is a Jet Database Engine file (a relational database engine by Microsoft)\u2014a mere data container, separate from the Microsoft Access GUI application.<br \/>\nThis means MDB files can be used without MS-Access installed.<br \/>\nOn PCs without MS-Access, you can manipulate tables via Excel VBA with ADO or download the Runtime from Microsoft\u2019s site to open it\u2014without violating licensing.<br \/>\nRuntime is \u201ca program stripped of development functions from software with both development and execution capabilities, leaving only execution functions\u201d\u2014an \u201cexecution environment without a development environment.\u201d<\/p>\n<div class=\"graybox\">\n<div class=\"maruck\">\n<ul>\n<li>http:\/\/www.microsoft.com\/ja-jp\/download\/details.aspx?id=4438<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<p>Notepad is a utility for editing text files, but it\u2019s not the only app that can\u2014just the same logic.<br \/>\nDB (RDB) connections from systems are mostly ADO or ODBC, differing as:<\/p>\n<div class=\"graybox\">\n<div class=\"maruck\">\n<ul>\n<li>ADO: Interface for manipulating OLE DB as ActiveX controls<\/li>\n<li>ODBC: Interface within OLE DB providing RDB<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<p>OLE DB includes an ODBC provider for RDB connections, but developers access OLE DB via ADO\u2019s interface.<br \/>\nGoing from Cikarang via \u201cDeveloper \u21d2 ODBC \u21d2 OLE (RDB, Excel, etc.)\u201d (backroads) takes 3 hours, but \u201cDeveloper \u21d2 ADO \u21d2 OLE (RDB, Excel, etc.)\u201d (toll road) takes 1.5 hours\u2014same idea lol.<br \/>\nFor generating connection and recordset objects, any method works.<br \/>\nUsing CreateObject creates a new object variable, assigning the reference with Set:<\/p>\n<pre><code class=\"prettyprint linenums\">Dim conn As Variant\r\nDim rs As Variant\r\nSet conn = CreateObject(\"ADODB.Connection\")\r\nSet rs = CreateObject(\"ADODB.Recordset\")<\/code><\/pre>\n<p>Using New assigns an object reference with Set and New:<\/p>\n<pre><code class=\"prettyprint linenums\">Dim conn As ADODB.Connection\r\nDim rs As ADODB.Recordset\r\nSet conn = New ADODB.Connection\r\nSet rs = New ADODB.Recordset<\/code><\/pre>\n<h2>Accessing MySQL from VB<\/h2>\n<p>Even with a 64-bit Windows PC, VB6 (development environment) is 32-bit, so use the 32-bit MySQL ODBC Connector.<br \/>\nThis doesn\u2019t matter for ADODB connections, but for DSN connections, installing a 32-bit ODBC driver on 64-bit Windows won\u2019t show the MySQL 5.1 driver in the Control Panel\u2019s ODBC settings\u2014panic! Use this command in Command Prompt to open the 32-bit ODBC Data Source Administrator for DSN setup:<\/p>\n<div class=\"graybox\">\n<div class=\"maruck\">\n<ul>\n<li>%windir%\\SysWOW64\\odbcad32.exe<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<p>For DSN connection:<\/p>\n<pre><code class=\"prettyprint linenums\">'Create connection object for MySQL 5.1 driver DSN\r\nDim conn As New ADODB.Connection\r\nDim rs As New ADODB.Recordset\r\nDim dsn As String\r\ndsn = \"dsn=xxx;uid=yyy;pwd=zzz\"\r\nconn.Open dsn\r\nconn.CursorLocation = 3<\/code><\/pre>\n<p>For ADODB connection:<\/p>\n<pre><code class=\"prettyprint linenums\">'Create connection object for MySQL 5.1 driver ADO\r\nDim conn As New ADODB.Connection\r\nDim rs As ADODB.Recordset\r\n\r\nDim strConn, xServer, xDatabase, xUID, xPass As String\r\nIf conn.State = 0 Then\r\n    xServer = \"localhost\"\r\n    xDatabase = \"xxx\"\r\n    xUID = \"yyy\"\r\n    xPass = \"zzz\"\r\n\r\n    strConn = \"DRIVER={MySQL ODBC 5.1 Driver};SERVER=\" &amp; xServer &amp; _\r\n               \";PORT=3306\" &amp; _\r\n               \";DATABASE=\" &amp; xDatabase &amp; _\r\n               \";USER=\" &amp; xUID &amp; _\r\n               \";PASSWORD=\" &amp; xPass &amp; _\r\n               \";OPTION=3;\"\r\n    conn.Open strConn\r\nEnd If\r\n\r\n'DELETE\r\nDim query1 As String\r\nquery1 = \"DELETE * FROM ORDER_SOURCE\"\r\nrs.Open query1, strConn\r\n\r\n'SELECT\r\nDim query2 As String\r\nquery2 = \"SELECT * FROM ORDER_SOURCE\"\r\nrs.Open query2, strConn\r\n\r\nDo Until rs.EOF\r\n    X1 = rs!Order_Code\r\n    X2 = rs!Order_Item\r\n    X3 = rs!Order_Qty\r\n\r\n    rs.MoveNext\r\nLoop<\/code><\/pre>\n<h2>Accessing Excel from VB<\/h2>\n<p>Add Microsoft Excel 12.0 Object Library from reference settings.<\/p>\n<pre><code class=\"prettyprint linenums\">'Excel-related objects\r\nDim xlApp As Excel.Application\r\nDim xlBook As Excel.Workbook\r\nDim xlSheet As Excel.Worksheet\r\n'File name (full path)\r\nDim strFilename As String\r\nstrFilename = \"C:\\SO\\Delivery Schedule.xls\"\r\n'Sheet name\r\nDim strSheetName As String\r\nstrSheetName = \"Delivery Schedule\"\r\n'Generate Application\r\nSet xlApp = CreateObject(\"Excel.Application\")\r\n'Open EXCEL\r\n'xlApp.Workbooks.Open FileName:=strFilename, UpdateLinks:=0\r\n'Generate Workbook object\r\n'Set xlBook = xlApp.Workbooks(Dir(strFilename))\r\nSet xlBook = xlApp.Workbooks.Open(strFilename)\r\n'Generate Worksheet object\r\nSet xlSheet = xlBook.Worksheets(strSheetName)\r\n'Get Worksheet data\r\nDim cnt As Integer\r\ncnt = 1\r\nDo\r\n    'ORDCOD = Worksheets(\"Delivery Schedule\").Cells(cnt, 1).Text works too\r\n    ORDCOD = xlSheet.Cells(cnt, 1).Text\r\n    ORDITM = xlSheet.Cells(cnt, 2).Text\r\n    ORDQTY = xlSheet.Cells(cnt, 3).Text\r\n\r\ncnt = cnt + 1\r\n\r\nLoop While ORDCOD &lt;&gt; \"\"<\/code><\/pre>\n<p>Note: When assigning a file name (full path) to a workbook object from a variable, remove the extension, or it won\u2019t work for some reason. Fixed paths as strings (like above) are fine.<br \/>\nUse InStrRev to find the period and exclude the extension:<\/p>\n<pre><code class=\"prettyprint linenums\">strFilename = CurDir &amp; \"\\data\\\" &amp; strFilename\r\n'Remove extension (required to avoid errors)\r\nstrFilename = Left(strFilename, InStrRev(strFilename, \".\") - 1)\r\n\r\nSet xlBook = xlApp.Workbooks.Open(strFilename)<\/code><\/pre>\n<p>Worksheet name retrieval and addition:<\/p>\n<pre><code class=\"prettyprint linenums\">'Retrieve worksheet names\r\nj = 0\r\nFor Each ws In xlBook.Worksheets\r\n    sheetArry(j) = ws.Name\r\n    j = j + 1\r\nNext\r\n\r\n'Add Worksheet 6\r\nSet xlSheet = xlBook.Worksheets.Add  'Add worksheet\r\nxlSheet.Name = \"New sheet\"<\/code><\/pre>\n<h2>Accessing Text Files (CSV) from VB<\/h2>\n<p>Add Microsoft Scripting Runtime from reference settings. FileSystemObject is a component for easily handling the file system in Windows.<\/p>\n<pre><code class=\"prettyprint linenums\">'Generate DB connection and recordset objects\r\nDim conn As New ADODB.Connection\r\nDim rs As New ADODB.Recordset\r\n'Generate component object for handling CSV files\r\nDim objFSO As New Scripting.FileSystemObject\r\n'Connect to DB\r\nconn.Open _\r\n    \"Provider = Microsoft.Jet.OLEDB.4.0; \" &amp; _\r\n    \"Data Source = \" &amp; CurDir &amp; \"\\SO.mdb\"\r\n'Generate empty recordset with SELECT\r\nquery1 = \"SELECT * FROM ORDER_SOURCE\"\r\nrs.Open query1, conn, adOpenStatic, adLockOptimistic\r\n'Load CSV file data into the object\r\nSet objFile = objFSO.OpenTextFile(\"C:\\APS_ERP\\VB\" &amp; CSVNAME)\r\n'Read line-by-line and store in recordset\r\ncnt = 0\r\nDo Until objFile.AtEndOfStream\r\n    strMastflow = objFile.ReadLine\r\n    arrMastflow = Split(strMastflow, \",\")\r\n\r\nrs.AddNew\r\nrs(\"Order_Code\") = arrMastflow(0)\r\nrs(\"Order_Item\") = arrMastflow(1)\r\nrs(\"Order_Qty\") = arrMastflow(2)\r\nrs.Update\r\n\r\ncnt = cnt + 1\r\n\r\nLoop<\/code><\/pre>\n<h2>OLE DB-Related Terminology<\/h2>\n<p>The relationship between middleware and components for DB access via ADO resembles shipping cargo from a Shipper to a Consignee via sea transport.<br \/>\n<a href=\"https:\/\/bahtera.jp\/wp-content\/uploads\/oledb.jpg\"><img decoding=\"async\" class=\"aligncenter wp-image-3685 size-full\" src=\"https:\/\/bahtera.jp\/wp-content\/uploads\/oledb.jpg\" alt=\"oledb\" width=\"615\" height=\"304\" srcset=\"https:\/\/bahtera.jp\/wp-content\/uploads\/oledb.jpg 615w, https:\/\/bahtera.jp\/wp-content\/uploads\/oledb-300x148.jpg 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" \/><\/a><br \/>\nTo access an RDB, Excel, or text file (Consignee) from a development application (Shipper), you request an OLE DB data provider (Forwarder) to handle it. Direct dealings with OLE DB involve cumbersome paperwork, so ADO (cargo handler) takes over.<\/p>\n<div class=\"graybox\">\n<div class=\"maruck\">\n<ul>\n<li>OLE DB (Object Linking and Embedding Database)<br \/>\nA programming interface developed by Microsoft to access databases uniformly, regardless of type.<br \/>\nDB (Consignee) &lt;&lt; OLE DB is implemented as a set of COM components (program parts), usable from any programming language. Conceptually, it\u2019s divided into \u201cconsumers\u201d (apps needing data access) and \u201cproviders\u201d (software components implementing interfaces to supply data to consumers).<\/li>\n<li>COM Interface<br \/>\nA mechanism in COM (Component Object Model) to expose internal data and processes, allowing plugins (ActiveX DLL files) or automation clients (Standard EXE files) to access and develop\/publicize application functions.<\/li>\n<li>ADO (ActiveX Data Object)<br \/>\nA component for accessing databases via OLE DB providers. A programming interface making OLE DB usable as ActiveX controls, enabling direct table record manipulation from VB.<\/li>\n<li>ActiveX Controls<br \/>\nAn evolution of OLE controls, enhanced for internet use. Downloaded from web servers via networks, they add functionality to Microsoft\u2019s Internet Explorer.<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<p>Plugin DLLs are developed for adding functions, while extended interface DLLs modify existing logic.<br \/>\nThe difference lies in defining plugin keys (e.g., KeyHookGeneric) or interfaces (e.g., IEIIFilterCalc, ReplenishOrderLotSizing) in the AutoRegistration method of the AsDefault class.<\/p>\n<h2>Difference Between Plugin Keys and Interfaces<\/h2>\n<p>Defining a plugin key (e.g., KeyHookGeneric) in the entry function call within the AutoRegistration method of a plugin\u2019s AsDefault class triggers the entry function when the corresponding event (e.g., added command execution) occurs in Asprova.<br \/>\nSimilarly, defining an interface (e.g., IEIIFilterCalc, ReplenishOrderLotSizing) in the entry function call within the AutoRegistration method of an EII (Extension Interface Implementation) triggers it when the corresponding event (e.g., auto-replenishment) occurs.<br \/>\nHowever, interfaces are customized to have less overhead than plugin keys, making them suitable for frequent logic changes like order explosion, auto-replenishment, or lot sizing.<br \/>\nPlugin DLLs add functions, while extended interface DLLs modify existing logic.<br \/>\n<img decoding=\"async\" class=\"alignnone wp-image-51873 size-full\" src=\"https:\/\/bahtera.jp\/wp-content\/uploads\/1-1208.png\" alt=\"Plugin DLL\" width=\"1067\" height=\"588\" srcset=\"https:\/\/bahtera.jp\/wp-content\/uploads\/1-1208.png 1067w, https:\/\/bahtera.jp\/wp-content\/uploads\/1-1208-300x165.png 300w, https:\/\/bahtera.jp\/wp-content\/uploads\/1-1208-768x423.png 768w, https:\/\/bahtera.jp\/wp-content\/uploads\/1-1208-1024x564.png 1024w\" sizes=\"(max-width: 1067px) 100vw, 1067px\" \/><br \/>\nExtended interfaces have less overhead than plugin keys, ideal for adding entry functions called frequently during rescheduling events.<\/p>\n<h2>Extended Interface (DLL) to Modify Replenishment Order Lot Sizes<\/h2>\n<p>Replenishment order lot sizes are determined by \u201cMax Manufacturing Lot Size,\u201d \u201cMin Manufacturing Lot Size,\u201d and \u201cUnit Manufacturing Lot Size.\u201d FilterCalcReplenishOrderLotSizing is called during linkage in the \u201cOrder Explosion\u201d command, retrieving three objects to modify (override) replenishment lot sizes and passing results to ArgList:<\/p>\n<div class=\"graybox\">\n<div class=\"maruno\">\n<ol>\n<li>Array of replenishment items: ArgAsObject(kArgItem)<\/li>\n<li>List of initial process operation input instructions for parent orders: ArgAsObject(kArgObjectList)<\/li>\n<li>List of lot sizes: ArgAsSafeArray(kSafeArrayArgDouble)<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<h3>Lot Sizes<\/h3>\n<div class=\"graybox\">\n<div class=\"maruno\">\n<ol>\n<li>Lot size is set to \u201cYes\u201d for auto-replenishment.<\/li>\n<li>Lot size is determined when generating replenishment orders.<\/li>\n<li>List of operation input instructions (2D array) and operation output instructions (items).<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<h3>Order Explosion Process<\/h3>\n<div class=\"graybox\">\n<div class=\"maruno\">\n<ol>\n<li>Generate parent order operations (input and output instructions).<br \/>\nInitial parent orders are received or registered manufacturing orders.<br \/>\nGenerate input and output instructions from the manufacturing BOM.<\/li>\n<li>Auto-replenishment.<br \/>\nCheck if parent order operation input instructions are linkage targets.<br \/>\nIf the parent order predates inventory (absolute quantity), input instructions aren\u2019t linkage targets. Automatically generate manufacturing orders for shortages.<br \/>\nGenerate child orders for shortages in parent order input instructions (input items).<br \/>\nGenerate grandchild orders for shortages in child order input instructions.<\/li>\n<li>First linkage between orders.<br \/>\nLink received orders, manufacturing orders, purchase orders, and inventory orders with auto-replenishment settings.<br \/>\nLink manufacturing orders without auto-replenishment settings (items connected via manufacturing BOM).<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<h3>Example of Batching Preceding Processes for 7 Manufacturing Order Completions<\/h3>\n<div class=\"graybox\">\n<div class=\"maruno\">\n<ol>\n<li>First auto-replenishment: Item B1 and list of B in inst.<br \/>\nAdd 7 times the shortage quantity of input instruction B1 for operation M4 (no inventory, so RemainingQty is B itself).<\/li>\n<li>Second auto-replenishment: Item C and list of A in inst.<br \/>\nAdd 7 times the shortage quantity of input instruction C for operation M2.<\/li>\n<li>Condition inst.Operation.OperationMainRes.Code=&#8217;M4&#8242;, so (2) isn\u2019t executed.<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<p><img decoding=\"async\" class=\"alignnone wp-image-51877 size-full\" src=\"https:\/\/bahtera.jp\/wp-content\/uploads\/2-vert-1.jpg\" alt=\"Plugin DLL\" width=\"1067\" height=\"2582\" srcset=\"https:\/\/bahtera.jp\/wp-content\/uploads\/2-vert-1.jpg 1067w, https:\/\/bahtera.jp\/wp-content\/uploads\/2-vert-1-124x300.jpg 124w, https:\/\/bahtera.jp\/wp-content\/uploads\/2-vert-1-768x1858.jpg 768w, https:\/\/bahtera.jp\/wp-content\/uploads\/2-vert-1-423x1024.jpg 423w\" sizes=\"(max-width: 1067px) 100vw, 1067px\" \/><br \/>\nTo access Asprova\u2019s tables, you must go through the project object deployed in memory. The method to obtain this differs between DLLs and EXEs. For DLLs, load a list of objects related to the plugin key (ASPArgList) from the args argument to get the project object. For EXEs, load a specific project file into a document object to retrieve the active project.<\/p>\n<h2>For Extended Plugins (DLLs)<\/h2>\n<pre><code class=\"prettyprint linenums\">'(Definition and retrieval of objects within ASPArgList)\r\n'------------------------------------------------------------------\r\nPublic Function AddPersonPlan(args As ASPArgList) As TReturnType\r\n'Access APS table objects via the project object\r\n'Obtain the project object from the args interface\r\n'Assign the project object from ASPArgList to an ASBProject-type variable to receive arguments\r\n\r\nDim project As aslib.ASBProject\r\nSet project = args.ArgAsObject(kArgProject)\r\n\r\n'Assign the RootObject from the project object to an ASORootObject-type variable\r\nDim root As aslib.ASORootObject\r\nSet root = project.RootObject\r\n\r\n'Assign the RootCalendar object from the project object to an ASBCalendar-type variable\r\nDim calendarRoot As aslib.ASBCalendar\r\nSet calendarRoot = project.RootCalendar\r\n\r\n'Assign the RootResource object from the project object to an ASBResource-type variable\r\nDim resourceRoot As aslib.ASBResource\r\nSet resourceRoot = project.RootResource\r\n\r\n'Assign the orderRoot object from the project object to an ASBOrder-type variable\r\nDim orderRoot As aslib.ASBOrder\r\nSet orderRoot = project.orderRoot\r\n\r\n'Assign the itemRoot object from the project object to an ASBItem-type variable\r\nDim itemRoot As aslib.ASBItem\r\nSet itemRoot = project.itemRoot\r\n\r\n'Define a variable for storing property IDs\r\nDim propID1 As Long\r\n\r\n'(Accessing retrieved objects)\r\n'------------------------------------------------------------------\r\n    'Number of children (records) in the calendar object\r\n    calendarCount1 = calendarRoot.ChildCount\r\n\r\n'Get the property ID for resource code using the root object\r\npropID1 = root.LookupPropID(\"Cal_Resource\")\r\n\r\n'Loop until records end\r\n'ChildAsCalendar argument starts from 1 (0 represents the last data)\r\nFor i = 1 To calendarCount1\r\n    'Access the record\r\n    Set calendar = calendarRoot.ChildAsCalendar(i)\r\n\r\n    'Access property values with GetAsStr method. Set the second argument to 1 if the property isn\u2019t an array value\r\n    res1 = calendar.GetAsStr(propID1, 1)\r\nNext\r\n\r\nEnd Function<\/code><\/pre>\n<h2>For Standard EXE<\/h2>\n<p>Unlike plugins or extended interfaces, EXE programs outside Asprova must retrieve Asprova objects. Load a specific project file into a document object and get the active project from it.<\/p>\n<pre><code class=\"prettyprint linenums\">Private Sub Command1_Click()\r\n'\u25a0\u25a0\u25a0Asprova-related objects\u25a0\u25a0\u25a0\r\n'Define Asprova project object\r\nDim m_pProject As ASBProject\r\nDim m_pDoc As ASFDocument\r\n\r\n'Get Asprova application\r\nDim app As ASFApplication\r\nSet app = New ASFApplication\r\n\r\n'Open Asprova document by specifying the name\r\nSet m_pDoc = app.Deserialize(CurDir &amp; \"\\data.aru\")\r\n\r\n'Get the active project from the document\r\nSet m_pProject = m_pDoc.ActiveProject\r\n\r\n'\u25a0\u25a0\u25a0Matching by end-order items from the operation table\u25a0\u25a0\u25a0\r\n'Get operation object\r\nDim operation As ASBOperation\r\nDim operationList As ASOObjectList\r\nSet operationList = m_pProject.GetOperationList(False)\r\n\r\n'Get root object\r\nDim root As ASORootObject\r\nSet root = m_pProject.RootObject\r\n\r\n'Get propID (from root object)\r\npropID1 = root.LookupPropID(\"Work_OperationOutMainItemQty\")\r\n\r\n'Get all project operations and match once\r\noperationList.FilterByExprStr (\"WorkUser_MostRightOrderItem=='\" &amp; Left(Combo1.Text, 7) &amp; \"')\r\n\r\n'Sort by production start time in ascending order\r\noperationList.SortByExprStr (\"Work_OperationProductionStartTime,a\")\r\n\r\nDo\r\n    'Get list data\r\n    Set operation = operationList.Object(i)\r\n    'Get value\r\n    PrdQty = operation.GetAsStr(propID1, 1)\r\n    'Save value\r\n    operation.SetAsStr propID1, 1, Text1.Text\r\n\r\n    i = i + 1\r\n\r\nLoop While i &lt;= operationList.ObjectCount\r\n\r\n'\u25a0\u25a0\u25a0Check resource table data one-by-one\u25a0\u25a0\u25a0\r\n'Define resource object\r\nDim RootResource As AsLib.ASBResource\r\nDim resource As AsLib.ASBResource\r\n\r\n'Get resource root object\r\nSet RootResource = m_pProject.RootResource\r\n\r\n'Get propID (from root object)\r\npropID2 = root.LookupPropID(\"Res_Code\")\r\n\r\n'Number of records in resource table\r\nResourceCount = RootResource.ChildCount\r\n\r\nFor i = 1 To ResourceCount\r\n    Set resource = RootResource.ChildAsResource(i)\r\n    ResCode = resource.GetAsStr(propID2, 1)\r\n\r\nNext i\r\n\r\n'\u25a0\u25a0\u25a0Matching by item code in the item table\u25a0\u25a0\u25a0\r\n'Define item object\r\nDim RootItem As ASBItem\r\nDim Item As ASBItem\r\n\r\n'Get item root object\r\nSet RootItem = m_pProject.RootItem\r\n\r\n'Get item object\r\nSet Item = RootItem.FindChild(Left(Combo1.Text, 7))\r\n\r\n'Number of matched records in item table\r\nItemCount=Item.ChildCount\r\n\r\nEnd Sub<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>The production scheduler Asprova exposes a COM interface, allowing functionality to be extended through EXE files or plugins (DLLs). It retrieves project objects deployed in memory from EXE or DLL files, accessing lower-level table objects in Asprova.<br \/>\nThe Asprova main body provides Hooks to execute plugins at specific event timings. These access points, represented by plugin keys, correspond to WordPress\u2019s do_action hook or apply_filters hook.<\/p>\n","protected":false},"author":2,"featured_media":81055,"parent":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[619],"tags":[],"class_list":["post-68112","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-production-scheduler"],"_links":{"self":[{"href":"https:\/\/bahtera.jp\/en\/wp-json\/wp\/v2\/posts\/68112","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bahtera.jp\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bahtera.jp\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bahtera.jp\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bahtera.jp\/en\/wp-json\/wp\/v2\/comments?post=68112"}],"version-history":[{"count":0,"href":"https:\/\/bahtera.jp\/en\/wp-json\/wp\/v2\/posts\/68112\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/bahtera.jp\/en\/wp-json\/wp\/v2\/media\/81055"}],"wp:attachment":[{"href":"https:\/\/bahtera.jp\/en\/wp-json\/wp\/v2\/media?parent=68112"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bahtera.jp\/en\/wp-json\/wp\/v2\/categories?post=68112"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bahtera.jp\/en\/wp-json\/wp\/v2\/tags?post=68112"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}