I have a need for a COM object that generates a GUID. I am a C# developer, but this will be deployed in a unix environment, so I'm thinking I need to build it in C++. This is my first Visual C++ project, and I'm having some trouble finishing it out.
Steps I've taken:
Created a new ATL project in Visual Studio (Dynamic-link library - no other options)
Right-click project -> Add class -> ATL Simple Object (Short name: GuidGenerator; ProgID: InfaGuidGenerator)
View -> ClassView -> IGuidGenerator -> Add Method (Method Name: Generate; Parameter Type: BSTR* [out]; Parameter Name: retGuid)
Added Boost to get the platform independent UUID generator.
// GuidGenerator.cpp : Implementation of CGuidGenerator
#include "stdafx.h"
#include "GuidGenerator.h"
#include <boost/lexical_cast.hpp>
#include <boost/uuid/uuid.hpp> // uuid class
#include <boost/uuid/uuid_generators.hpp> // generators
#include <boost/uuid/uuid_io.hpp> // streaming operators etc.
STDMETHODIMP CGuidGenerator::Generate(BSTR* retGuid)
{
boost::uuids::uuid uuid = boost::uuids::random_generator()();
std::string uuidStr = boost::lexical_cast<std::string>(uuid);
//not really sure what to do from here.
//I've tried to convert to BSTR.
//When I assign the resulting value to retGuid, I often get an error:
//A value of type BSTR cannot be assigned to an entity of type BSTR*
return S_OK;
}
Can anyone provide me some guidance on the next step?
Thanks.
Edit from comments:
I have already tried to convert to BSTR using the following, but I get an error:
STDMETHODIMP CGuidGenerator::Generate(BSTR* retGuid)
{
boost::uuids::uuid uuid = boost::uuids::random_generator()();
std::string uuidStr = boost::lexical_cast<std::string>(uuid);
int wslen = ::MultiByteToWideChar(CP_ACP, 0 /* no flags */,
uuidStr.data(), uuidStr.length(),
NULL, 0);
BSTR wsdata = ::SysAllocStringLen(NULL, wslen);
::MultiByteToWideChar(CP_ACP, 0 /* no flags */,
uuidStr.data(), uuidStr.length(),
wsdata, wslen);
retGuid = wsdata;
//ERROR: A value of type BSTR cannot be assigned to an entity of type BSTR*
return S_OK;
}
Assuming that std::string uuidStr is your string you want to return as BSTR output parameter, consider code like this:
#include <atlbase.h> // for CComBSTR
#include <atlconv.h> // for CA2W
STDMETHODIMP CGuidGenerator::Generate(BSTR* retGuid)
{
try
{
....
// Convert uuidStr from ASCII to Unicode
CA2W wszUuid( uuidStr.c_str() );
// Build a COM BSTR from the Unicode string
CComBSTR bstrUuid( wszUuid );
// Return the BSTR as output parameter
*retGuid = bstrUuid.Detach();
// All right
return S_OK;
}
//
// Catch exceptions and convert them to HRESULTs,
// as C++ exceptions can't cross COM module boundaries.
//
catch(const CAtlException& ex)
{
return static_cast<HRESULT>(ex);
}
catch(const std::exception& ex)
{
// May log the exception message somewhere...
return E_FAIL;
}
}
Note how a RAII helper class like CA2W simplifies the conversion from ASCII to Unicode, and CComBSTR simplifies the management of raw BSTRs.
This Microsoft article on COM under Unix might help more generally.
As to the next step, a BSTR is not your usual string; .NET hides this complexity from you. A BSTR is a specialised string that's intended to be marshalled across thread/process boundaries.
Have a look at this answer to see how to convert your std::string to a BSTR.
Related
I make an OCX in C++ Builder Borland. My OCX has these function as I describe it:
Login
Write
Read
DebugOutput
GetVal
I am using this OCX in my c# application. In c# side I have a button and list box. The button calls login method from OCX and the list box shows this method's output.
In OCX side, the login method creates a command for server (with socket programming) to get authentication. Then call Write function to writes on socket. Then gets response from socket and calls Read function to reads the socket response.
The Read method reading the result and send it to DebugOutput to debug the output stream and call GetVal to find Last main server response. Then they pass their parameters to each other. after all the C# side show the result (SUCCESS | FAIL ) for login methods.
I used BSTR. (I read the topics about BSTR in stackoverflow and on MSDN but I think I did not get it well in my solution). These are my code in OCX side:
BSTR STDMETHODCALLTYPE TVImpl::Login(BSTR PassWord)
{
wchar_t wcs1[500];
Var *var=new Var();
//here make the command
...................
//get the server response to show to user
BSTR read=::SysAllocString(Write(wcs1));
if(read!=NULL) ::SysFreeString(read);
return read;
}
BSTR STDMETHODCALLTYPE TVImpl::Read()
{
BSTR str ;
try
{
IdTCPClient1->ReadTimeout=100;
str =::SysAllocString( IdTCPClient1->IOHandler->ReadLn().c_str());
}
catch(Exception &e)
{
str= e.Message.c_str();
}
str=(DebugOutput(str));
return str;
}
BSTR STDMETHODCALLTYPE TVImpl::Write(BSTR str)
{
IdTCPClient1->IOHandler->WriteLn(str) ;
BSTR str2=::SysAllocString(L"TST");
str2=Read();
return str2;
}
BSTR STDMETHODCALLTYPE TVImpl::GetVal(BSTR st,BSTR ValTyp)
{
BSTR res;
AnsiString stAnsi;
//Do some thing with st and save it to stAnsi
.................
res=(BSTR)WideString(stAnsi);
return ::SysAllocString(res);
}
BSTR STDMETHODCALLTYPE TVImpl::DebugOutput(BSTR st)
{
Var *val=new Var();
BSTR res;
res=GetVal(st,val->CMD_CMD);
if(res==val->CMD_AUTHENTICATE)
res=GetVal(st,val->XPassword);
return res;
}
In C3 code it was hanging. I know my problem is to use sysAllocString. but I when I used ::sysFreestring for each one in each method again my C# code hanging.
This is my C# code :
private void button4_Click(object sender, EventArgs e)
{
VinSockCmplt.Vin vin = new Vin();
listBox1.Items.Add(vin.Login("1234"));
}
You should not return BSTR (or any "complex" type that various compilers compile in various ways). The recommended return type for COM methods is HRESULT (a 32-bit integer). Also, you must not release a BSTR you just allocated and return it to the caller.
Here is how you could layout your methods:
HRESULT STDMETHODCALLTYPE TVImpl::Login(BSTR PassWord, /*out*/ BSTR *pRead)
{
...
*pRead = ::SysAllocString(L"blabla"); // allocate a BSTR
...
// don't SysFreeString here, the caller should do it
...
return S_OK;
}
If you were defining your interface in a .idl file, it would be something like:
interface IMyInterface : IUnknown
{
...
HRESULT Login([in] BSTR PassWord, [out, retval] BSTR *pRead);
...
}
retval is used to indicate assignment semantics are possible for languages that support it, like what you were trying to achieve initially with code like this var read = obj.Login("mypass")
There are lots of mistakes in your code:
if(read!=NULL) ::SysFreeString(read); return read; frees a string and then returns it
str= e.Message.c_str(); ... return str; returns a pointer to something that isn't even a BSTR
BSTR str2=::SysAllocString(L"TST"); str2=Read(); allocates a string and then leaks that string immediately
res=(BSTR)WideString(stAnsi); creates a dangling pointer
All of these are undefined behaviour (except the leak) and might cause a crash.
Also, I'm not sure if it is valid to have your functions return BSTR; the normal convention in COM programming is that functions return HRESULT and any "return values" go back via [out] parameters. This is compatible with all languages that have COM bindings; whereas actually returning BSTR limits your code compatibility. (I could be wrong - would welcome corrections here). You can see this technique used in the other question you linked.
To get help with a question like "Why is my code crashing?", see How to Ask and How to create a Minimal, Complete, and Verifiable example.
I have been poking around with WRL at the ABI layer for the last couple of weeks and have run into this problem.
I have an interface defined in IDL as follows:
namespace Async{
[uuid(f469e110-7ef5-41df-a237-9ddef9aed55c), version(1.0)]
interface IDownloader : IInspectable
{
HRESULT GetFeed([in] HSTRING url,[out, retval] Windows.Web.Syndication.SyndicationFeed ** feed);
[propget]HRESULT Feed([out, retval]Windows.Web.Syndication.SyndicationFeed ** feed);
}
[version(1.0), activatable(1.0)]
runtimeclass Downloader
{
[default] interface IDownloader;
}
}
Which I have defined in my header file like so:
#pragma once
#include "Async_h.h"
namespace ABI {
namespace Async {
class Downloader : public Microsoft::WRL::RuntimeClass<ABI::Async::IDownloader>
{
InspectableClass(L"Async.Downloader", BaseTrust);
public:
Downloader();
STDMETHOD(GetFeed)(HSTRING url, ABI::Windows::Web::Syndication::ISyndicationFeed ** feed);
STDMETHOD(get_Feed)(ABI::Windows::Web::Syndication::ISyndicationFeed ** feed);
private:
//Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Uri> feedUrl;
Microsoft::WRL::ComPtr<ABI::Windows::Web::Syndication::ISyndicationFeed> m_feed;
};
ActivatableClass(Downloader);
}
}
In my cpp file I implement the functions:
STDMETHODIMP Downloader::GetFeed(HSTRING url, ISyndicationFeed** feed)
{
HRESULT hr;
RoInitializeWrapper ro(RO_INIT_MULTITHREADED);
ComPtr<IUriRuntimeClass> uri;
ComPtr<IUriRuntimeClassFactory> uriFactory;
hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_Uri).Get(), &uriFactory);
hr = uriFactory->CreateUri(url, uri.GetAddressOf());
ComPtr<ISyndicationClient> client;
ComPtr<IInspectable> inspectable;
RoActivateInstance(HStringReference(RuntimeClass_Windows_Web_Syndication_SyndicationClient).Get(), &inspectable);
hr = inspectable.As(&client);
Event timerCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
auto callback = Callback<IAsyncOperationWithProgressCompletedHandler<SyndicationFeed*,RetrievalProgress>>([&](IAsyncOperationWithProgress<SyndicationFeed*,RetrievalProgress> *op, AsyncStatus status) ->HRESULT
{
auto error = GetLastError();
if (status == AsyncStatus::Completed)
{
hr = op->GetResults(m_feed.GetAddressOf());
*feed = m_feed.Get();
}
return S_OK;
});
ComPtr<IAsyncOperationWithProgress<SyndicationFeed*,RetrievalProgress>> operation;
hr = client->RetrieveFeedAsync(uri.Get(), operation.GetAddressOf());
operation->put_Completed(callback.Get());
return S_OK;
}
STDMETHODIMP Downloader::get_Feed(ISyndicationFeed** feed)
{
*feed = m_feed.Get();
return S_OK;
}
The property works as expected it is projected to c++/cx as it should be. However,in the GetFeed method, when I attempt to set the feed parameter to the retrieved feed I get an access violation. Obviously I know that the memory is bad but the way I understand COM properties, they are essentially function calls and the property method and the GetFeed method are doing exactly the same thing minus the retrieval part.
Here are my questions:
What is the difference between COM property methods and regular interface methods in terms of the projected return value if any?
Why is the parameter to the property method initialized to nullptr and the parameter to the GetFeed Method not when they are described exactly the same in IDL?
If the out parameters in property methods are initialized, what part of the COM runtime is doing that for me and is that controllable? IE is there a way to get memory that I can write to passed to me?
I know that I could probably design that away but that is not the point. I am just trying to learn how it all works.
Thanks.
In your lambda you are capturing by reference with [&]. You need to capture the feed parameter by value, since the stack frame is long gone by the time your lambda executes.
The bigger issue is that the client has no idea when they can retrieve the results since you don't provide that information. (I see you create an unused Win32 Event object, so maybe there's some other code to make that work that you've deleted).
Let me start off by saying that VB is not my strong suit.
I am developing a C++ dll to be used in a VB6 application's dll.
I have successfully instantiated the (C++) classes in VB. I am trying to access data members of the class using this syntax: "vbCppObj.dataMemberName".
I can confirm that this works for boolean and enum types and it invokes the getter methods defined in my class.
I have to access a string from the (C++) class as well.
The getter function for the string is given below:
class MyCPPClass
{
private:
WCHAR* CPPErrorString = L"This is a string";
public:
HRESULT __stdcall get_CPPErrorString(BSTR* pVal)
{
BSTR str = ::SysAllocString(CPPErrorString);
if(str)
*pVal = str;
return S_OK;
}
};
I am unable to debug the C++ dll right now.
I access this value in the VB6 code as follows:
ErrorString = vbCppObj.CPPErrorString
Logger.log "[Log]:" & ErrorString
"ErrorString" is a String type in VB. When this line executes, the "ErrorString" object shows "<Out of memory>" (when I hover over it). If I step further, to the logging code, it gives me a "Error 14: Out of string space".
Also, I have typed this code in the browser so, it may not be 100% correct.
As it turns out, I had to convert the string into a "_b_str" and then to a "BSTR". That worked for me.
I had tried it earlier but I don't know why it didn't work at that time.
Why you just don't use LPCTSTR?
I'm not an advanced C/C++ programmer, but this should work
class MyCPPClass
{
private:
LPCTSTR CPPErrorString = "This is a string";
public:
HRESULT __stdcall get_CPPErrorString(LPCTSTR * pVal)
{
// copy the value
*pVal = CPPErrorString;
// return
return S_OK;
}
}
everyone. I know Microsoft have given an example about the save method of IXMLDOMDocument like this:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd874226(v=vs.85).aspx
But when I changed the parameter of save to a variable like CString or char* instead of a constant, I got an exception in the save method like this:
"Unhandled exception in VisualADS.exe: 0xC0000005: Access Violation."
The exception position is:
#pragma implementation_key(76)
inline HRESULT MSXML2::IXMLDOMDocument::save ( const _variant_t & destination ) {
HRESULT _hr = raw_save(destination);
if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
return _hr;
}
This is a MFC MBCS project in Visual Studio 6, here's my code, thx!
CString strFilePathName = ar.GetFile()->GetFilePath();
CComBSTR ccbsFilePathName(strFilePathName);
CComVariant ccvFilePathName(ccbsFilePathName);
hr = pXMLDoc->save(ccvFilePathName);
The variant you pass to IXMLDOMDocument::save has to carry a BSTR. If your project is an UNICODE one, there is great chances that you'll have no conversions to do, and no reasons to crash using weird attempts.
If you are in a MBCS project, things are different: you have to somehow convert the file name from MBCS to UNICODE before using it in OLE/COM. I suggest doing so using the CComBSTR class
CString strFilePathName;
[...]
CComBSTR ccbsFilePathName( strFilePathName );
CComVariant ccvFilePathName( ccbsFilePathName );
hr = pXMLDoc->save( ccvFilePathName );
From book ATL Internals, I knew BSTR is different from OLECHAR*, and there are CComBSTR and CString for BSTR.
According MSDN Allocating and Releasing Memory for a BSTR, I knew memory management responsibility for caller/callee.
Take this line from MSDN,
HRESULT CMyWebBrowser::put_StatusText(BSTR bstr)
I still do not know how to handle bstr properly in my implementation. Since I still have a basic question for BSTR -- should we treat bstr as a value (like int) or as a reference (like int*), at least on COM interface boundary.
I want to convert BSTR as soon as possible to CString/CComBSTR in my implementation. Value or Reference semantic will be totally different case for the conversion. I've digged into CComBSTR.Attach, CComBSTR.AssignBSTR, etc. But the code cannot solve my doubts.
MSDN CComBSTR.Attach has some code snip, I feel it is wrong since it is not obey Allocating and Releasing Memory for a BSTR. ATL Internals said SetSysString will 'free the original BSTR passed in', if I used it, it will violate BSTR argument convention, just like CComBSTR.Attach.
All in all, I want to using CString to handle raw BSTR in implementation, but do not know the correct way...I've written some just work code in my projects, but I always feel nervous since I don't know whether I am correct.
Let me talk coding language
HRESULT CMyWebBrowser::put_StatusText(BSTR bstr)
{
// What I do NOT know
CString str1; // 1. copy bstr (with embeded NUL)
CString str2; // 2. ref bstr
// What I know
CComBSTR cbstr1;
cbstr1.AssignBSTR(bstr); // 3. copy bstr
CComBSTR cbstr2;
cbstr2.Attach(bstr); // 4. ref bstr, do not copy
// What I do NOT know
// Should we copy or ref bstr ???
}
CComBSTR is just a RAII wrapper around raw BSTR. So feel free to use CComBSTR instead of raw BSTR to help writing code that is exception-safe, that makes it harder to leak resources (i.e. the raw BSTR), etc.
If the BSTR is an input parameter, it's just like a const wchar_t* (with length prefixed, and potentially some NULs L'\0' characters inside). If the BSTR doesn't have NULs embedded inside, you can simply pass it to a CString constructor, that will make a deep-copy of it, and you can locally work with your CString. Modifications to that CString won't be visible on the original BSTR. You can use std::wstring as well (and note that std::wstring can handle embedded NULs as well).
void DoSomething(BSTR bstrInput)
{
std::wstring myString(bstrInput);
// ... work with std::wstring (or CString...) inside here
}
Instead, if the BSTR is an output parameter, then it is passed using another level of indirection, i.e. BSTR*. In this case, you can use CComBSTR::Detach() inside your method to release the BSTR safely wrapped into the CComBSTR, and transfer its ownership to the caller:
HRESULT DoSomething( BSTR* pbstrOut )
{
// Check parameter pointer
if (pbstrOut == nullptr)
return E_POINTER;
// Guard code with try-catch, since exceptions can't cross COM module boundaries.
try
{
std::wstring someString;
// ... work with std::wstring (or CString...) inside here
// Build a BSTR from the ordinary string
CComBSTR bstr(someString.c_str());
// Return to caller ("move semantics", i.e. transfer ownership
// from current CComBSTR to the caller)
*pbstrOut = bstr.Detach();
// All right
return S_OK;
}
catch(const std::exception& e)
{
// Log exception message...
return E_FAIL;
}
catch(const CAtlException& e)
{
return e; // implicit cast to HRESULT
}
}
Basically, the idea is to use BSTR (wrapped in a RAII class like CComBSTR) only at the boundary, and do the local work using std::wstring or CString.
As a "bouns reading", consider Eric Lippert's guide to BSTR semantics.
Having BSTR on input, you are not responsible to release it. Converting to CString is easy:
CString sValue(bstr);
or, if you prefer to keep Unicode characters on MBCS build:
CStringW sValue(bstr);
If you need to convert back when you have [out] parameter, you do (simple version):
VOID Foo(/*[out]*/ BSTR* psValue)
{
CString sValue;
*psValue = CComBSTR(sValue).Detach();
}
Full version is:
STDMETHODIMP Foo(/*[out]*/ BSTR* psValue)
{
_ATLTRY
{
ATLENSURE_THROW(psValue, E_POINTER); // Parameter validation
*psValue = NULL; // We're responsible to initialize this no matter what
CString sValue;
// Doing our stuff to get the string value into variable
*psValue = CComBSTR(sValue).Detach();
}
_ATLCATCH(Exception)
{
return Exception;
}
return S_OK;
}