导入静态COM模块,如WriteProfileString

时间:2013-08-11 11:05:50

标签: c# com vb6 com-interop tlbimp

我有一个使用WriteProfileString的旧版VB 6应用程序,该应用程序仅用于与16位版本的Windows兼容。

我正在将其迁移到功能等价物(这意味着一个完全克隆) .NET应用程序。 这需要我使用TlbImp.exeWriteProfileString 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方法应用哪些修饰符,以确保它可以与互操作一起使用?

2 个答案:

答案 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文件是一个很好的替代品。