为什么RegAsm在某些方法签名上添加“W”?

时间:2014-11-03 13:31:49

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

我正在使用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:如何抑制这种“改动”?

2 个答案:

答案 0 :(得分:2)

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

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

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

  • 在用C或C ++编写时,选择另一个名称,减少客户端代码中的意外事故。
  • 在#include之后为winapi标头添加#undef GetUserName,以便禁用此宏。根据需要重复。

答案 1 :(得分:1)

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

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

由于FooMethodA是原始文本替换,因此会影响所有内容。包括在您的类中声明的恰好共享Windows API名称的方法,GetSystemDirectoryGetUserName

要解决此问题,请FooMethodW GetSystemDirectory和GetUserName。如果您需要使用这些方法,请明确调用真实姓名#define#undef