2014-11-03 63 views
0

我正在用COM Interop写一个C++/CLI项目来公开VBA客户端的方法和对象。为什么RegAsm在某些方法签名上添加'W'?

到目前为止都很好,但是我遇到了一个奇怪的问题:某些方法会导出一个(不需要的)'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

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

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

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

  • 选取其他的名称,在客户端代码少出事故那种方式,以及当它是用C或C++。
  • 将#include添加到winapi头文件后添加#undef GetUserName,以便禁用此宏。必要时重复。
+0

非常微妙。 C#中不存在的另一个C++/CLI陷阱。因此,在添加stdafx.h之后,我添加了'#undef GetUserName'和'#undef GetSystemDirectory',但之前添加了MyUtils.h并且它可以正常工作!非常感谢你! – 2014-11-03 14:17:04

1

它不是RegAsm修改你的程序集,它首先被编译成这样。

如果您在WINDOWS.H看,你会发现有一个为每个可以采取一个字符串作为参数法两个签名:FooMethodA,需要一个char*,并FooMethodW,需要一个wchar_t*。 ('A'和'W'代表ASCII和Widechar。)但是,您不需要键入尾部'A'或'W',只需输入方法的名称即可:这是通过#define完成的。对于采用字符串的每种方法,FooMethod#define编辑为FooMethodAFooMethodW

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

为了解决这个问题,#undef GetSystemDirectory和GetUserName。如果您需要使用这些方法,请明确拨打实际名称,GetSystemDriectoryWGetUserNameW

+1

'A'用于ANSI而不是ASCII。 – 2014-11-04 01:17:43

相关问题