I want to call a COM method which expects a BSTR array and separate parameter specifying the size of the array, it then populates the array. Will the following work properly - it compiles but I want to be sure about the &* since I know sys-strings are generally waiting to trip me up at every opportunity!
vector<BSTR> strings(5);
BSTR *pStrings = &*strings.begin();
pComInterface->method(strings.size(),pStrings);
Assuming the COM interface method receives a std::size_t (or equivalent) and a BSTR*, you probably should use std::vector::data() instead of dereferencing an iterator:
pComInterface->method(strings.size(), strings.data());
Related
Here is my problem:
std::shared_ptr<ULARGE_INTEGER> freeBytesAvailable = std::shared_ptr<ULARGE_INTEGER>(new ULARGE_INTEGER);
std::shared_ptr<ULARGE_INTEGER> totalNumberOfBytes = std::shared_ptr<ULARGE_INTEGER>(new ULARGE_INTEGER);
std::shared_ptr<ULARGE_INTEGER> totalNumberOfFreeBytes = std::shared_ptr<ULARGE_INTEGER>(new ULARGE_INTEGER);
ULARGE_INTEGER *test = new ULARGE_INTEGER;
ULARGE_INTEGER *test2 = new ULARGE_INTEGER;
ULARGE_INTEGER *test3 = new ULARGE_INTEGER;
if (GetDiskFreeSpaceEx(NULL, test3, test2, test))
{
//do something
}
as you can see, I want to call GetDiskFreeSpaceEx, and as a parameter it expects a PULARGE_INTEGER, so a pointer to a ULARGE_INTEGER
If I am using test, test2 and test3 as parameter it works totally fine.
However, I want to learn about smart pointers and therefor try to include some in my code and use them.
Everytime I try to pass my smart pointers, I get a conversion error, so something has to be wrong with the data type, but I just can't figure out the problem.
Shouldn't both be pointing to a ULARGE_INTEGER?
Can someone help? My guess would be that I would have to kind of unpack the raw pointer out of the smart pointer wrapper, but I'm not sure how to do so.
Shared pointers are really just objects encapsulating raw pointers. Hence, you can pass shared pointers as function parameters only if the called function accepts a shared pointer as a parameter! You cannot pass a raw pointer in lieu of a shared pointer and vice versa. However, there are instances where you may want to pass the underlying raw pointer as an argument to a function call. This is typically the case when you have to make a C- library call. In such cases, just extract the underlying raw pointer using the get() method on the shared pointer object.
So in your case, the call to GetDiskFreeSpaceEx should be:
GetDiskFreeSpaceEx(NULL,freeBytesAvailable.get(), totalNumberOfBytes.get(), totalNumberOfFreeBytes.get() )
that is assuming that the function takes these parameters in this order.
I found a solution, so I will write it down for anyone looking for a quick solution as well:
.get()
will return the raw pointer, so if you are trying to pass a smart pointer to a function expecting a pointer, just add it and you are good to go.
I have a managed C++ wrapper class for a non-managed C library. I came across an issue where it seems that the pointer I am sending from my managed C++ class is not pointing to the same memory location which is used by the non-managed C code library method.
MyNonManagedType* dataPointer;
getDataFromNonmanagedCLibrary(dataPointer);
// this gives me junk data, where field should be a char array
String^ myFieldValue = gcnew String(dataPointer->field);
Is it possible that the dataPointer is not pointing to the same address used by the C library? Or maybe there is some kind of marshal method I need to use for this, or other pitfalls I may be missing?
If the pointer is not managed, given the code you have shown, there is no way for a C function to do anything with the uninitialized pointer except either:
Check it for NULL, and if so, do nothing with it, or
Use the address passed, and disaster happens.
You are passing the pointer by value, and passing by value means that the function will be using a local copy of the parameter that is passed, thus you see no changes when the function returns. The function cannot set the pointer and have those changes reflect back to the caller the way it stands now.
Since we're talking about a C interface, you should change the C interface to this:
void getDataFromNonmanagedCLibrary(MyNonManagedType**);
A pointer to the pointer is passed. Since we want to change the value passed to the function and have it reflect back to the caller, a pointer to the value is passed. Since the value just happens to be a pointer, we pass a pointer to the pointer.
Then you rewrite getDataFromNonmanagedCLibrary to initialize the pointer by dereferencing it:
void getDataFromNonmanagedCLibrary(MyNonManagedType** ptr)
{
*ptr = <the_address_you_expected_on_return>;
}
Then on the client side:
MyNonManagedType* dataPointer;
getDataFromNonmanagedCLibrary(&dataPointer);
Note that the address of the pointer is passed, no different than if you wanted to have a function change a non-pointer variable by passing the variable's address.
I have a "const char* str" with a very long string.
I need to pass it from a cpp client to a .Net COM method which expects BSTR type.
Currently I use:
CComBSTR bstr = str;
This has the following issues:
Sometimes this line fails with out of memory message
When I pass the bstr to the COM class it takes a lot of memory (much more than the string size) so it can fail with out of memory
Questions:
Am I converting to CComBSTR wisely? e.g. is there a way to use the heap or something
Is it better to use BSTR instead?
Any other suggestion is also welcomed...
If a method is expecting a BSTR passing a BSTR is the only correct way.
To convert char* to a BSTR you use MultiByteToWideChar() Win32 API function for conversion and SysAllocStringLen() for memory allocation. You can't get around that - you need SysAllocStringLen() for memory allocation because otherwise the COM server will fail if it calls SysStringLen().
When you use CComBSTR and assign a char* to it the same sequence is run - ATL is available as headers and you can enjoy reading it to see how it works. So in fact CComBSTR does exactly the minimal set of necessary actions.
When you pass a BSTR to a COM server CComBSTR::operator BSTR() const is called that simply returns a pointer to the wrapped BSTR - the BSTR body is not copied. Anything that happens next is up to the COM server or the interop being used - they decide for themselves whether they want to copy the BSTR body or just read it directly.
Your only bet for resolving the memory outages is to change the COM interface so that it accepts some reader and requests the data in chunks through that reader.
Is it an In-Process COM server do you have the code for it or is it a 3rd party? because you can pass the actual char* pointer to the COM server and not pay the price of allocate+copy+free. You will need to add a new method/property that will be available only to C++ clients.
Instead of passing BSTR you can wrap your char* in a Stream interface, the .NET server should get a Stream instead of a string.
On the C++ side implement a COM class that support the IStream COM interface, this class is a read only stream which wraps the char*, you can pass this class as UCOMIStream interface to the .NET server.
On the .NET side use the UCOMIStream methods to read the string, be careful not to read the entire stream in one pass.
A CComBSTR is a wrapper around a BSTR which in turn is counted Unicode string with special termination.
You would thus expect it to be about twice the size of the corresponding char* form (for character sets that mainly use single-byte characters).
Using a CComBSTR is a good idea in general, since the destructor will free the memory associated with the BSTR for you.
With a COM interface method declared as this:
[ object,
uuid(....),
]
interface IFoo : IUnknown
{
HRESULT Foo([in, out] CACLSID * items);
}
With regards to marshalling, is the server allowed to reallocate the counted array? (I think it is, but I am not sure anymore)
Its current implementation only replaces the existing ID's, but I'd like to implement a change (that would not break contract) that may return more items without introducing a new interface.
[edit] please note that CACLSID is already an array, containing a count and a pointer.
I have not done COM for a very long time but is it even possible to allocate a new array? In that case should it not be CACLSID ** items ?
You should give the Count as the second parameter which indicates the space for so many number of elements, using this COM library marshals the elements
First, if you want Foo to accept an in array, you should add a paramter that gives the count, e.g.:
HRESULT Foo([in] int cItems, [in, out, size_is(cItems)] CACLSID * items);
Warning: this code has not been compiled, just going off documentation.
Secondly, you cannot modify the extern behavior of this method without changing its declaration. To support resizing, you need to be able to reallocate the array and pass back its address. You can use a SAFEARRAY or declare cItems and items as pointers to the original type Foo takes, e.g.:
HRESULT FooMutate([in, out] int *cItems, [in, out, size_is(*cItems)] CACLSID **items);
Again, not compiled, so you will actually have to know what you're doing if you use this.
I have a BSTR object that I would like to convert to copy to a wchar__t object. The tricky thing is the length of the BSTR object could be anywhere from a few kilobytes to a few hundred kilobytes. Is there an efficient way of copying the data across? I know I could just declare a wchar_t array and alway allocate the maximum possible data it would ever need to hold. However, this would mean allocating hundreds of kilobytes of data for something that potentially might only require a few kilobytes. Any suggestions?
First, you might not actually have to do anything at all, if all you need to do is read the contents. A BSTR type is a pointer to a null-terminated wchar_t array already. In fact, if you check the headers, you will find that BSTR is essentially defined as:
typedef BSTR wchar_t*;
So, the compiler can't distinguish between them, even though they have different semantics.
There is are two important caveat.
BSTRs are supposed to be immutable. You should never change the contents of a BSTR after it has been initialized. If you "change it", you have to create a new one assign the new pointer and release the old one (if you own it).
[UPDATE: this is not true; sorry! You can modify BSTRs in place; I very rarely have had the need.]
BSTRs are allowed to contain embedded null characters, whereas traditional C/C++ strings are not.
If you have a fair amount of control of the source of the BSTR, and can guarantee that the BSTR does not have embedded NULLs, you can read from the BSTR as if it was a wchar_t and use conventional string methods (wcscpy, etc) to access it. If not, your life gets harder. You will have to always manipulate your data as either more BSTRs, or as a dynamically-allocated array of wchar_t. Most string-related functions will not work correctly.
Let's assume you control your data, or don't worry about NULLs. Let's assume also that you really need to make a copy and can't just read the existing BSTR directly. In that case, you can do something like this:
UINT length = SysStringLen(myBstr); // Ask COM for the size of the BSTR
wchar_t *myString = new wchar_t[length+1]; // Note: SysStringLen doesn't
// include the space needed for the NULL
wcscpy(myString, myBstr); // Or your favorite safer string function
// ...
delete myString; // Done
If you are using class wrappers for your BSTR, the wrapper should have a way to call SysStringLen() for you. For example:
CComBString use .Length();
_bstr_t use .length();
UPDATE: This is a good article on the subject by someone far more knowledgeable than me:
"Eric [Lippert]'s Complete Guide To BSTR Semantics"
UPDATE: Replaced strcpy() with wcscpy() in the example.
BSTR objects contain a length prefix, so finding out the length is cheap. Find out the length, allocate a new array big enough to hold the result, process into that, and remember to free it when you're done.
There is never any need for conversion. A BSTR pointer points to the first character of the string and it is null-terminated. The length is stored before the first character in memory. BSTRs are always Unicode (UTF-16/UCS-2). There was at one stage something called an 'ANSI BSTR' - there are some references in legacy APIs - but you can ignore these in current development.
This means you can pass a BSTR safely to any function expecting a wchar_t.
In Visual Studio 2008 you may get a compiler error, because BSTR is defined as a pointer to unsigned short, while wchar_t is a native type. You can either cast or turn off wchar_t compliance with /Zc:wchar_t.
One thing to keep in mind is that BSTR strings can, and often do, contain embedded nulls. A null does not mean the end of the string.
Use ATL, and CStringT then you can just use the assignment operator. Or you can use the USES_CONVERSION macros, these use heap alloc, so you will be sure that you won't leak memory.