2014-11-03 1 views
0

Я пишу проект C++/CLI с помощью COM Interop, чтобы разоблачить методы и объекты для клиента 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? 

Обратите внимание на суффикс 'W' на GetSystemDirectory и GetUserName методах!

Промежуточный вопрос: откуда он взялся?

Хорошо, посмотрите в сгенерированный 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

Вы используете идентификаторы, которые также используются winapi для Windows. Как GetUserName() и GetSystemDirectory(). Эти функции winapi существуют в двух версиях, версия W принимает строку Unicode, а версия A использует унаследованную 8-битную строку. Чтобы сделать эту работу, заголовок SDK содержит макросы, чтобы переименовать GetUserName() в GetUserNameW(), если определен макрос UNICODE.

Стандартная проблема с макросами заключается в том, что они не являются выборочными, каждый идентификатор с именем «GetUserName» будет переименован. В том числе и ваш.

два основных пути решения этой проблемы:

  • Pick другое имя, меньше несчастных случаев в коде клиента, что, как хорошо, когда он написан на C или C++.
  • Добавьте #undef GetUserName после #include для заголовков winapi, чтобы этот макрос был отключен. При необходимости повторите.
+0

Очень тонкий. Еще одна ловушка C++/CLI, которая не существует с C#. Итак, я добавляю '#undef GetUserName' и' #undef GetSystemDirectory' после включения stdafx.h, но ПЕРЕД включением MyUtils.h и он работает! Большое спасибо! –

1

Это не RegAsm, который изменяет вашу сборку, и это скомпилировано таким образом, в первую очередь.

Если вы посмотрите в Windows.h, вы увидите, что есть две подписи для каждого метода, который может принимать строку в качестве параметра: FooMethodA, который принимает char* и FooMethodW, который принимает wchar_t*. («A» и «W» означают ASCII и Widechar.) Однако вам не нужно вводить конечные «A» или «W», просто имя метода: это выполняется с помощью #define. Для каждого метода, который принимает строку, FooMethod: #define ed либо FooMethodA, либо FooMethodW.

Поскольку #define является необработанной заменой текста, это влияет на все. Включая методы, объявленные внутри вашего класса, которые делят имя Windows API, GetSystemDirectory и GetUserName.

Чтобы исправить это, #undef GetSystemDirectory и GetUserName. Если вам нужно использовать эти методы, вызовите реальные имена явно, GetSystemDriectoryW и GetUserNameW.

+1

«A» для ANSI, а не ASCII. –