【问题标题】:Why RegAsm add 'W' on some method signatures?为什么 RegAsm 在某些方法签名上添加“W”?
【发布时间】:2014-12-30 04:17:45
【问题描述】:

我正在编写一个带有 COM Interop 的 C++/CLI 项目,以便为 VBA 客户端公开方法和对象。

到目前为止一切都很好,但我遇到了一个奇怪的问题:某些方法以(不需要的)“W”后缀(例如GetUserNameW)导出!

让我详细解释一下……

我有一个这样写的实用程序类:

MyUtils.h

#pragma once

namespace MyApp {

    [System::Runtime::InteropServices::ComVisible(true)]
    [System::Runtime::InteropServices::Guid("...guid...")]
    [System::Runtime::InteropServices::InterfaceType(System::Runtime::InteropServices::ComInterfaceType::InterfaceIsDual)]
    public interface class IMyUtils
    {
        System::String^ CombinePaths(System::String^ path1, System::String^ path2);
        System::String^ GetDocumentsDirectory();
        System::String^ GetMachineName();
        System::String^ GetSystemDirectory();
        System::String^ GetUserName();
    };

    [System::Runtime::InteropServices::ComVisible(true)]
    [System::Runtime::InteropServices::Guid("...another guid...")]
    [System::Runtime::InteropServices::ClassInterface(System::Runtime::InteropServices::ClassInterfaceType::None)]
    public ref class MyUtils : IMyUtils
    {
    public:
        MyUtils();

        virtual System::String^ CombinePaths(System::String^ path1, System::String^ path2);

        virtual System::String^ GetDocumentsDirectory();

        virtual System::String^ GetMachineName();

        virtual System::String^ GetSystemDirectory();

        virtual System::String^ GetUserName();
    };
}

MyUtils.cpp

#include "stdafx.h"
#include "MyUtils.h"

using namespace System;
using namespace System::Data;
using namespace System::IO;
using namespace System::Runtime::InteropServices;

namespace MyApp
{
    MyUtils::MyUtils()
    {
        // Do nothing...
    }

    String^ MyUtils::CombinePaths(String^ path1, String^ path2)
    {
        return Path::Combine(path1, path2);
    }

    String^ MyUtils::GetDocumentsDirectory()
    {
        return Environment::GetFolderPath(Environment::SpecialFolder::MyDocuments);
    }

    String^ MyUtils::GetMachineName()
    {
        return Environment::MachineName;
    }

    String^ MyUtils::GetSystemDirectory()
    {
        return Environment::SystemDirectory;
    }

    String^ MyUtils::GetUserName()
    {
        return Environment::UserName;
    }
}

这里没什么难的。

然后,我创建 TLB 并使用此命令行注册程序集:

regasm MyLib.dll /codebase /tlb

现在,当我尝试在 VBA 客户端上使用该对象时,我有这个:

Dim utils As New MWUtils

utils.CombinePaths "C:\Windows", "System32"
utils.GetDocumentsDirectory
utils.GetMachineName
utils.GetSystemDirectoryW '<== What?
utils.GetUserNameW '<== Again?

注意GetSystemDirectoryGetUserName 方法上的“W”后缀!

中间问题:它来自哪里?

好的,查看生成的接口IDL:

[
  uuid(...guid...),
  dual,
  oleautomation
]
interface IMyUtils : IDispatch {
    [id(0x60020000)] 
    HRESULT CombinePaths(
        [in] BSTR path1,
        [in] BSTR path2,
        [out, retval] BSTR* pRetVal
    );
    [id(0x60020001)] 
    HRESULT GetDocumentsDirectory([out, retval] BSTR* pRetVal);
    [id(0x60020002)] 
    HRESULT GetMachineName([out, retval] BSTR* pRetVal);
    [id(0x60020003)] 
    HRESULT GetSystemDirectoryW([out, retval] BSTR* pRetVal);
    [id(0x60020004)] 
    HRESULT GetUserNameW([out, retval] BSTR* pRetVal);

};

现在很明显,RegAsm 工具已经改变了方法的签名,但是为什么

'W'后缀表示一个Unicode字符串,但我还是第一次看到这种改动!

最后一个问题 #1:为什么 RegAsm 表示这两种方法的 Unicode 字符串?

最后一个问题 #2:我怎样才能抑制这种“改变”?

【问题讨论】:

    标签: .net visual-studio-2010 com c++-cli com-interop


    【解决方案1】:

    您正在使用 Windows winapi 也在使用的标识符。像 GetUserName() 和 GetSystemDirectory()。这些 winapi 函数有两个版本,W 版本采用 Unicode 字符串,A 版本采用传统 8 位字符串。为了实现这一点,如果定义了 UNICODE 宏,SDK 标头包含 macros 以将 GetUserName() 重命名为 GetUserNameW()。

    宏的标准问题是它们没有选择性,每个名为“GetUserName”的标识符都会被重命名。包括你的。

    解决这个问题的两种基本方法:

    • 选择另一个名称,这样使用 C 或 C++ 编写的客户端代码中的事故也会更少。
    • 在你的#include 之后添加#undef GetUserName 用于winapi 标头,以便禁用此宏。根据需要重复。

    【讨论】:

    • 非常微妙。 C# 中不存在的另一个 C++/CLI 陷阱。所以,我在包含 stdafx.h 之后添加 #undef GetUserName#undef GetSystemDirectory,但在包含 MyUtils.h 之前添加它,它可以工作!非常感谢!
    【解决方案2】:

    修改程序集的不是 RegAsm,而是首先以这种方式编译的。

    如果您查看 Windows.h,您会发现每个方法都有两个可以将字符串作为参数的签名:FooMethodA 采用 char*FooMethodW 采用 @987654326 @。 ('A' 和 'W' 代表 ASCII 和 Widechar。)但是,您不需要输入尾随的 'A' 或 'W',只需输入方法的名称:这是通过 #define 完成的。对于每个采用字符串的方法,FooMethod#defineed 到 FooMethodAFooMethodW

    由于#define 是原始文本替换,它会影响一切。包括在你的类中声明的方法,这些方法恰好共享 Windows API 的名称,GetSystemDirectoryGetUserName

    要解决这个问题,#undef GetSystemDirectory 和 GetUserName。如果您需要使用这些方法,请显式调用真实姓名,GetSystemDriectoryWGetUserNameW

    【讨论】:

    • 'A' 用于 ANSI 而不是 ASCII。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-13
    • 1970-01-01
    • 2012-04-10
    • 2014-09-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多