0

我有一个旧版 VB 6 应用程序,WriteProfileString它仅用于与 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);
    };
};

幸运的是,微软已经开源了名为 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以确保它可以与互操作一起使用?

4

2 回答 2

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);
于 2013-08-11T11:11:20.287 回答
0

类型库支持 Tlbimp.exe 支持的一组超声明。从 DLL 声明导出的功能并不经常使用,类型库首先是导出 COM 声明的工具。

Tlbimp 几乎无法解决这个问题。您也不应该尝试解决它,因为替代方案是如此简单。只需编写一个pinvoke 声明

请考虑下一步,Read/WritePrivateProfileString() 的运行时行为非常糟糕,因为 Windows 内置了沉重的遗留 appcompat。读取单个 INI 参数大约需要 50 毫秒。阅读其中的 20 篇,你已经浪费了你的启动时间,而没有做任何非常有用的事情。而且它非常有损,它完全与文本文件编码无关。您通常不能在库中使用对设置的内置 .NET 支持,但 XML 文件是一个很好的替代品。

于 2013-08-11T11:13:24.280 回答