Working with Authentication Server
For customers who have a licensed version of sbo objects, you will need to go through Authentication server for credentials. you will then communicate with the sbo objects using this _Database (sample connection below) connection.
- private DatabaseConnection _Database;
- public DatabaseConnection Database
- {
- get
- {
- if (_Database == null)
- {
- _Database = new DatabaseConnection("http://localhost/AuthenticationService.asmx");
- _Database.UserCredentials.DatabaseType = DatabaseEngineType.SqlServer;
- _Database.UserCredentials.Server = "yourservernamehere";
- _Database.UserCredentials.Database = "your database VMFG here";
- _Database.UserCredentials.UserID = "your program UserId here";
- _Database.UserCredentials.Password = "your program UserId password here";
- _Database.UserCredentials.ConnectionStringAppend = "Trusted_Connection=False;Encrypt=False;";
- }
- if (!_Database.Connected) _Database.Connect();
- if (!_Database.Connected) MessageBox.Show(this,"No Connection","Database Connection", MessageBoxButtons.OK);
- return _Database;
- }
- }
A call would look similar to:
- CUSTOMER_ORDER CustOrder = new CUSTOMER_ORDER(Database)
There are times when, as a developer you may want to run some additional code from Infor Visual ERP. This can be done by running a .exe or opening a .dll (which is not registered in the GAC) and executing code from the .dll.
You can pass primary key information from these windows to a third-party external program:
|
|
Part Maintenance
Purchase Management Window
Shop Resource Maintenance
|
Customer Order Entry
Order Management Window
Purchase Order Entry
Requisition Entry
Vendor Maintenance
|
For example, if you are working in Customer Maintenance, you can set up a button on the user
toolbar that opens your custom program and sends it the Customer ID from the current customer
record. The information is sent by attaching comma-delimited keys to the command line that launches
your application. In this example, this is attached to the command line:
VM800,SYSADM,@@@@@,FLEET,,,,,,,,,
VM800 in the database name, SYSADM is the user name, the @@@@@ is the user’s encrypted
password, and FLEET is the customer ID.
The entire command line is enclosed in single quotes.
You can set up your custom application to parse the information in the command line and extract
additional information from the database.
In order to test if the module you are going to run a .exe can pass more than just the database,user id, user password you can try the following code in a sample program:
- public frmMyWinForm()
- {
- InitializeComponent();
- //
- // Arguments passed in from Infor Visual ERP
- //
- string[] Args = Environment.GetCommandLineArgs();
- if (Args.Count<string>() > 1)
- {
- // When the Visual ERP Flag Use Key in Cmd Line we will have our database/user id/password
- // Some visual erp modules can pass more information, this is a test to see what can be parsed out of the args[1] field.
- string UserInfo = Args[1].Replace("'","");
- string[] Connection = UserInfo.Split(',');
- _Database = Connection[0];
- _User = Connection[1];
- _Password = Connection[2];
- }
- StringBuilder MyArgs = new StringBuilder();
- foreach (string arg in Args)
- {
- MyArgs.AppendLine(arg);
- }
- MessageBox.Show(this, MyArgs.ToString());
- }
Macro Interface
<see Creating a callable library>
Working with WM-Synergy Macro Server
One of the first things you will need to add to your project is a Connected Service to the MacroServer web api.
Make sure that your project has legacy .csproj, not SDK type. As I discovered, on SDK based project Visual Studio will not support legacy WebServices, ever if they were created before (e.g. no possibility to use "Update Web Reference" functionality by right click).
Right click on a project:

Type in the url of the macro server in the Address bar:
The standard default settings are fine, as for most applications running as a task-based operation gives good results.
Press 'OK'
In your project, you should now see a folder that looks similar to the following:
Great, you have now created your client side connection to the SRI Macro Server. The following class may help you with integration for making calls to the Macro Server, this code is attached to this article as a boiler plate for your use.
Creating a callable library
When creating a library, (.dll), module the important thing to know is that there are 2 required Public Properties that must be a part of the class file.
- [ComVixible(true)]
- public class MyClass
- {
- #region MacroServer
- [ComVixible(true)]
- public string ServerURL { get; set; } //Url to ServerLib of MacroServer
- [ComVixible(true)]
- public string Key { get; set; } // Encrypted Database Key
- internal MacroServer MacroServer
- {
- get
- {
- if (_MacroServer == null)
- {
- _MacroServer = new MacroServer(ServerURL, Key);
- }
- return _MacroServer;
- }
- }
- #endregion
A typical WM-Synergy macro call would look like the following:
- dim obj
- lib.ActivateObject "obj", "MyFamousibrary.dll", "MyFamouslibrary.MyClass"
- set obj = lib.GetObject("obj")
- obj.Key = util.authenticationHash
- obj.ServerURL = util.ServerURL & "/ServerLib.asmx"
- obj.DatabaseType = "SQLServer"
- obj.MyMethod
It will be important to store your library in the working directory for the Macro Server Web API.
Create a .dll for a Macro
A Visual ERP Macro must be made as a COM compliant .dll. In order to do this, the developer should set the following at the project level:
The next step is to set some compile directives on your base class for the .dll
It is suggested to avoid a Constructor as it may cause issues when loading the .dll (you should validate if it is an issue with your design)
- [ComVisible(true)] // Expose the class
- [ClassInterface(ClassInterfaceType.None)] // Select None, as we want to specify what is to be exposed.
- [Guid("5b37be8d-293c-46ba-9503-a4ac961b7101")] // not required would auto pull from project guid
- [ProgId("CreatePPGSRequests.CreatePPGSRequests")] // suggested, should match with how it would be evoked.
- public class CreatePPGSRequests
- {
- #region Private Variables
- private MacroServer _MacroServer = null;
- private DatabaseConnection _Database = null;
- #endregion
- #region MacroServer Required Properties
- [ComVisible(true)] // Place these here to ensure the program is making visible to com call those items you want visible
- public string Key { get; set; } // Key for Authenticator
- [ComVisible(true)]
- public string ServerURL { get; set; } // MacroServer Authenticator
- [ComVisible(true)]
- public string DatabaseType { get; set; } // SQLServer vs Oracle
- internal MacroServer MacroServer
- {
- get
- {
- if (_MacroServer == null)
- {
- _MacroServer = new MacroServer(ServerURL, Key);
- }
- return _MacroServer;
- }
- }
- internal string ServerXMLURL
- {
- get
- {
- return string.Format("{0}/Credentials/{1}.xml", Regex.Replace(ServerURL, "/Serverlib.asmx", "", RegexOptions.IgnoreCase), Key);
- }
- }
- #endregion
For each public method, make sure to add the [ComVisible(true)], this ensures that this method will be available to the vbscript.
These extra steps help to ensure that when the project Make project com visible, is actually making visible those objects that you as a developer need exposed to the calling vbscript code base.
- #region Public Methods
- /// <summary>
- /// <c>ProcessWorkOrder</c>
- /// <para>Create the PPGS OrderImport requests, and move inentory to a temporary holding area,
- /// until the PPGS has completed all the pulls for the given Pick.</para>
- /// </summary>
- /// <param name="SiteId"></param>
- /// <param name="WorkOrder"></param>
- /// <returns>true if success</returns>
- [ComVisible(true)]
- public bool ProcessWorkOrder(string SiteId, string WorkOrder, string UserId)
- { ...
Sample Macro Call
When creating an SRI style macro for use with Visual ERP, make sure your .dll can be found from a directory withing Macro Server Web API directory.
- Dim Obj
- Lib.ActivateObject "obj","YourSubDirectory/MyFamousDll.dll","MyFamousDll.Client" // Note the direction of the '/' as this is going through MacroServerer API.
- set Obj = lib.GetObject ( "obj" ) // Instantiate the Reflection into your .dll method
- Obj.Key = util.authenticationHash // Your .dll must have a propert for Key
- Obj.ServerUrl = util.ServerURL // Your .dll must have a property for ServerUrl
- Obj.LoadForm ORDER_ID
.dll Code Template for your Visual ERP Macro method call
- #region MacroServer Required Properties
- public string Key { get; set; } // Key for Authenticator
- public string ServerURL { get; set; } // MacroServer Authenticator
- #endregion
If using sbo objects with your .dll then use the following code snippet to connect sbo objects
- using Synergy.BusinessObjects;
- using Synergy.BusinessObjects.VE;
- .....
- private DataBaseConnection _Database = null;
- public bool Client()
- {
- _Database = new DatabaseConnection();
- _Database.UserCredentials.MacroServerXmlUrl = $"{ServerURL.Replace("/Serverlib.asmx", "")}/Credentials/{Key}.xml";
- _Database.Connect();
- using (SqlCommand Cmd = (SqlCommand)_Database.CreateNewCommand)
- {
- Cmd.CommandText = $@"SELECT DBVERSION FROM APPLICATION_GLOBAL";
- if (Convert.ToString(SqlCmd.ExecuteScalar()) != "9.0.8")
- {
- MessageBox.Show(this,"This code only works with Visual ERP 9.0.8","Connect to Server");
- return false;
- }
- // Sample of reading data records
- using (IDbCommand Cmd = _Database.CreateNewCommand)
- {
- Cmd.CommandText = "SELECT PART_ID from dbo.PART";
- using (IDataReader Reader = cmd.ExecuteReader())
- {
- while (Reader.Read())
- {
- string PartId = Reader["PART_ID"].ToString();
- ...
Interface with Database Server
There are two methods that can be used when your application needs to connect to the database
- Connect through MacroServer
- Connect through Synergy BusinessObjects
MacroServer
Given a connection to the Macro Server, try the following:
- string SqlCmd = $@"
- SELECT CODE, DESCRIPTION
- FROM {TablePrefix}.SRI_TAX_ENTITY_USE_CODES
- ";
- SRIMacroServer.ExecuteQueryResults Results = MacroServer.ExecuteQuery(SqlCmd);
SynergyBusinessObjects
Given a MacroServer connection one can try the following:
- using Synergy.BusinessObjects;
- using Synergy.BusinessObjects.VE;
- namespace YourWonderfulProgram
- {
- public class MyClass
- {
- #region Private Variables
- private DatabaseConnection _Database =null;
- #endregion
- #region MacroServer Required Properties
- public string Key { get; set; } // Key for Authenticator
- public string ServerURL { get; set; } // MacroServer Authenticator
- public string DatabaseType { get; set; } // SQLServer vs Oracle
- internal MacroServer MacroServer
- {
- get
- {
- if (_MacroServer == null)
- {
- _MacroServer = new MacroServer(ServerURL, Key);
- }
- return _MacroServer;
- }
- }
- internal string ServerXMLURL
- {
- get
- {
- return string.Format("{0}/Credentials/{1}.xml", Regex.Replace(ServerURL, "/Serverlib.asmx", "", RegexOptions.IgnoreCase), Key);
- }
- }
- #endregion
- public void myMethod()
{ - _Database = new DatabaseConnection();
- _Database.UserCredentials.MacroServerXmlUrl = ServerXMLURL;
- _Database.UserCredentials.ConnectionStringAppend = "TrustServerCertificate=True";
- _Database.Connect();
- _Database.Transaction = _Database.Connection.BeginTransaction();
- StringBuilder SqlCmd = new StringBuilder();
- SqlCmd.Append("SELECT TOP(1) [TempWarehouse], [TempLocation], [AutoProcess], [AutoEmail] FROM dbo.[SRI_TravelerConfig]");
- IDbCommand GetCommand = _Database.CreateNewCommand;
- GetCommand.CommandText = SqlCmd.ToString();
- IDataReader RecReader = GetCommand.ExecuteReader();
Reflection method of a .dll using SBO
When you are creating a .dll that will be reflected into, it will be important to use the assembly redirect method, to make sure your nuget package references are used. It is also important to include the nuget packages in your project reference area.
Failure to include all libraries in your project references (use nuget packages for best results) will mean your project .dll will not know about any of the required .dll's.
From your class constructor add the following line:
- //
- // We have nuget packages which require binding redirects to load in order for this code to run properly
- //
- AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
in the class add the following static method (you will need to modify/edit for your requirements) this is a sample for an idea only. Again below is a sample only and needs to be modified to fit YOUR code requirements. This will typically make sure that no matter what version of the sbo objects .dll's nuget package you use, the code will use the .dll as found in the runtime code of where you have placed all of the .dll's required. (not only for your project use, but sbo objects as well)
- /// <summary>
- /// <c>CurrentDomain_AssemblyResolve</c> will Dynamically resolve assemblies at runtime
- ///
- /// <para>In the Constructor, before InitializeComponent(), perform an override of the AssemblyResolve
- /// <code>
- /// AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
- /// // Define the libraries that the code needs to load (this will act as a Binding Redirect)
- /// Assembly.Load("Avalara.AvaTax.RestClient.dll");
- /// Assembly.Load("Newtonsoft.Json.dll");
- /// InitializeComponet();
- /// ...
- /// </code></para>
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- /// <returns></returns>
- /// <exception cref="NotImplementedException"></exception>
- private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
- {
- string DllLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); // AppDomain.CurrentDomain.BaseDirectory;
- string AssemblyName = new AssemblyName(args.Name).Name;
- string DllPath = Path.Combine(DllLocation, AssemblyName);
- try
- {
- //Original LMS Plugin Synergy resource handler
- //ignore missing resources
- if (args.Name.Contains(".resources")) return null;
- Assembly Assy = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
- if (Assy != null) return Assy;
- //load by file name from the Plugin Directory
- string FileName = args.Name.Split(',')[0] + ".dll";
- string PluginDirectory = Assembly.GetExecutingAssembly().CodeBase;
- PluginDirectory = PluginDirectory.Remove(PluginDirectory.LastIndexOf("/"));
- string AsmFile = Path.Combine(PluginDirectory, FileName);
- try
- {
- return Assembly.LoadFrom(AsmFile);
- }
- catch { }
- } catch { }
- // Add the following to specifically pull the defined .dll's
- //if (Redirects.ContainsKey(args.Name))
- //{
- // DllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Redirects[args.Name]);
- // return Assembly.LoadFrom(DllPath);
- //}
- // When this dll looks for either PathGuide or NewtonSoft use what is found in the current directory.
- if (AssemblyName.StartsWith("Microsoft.SqlServer") || AssemblyName.StartsWith("NewtonSoft") || AssemblyName.StartsWith("Oracle"))
- {
- AssemblyName = $"{AssemblyName}.dll";
- //
- //
- //MessageBox.Show($"Import Path: {DllLocation}\n\rName: {AssemblyName}\n\rFull Path: {DllPath}\n\rImport Server: {ImportFile}", "BindingRedirect", MessageBoxButtons.OK);
- //
- // Setup the paths to import the file from, and save in..
- //
- DllPath = Path.Combine(DllLocation, AssemblyName);
- try
- {
- if (File.Exists(DllPath))
- return Assembly.LoadFrom(DllPath);
- }
- catch (Exception )
- {
- }
- }
- #if DEBUG
- else
- {
- LogWriter Log = new LogWriter($"Looking for...Path: {DllLocation}\n\rName: {AssemblyName}\n\rFull Path: {DllPath}");
- }
- #endif
- return null;
- }
When Synergy compiles a .dll to be used by an .exe program we add the following to the post command of the project to make sure we are placing all of the required .dll's for the given project into the .exe runtime. (sample xcopy below)
(visual studio project look for Post-build and add the following)
Post-build event command line:
XCOPY "$(TargetDir)*.*" "$(SolutionDir)ReleaseKit\bin\" /Y
Errors
A connection was successfully established with the server, but then an error occurred during the login process. (provider: SSL Provider, error: 0 - The certificate chain was issued by an authority that is not trusted.)
If your SQL Server is using a self-signed certificate, it won't be trusted by default. You can either disable encryption or trust the self-signed certificate.
Solution: Add TrustServerCertificate=True to your connection string.
Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;TrustServerCertificate=True;
If the certificate is from a non-trusted authority, you need to add it to the Trusted Root Certification Authorities store on the client machine.
Solution: Export the server certificate and install it on the client machine2.
Export the server certificate using MMC.
Import it into the Trusted Root Certification Authorities store on the client machine.
As a quick fix, you can disable encryption in your connection string, but this is not recommended for production environments.
Solution: Add Encrypt=False to your connection string3.
Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;Encrypt=False;
By following these steps, you can resolve the SSL provider error and establish a secure connection to your SQL Server.