使用 C# 自动更新 NAV






4.83/5 (30投票s)
本文包含一个 DLL,其中包含用作 C# Windows 应用程序中参考的示例代码,用于将 FOB 导入 Navision。
引言
此示例应用程序在 C# 中作为库开发,项目名称为“ImportFOB”。DLL “ImportFOB.dll” 可用于 C# Windows 应用程序,在不打开 Navision Object Designer 的情况下将 Microsoft Dynamics Navision FOBS 导入 Navision。此示例 DLL 使用 Microsoft 运行时 COM Interop 服务。此 DLL 包含可用于将 FOB 导入和导出到 Navision 的函数。创建的示例 Windows 应用程序用于演示导入功能,而无需打开和使用 Navision Object Designer。
我已将“ImportFOB”开发为一个库项目,可以在任何项目中作为参考使用,从而在不打开 Navision Object Designer 的情况下导入和编译 Navision FOBS。
为了演示目的,我提供了一个在 C# 中开发的示例 Windows 应用程序,提供了一个用户界面,任何人都可以使用该界面将以下 FOB 类型导入 Microsoft Dynamics Navision。
- CodeUnit
- 表单
- 报表
我们可以使用“ImportFOB.dll”为 Microsoft Dynamics Navision 开发自己的版本管理系统。
此 ImportFOB DLL 可以连接到 Navision Native 数据库或 Navision Native 数据库服务器。
场景 1
如果 Navision 未作为数据库服务器安装,并且您正在 PC 上使用 Navision 进行此示例应用程序的测试,那么运行 Navision 客户端并打开一个示例数据库是必要的。在这种情况下,示例应用程序将自动连接到数据库并将所需的 FOB 导入 Navision。
场景 2
如果 Navision 已作为数据库服务器安装,则无需打开 Navision 客户端。唯一的要求是 Navision 数据库服务器必须正在运行。我们将在**步骤 1** 中创建 **DLL**,并在**步骤 2** 中创建示例应用程序。示例应用程序将使用 DLL 作为参考。
让我们开始...
步骤 1:创建“ImportFOB.dll”
创建一个类型为 Class Library 的 C# 项目,命名空间为 IMPORTFOB.NAV
。假设我们已将解决方案命名为“ImportFOB”。
步骤 A
首先,我们将创建“IObjectDesigner
”接口,如下所示
namespace IMPORTFOB.NAV
{
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
[ComImport, Guid("50000004-0000-1000-0001-0000836BD2D2"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IObjectDesigner
{
[PreserveSig, DispId(1)]
int ReadObject([In] int objectType, [In] int objectId, [In] IStream destination);
[PreserveSig, DispId(2)]
int ReadObjects([In] string filter, [In] IStream destination);
[PreserveSig, DispId(3)]
int WriteObjects([In] IStream source);
[PreserveSig, DispId(4)]
int CompileObject([In] int objectType, [In] int objectId);
[PreserveSig, DispId(5)]
int CompileObjects([In] string filter);
[PreserveSig, DispId(6)]
int GetServerName(out string serverName);
[PreserveSig, DispId(7)]
int GetDatabaseName(out string databaseName);
[PreserveSig, DispId(8)]
int GetServerType(out int serverType);
[PreserveSig, DispId(9)]
int GetCSIDEVersion(out string csideVersion);
[PreserveSig, DispId(10)]
int GetApplicationVersion(out string applicationVersion);
[PreserveSig, DispId(11)]
int GetCompanyName(out string companyName);
}
}
上面的代码清楚地说明了 COM Interop 服务的使用。在注册表中,它显示了十四个方法。大多数方法都非常简单,只是返回一个字符串,因此实现起来非常容易。例如
Public string GetDatabaseName ()
{
string databaseName;
int num = this._objectDesigner.GetDatabaseName(out databaseName);
return databaseName;
}
步骤 B
现在,我们将创建 StreamSupport
类,它在 .NET 中处理它
namespace IMPORTFOB.NAV
{
using System;
using System.IO;
using System.Runtime.InteropServices.ComTypes;
public class StreamSupport
{
public static unsafe IStream ToIStream(Stream stream,
ref IStream comStream)
{
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
uint num = 0;
IntPtr pcbWritten = new IntPtr((void*) &num);
comStream.Write(buffer, buffer.Length, pcbWritten);
return comStream;
}
public static unsafe MemoryStream ToMemoryStream(IStream comStream)
{
MemoryStream stream = new MemoryStream();
byte[] pv = new byte[100];
uint num = 0;
IntPtr pcbRead = new IntPtr((void*) &num);
do
{
num = 0;
comStream.Read(pv, pv.Length, pcbRead);
stream.Write(pv, 0, (int) num);
}
while (num > 0);
return stream;
}
}
}
上面的代码指定了简单的流处理函数,用于读取和写入。
步骤 C
现在,我们将创建 ObjectDesigner
类,如下所示
namespace IMPORTFOB.NAV
{
using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Security;
public class ObjectDesigner
{
private IObjectDesigner _objectDesigner;
private const string DefaultMonikerName = "!C/SIDE";
private const string ObjectDesignerGuid = "50000004-0000-1000-0001-0000836BD2D2";
public ObjectDesigner()
{
Guid guid = new Guid("50000004-0000-1000-0001-0000836BD2D2");
Hashtable runningObjects = GetActiveObjectList(DefaultMonikerName);
foreach (DictionaryEntry de in runningObjects)
{
string progId = de.Key.ToString();
if (progId.IndexOf("{") != -1)
{
// Convert a class id into a friendly prog Id
progId = ConvertClassIdToProgId(de.Key.ToString());
}
object getObj = GetActiveObject(progId);
if (getObj != null)
{
this._objectDesigner = getObj as IObjectDesigner;
if (this._objectDesigner == null)
{
throw new Exception("Could not connect to Dynamics NAV");
}
}
}
}
public void CompileObject(NavObjectType navObjectType, int objectId)
{
int result = this._objectDesigner.CompileObject((int) navObjectType,
objectId);
this.ProcessResult(result);
}
public void CompileObjects(string filter)
{
int result = this._objectDesigner.CompileObjects(filter);
this.ProcessResult(result);
}
[DllImport("ole32.dll")]
public static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
[DllImport("OLE32.DLL")]
public static extern int CreateStreamOnHGlobal(int hGlobalMemHandle,
bool fDeleteOnRelease, out IStream pOutStm);
public string GetCompanyName()
{
string companyName;
int num = this._objectDesigner.GetCompanyName(out companyName);
return companyName;
}
public string GetDatabaseName()
{
string databaseName;
int num = this._objectDesigner.GetDatabaseName(out databaseName);
return databaseName;
}
public static Hashtable GetActiveObjectList(string filter)
{
Hashtable result = new Hashtable();
IntPtr numFetched = IntPtr.Zero;
IRunningObjectTable runningObjectTable;
IEnumMoniker monikerEnumerator;
IMoniker[] monikers = new IMoniker[1];
GetRunningObjectTable(0, out runningObjectTable);
runningObjectTable.EnumRunning(out monikerEnumerator);
monikerEnumerator.Reset();
Console.WriteLine("Select client:");
int clientNo = 0;
while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
{
IBindCtx ctx;
CreateBindCtx(0, out ctx);
string runningObjectName;
monikers[0].GetDisplayName(ctx, null, out runningObjectName);
System.Guid classId;
monikers[0].GetClassID(out classId);
object runningObjectVal;
runningObjectTable.GetObject(monikers[0], out runningObjectVal);
if (runningObjectName.IndexOf(filter) != -1 &&
runningObjectName.IndexOf("company") != -1)
{
clientNo += 1;
result[runningObjectName] = runningObjectVal;
}
}
return result;
}
[DllImport("oleaut32.dll", CharSet=CharSet.Unicode)]
internal static extern int GetErrorInfo(int dwReserved,
[MarshalAs(UnmanagedType.Interface)] out IErrorInfo ppIErrorInfo);
public static object GetObjectFromRot(string monikerName, Guid guid)
{
IRunningObjectTable prot;
IEnumMoniker ppenumMoniker;
IntPtr pceltFetched = IntPtr.Zero;
IMoniker[] rgelt = new IMoniker[1];
object ppvResult = null;
GetRunningObjectTable(0, out prot);
prot.EnumRunning(out ppenumMoniker);
ppenumMoniker.Reset();
while (ppenumMoniker.Next(1, rgelt, pceltFetched) == 0)
{
IBindCtx ppbc;
string ppszDisplayName;
CreateBindCtx(0, out ppbc);
rgelt[0].GetDisplayName(ppbc, null, out ppszDisplayName);
if (!(!ppszDisplayName.StartsWith(monikerName) ||
ppszDisplayName.Equals(monikerName)))
{
rgelt[0].BindToObject(ppbc, null, ref guid, out ppvResult);
return ppvResult;
}
}
return ppvResult;
}
public static object GetActiveObject(string progId)
{
// Convert the prog id into a class id
string classId = ConvertProgIdToClassId(progId);
IRunningObjectTable prot = null;
IEnumMoniker pMonkEnum = null;
try
{
IntPtr Fetched = IntPtr.Zero;
// Open the running objects table.
GetRunningObjectTable(0, out prot);
prot.EnumRunning(out pMonkEnum);
pMonkEnum.Reset();
IMoniker[] pmon = new IMoniker[1];
// Iterate through the results
while (pMonkEnum.Next(1, pmon, Fetched) == 0)
{
IBindCtx pCtx;
CreateBindCtx(0, out pCtx);
string displayName;
pmon[0].GetDisplayName(pCtx, null, out displayName);
Marshal.ReleaseComObject(pCtx);
if (displayName.IndexOf(classId) != -1)
{
// Return the matching object
object objReturnObject;
prot.GetObject(pmon[0], out objReturnObject);
return objReturnObject;
}
}
return null;
}
finally
{
// Free resources
if (prot != null)
Marshal.ReleaseComObject(prot);
if (pMonkEnum != null)
Marshal.ReleaseComObject(pMonkEnum);
}
}
public static Hashtable GetRunningObjectTable()
{
IRunningObjectTable prot;
IEnumMoniker ppenumMoniker;
IntPtr pceltFetched = IntPtr.Zero;
Hashtable hashtable = new Hashtable();
IMoniker[] rgelt = new IMoniker[1];
GetRunningObjectTable(0, out prot);
prot.EnumRunning(out ppenumMoniker);
ppenumMoniker.Reset();
while (ppenumMoniker.Next(1, rgelt, pceltFetched) == 0)
{
IBindCtx ppbc;
string ppszDisplayName;
object ppunkObject;
CreateBindCtx(0, out ppbc);
rgelt[0].GetDisplayName(ppbc, null, out ppszDisplayName);
prot.GetObject(rgelt[0], out ppunkObject);
hashtable[ppszDisplayName] = ppunkObject;
}
return hashtable;
}
[DllImport("ole32.dll", PreserveSig = false)]
private static extern void CLSIDFromProgIDEx(
[MarshalAs(UnmanagedType.LPWStr)] string progId, out Guid clsid);
[DllImport("ole32.dll", PreserveSig = false)]
private static extern void CLSIDFromProgID(
[MarshalAs(UnmanagedType.LPWStr)] string progId, out Guid clsid);
public static string ConvertProgIdToClassId(string progID)
{
Guid testGuid;
try
{
CLSIDFromProgIDEx(progID, out testGuid);
}
catch
{
try
{
CLSIDFromProgID(progID, out testGuid);
}
catch
{
return progID;
}
}
return testGuid.ToString().ToUpper();
}
[DllImport("ole32.dll")]
private static extern int ProgIDFromCLSID([In()]ref Guid clsid,
[MarshalAs(UnmanagedType.LPWStr)]out string lplpszProgID);
public static string ConvertClassIdToProgId(string classID)
{
Guid testGuid = new Guid(classID.Replace("!", ""));
string progId = null;
try
{
ProgIDFromCLSID(ref testGuid, out progId);
}
catch (Exception)
{
return null;
}
return progId;
}
[DllImport("ole32.dll")]
public static extern void GetRunningObjectTable(int reserved,
out IRunningObjectTable prot);
public string GetServerName()
{
string serverName;
int num = this._objectDesigner.GetServerName(out serverName);
if (serverName != null)
return serverName;
else
return string.Empty;
}
private void ProcessResult(int result)
{
if (result != 0)
{
IErrorInfo ppIErrorInfo = null;
GetErrorInfo(0, out ppIErrorInfo);
string pBstrDescription = string.Empty;
if (ppIErrorInfo != null)
{
ppIErrorInfo.GetDescription(out pBstrDescription);
}
string message = string.Format(CultureInfo.CurrentCulture,
"Method returned an error. HRESULT = 0x{0:X8}",
new object[] { result });
if (pBstrDescription != string.Empty)
{
message = message + " : " + pBstrDescription;
}
throw new Exception(message);
}
}
public MemoryStream ReadObjectToStream(NavObjectType navObjectType,
int objectId)
{
IStream pOutStm = null;
CreateStreamOnHGlobal(0, true, out pOutStm);
int result = this._objectDesigner.ReadObject((int) navObjectType,
objectId, pOutStm);
this.ProcessResult(result);
return this.ToMemoryStream(pOutStm);
}
private unsafe IStream ToIStream(Stream stream)
{
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
uint num = 0;
IntPtr pcbWritten = new IntPtr((void*) &num);
IStream pOutStm = null;
CreateStreamOnHGlobal(0, true, out pOutStm);
pOutStm.Write(buffer, buffer.Length, pcbWritten);
pOutStm.Seek((long) 0, 0, IntPtr.Zero);
return pOutStm;
}
private unsafe MemoryStream ToMemoryStream(IStream comStream)
{
MemoryStream stream = new MemoryStream();
byte[] pv = new byte[100];
uint num = 0;
IntPtr pcbRead = new IntPtr((void*) &num);
comStream.Seek((long) 0, 0, IntPtr.Zero);
do
{
num = 0;
comStream.Read(pv, pv.Length, pcbRead);
stream.Write(pv, 0, (int) num);
}
while (num > 0);
return stream;
}
public void WriteObjectFromStream(Stream stream)
{
IStream source = this.ToIStream(stream);
int result = this._objectDesigner.WriteObjects(source);
this.ProcessResult(result);
}
[ComImport, SuppressUnmanagedCodeSecurity, Guid(
"1CF2B120-547D-101B-8E65-08002B2BD119"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IErrorInfo
{
[PreserveSig]
int GetGUID();
[PreserveSig]
int GetSource([MarshalAs(UnmanagedType.BStr)] out string pBstrSource);
[PreserveSig]
int GetDescription(
[MarshalAs(UnmanagedType.BStr)] out string pBstrDescription);
}
}
}
在上面的类中,创建了各种函数来获取运行 CSIDE 对象的连接。建立连接后,它会获取 Microsoft Dynamics Navision Object Designer。创建 CompileObject
函数以编译导入的 Navision FOB。
步骤 D
现在,我们将声明一个枚举,其中包含需要导入的 Navision 对象类型的列表。
Namespace IMPORTFOB.NAV
{
using System;
public enum NavObjectType
{
Codeunit = 5,
Dataport = 4,
Form = 2,
Report = 3,
Table = 1
}
}
我们已经完成了“ImportFob”类库的创建。现在我们将构建解决方案。
步骤 2:创建将调用“ImportFOB.dll”的示例应用程序
让我们将示例应用程序命名为FobNameID。创建一个 C# Windows 项目FobNameID。将命名空间命名为“FobNameID
”。
步骤 A
将“ImportFOB.dll”的引用添加到此项目。右键单击“解决方案资源管理器”中的引用,然后选择添加引用。浏览“ImportFOB.dll”的路径并将其添加到FobNameID项目中。
添加 DLL 后,声明以下内容
using IMPORTFOB.NAV;
步骤 B
将 Program
类重命名为 FOBImport
,并创建具有三个参数的 ImportFobtoNav
函数,如下所示
String FobName, int objecttype, int objid
创建“ImportFobtoNav
”函数,如下所示
public void ImportFobtoNav(String FobName, int objecttype, int objid)
{
ObjectDesigner nav = new ObjectDesigner();
MemoryStream memStream = new MemoryStream();
// Create the data to write to the stream.
FileStream fs = File.OpenRead(FobName);
byte[] firstString = new byte[fs.Length];
fs.Read(firstString, 0, firstString.Length);
memStream.Write(firstString, 0, firstString.Length);
memStream.Seek(0, SeekOrigin.Begin);
nav.WriteObjectFromStream(memStream);
if ((objecttype) == 2)
{
nav.CompileObject(NavObjectType.Form, objid);
}
if ((objecttype) == 5)
{
nav.CompileObject(NavObjectType.Codeunit, objid);
}
if ((objecttype) == 3)
{
nav.CompileObject(NavObjectType.Report, objid);
}
fs.Close();
memStream.Close();
}
此函数接受三个参数:FOB 名称、对象类型(CodeUnit、Form、Report)和对象 ID。在函数内部,ObjectDesigner
的实例被创建为 nav
,以便从“ImportFOB.DLL”调用 WriteObjectFromStream
方法。
还从ImportFOB.DLL调用 CompileObject
方法,以便在导入 FOB 后在 Navision 中编译它。流处理用于读取和写入 FOB。
注意:要将 FOBS 导入 Navision,要求 FOBS 必须位于示例 Windows 应用程序的Debug文件夹中。
步骤 C
创建一个表单“ImportFOB
”。在表单中创建以下控件
- 标题为FOB Name的
Label
控件和名为txtfobname
的Text
控件。 - 标题为Object ID的
Label
控件和名为txtobjid
的Text
控件。 - 标题为Object Type的
Label
控件和名为lstobjtype
的ListBox
控件。 - CodeUnit
- 表单
- 报表
- 标题为Import的
Button
控件,名为btnImport
。 - 标题为Close的
Button
控件,名为btnclose
。
在ListBox
控件中创建以下项目
步骤 D
在Import按钮的Click
事件中编写以下代码
private void btnImport_Click(object sender, EventArgs e)
{
FobImport fobimp = new FobImport();
try
{
if (lstobjtype.Text == "Codeunit")
{
fobimp.ImportFobtoNav(txtfobname.Text + ".txt", 5,
Convert.ToInt32(txtobjid.Text));
}
if (lstobjtype.Text == "Form")
{
fobimp.ImportFobtoNav(txtfobname.Text + ".txt", 2,
Convert.ToInt32(txtobjid.Text));
}
if (lstobjtype.Text == "Report")
{
fobimp.ImportFobtoNav(txtfobname.Text + ".txt", 3,
Convert.ToInt32(txtobjid.Text));
}
MessageBox.Show("Fob Imported Successfully");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
创建此 Windows 应用程序中创建的 FOBImport
类的实例。请注意,“Lstobjtype
”是一个 ListBox
,其Item
为 CodeUnit、Form 和 Report。根据“ImportFOB.dll”中对象的枚举值,我们调用“ImportFobNav
”函数将 FOB 导入 Navision。ImportFobNav
函数在**步骤 2**的**步骤 B**中创建。
最后,运行它,尽情享受吧!
运行示例 Windows 应用程序“FobNameID”。输入 FOB 名称、对象类型和对象 ID。请注意,FOB 名称应为位于 Windows 应用程序“FobNameID”的Debug目录中的 FOB 文件名。
历史
- 1.0.0.0
- 第一个发行版本。