I have a COM API foo, the IDL looks like:
foo([in] unsigned long ulSize, [in, size_is(ulSize)] unsigned char* pData)
when I consume this function with foo(0,NULL);
I get an error - NULL argument passed. Is there a way to workaround this?
Have you tried passing an empty string?
unsigned char Data = 0;
foo(0,&Data);
Don't use char* in COM APIs -- use BSTR instead. Then pass an empty string.
foo([in] unsigned long ulSize, [in] BSTR pData)
...
foo(1, _bstr_t(""));
You should probably mark the char* as as string to get some assistance with the marshaling.
foo([in] unsigned long ulSize, [in,string,size_is(ulSize)] unsigned char* pData)
We don't use the size_is option in the IDL, perhaps it is forcing the issue of having a non NULL address?
foo([in] unsigned long ulSize, [in,string] unsigned char* pData)
I'd certainly recommend using BSTR or SAFEARRAY rather than char. The issue would then be how to handle this empty case best, possibly treating the same as an empty string, or having a separate method.
Passing pointers in COM is very bad form, like passing a pointer using shared memory the (potentially/likely) remote process will not have access to the memory. As such COM tries to help by martialling the actual data for you, but if you have hidden it behind a different data type it won't be martialling the data properly. For instance using wchar_t* it will create a system allocated string available between the processes. or you can do the same and have an interface taking a bstring and pass it the result of a sysallocstring()
Perhaps you could tell us more about the structure you want to use, it might be more appropriate to expand the com interface with objects of this type. Or there may be some other trick in martialling to transfer the data, you can write custom martialling methods to serialize and deserialize the content.
If you're passing in a BSTR you should just pass the BSTR value - they're already length counted (use SysStrLength to find the length).
If you want to pass in a null terminated string, use the [string] attribute as Greg said
But the answer to your actual question is that you need to mark the string parameter as "unique" - that lets the MIDL compiler (and the RPC runtime library) know that it's ok for that parameter to be NULL.
So use:
foo([in, string] unsigned char* pData)
You don't need the length field because it's a null terminated string - so you can use strlen on the string.
foo is probably implemented like this:
HRESULT foo(unsigned long ulSize, unsigned char* pData) {
if (!pData) {
return E_POINTER;
}
...
}
In this case the only workaround is to pass non-NULL pData.
Related
Can someone explain and help me out here please.
Lets say i have function like this where lpData holds a pointer to the data i want.
void foo(LPVOID lpData) {
}
What is the proper way to retreive this. This works but i get weird characters at the end
void foo(LPVOID lpData) {
LPVOID *lpDataP = (LPVOID *)lpData;
char *charData = (char*)lpDataP;
//i log charData....
}
I would prefer to use strings but I don't understand how to retrieve the data, i just get null pointer error when i try to use string. lpData holds a pointer right? (But my function is lpData not *lpData) so it isn't working? Am i doing this all wrong?
string *datastring = reinterpret_cast<std::string *>(lpData);
is what im trying.
This works but i get weird characters at the end
That means that your string isn't null-terminated—that is, it doesn't have a NUL byte (0) marking the end of the string.
C strings have to be null-terminated.* When you log a C string (char *), it keeps logging characters until it finds a NUL. If there wasn't one on the end of the string, it'll keep going through random memory until it finds one (or until you hit a page fault and crash). This is bad. And there's no way to fix it; once you lose the length, there's no way to get it back.
However, an unterminated string along with its length can be useful. Many functions can take the length alongside the char *, as an extra argument (e.g., the string constructor) or otherwise (e.g., width specifiers in printf format strings).
So, if you take the length, and only call functions that also take the length—or just make a null-terminated copy and use that—you're fine. So:
void foo(LPVOID lpData, int cchData) {
string sData(static_cast<const char *>(lpData), cchData);
// now do stuff with sData
}
Meanwhile, casting from LPVOID (aka void *, aka pointer-to-anything) to LPVOID * (aka void **, aka pointer to pointer-to-anything) to then cast to char * (pointer-to-characters) is wrong (and should be giving you a compiler warning in the second cast; if you're getting warnings and ignoring them, don't do that!). Also, it's generally better to use modern casts instead of C-style casts, and it's always better to be const-correct when there's no down side; it just makes things more explicit to the reader and safer in the face of future maintenance.
Finally:
string *datastring = reinterpret_cast<std::string *>(lpData);
This is almost certainly wrong.** The LPVOID is just pointing at a bunch of characters. You're saying you want to interpret those characters as if they were a string object. But a string object is some header information (maybe a length and capacity, etc.) plus a pointer to a bunch of characters. Treating one as the other is going to lead to garbage or crashes.***
* Yes, you're using C++, not C, but a char * is a "C string".
** If you actually have a string object that you've kept alive somewhere, and you stashed a pointer to that object in an LPVOID and have now retrieved it (e.g., with SetWindowLongPtr/GetWindowLongPtr), then a cast from LPVOID to string * would make sense. But I doubt that's what you're doing. (If you are, then you don't need the reinterpret_cast. The whole point of void * is that it's not interpreted, so there's nothing to reinterpret from. Just use static_cast.)
*** Or, worst of all, it may appear to work, but then lead to hard-to-follow crashes or corruption. Some standard C++ libraries use a special allocator to put the header right before the characters and return a pointer to the first character, so that a string can be used anywhere a char * can. Inside the string class, every method has to fudge the this pointer backward; for example, instead of just saying m_length it has to do something like static_cast<_string_header *>(this)[-1]->m_length. But the other way around doesn't work—if you just have a bunch of characters, not a string object, that fudge is going to read whatever bytes happened to be allocated right before the characters and try to interpret them as an integer, so you may end up thinking you have a string of length 0, or 182423742341241243.
There are at least two ways:
void foo(LPVOID lpData)
{
char *charData = (char*)lpData;
//i log charData....
}
or
void foo(LPVOID lpData)
{
char *charData = static_cast<char*>lpData;
//i log charData....
}
HRESULT UrlCanonicalize(
_In_ PCTSTR pszUrl,
_Out_ PTSTR pszCanonicalized,
_Inout_ DWORD *pcchCanonicalized,
DWORD dwFlags
);
Example:
LPCTSTR pszURL = URL.c_str();
LPSTR pszOutPut = new CHAR[ strUrl.length ];
DWORD* dwCount = new DWORD[ strUrl.length ];
hRes = UrlCanonicalize( pszURL, pszOutPut,dwCount, URL_ESCAPE_UNSAFE );
Output:
E_INVALIDARG
This API fails and returns E_INVALIDARG every time I try to call it. Please give me a working code snippet to call the UrlCanonicalize function.
If you know the C++ language, the SDK documentation for the function pretty much tells you everything that you need to know:
You pass it a C-style nul-terminated string that contains your URL.
You pass it pointer to a buffer to receive the output string.
You pass it one or more flags that customize the function's behavior.
And finally, it returns to you an HRESULT value, which is an error code. If it succeeds, that value will be S_OK. If it fails, it will be some other error code.
It works like this:
std::wstring originalURL(L"http://www.example.com/hello/cruel/../world/");
// Allocate a buffer of the appropriate length.
// It needs to be at least as long as the input string.
std::wstring canonicalURL(originalURL.length() + 1, L'\0');
DWORD length = originalURL.length() + 1;
// Call the function to modify the string.
HRESULT hr = UrlCanonicalize(originalURL.c_str(), // input string
&canonicalURL[0], // buffer
&length, // pointer to a DWORD that contains the length of the buffer
URL_UNESCAPE | URL_ESCAPE_UNSAFE);
if (SUCCEEDED(hr))
{
// The function succeeded.
// Your canonicalized URL is in the canonicalURL string.
MessageBox(nullptr, canonicalURL.c_str(), L"The URL is:", MB_OK);
}
else
{
// The function failed.
// The hr variable contains the error code.
throw std::runtime_error("The UrlCanonicalize function failed.");
}
If you want to make sure that the buffer is sufficiently long (and avoid having to handle that error), use the constant INTERNET_MAX_URL_LENGTH (declared in WinInet.h) when allocating it:
std::wstring canonicalURL(INTERNET_MAX_URL_LENGTH, L'\0');
DWORD length = INTERNET_MAX_URL_LENGTH;
The code you tried has a couple of problems:
You've incorrectly initialized the dwCount variable. The function wants a pointer, but that doesn't mean you should declare the variable as a pointer. Nor do you want an array; this is a single DWORD value. So you need to declare it as a regular DWORD, and then use the address-of operator (&) to pass the function a pointer to that variable. Right now, you're passing the function garbage, so it's failing.
You're using C-style strings, which you should avoid in C++ code. Use the C++ string class (std::wstring for Windows code), which is exception safe and manages memory for you. As you already know, the c_str() member function gives you easy access to a C-style nul-terminated string like all C APIs want. This works fine, you do not need to use raw character arrays yourself. Avoid new whenever possible.
Potentially, a third problem is that you're trying to use the C++ string type std::string instead of std::wstring. The former is an 8-bit string type and doesn't support Unicode in a Windows environment. You want std::wstring, which is a wide string with Unicode support. It's what all the Windows API functions expect if you have the UNICODE symbol defined for your project (which it is by default).
Here you go:
LPCTSTR pszURL = URL.c_str();
DWORD nOutputLength = strUrl.length * 2 + 32;
LPTSTR pszOutPut = new TCHAR[nOutputLength];
hRes = UrlCanonicalize( pszURL, pszOutPut, &nOutputLength, URL_ESCAPE_UNSAFE);
On the third parameter you provided garbage instead of pointer to initialized value, so you had API failure back. MSDN has it all for you:
A pointer to a value that, on entry, is set to the number of characters in the pszCanonicalized buffer.
I'm trying to make an RPC call which requests 2 numbers and a string from the RPC server, the IDL looks like this:
void GetCurrentStatus([in] handle_t hBinding, [out, ref] DWORD *dwRef1, [out, ref] DWORD *dwRef2, UINT *nLength, [out, size_is(, *nLength)] LPWSTR *pszName);
In the server-side call I do this:
// name = std::wstring
*pszName = (wchar_t*)midl_user_allocate(name.length()+1 * sizeof(wchar_t));
_tcscpy(*pszName, name.c_str());
*nLength = name.length();
But any attempt to call from the client-side results in nothing returned the error The array bounds are invalid.
What is the correct way to return a string from an RPC call?
Thanks,
J
If you have a choice in the matter, use BSTR (i.e. SysAllocString). RPC knows all about this data type and how to copy it and find its length.
Just
[out, retval] BSTR* pstrName
is enough, no separate length parameter needed.
The server is not able to pass string value back to client since it doesn't know how to marshall the string..
When you use BSTR type, the server knows to the length of the string. BSTR must be preceded by a 4-byte length field and terminated by a single null 2-byte character.
Where you have written:
*nLength = name.length();
I believe you need
*nLength = (name.length() + 1) * sizeof(WCHAR);
In particular, if you have an empty (length zero) string, then returning a size_is(0) array is not legal -- so you must add space for the string-terminating NUL (L'\0').
You also want to supply the size in bytes, where each Unicode character uses two bytes -- therefore you must multiply by the character size.
I've got a function that returns a std::string object. I'm working with Cocoa/CoreGraphics and I need a way to get the data from that string into a CFData object so that I can feed that into a CGDataProviderCreateWithCFData object to make a CGImage.
The CreateCFData function wants a const UInt8* object (UInt8 being a typedef for unsigned char). The string represents the bytes from a decoded Base64 string (image data), so it appears to contain many null "characters" so the obvious casting of the .c_str() output to an unsigned char* object won't work.
I'm less experienced with C++ and very new to Cocoa/CoreGraphics, so if there's a much better way to accomplish what I'm wanting to do please let me know.
CFDataCreate( NULL, (const UInt8*) myString.data(), myString.size() )
I am writing a C++ DLL that is called by an external program.
1.) I take an array of strings (as char *var) as an argument from this program.
2.) I want to iterate through this array and call a COM function on each element of the string array. The COM function must take a BSTR:
DLL_EXPORT(void) runUnitModel(char *rateMaterialTypeNames) {
HRESULT hr = CoInitialize(NULL);
// Create the interface pointer.
IUnitModelPtr pIUnit(__uuidof(BlastFurnaceUnitModel));
pIUnit->initialiseUnitModel();
int i;
for(i=0; i < sizeOfPortRatesArray; i++)
pIUnit->createPort(SysAllocString(BSTR((const char *)rateMaterialTypeNames[i])));
I think its the SysAllocString(BSTR((const char *)rateMaterialTypeNames[i])) bit that is giving me problems. I get an access violation when the programs runs.
Is this the right way to access the value of the rateMaterialTypeName at i? Note I am expecting something like "IronOre" as the value at i, not a single character.
If you're using Microsofts ATL, you can use the CComBSTR class.
It will accept a char* and create a BSTR from it, also, you don't need to worry about deleting the BSTR, all that happens in the dtor for CComBSTR.
Also, see Matthew Xaviers answer, it doesn't look like you're passing your array of strings into that function properly.
Hope this helps
Because a variable holding a C string is just a pointer to the first element (a char*), in order to pass an array of C strings, the parameter to your function should be a char**:
DLL_EXPORT(void) runUnitModel(char **rateMaterialTypeNames)
This way, when you evaluate rateMaterialTypeNames[i], the result will be a char*, which is the parameter type you need to pass to SysAllocString().
Added note: you will also need to convert the strings to wide chars at some point, as Tommy Hui's answer points out.
If the parameter to the function rateMaterialTypeNames is a string, then
rateMaterialTypeNames[i]
is a character and not a string. You should use just the parameter name itself.
In addition, casts in general are bad. The conversion to a BSTR is a big flag. The parameter type for SysAllocString is
const OLECHAR*
which for 32-bit compilers is a wide character. So this will definitely fail because the actual parameter is a char*.
What the code needs is a conversion of narrow string to a wide string.
const OLECHAR* pOleChar = A2COLE( *pChar );
BSTR str = SysAllocString( pOleChar );
// do something with the 'str'
SysFreeString( str ); // need to cleanup the allocated BSTR