Delphi程序使用VC做的服务器组件数据发生的奇怪问题!请高手帮忙,分不够可以再给!!!

zhangjl716 2003-09-12 12:13:56
我用VC做了一个简单的EXE形式的服务器组件,组件接口提供方法为
GetData([in,out]MYSTRUCT *pMyDataIn_Out);

数据结构类型为:
typedef struct tagMYSTRUCT{
unsigned char v1;
float v2;
double v3;
}MYSTRUCT


我在服务器组件中只修改了结构pMyDataIn_Out数据成员的值。


问题:(1)当用Delphi程序做客户程序,调用该组件的方法,返回的参数不正确。比如本来我需要返回的v3= 1.1,但程序可能读出的数据为-12344E-304这样的数据。
(2)我原来做过这样的程序,在WIN2000平台上,使用Delphi6和VC6,开发的程序没有问题。而我现在的平台是WINXP,请高手指点是否因为平台和开发环境的问题?怎么解决??

另外:如果我的输入输出数据结构定义成:
typedef struct tagMYSTRUCT{
unsigned char v1;
unsigned char v2;
unsigned char v3;
}MYSTRUCT

typedef struct tagMYSTRUCT{
double v1;
double v2;
double v3;
}MYSTRUCT时(即结构里的数据元素同种类型),程序调用返回的结果都没问题!!!!


那为兄弟请帮忙解决一下,将不胜感激!!!

...全文
84 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhangjl716 2003-09-15
  • 打赏
  • 举报
回复
问题已经解决,特别谢谢masterz和akun!
xiaohedou 2003-09-14
  • 打赏
  • 举报
回复
Look ^ study
zhangjl716 2003-09-13
  • 打赏
  • 举报
回复
谢谢akun,问题还没最终解决:

昨天在我的IDL文件前加了#pragma pack(1),可以把in属性的参数问题解决了,但out属性的参数返回的值还是不对(同样必须要求结构内数据元素类型相同才行)


masterz兄弟,因为今天比较忙,大概看了一下你的解释,还没来得及按照说的做,非常感谢你的指导。
masterz 2003-09-13
  • 打赏
  • 举报
回复
//////////////////////////////////////////////////////////
HOWTO: Use structs in Automation-compatible interfaces

Structs, also known as User Defined Types (UDTs), can be used in Automation- compatible interfaces since Win NT4 SP4 and DCOM 1.2 for Win9x. An Automation- compatible struct may contain only primitive Automation types as its members. Nesting structs is not allowed, but VARIANT is allowed thus enabling nested structs (you can store a struct in a VARIANT). In order for a struct to be usable for Automation-compatible interfaces, the struct must be described in a type library and it must be declared with its own GUID:


[uuid(21602F40-CC62-11d4-AA2B-00A0CC39CFE0)]
struct MyStruct
{
[helpstring("A long value")]
long nLongValue;

[helpstring("A string")]
BSTR bstrStringValue;
};

// Later in the IDL file
[uuid(...), version(...), helpstring(...)]
library MyLib
{
...
struct MyStruct;
};

For unknown reason, the MIDL compiler bundled with VC6 has not been updated even in SP4. Therefore, you need the latest Platform SDK installed on your computer (or at least no older than Platform SDK Jul'99) in order to compile the above IDL code and any IDL using structs with Automation.

NOTE: The current release of MIDL at the time of writing (5.03.0280) does not support the helpstring() attribute on the struct itself. helpstring() is supported on the struct fields, though.

Once your struct is declared as above, you can pass it via Automation- compatible interfaces. Keep in mind, however, that only struct pointers can be passed:


HRESULT SendStruct([in] struct MyStruct *pData);
HRESULT GetStruct([out, retval] struct MyStruct *pData);

Structs can also be passed in safe arrays and VARIANTs:


HRESULT SendManyStructs([in] SAFEARRAY(struct MyStruct) *psaData);

Neither IDL nor Automation define byte alignment for a struct. VB assumes 4-byte alignment, while #import in VC assumes 8-byte alignment. For most Automation structs this isn't an issue. However, if you use 8-byte types like double, this may make a difference:


[uuid(...), helpstring(...)]
struct BadAlign
{
long nLongValue;
double dblDoubleValue;
};

For VB, the double field starts at the fourth byte of the struct, while for VC's #import it starts at the eight byte. This poses a significant problem. It can be solved by adding an inject_statement clause to #import:


#import "My.tlb" inject_statement("#pragma pack(4)")

IMPORTANT: Structs cannot be used by scripting clients!

Implementing structs in code is usually trivial - just use the struct directly. There are two specific struct uses when the code is fairly non-trivial - when using a safe array of structs and when stuffing the struct into a VARIANT. Let's note first that in Automation any struct is represented via an IRecordInfo interface pointer. The object exposing this interface is implemented by the Automation runtime and is acquired via the following two API functions:


GetRecordinfoFromGuids
GetRecordInfoFromTypeInfo

When structs are used in say dual interfaces, this detail is hidden from the implementer and the user of the interface. Only the marshaler knows and uses it. However, when we need to pass structs in a VARIANT or a safe array, we have to delve into these details ourselves. Here's the C++ code for populating a VARIANT with a struct (error checking omitted):


VARIANT vnt;
IRecordInfo *pRI;
MyStruct data;
HRESULT hr;

data.nLongValue = 5;
data.bstrStringValue = SysAllocString(L"Something");
hr = GetRecordInfoFromGuids(LIBID_MyLib, 1, 0, 0x409, uuidof(MyStruct), &pRI);
VariantInit(&vnt);
vnt.vt = VT_RECORD;
vnt.pvRecord = &data;
vnt.pRecInfo = pRI;
pRI = NULL;

Note that the VARIANT doesn't own the storage for the embedded struct, but VariantClear will free the content of the struct by calling IRecordInfo::RecordClear. Note also that the common VARIANT wrapper classes like CComVariant and _variant_t don't support structs. Finally, note that the mechanism for storing struct data in a VARIANT is not fully documented. For example it isn't clear how a struct can be returned in an [out] VARIANT argument without producing memory leaks.

Now let's see how to populate a safe array with structs. The following code is a trimmed down version of the MSDN example code (error checking omitted):


SAFEARRAY *psa;
SAFEARRAYBOUND sab = {2, 0};
MyStruct *pData;
IRecordInfo *pRI;
HRESULT hr;

hr = GetRecordInfoFromGuids(LIBID_MyLib, 1, 0, 0x409, uuidof(MyStruct), &pRI);
psa = SafeArrayCreateEx(VT_RECORD, 1, &sab, pRI);
pRI->Release();
pRI = NULL;
hr = SafeArrayAccessData(psa, (void**)&pData);
pData[0].nLongValue = 1;
pData[0].bstrStringValue = SysAllocString(L"First");
pData[1].nLongValue = 2;
pData[1].bstrStringValue = SysAllocString(L"Second");
hr = SafeArrayUnaccessData(psa);

Unlike a VARIANT, a safe array owns the storage of its structs.

The UDT documentation is located at: mk:@MSITStore:D:\MSDN\Automat.Chm::/htm/chap12_3rcj.htm (Copy the link and paste it in the "Jump to URL..." box. Substitute the path with the path to your MSDN files. This link is obtained from MSDN Library Oct'2000.)
masterz 2003-09-13
  • 打赏
  • 举报
回复
HOWTO: Use structs in COM interfaces when Automation compatibility is not an issue

You can use any IDL type for the struct fields. You don't need to associate a GUID with the struct. Structs can be nested. About the only issue is using data pointers within the structs. When a struct is marshaled, all embedded pointers have to be marshaled as well. This amounts to copying the target locations of those pointers in the marshaling packet. Therefore, all pointers must be annotated, especially if they point to arrays. If an embedded pointer is not annotated with one of the three pointer types (ref, unique, and ptr), the pointer_default() attribute of the interface the struct is used in determines the type of the pointer. Since different interfaces can potentially use different default embedded pointer types, it is generally a good idea to always annotate the struct's embedded pointers. Here's an example:


struct Data
{
DWORD nData1;
float fltData2;
};

struct MyStruct
{
int nCount;
[unique, size_is(nCount)] struct Data *aData;
};

When passing structs one should be careful what gets transmitted in the marshaling packet. In the above example the whole array of Data structs is transferred. This is expected and well annotated. However, there are bad struct designs as well. Consider the following single-linked list:


struct Node
{
struct Data data;
[unique] struct Node *pNext;
};

When the head of the list is passed, all elements in the list are replicated in the marshaling packet. Such architecture needs to be redesigned so the complete list does not get transferred on each method call (a possible design is to hide the list itself behind an object and expose methods on the object's interface for manipulating the list).

Rarely, but sometimes nevertheless, pointer aliasing has to be considered as well. In the examples so far no two pointers could point to the same memory address. However, consider this doubly- linked list:


struct Node
{
struct Data data;
[ptr] struct Node *pNext;
[ptr] struct Node *pNext;
};

True, this is a bad design, but nonetheless without the pointer type set to [ptr], the result would be disastrous.

When a struct is an [in] argument, it doesn't matter how the memory pointed to by the embedded pointers is allocated. However, for [out] and [in, out] arguments all memory pointed to by the embedded pointers of a struct must be allocated via the COM memory allocator (CoTaskMemAlloc). This is necessary so that the marshaling code can deallocate the data at the callee and reallocate it at the caller.

A final point: IDL doesn't define the struct packing byte alignment. The default for the VC compiler is 8. However, it is safer to manually inject a #pragma pack statement:


#pragma pack(push, 8)
...
#pragma pack(pop)

If you manually set the byte alignment, don't use #import for importing the struct definition from a type library. It adds #pragma pack(push, 8) at the beginning of the generated TLH file. Your pragma is not persisted in the generated type library. Generally, it is not advised to use #import for non-Automation-compatible interfaces.

IMPORTANT: since your interfaces are not Automation-compatible, you have to build and register the proxy/stub DLL for their marshaling support!


akun 2003-09-12
  • 打赏
  • 举报
回复
可能是数据对齐的问题
+上下面一句
#pragma pack(1)

3,245

社区成员

发帖
与我相关
我的任务
社区描述
ATL,Active Template Library活动(动态)模板库,是一种微软程序库,支持利用C++语言编写ASP代码以及其它ActiveX程序。
社区管理员
  • ATL/ActiveX/COM社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧