我有一个使用WriteProfileString
的旧版VB 6应用程序,该应用程序仅用于与16位版本的Windows兼容。
我正在将其迁移到功能等价物(这意味着一个完全克隆) .NET应用程序。
这需要我使用TlbImp.exe从WriteProfileString
COM对象的TLB文件生成Interop程序集。 这是一项要求,不能使用P / Invoke。
当我这样做时,TlbImp.exe会生成一个空的程序集。
WriteProfileString
具有以下IDL:
// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: WriteProfileString.tlb
[
uuid(13C9AF40-856A-101B-B9C2-04021C007003),
version(0.0),
helpstring("WritePrivateProfileStringW API Type Library")
]
library igrWriteProfileStringW
{
// TLib : // Forward declare all types defined in this typelib
[
dllname("KERNEL32"),
helpstring("KERNEL API Calls")
]
module KernelAPI {
[entry("WritePrivateProfileStringW"), helpstring("Sets the value of a .ini file setting.")]
long _stdcall WritePrivateProfileStringW(
[in] BSTR lpApplicationName,
[in] BSTR lpKeyName,
[in] BSTR lpString,
[in] BSTR lpFileName);
};
};
幸运的是,Microsoft已经开源了名为TlbImp2.exe的下一版TlbImp.exe。
我能够通过代码进行调试,发现TlbImp.exe和TlbImp2.exe都不会从模块导入方法。我不得不破解代码以使TlbImp2.exe导出模块的静态方法。
我不得不更改ConvModule.cs文件:
public override void OnCreate()
{
//
// Avoid duplicate creation
//
if (m_type != null)
return;
// Create constant fields for the module
ConvCommon.CreateConstantFields(m_info, RefNonAliasedTypeInfo, m_typeBuilder, ConvType.Module);
ConvCommon.CreateMethodsForModule(m_info, RefNonAliasedTypeInfo, m_typeBuilder, ConvType.Module);
m_type = m_typeBuilder.CreateType();
}
并将以下方法添加到ConvCommon.cs:
public static void CreateMethodsForModule(ConverterInfo info, TypeInfo type, TypeBuilder typeBuilder, ConvType convType)
{
using (TypeAttr attr = type.GetTypeAttr())
{
int cVars = attr.cFuncs;
for (int n = 0; n < cVars; ++n)
{
using (var func = type.GetFuncDesc(n))
{
string methodName = type.GetDocumentation(func.memid);
CreateMethod(new InterfaceInfo(info, typeBuilder, true, type, attr, false), new InterfaceMemberInfo(info, type, n, methodName, methodName, InterfaceMemberType.Method, TypeLibTypes.Interop.INVOKEKIND.INVOKE_FUNC, func.memid, func, null), CreateMethodMode.InterfaceMethodMode);
}
}
}
}
所以现在它正确地导出方法,但我仍然想知道它为什么不首先导出这些方法。
这是导出的程序集的代码:
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace igrWriteProfileStringW
{
public static class KernelAPI
{
[DispId(1610612736)]
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public abstract int WritePrivateProfileStringW([MarshalAs(UnmanagedType.BStr), In] string lpApplicationName, [MarshalAs(UnmanagedType.BStr), In] string lpKeyName, [MarshalAs(UnmanagedType.BStr), In] string lpString, [MarshalAs(UnmanagedType.BStr), In] string lpFileName);
}
}
它生成一个带有抽象成员的静态类。这没有任何意义。当然我可以更改代码以不同方式导出它,但为什么TlbImp2.exe首先会这样做?
我假设它以这种方式导出它,因为它导出了模块的常量。我对吗?
我应该对WriteProfileString
方法应用哪些修饰符,以确保它可以与互操作一起使用?
答案 0 :(得分:1)
直接使用本机api更容易,而不是跳过所有COM箍..
来自pinovke.net:
[DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool WritePrivateProfileString(string lpAppName,
string lpKeyName, string lpString, string lpFileName);
答案 1 :(得分:0)
类型库支持Tlbimp.exe支持的一组超级声明。不经常使用从DLL声明导出的能力,类型库是导出COM声明的首要工具。
你无法用Tlbimp来解决这个问题。您也不应该尝试解决它,因为替代方案非常简单。只需写一个pinvoke declaration。
考虑到下一步,Read / WritePrivateProfileString()由于Windows中内置了大量的遗留appcompat而具有非常糟糕的运行时行为。读取单个INI参数的成本约为50毫秒。阅读其中的20个并且你没有做任何非常有用的事情,从而浪费了你的启动时间。它非常有损,它完全不受文本文件编码的影响。您通常不能使用库中的“设置”的内置.NET支持,但XML文件是一个很好的替代品。