我正在使用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?
注意GetSystemDirectory
和GetUserName
方法的'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:如何抑制这种“改动”?
答案 0 :(得分:2)
您正在使用Windows winapi也在使用的标识符。像GetUserName()和GetSystemDirectory()。这些winapi函数有两个版本,W版本采用Unicode字符串,A版本采用传统的8位字符串。要使其工作,SDK标头包含宏,如果定义了UNICODE宏,则将GetUserName()重命名为GetUserNameW()。
宏的标准问题是它们没有选择性,将重命名名为“GetUserName”的每个标识符。包括你的。
解决此问题的两种基本方法:
#undef GetUserName
,以便禁用此宏。根据需要重复。答案 1 :(得分:1)
RegAsm不是修改你的程序集,它首先是以这种方式编译的。
如果查看Windows.h,您会看到每个方法都有两个签名,可以将字符串作为参数:FooMethodA
,其中char*
和{{1}需要FooMethodW
。 ('A'和'W'代表ASCII和Widechar。)但是,您不需要输入尾随的'A'或'W',只需要输入方法的名称:这是通过{{1}完成的}。对于采用字符串的每个方法,wchar_t*
#define
为FooMethod
或#define
。
由于FooMethodA
是原始文本替换,因此会影响所有内容。包括在您的类中声明的恰好共享Windows API名称的方法,GetSystemDirectory和GetUserName。
要解决此问题,请FooMethodW
GetSystemDirectory和GetUserName。如果您需要使用这些方法,请明确调用真实姓名#define
和#undef
。