I'm evaluating Server 2008. My C++ executable is getting this error. I've seen this error on MSDN that seems to have required a hot-fix for several previous OSes. Anyone else seen this? I get the same results for the 32 & 64 bit OS.
Code snippet:
HRESULT GroupStart([in] short iClientId, [in] VARIANT GroupDataArray,
[out] short* pGroupInstance, [out] long* pCommandId);
Where the GroupDataArray VARIANT argument wraps a single-dimension SAFEARRAY of VARIANTs wrapping a DCAPICOM_GroupData struct entries:
// DCAPICOM_GroupData
[
uuid(F1FE2605-2744-4A2A-AB85-1E1845C280EB),
helpstring("removed")
]
typedef struct DCAPICOM_GroupData {
[helpstring("removed")]
long m_lImageID;
[helpstring("removed")]
unsigned char m_ucHeadID;
[helpstring("removed")]
unsigned char m_ucPlateID;
} DCAPICOM_GroupData;
After opening a support case with Microsoft, I can now answer my own question. This is (now) a recognized bug. The issue has to do with marshalling on the server side, but before the server code is called. Our structure is 6 bytes long, but this COM implementation is interpreting it as 8, so the marshalling fails, and this is the error you get back. The workaround, until a Service Pack is released to deal with this, is to add two extra bytes to the structure to pad it up to 8 bytes. We haven't run across any more instances that fail yet, but we still have a lot of testing to do still.
We ran into the same error recently with a client/server app communicating via DCOM. It turned out that the size of a marshalled interface pointer going across the wire (i.e., not local) had changed (gotten bigger). You might like to check whether your code is doing any special marshalling via CoMarshalInterface or the like.
Related
I'm struggling a lot porting an MFC application from 32bit to 64bit.
I have same classes with CStringArray members and they use the CArchive serialization and all works fine in 32 bit app.
Now I split the same application into two parts, one in 32bit and the other in 64 bit and they need to share some serialized data; when I serialize a CStringArray member and try to deserialize it in the 64 bit app I get an CArchiveException, with cause=3 that should be "endOfFile".
It's not clear what's going on, I suspect that I can't serialize it a data in 32 bit and read it on 64 bit app due to the size. If I follow the GetSize() function of CStringArray, I see that the return is INT_PTR that is defined as follows:
This means there is no way to make a CStringArray serialization on 32bit and deserialization on 64 bit? Exist an workaround or something similar? There are other MFC data that I should check between 32 and 64 app?
EDIT: The problem is not related strictly on the CStringArray that (see the comments) is fine between 32/64.
I answer to my question for future readers. The problem was not related to the CStringArray but to other parts of code near it. The problem was that before CStringArray obj there is another member that was an CArray of a custom struct. This struct have a couple of members that use an override of the CArchive SerializeElements function to made a correct data serialization in 32 bit.
This serialize element was written in this way:
template<class TYPE> void AFXAPI SerializeElements (CArchive& ar, CRect* pElements, int nCount);
but the 64bit version does not use this override because the last parameter was int instead of INT_PTR and without using SerializeElements, I made a wrong serialization (less byte that needed, this is why I reach the endOfFile to soon while reading).
To fix it, just use the right declaration for the SerializeElements that is:
template<class TYPE> void AFXAPI SerializeElements (CArchive& ar, CRect* pElements, **INT_PTR** nCount);
Hope can help
This is a simplified question for the one I asked here. I'm using VS2010 (CRT v100) and it doesn't complain, in any way ever, when i double free a BSTR.
BSTR s1=SysAllocString(L"test");
SysFreeString(s1);
SysFreeString(s1);
Ok, the question is highly hypothetical (actually, the answer is :).
SysFreeString takes a BSTR, which is a pointer, which actually is a number which has a specific semantic. This means that you can provide any value as an argument to the function, not just a valid BSTR or a BSTR which was valid moments ago. In order for SysFreeString to recognize invalid values, it would need to know all the valid BSTRs and to check against all of them. You can imagine the price of that.
Besides, it is consistent with other C, C++, COM or Windows APIs: free, delete, CloseHandle, IUnknown::Release... all of them expect YOU to know whether the argument is eligible for releasing.
In a nutshell your question is: "I am calling SysFreeString with an invalid argument. Why compiler allows me this".
Visual C++ compiler allows the call and does not issue a warning because the call itself is valid: there is a match of argument type, the API function is good, this can be converted to binary code that executes. The compiler has no knowledge whether your argument is valid or not, you are responsible to track this yourselves.
The API function on the other hand expects that you pass valid argument. It might or might not check its validity. Documentation says about the argument: "The previously allocated string". So the value is okay for the first call, but afterward the pointer value is no longer a valid argument for the second call and behavior is basically undefined.
Nothing to do with the CRT, this is a winapi function. Which is C based, a language that has always given programmers enough lengths of rope to hang themselves by invoking UB with the slightest mistake. Fast and easy-to-port has forever been at odds with safe and secure.
SysFreeString() doesn't win any prizes, clearly it should have had a BOOL return type. But it can't, the IMalloc::Free() interface function was fumbled a long time ago. Nothing you can't fix yourself:
BOOL SafeSysFreeString(BSTR* str) {
if (str == NULL) {
SetLastError(ERROR_INVALID_ARGUMENT);
return FALSE;
}
SysFreeString(*str);
*str = NULL;
return TRUE;
}
Don't hesitate to yell louder, RaiseException() gives a pretty good bang that is hard to ignore. But writing COM code in C is cruel and unusual punishment, outlawed by the Geneva Convention on Programmers Rights. Use the _bstr_t or CComBSTR C++ wrapper types instead.
But do watch out when you slice the BSTR out of them, they can't help when you don't or can't use them consistently. Which is how you got into trouble with that VARIANT. Always pay extra attention when you have to leave the safety of the wrapper, there are C sharks out there.
See this quote from MSDN:
Automation may cache the space allocated for BSTRs. This speeds up
the SysAllocString/SysFreeString sequence.
(...)if the application allocates a BSTR and frees it, the free block
of memory is put into the BSTR cache by Automation(...)
This may explain why calling SysFreeString(...) twice with the same pointer does not produce a crash,since the memory is still available (kind of).
I have been reading the MSDN dev guide to COM. However the code on this page is confusing. Reproducing here:
The following code sample shows the recommended way of handling unknown errors:
HRESULT hr;
hr = xxMethod();
switch (GetScode(hr))
{
case NOERROR:
// Method returned success.
break;
case x1:
// Handle error x1 here.
break;
case x2:
// Handle error x2 here.
break;
case E_UNEXPECTED:
default:
// Handle unexpected errors here.
break;
}
The GetScode function doesn't seem to be defined, nor is NOERROR, and searching MSDN didn't help. A web search indicated that GetScode is a macro that converts HRESULT to SCODE, however those are both 32-bit ints so I'm not sure what it is for.
It was suggested that it is a historical artifact that does nothing on 32-bit systems, but on 16-bit systems it converts hr to a 16-bit int. However, if that is true, then I do not see how E_UNEXPECTED would be matched, since that is 0x8000FFFF. Also, it's unclear whether x1 and x2 are meant to be 0x800..... values, or some sort of truncated version.
Finally, this code treats all-but-one of the success values as errors. Other pages on the same MSDN guide say that SUCCEEDED(hr) or FAILED(hr) should be used to determine between a success or failure.
So, is this code sample really the "recommended way" or is this some sort of documentation blunder?
This is (pretty) old stuff. The winerror.h file in the SDK says this:
////////////////////////////////////
// //
// COM Error Codes //
// //
////////////////////////////////////
//
// The return value of COM functions and methods is an HRESULT.
// This is not a handle to anything, but is merely a 32-bit value
// with several fields encoded in the value. The parts of an
// HRESULT are shown below.
//
// Many of the macros and functions below were orginally defined to
// operate on SCODEs. SCODEs are no longer used. The macros are
// still present for compatibility and easy porting of Win16 code.
// Newly written code should use the HRESULT macros and functions.
//
I think it's pretty clear. I would trust the SDK first, and the doc after that.
We can see SCODE is consistently defined like this in WTypesbase.h (in recent SDKs, in older SDKs, I think it was in another file):
typedef LONG SCODE;
So it's really a 32-bit.
The text is correct; one should be wary of blindly returning failure codes from internal functions, particularly if your code uses a facility code defined elsewhere in the system.
Specifically, at the COM interface function level, you should ensure that the error codes you're returning are meaningful for your interface, and you should remap errors that originate from inside the function to meaningful error codes.
Practically speaking, however, nobody does this, which is why you see bizarre and un-actionable error dialogs like "Unexpected error".
I'm not quite sure how [in] and [out] interact with the pass-by-value and pass-by-reference concepts. The MSDN documentation clearly states that [in] means data flows from caller to callee, and [out] is required for data to flow from callee to caller.
However someone suggested to me that I use [in] parameters for objects where the caller can retrieve the results.
Example method definition in IDL:
HRESULT _stdcall a_method( [in] long *arg1, [in] BSTR arg2, [in] IAnObject *arg3 );
In my server's implementation of this method (using C++), I can write:
*arg1 = 20;
arg2[0] = L'X'; // after checking length of string is not 0
arg3->set_value(50);
In the client code, using C++:
long val1 = 10;
BSTR val2 = SysAllocString(L"hello");
IAnObject *val3 = AnObject_Factory::Create();
ptr->a_method(&val1, val2, val3);
When I tried this out (using my object via in-process server), all three changes from the server were propagated to the client, i.e. val1 == 20, val2 was "Xello", and val3->get_value() got 50.
My question is: Is this guaranteed behaviour, i.e. if I am using out-of-process server, or DCOM to another machine, will it see the same changes in val1, val2, and val3 ?
I previously thought that [in] indicated to the underlying RPC that the argument only had to be marshaled in one direction; it didn't have to try and send changes back to the caller. But now I am not so sure.
I am intending that my object is Automation-compatible (i.e. usable from VB6, Java etc. - no custom marshaling required), and that it ought to be able to be used via DCOM instead of in-process, without any changes required in the client code.
You shouldn't change the contents of [in] arguments, so the following code is wrong:
*arg1 = 20;
arg2[0] = L'X'; // after checking length of string is not 0
You're seeing the changes being reflected because you're making calls in the same apartment, where marshaling isn't happening. The proper way to return values is with [out] or [in, out] arguments.
However, you may access its contents and call its methods (for interface pointers), so the following code is right:
arg3->set_value(50);
EDIT: Further answering your questions.
Marshaling can occur both ways, and the [in] and [out] attributes tell the way(s).
For automation, I recommend you don't return more than the typical [out, retval] argument, to support scripting languages. If you must return multiple values, return an IDispatch with properties. Take a look at this blog post as a good starting point if you're taking scriptable automation seriously.
To expand upon #Paulo-madeira's answer, I can guarantee that if a proxy is involved, that
*arg1 = 20;
arg2[0] = L'X'; // after checking length of string is not 0
will at best be ignored, and at worst will corrupt the heap.
Hi everyone.
I have to work with old utility: which converts xls into txt.
There was a small problem in logic of the utility, but the problem is in other thing...
The utility consists of two parts: exe module and dll module, and uses MFC.
In exe project we have
pInit = (t_bXR_Init)GetProcAddress(hExcel, _T("bXR_Init"));
and
pInit("logfiles",false);
In dll project we have
typedef bool (*t_bXR_Init) (CString const &strlogfilespath, bool btxtfile);
XLSREADER_API bool bXR_Init(CString const &strlogfilespath, bool btxtfile);
The problem is when we send argument "logfiles" into the function it doesn't get it. It's strange, 'cause all other parameters are send properly.
The reason is somehow connected with using of CString. But I don't know how...
XLSREADER_API is defined as:
#define XLSREADER_API extern "C" __declspec(dllimport)
Also I've added
AFX_MANAGE_STATE(AfxGetStaticModuleState());
in the beginning of function's body (for bXR_Init). But it didn't help.
Also I tried to change some settings for these two projects, all settings are the same (e.g. calling conversion is __cldecl(/Gd); I build either debug versions exe and dll or release version of exe and dll simultaneously).
Also I tried to use CString instead of CString& - the same situation. It works properly if use char*, but boss says to find what the origin of the problem is at first.
What may lead to the problem (the function doesn't get CString parameter)?
To pass a complex type such as a CString across a DLL boundary you have to make sure that both the DLL and the exe are using the exact same DLL libraries. Set "Runtime Library" to multi-threaded DLL and set "Use of MFC" to Use MFC in a Shared DLL. Also, don't mix debug and release modules: Both must be the same.
Without these conditions you get two different heaps, and you can't keep the allocations/deletions compatible with two heaps.
Try passing an actual CString parameter to the call:
CString sPath = "logfiles";
pInit(sPath,false);
wtfigo! (what the f is going on)
the problem is solved.
I discovered, that exe project had "character set" = "use multibyte character set"
and dll project had "character set" = "use unicode character set".
So, dll function got CString with char* inside, but considered it as CString with wchat_t* inside. And it looked as garbage (as complete garbage on my pc and as chinese symbols on my workmate's pc).
I changed "character set" for exe project to "use unicode character set" and discovered about 60 errors.
Then I read an article http://habrahabr.ru/post/164193/ (in russian; or in english: http://www.codeproject.com/Articles/76252/What-are-TCHAR-WCHAR-LPSTR-LPWSTR-LPCTSTR-etc).
And fixed all errors, widely used macroses from TCHAR.h (MSDN helped me).
Thanks everybody for your help.