【问题标题】:How do I instantiate a Scripting.Dictionary using CoCreateInstance?如何使用 CoCreateInstance 实例化 Scripting.Dictionary?
【发布时间】:2016-09-03 13:43:15
【问题描述】:

我一直在阅读 COM 和 VBScript,现在我很好奇是否可以使用 CoCreateInstance 函数从 C++ 创建一个 Scripting.Dictionary 对象。

我想要完成的是这样的:

script.vbs

' reminder: run with "cscript script.vbs"
Option Explicit

Dim myDictionary

Set myDictionary = CreateObject("Scripting.Dictionary")

myDictionary.Add "Foo", "Bar"

MsgBox myDictionary.Item("Foo")

我实际上并不了解实例化 COM 对象的过程是如何工作的,但据我了解,它看起来应该是这样的:

ma​​in.cpp

/* reminder: use Unicode */
#include <Windows.h>
#include <stdio.h>

/**
 * Takes a guid and dumps it out to stdin as a string.
 * @param guid guid to dump to stdin.
 */
void DumpGUID(GUID guid)
{

    char dumpData[81];
    sprintf_s (
        &dumpData[0], 
        81,
        "{"
            "\"Data1\": \"%u\","
            "\"Data2\": \"%u\","
            "\"Data3\": \"%u\","
            "\"Data4\": \"%u\""
        "}", 
        guid.Data1, 
        guid.Data2, 
        guid.Data3, 
        guid.Data4
    );
   puts(dumpData);
}

int main(int argc, char **argv)
{
    GUID guid;
    const wchar_t *ScriptingDictionaryGUID = L"EE09B103-97E0-11CF-978F-00A02463E06F";

    void *myDictionary = NULL;

    /* anonymous scope: set guid to 0 so I can tell if it has changed */     
    {
        memset(&guid, 0, sizeof(guid));    
    }     

    DumpGUID(guid);

    /* anonymous scope: convert my string to guid */
    {
        HRESULT whyNoConvert = CLSIDFromString((LPCOLESTR)ScriptingDictionaryGUID, &guid);
        if (FAILED(whyNoConvert)) {
            printf("can't convert string to guid, got error %d.\n", whyNoConvert);

            /* ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680589(v=vs.85).aspx */
            switch (whyNoConvert) {
            case NOERROR:
                puts("The CLSID was obtained successfully.");
                break;
            case CO_E_CLASSSTRING:
                puts("The class string was improperly formatted.");
                break;
            case REGDB_E_CLASSNOTREG:
                puts("The CLSID corresponding to the class string was not found in the registry.");
                break;
            case REGDB_E_READREGDB:
                puts("The registry could not be opened for reading.");
                break;
            }
            system("pause");
        }
    }
    DumpGUID(guid);

    HRESULT instantiationResult = CoCreateInstance (
        guid,
        NULL,
        CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER,
        IID_IDispatch,
        &myDictionary
    );
    DumpGUID(guid);

    if (FAILED(instantiationResult)) {
        puts("Can't create a dictionary :(");
    } else {
        puts("I HAVE DONE IT!");
    }

    system("pause");
    return 0;
}

我的代码无法创建字典对象(但可以编译)... 它也无法从我的字符串实际转换为 GUID,并出现错误“类字符串格式不正确。”。

我不知道我做错了什么(或者我做错了什么)。

编辑:工作代码(仍然需要弄清楚创建对象后如何与对象交互)

#ifndef UNICODE
    #define UNICODE
#endif
#ifndef NOMINMAX
    #define NOMINMAX
#endif
#ifndef STRICT
    #define STRICT
#endif
#pragma comment(lib, "uuid.lib")
#pragma comment(lib, "ole32.lib")

#include <windows.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    GUID dictionary_guid = {0xEE09B103, 0x97E0, 0x11CF, {0x97, 0x8F, 0x00, 0xA0, 0x24, 0x63, 0xE0, 0x6F}};
    IDispatch* p_dictionary = nullptr;

    /* anonymous scope: initializing COM */
    {
        HRESULT result = CoInitialize(NULL);
        if (!SUCCEEDED(result)) {
            printf("CoInitialize failed: %u.\n", result);
            system("pause");
            exit(0);
        }
    }

    /* anonymous scope: creating instance and checking */
    {
        HRESULT result = CoCreateInstance (
            dictionary_guid,
            nullptr,
            CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER,
            IID_IDispatch,
            reinterpret_cast<void**>(&p_dictionary)
        );
        if (!SUCCEEDED(result)) {
            puts("CoCreateInstance failed");
            system("pause");
            exit(0);
        } 

        puts ("I HAVE DONE IT!");
        printf("HRESULT = %u.\n", result);
        printf("p_dictionary: %u.\n", p_dictionary);
    }
    p_dictionary->Release(); 
    CoUninitialize();   

    system("pause");
    return EXIT_FAILURE;
}

提前谢谢!

【问题讨论】:

  • 首先你要使用Unicode字符串。
  • 我做了更多检查,它说我的 GUID 格式不正确,不知道如何处理。
  • 改用CLSIDFromProgID()CLSIDFromProgID(L"Scripting.Dictionary", &amp;guid);
  • 字典的属性和方法是documented。使用IDispatch.Invoke() 访问它们。

标签: c++ winapi com


【解决方案1】:

如果您使用 Visual Studio,有一个很酷的 #import directive 可以在 Type Library 可用时生成简化 COM 的包装器。

大多数情况下,类型库包含在 COM 对象本身(为您所追求的对象提供服务的 DLL)或 DLL 旁边的 .TLB 中。这是一个简单的示例,其中包含位于 C:\Windows\System32\scrrun.dll 中的脚本对象:

#include "stdafx.h"

// import the .TLB that's compiled in scrrun.dll
#import "C:\Windows\System32\scrrun.dll" \
    rename("FreeSpace", "FreeSpace2") // avoid name collision with Windows SDK's GetFreeSpace macro, this is specific to scrrun.dll

using namespace Scripting;

// sample usage of Scripting.Dictionary
void CreateDicAndAdd()
{
    IDictionaryPtr ptr(__uuidof(Dictionary)); // create an instance of the Dictionary coclass and get an IDictionary pointer back
    _variant_t foo = L"foo";
    _variant_t bar = L"bar";
    ptr->Add(&foo, &bar); // call the Add method

    wprintf(L"%i\n", ptr->Count); // call the Count property (wrapper that was generated automatically)

    _variant_t outBar;
    ptr->get_Item(&foo, &outBar); // get the item for "foo"

    // here we know it's a string (outBar.vt could tell you, in case you didn't know)
    // in fact, it's a BSTR, but a BSTR is also a LPWSTR
    // (the reverse is false, welcome to Automation :-)
    wprintf(L"%s\n", outBar.bstrVal);
}

// sample driver code.
int main()
{
    CoInitialize(NULL);
    CreateDicAndAdd();
    CoUninitialize();
    return 0;
}

很酷的是所有这些包装器(_variant_t、IDictionaryPtr 等)都很智能,这意味着您不必显式地释放或处置它们。最后,它非常类似于使用高级语言(VBScript、JScript、C# 等)对 COM 进行编程

【讨论】:

  • 感谢您的回复。到目前为止,我有点设法创建字典并插入项目(我认为?),但我不知道如何从地图中“获取”项目。我尝试使用 get_Item,这似乎失败了,我尝试使用 Items 来获取项目,但我不知道如何遍历它们。你能证明通过键 foo 从字典中取出 bar 吗?这是我走了多远:hastebin.com/icidixopif.coffee(如您所见,我显然对此很陌生)。
  • nvm 我明白了:O hastebin.com/ezujoyakun.coffee。虽然我希望在没有包装的情况下完成这项工作。
  • 知道如何解决此问题以使其正常工作吗? hastebin.com/dexewajuwi.coffee 和你的类似,但我正在尝试更明确地做到这一点。
  • hastebin.com/mokayeriko.coffee 版本有效,除了最后一个变体使用 _variant_t 而不是 VARIANT 之外,它的作用完全相同。我怎样才能消除它?
  • 好的,我消除了它。我认为问题在于 get_Item 具有条件行为,无论 bStr 是否为 NULL,以及 vt 是否为 0。除非 VARIANT 被清零,或者 VARIANT 中的内存可通过 SysAllocString 获得,否则它将尝试写入该内存并导致内存冲突。 hastebin.com/idovesasox.coffee
【解决方案2】:

此代码适用于我:

#undef UNICODE
#define UNICODE
#undef NOMINMAX
#define NOMINMAX
#undef STRICT
#define STRICT
#include <windows.h>

#include <iostream>
#include <stdexcept>
#include <string>

auto fail( std::string const& s )
    -> bool
{ throw std::runtime_error( s ); }

struct Succeeded {};

auto operator>>( HRESULT const hr, Succeeded )
    -> bool
{ return SUCCEEDED( hr ); }     // A macro from <windows.h>.

struct Com_library
{
    ~Com_library()
    { CoUninitialize(); }

    Com_library()
    {
        CoInitialize( 0 )
            >> Succeeded() || fail( "CoInitialize failed" );
    }
};

namespace sys {
    using String = std::wstring;
    auto& out = std::wcout;
    auto& err = std::wcerr;
}  // namespace sys

void cppmain()
{
    Com_library const using_Com;

    GUID const dictionary_guid =
    {0xEE09B103, 0x97E0, 0x11CF, {0x97, 0x8F, 0x00, 0xA0, 0x24, 0x63, 0xE0, 0x6F}};

    IDispatch* p_dictionary = nullptr;
    CoCreateInstance (
        dictionary_guid,
        nullptr,
        CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER,
        IID_IDispatch,
        reinterpret_cast<void**>( &p_dictionary )
        )
        >> Succeeded() || fail( "CoCreateInstance failed" );

    sys::out << "I HAVE DONE IT!\n";
    /* figure out how to use the dictionary handle */

    p_dictionary->Release();        // You want to automate this, e.g. smart pointer.
}

auto main()
    -> int
{
    using std::exception;
    try
    {
        cppmain();
        return EXIT_SUCCESS;
    }
    catch( exception const& x )
    {
        sys::err << x.what() << "\n";
    }
    return EXIT_FAILURE;
}

用 g++ 编译:

g++ com_example.cpp -lole32 -luuid

用 Visual C++ 编译:

cl com_example.cpp ole32.lib uuid.lib

重要的是要记住通过调用CoInitialize 来初始化COM。另外,请注意,直接使用 IDispatch 是 C++ 中的 PITA(在脚本语言中非常简单,但在 C++ 中不好用),所以如果字典类支持 双接口您最好查询普通的更 C++ 友好的界面。或者使用一些 3rd 方的 C++ IDispatch 包装器,这样的包装器可能存在(因为我曾经做过一个,我猜其他人也可能有,并将其放在网上)。

【讨论】:

  • hastebin.com/ahuvihifoz.vbs 在 Visual Studio 上,hastebin.com/axefipagap.tex 在 mingw 虽然我怀疑这是因为没有 -std=c++11(auto)。添加-std=c++11后:hastebin.com/gonusaloxi.tex
  • @Dmitry:是的,这显然是 C++11 之前的编译器行为。我使用了 g++ 5.1.0 和 Visual C++ 2015 更新 2。
  • 请记住,我使用的是 Visual Studio 2012,因此必须在一些非常新的功能上稍微轻松​​一点(它仍然支持像自动这样的东西,它也不适合我编译,因为更新功能或配置错误)
  • Uno momento,让我启动 Visual Studio 2012。
  • 使用 String = std::wstring 注释掉;它编译。
猜你喜欢
  • 2011-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-22
  • 2019-08-23
  • 2013-12-19
  • 1970-01-01
相关资源
最近更新 更多