oleaut32: Add tests for DispCallFunc and fix a number of corner cases.

This commit is contained in:
Alexandre Julliard 2010-08-26 12:22:20 +02:00
parent 56b8d5d384
commit a0a4667fde
2 changed files with 288 additions and 69 deletions

View File

@ -60,6 +60,8 @@ static HRESULT WINAPI (*pUnRegisterTypeLibForUser)(REFGUID,WORD,WORD,LCID,SYSKIN
static const WCHAR wszStdOle2[] = {'s','t','d','o','l','e','2','.','t','l','b',0};
static const int is_win64 = sizeof(void *) > sizeof(int);
static void init_function_pointers(void)
{
HMODULE hmod = GetModuleHandleA("oleaut32.dll");
@ -628,6 +630,191 @@ static void test_TypeInfo(void)
ITypeLib_Release(pTypeLib);
}
static int WINAPI int_func( int a0, int a1, int a2, int a3, int a4 )
{
ok( a0 == 1, "wrong arg0 %x\n", a0 );
ok( a1 == -1, "wrong arg1 %x\n", a1 );
ok( a2 == (0x55550000 | 1234), "wrong arg2 %x\n", a2 );
ok( a3 == 0xdeadbeef, "wrong arg3 %x\n", a3 );
ok( a4 == 0x555555fd, "wrong arg4 %x\n", a4 );
return 4321;
}
static double WINAPI double_func( double a0, float a1, double a2, int a3 )
{
ok( a0 == 1.2, "wrong arg0 %f\n", (double)a0 );
ok( a1 == 3.25, "wrong arg1 %f\n", (double)a1 );
ok( a2 == 1.2e12, "wrong arg2 %f\n", (double)a2);
ok( a3 == -4433.0, "wrong arg3 %f\n", (double)a3 );
return 4321;
}
static LONGLONG WINAPI longlong_func( LONGLONG a0, CY a1 )
{
ok( a0 == (((ULONGLONG)0xdead << 32) | 0xbeef), "wrong arg0 %08x%08x\n", (DWORD)(a0 >> 32), (DWORD)a0);
ok( a1.int64 == ((ULONGLONG)10000 * 12345678), "wrong arg1 %08x%08x\n",
(DWORD)(a1.int64 >> 32), (DWORD)a1.int64 );
return ((ULONGLONG)4321 << 32) | 8765;
}
static VARIANT WINAPI variant_func( int a0, BOOL a1, DECIMAL a2, VARIANT a3 )
{
VARIANT var;
ok( a0 == 2233, "wrong arg0 %x\n", a0 );
ok( a1 == 1 || broken(a1 == 0x55550001), "wrong arg1 %x\n", a1 );
V_VT(&var) = VT_LPWSTR;
V_UI4(&var) = 0xbabe;
ok( a2.Hi32 == 1122, "wrong arg2.Hi32 %x\n", a2.Hi32 );
ok( a2.Lo64 == 3344, "wrong arg2.Lo64 %08x%08x\n", (DWORD)(a2.Lo64 >> 32), (DWORD)a2.Lo64 );
ok( V_VT(&a3) == VT_EMPTY, "wrong arg3 type %x\n", V_VT(&a3) );
ok( V_UI4(&a3) == 0xdeadbeef, "wrong arg3 value %x\n", V_UI4(&a3) );
return var;
}
static int CDECL void_func( int a0, int a1 )
{
if (is_win64) /* VT_EMPTY is passed as real arg on win64 */
{
ok( a0 == 0x55555555, "wrong arg0 %x\n", a0 );
ok( a1 == 1111, "wrong arg1 %x\n", a1 );
}
else
{
ok( a0 == 1111, "wrong arg0 %x\n", a0 );
ok( a1 == 0, "wrong arg1 %x\n", a1 );
}
return 12;
}
static int WINAPI stdcall_func( int a )
{
return 0;
}
static int WINAPI inst_func( void *inst, int a )
{
ok( (*(void ***)inst)[3] == inst_func, "wrong ptr %p\n", inst );
ok( a == 3, "wrong arg %x\n", a );
return a * 2;
}
static const void *vtable[] = { NULL, NULL, NULL, inst_func };
static void test_DispCallFunc(void)
{
const void **inst = vtable;
HRESULT res;
VARIANT result, args[5];
VARIANTARG *pargs[5];
VARTYPE types[5];
int i;
for (i = 0; i < 5; i++) pargs[i] = &args[i];
memset( args, 0x55, sizeof(args) );
types[0] = VT_UI4;
V_UI4(&args[0]) = 1;
types[1] = VT_I4;
V_I4(&args[1]) = -1;
types[2] = VT_I2;
V_I2(&args[2]) = 1234;
types[3] = VT_UI4;
V_UI4(&args[3]) = 0xdeadbeef;
types[4] = VT_UI4;
V_I1(&args[4]) = -3;
memset( &result, 0xcc, sizeof(result) );
res = DispCallFunc( NULL, (ULONG_PTR)int_func, CC_STDCALL, VT_UI4, 5, types, pargs, &result );
ok( res == S_OK, "DispCallFunc failed %x\n", res );
ok( V_VT(&result) == VT_UI4, "wrong result type %d\n", V_VT(&result) );
ok( V_UI4(&result) == 4321, "wrong result %u\n", V_UI4(&result) );
/* the function checks the argument sizes for stdcall */
if (!is_win64) /* no stdcall on 64-bit */
{
res = DispCallFunc( NULL, (ULONG_PTR)stdcall_func, CC_STDCALL, VT_UI4, 0, types, pargs, &result );
ok( res == DISP_E_BADCALLEE, "DispCallFunc wrong error %x\n", res );
res = DispCallFunc( NULL, (ULONG_PTR)stdcall_func, CC_STDCALL, VT_UI4, 1, types, pargs, &result );
ok( res == S_OK, "DispCallFunc failed %x\n", res );
res = DispCallFunc( NULL, (ULONG_PTR)stdcall_func, CC_STDCALL, VT_UI4, 2, types, pargs, &result );
ok( res == DISP_E_BADCALLEE, "DispCallFunc wrong error %x\n", res );
}
memset( args, 0x55, sizeof(args) );
types[0] = VT_R8;
V_R8(&args[0]) = 1.2;
types[1] = VT_R4;
V_R4(&args[1]) = 3.25;
types[2] = VT_R8;
V_R8(&args[2]) = 1.2e12;
types[3] = VT_I4;
V_I4(&args[3]) = -4433;
memset( &result, 0xcc, sizeof(result) );
res = DispCallFunc( NULL, (ULONG_PTR)double_func, CC_STDCALL, VT_R8, 4, types, pargs, &result );
ok( res == S_OK, "DispCallFunc failed %x\n", res );
ok( V_VT(&result) == VT_R8, "wrong result type %d\n", V_VT(&result) );
ok( V_R8(&result) == 4321, "wrong result %f\n", V_R8(&result) );
memset( args, 0x55, sizeof(args) );
types[0] = VT_I8;
V_I8(&args[0]) = ((ULONGLONG)0xdead << 32) | 0xbeef;
types[1] = VT_CY;
V_CY(&args[1]).int64 = (ULONGLONG)10000 * 12345678;
memset( &result, 0xcc, sizeof(result) );
res = DispCallFunc( NULL, (ULONG_PTR)longlong_func, CC_STDCALL, VT_I8, 2, types, pargs, &result );
ok( res == S_OK || broken(res == E_INVALIDARG), /* longlong not supported on <= win2k */
"DispCallFunc failed %x\n", res );
if (res == S_OK)
{
ok( V_VT(&result) == VT_I8, "wrong result type %d\n", V_VT(&result) );
ok( V_I8(&result) == (((ULONGLONG)4321 << 32) | 8765), "wrong result %08x%08x\n",
(DWORD)(V_I8(&result) >> 32), (DWORD)V_I8(&result) );
}
memset( args, 0x55, sizeof(args) );
types[0] = VT_I4;
V_I4(&args[0]) = 2233;
types[1] = VT_BOOL;
V_BOOL(&args[1]) = 1;
types[2] = VT_DECIMAL;
V_DECIMAL(&args[2]).Hi32 = 1122;
V_DECIMAL(&args[2]).Lo64 = 3344;
types[3] = VT_VARIANT;
V_VT(&args[3]) = VT_EMPTY;
V_UI4(&args[3]) = 0xdeadbeef;
types[4] = VT_EMPTY;
memset( &result, 0xcc, sizeof(result) );
res = DispCallFunc( NULL, (ULONG_PTR)variant_func, CC_STDCALL, VT_VARIANT, 5, types, pargs, &result );
ok( res == S_OK, "DispCallFunc failed %x\n", res );
ok( V_VT(&result) == VT_LPWSTR, "wrong result type %d\n", V_VT(&result) );
ok( V_UI4(&result) == 0xbabe, "wrong result %08x\n", V_UI4(&result) );
memset( args, 0x55, sizeof(args) );
types[0] = VT_EMPTY;
types[1] = VT_I4;
V_I4(&args[1]) = 1111;
types[2] = VT_EMPTY;
types[3] = VT_I4;
V_I4(&args[3]) = 0;
types[4] = VT_EMPTY;
memset( &result, 0xcc, sizeof(result) );
res = DispCallFunc( NULL, (ULONG_PTR)void_func, CC_CDECL, VT_EMPTY, 5, types, pargs, &result );
ok( res == S_OK, "DispCallFunc failed %x\n", res );
ok( V_VT(&result) == VT_EMPTY, "wrong result type %d\n", V_VT(&result) );
if (is_win64)
ok( V_UI4(&result) == 12, "wrong result %08x\n", V_UI4(&result) );
else
ok( V_UI4(&result) == 0xcccccccc, "wrong result %08x\n", V_UI4(&result) );
memset( args, 0x55, sizeof(args) );
types[0] = VT_I4;
V_I4(&args[0]) = 3;
memset( &result, 0xcc, sizeof(result) );
res = DispCallFunc( &inst, 3 * sizeof(void*), CC_STDCALL, VT_I4, 1, types, pargs, &result );
ok( res == S_OK, "DispCallFunc failed %x\n", res );
ok( V_VT(&result) == VT_I4, "wrong result type %d\n", V_VT(&result) );
ok( V_I4(&result) == 6, "wrong result %08x\n", V_I4(&result) );
}
/* RegDeleteTreeW from dlls/advapi32/registry.c */
static LSTATUS myRegDeleteTreeW(HKEY hKey, LPCWSTR lpszSubKey)
{
@ -2603,9 +2790,9 @@ static void test_register_typelib(BOOL system_registration)
}
if (system_registration)
hr = UnRegisterTypeLib(&LIBID_register_test, 1, 0, LOCALE_NEUTRAL, sizeof(void*) == 8 ? SYS_WIN64 : SYS_WIN32);
hr = UnRegisterTypeLib(&LIBID_register_test, 1, 0, LOCALE_NEUTRAL, is_win64 ? SYS_WIN64 : SYS_WIN32);
else
hr = pUnRegisterTypeLibForUser(&LIBID_register_test, 1, 0, LOCALE_NEUTRAL, sizeof(void*) == 8 ? SYS_WIN64 : SYS_WIN32);
hr = pUnRegisterTypeLibForUser(&LIBID_register_test, 1, 0, LOCALE_NEUTRAL, is_win64 ? SYS_WIN64 : SYS_WIN32);
ok(SUCCEEDED(hr), "got %08x\n", hr);
ITypeLib_Release(typelib);
@ -2622,6 +2809,7 @@ START_TEST(typelib)
test_TypeComp();
test_CreateDispTypeInfo();
test_TypeInfo();
test_DispCallFunc();
test_QueryPathOfRegTypeLib(32);
if(sizeof(void*) == 8)
test_QueryPathOfRegTypeLib(64);

View File

@ -5706,7 +5706,7 @@ static HRESULT WINAPI ITypeInfo_fnGetIDsOfNames( ITypeInfo2 *iface,
#ifdef __i386__
extern LONGLONG CDECL call_method( void *func, int nb_args, const DWORD *args );
extern LONGLONG call_method( void *func, int nb_args, const DWORD *args, int *stack_offset );
__ASM_GLOBAL_FUNC( call_method,
"pushl %ebp\n\t"
__ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
@ -5718,16 +5718,20 @@ __ASM_GLOBAL_FUNC( call_method,
"pushl %edi\n\t"
__ASM_CFI(".cfi_rel_offset %edi,-8\n\t")
"movl 12(%ebp),%edx\n\t"
"movl %esp,%edi\n\t"
"shll $2,%edx\n\t"
"jz 1f\n\t"
"subl %edx,%esp\n\t"
"andl $~15,%esp\n\t"
"subl %edx,%edi\n\t"
"andl $~15,%edi\n\t"
"movl %edi,%esp\n\t"
"movl 12(%ebp),%ecx\n\t"
"movl 16(%ebp),%esi\n\t"
"movl %esp,%edi\n\t"
"cld\n\t"
"rep; movsl\n"
"1:\tcall *8(%ebp)\n\t"
"subl %esp,%edi\n\t"
"movl 20(%ebp),%ecx\n\t"
"movl %edi,(%ecx)\n\t"
"leal -8(%ebp),%esp\n\t"
"popl %edi\n\t"
__ASM_CFI(".cfi_same_value %edi\n\t")
@ -5738,6 +5742,9 @@ __ASM_GLOBAL_FUNC( call_method,
__ASM_CFI(".cfi_same_value %ebp\n\t")
"ret" )
/* same function but returning floating point */
static const double (*call_double_method)(void*,int,const DWORD*,int*) = (void *)call_method;
/* ITypeInfo::Invoke
*
* Invokes a method, or accesses a property of an object, that implements the
@ -5746,6 +5753,7 @@ __ASM_GLOBAL_FUNC( call_method,
DWORD
_invoke(FARPROC func,CALLCONV callconv, int nrargs, DWORD *args) {
DWORD res;
int stack_offset;
if (TRACE_ON(ole)) {
int i;
@ -5758,7 +5766,7 @@ _invoke(FARPROC func,CALLCONV callconv, int nrargs, DWORD *args) {
switch (callconv) {
case CC_STDCALL:
case CC_CDECL:
res = call_method( func, nrargs, args );
res = call_method( func, nrargs, args, &stack_offset );
break;
default:
FIXME("unsupported calling convention %d\n",callconv);
@ -5796,6 +5804,10 @@ __ASM_GLOBAL_FUNC( call_method,
"movq 8(%rsp),%rdx\n\t"
"movq 16(%rsp),%r8\n\t"
"movq 24(%rsp),%r9\n\t"
"movq %rcx,%xmm0\n\t"
"movq %rdx,%xmm1\n\t"
"movq %r8,%xmm2\n\t"
"movq %r9,%xmm3\n\t"
"callq *%rax\n\t"
"leaq -16(%rbp),%rsp\n\t"
"popq %rdi\n\t"
@ -5808,6 +5820,9 @@ __ASM_GLOBAL_FUNC( call_method,
__ASM_CFI(".cfi_same_value %rbp\n\t")
"ret")
/* same function but returning floating point */
static const double (CDECL *call_double_method)(void*,int,const DWORD_PTR*) = (void *)call_method;
#endif /* __x86_64__ */
static HRESULT userdefined_to_variantvt(ITypeInfo *tinfo, const TYPEDESC *tdesc, VARTYPE *vt)
@ -5989,10 +6004,10 @@ DispCallFunc(
VARTYPE* prgvt, VARIANTARG** prgpvarg, VARIANT* pvargResult)
{
#ifdef __i386__
int argspos;
int argspos, stack_offset;
void *func;
UINT i;
DWORD *args;
LONGLONG ret;
TRACE("(%p, %ld, %d, %d, %d, %p, %p, %p (vt=%d))\n",
pvInstance, oVft, cc, vtReturn, cActuals, prgvt, prgpvarg,
@ -6005,21 +6020,26 @@ DispCallFunc(
}
/* maximum size for an argument is sizeof(VARIANT) */
args = HeapAlloc( GetProcessHeap(), 0, sizeof(VARIANT) * cActuals + sizeof(DWORD) );
args = HeapAlloc( GetProcessHeap(), 0, sizeof(VARIANT) * cActuals + sizeof(DWORD) * 2 );
argspos = 0;
/* start at 1 in case we need to pass a pointer to the return value as arg 0 */
argspos = 1;
if (pvInstance)
{
args[0] = (DWORD)pvInstance; /* the This pointer is always the first parameter */
argspos++;
const FARPROC *vtable = *(FARPROC **)pvInstance;
func = vtable[oVft/sizeof(void *)];
args[argspos++] = (DWORD)pvInstance; /* the This pointer is always the first parameter */
}
else func = (void *)oVft;
for (i=0;i<cActuals;i++)
for (i = 0; i < cActuals; i++)
{
VARIANT *arg = prgpvarg[i];
switch (prgvt[i])
{
case VT_EMPTY:
break;
case VT_I8:
case VT_UI8:
case VT_R8:
@ -6029,16 +6049,13 @@ DispCallFunc(
argspos += sizeof(V_I8(arg)) / sizeof(DWORD);
break;
case VT_DECIMAL:
memcpy( &args[argspos], &V_DECIMAL(arg), sizeof(V_DECIMAL(arg)) );
argspos += sizeof(V_DECIMAL(arg)) / sizeof(DWORD);
break;
case VT_VARIANT:
memcpy( &args[argspos], arg, sizeof(*arg) );
argspos += sizeof(*arg) / sizeof(DWORD);
break;
case VT_RECORD:
FIXME("VT_RECORD not implemented\n");
/* fall through */
case VT_BOOL: /* VT_BOOL is 16-bit but BOOL is 32-bit, needs to be extended */
args[argspos++] = V_BOOL(arg);
break;
default:
args[argspos++] = V_UI4(arg);
break;
@ -6047,29 +6064,47 @@ DispCallFunc(
dump_Variant(arg);
}
if (pvInstance)
switch (vtReturn)
{
FARPROC *vtable = *(FARPROC**)pvInstance;
ret = call_method(vtable[oVft/sizeof(void *)], argspos, args);
case VT_EMPTY:
call_method( func, argspos - 1, args + 1, &stack_offset );
break;
case VT_R4:
V_R4(pvargResult) = call_double_method( func, argspos - 1, args + 1, &stack_offset );
break;
case VT_R8:
case VT_DATE:
V_R8(pvargResult) = call_double_method( func, argspos - 1, args + 1, &stack_offset );
break;
case VT_DECIMAL:
case VT_VARIANT:
args[0] = (DWORD)pvargResult; /* arg 0 is a pointer to the result */
call_method( func, argspos, args, &stack_offset );
break;
case VT_I8:
case VT_UI8:
case VT_CY:
V_UI8(pvargResult) = call_method( func, argspos - 1, args + 1, &stack_offset );
break;
default:
V_UI4(pvargResult) = call_method( func, argspos - 1, args + 1, &stack_offset );
break;
}
else
/* if we aren't invoking an object then the function pointer is stored
* in oVft */
ret = call_method((FARPROC)oVft, argspos, args);
if (pvargResult && (vtReturn != VT_EMPTY))
HeapFree( GetProcessHeap(), 0, args );
if (stack_offset && cc == CC_STDCALL)
{
TRACE("Method returned %s\n",wine_dbgstr_longlong(ret));
V_VT(pvargResult) = vtReturn;
V_UI8(pvargResult) = ret;
WARN( "stack pointer off by %d\n", stack_offset );
return DISP_E_BADCALLEE;
}
HeapFree(GetProcessHeap(),0,args);
if (vtReturn != VT_VARIANT) V_VT(pvargResult) = vtReturn;
TRACE("retval: "); dump_Variant(pvargResult);
return S_OK;
#elif defined(__x86_64__)
int argspos;
UINT i;
DWORD_PTR *args, ret;
DWORD_PTR *args;
void *func;
TRACE("(%p, %ld, %d, %d, %d, %p, %p, %p (vt=%d))\n",
pvInstance, oVft, cc, vtReturn, cActuals, prgvt, prgpvarg,
@ -6081,15 +6116,18 @@ DispCallFunc(
return E_INVALIDARG;
}
/* maximum size for an argument is sizeof(VARIANT) */
args = HeapAlloc( GetProcessHeap(), 0, sizeof(VARIANT) * cActuals + sizeof(DWORD_PTR) );
/* maximum size for an argument is sizeof(DWORD_PTR) */
args = HeapAlloc( GetProcessHeap(), 0, sizeof(DWORD_PTR) * (cActuals + 2) );
argspos = 0;
/* start at 1 in case we need to pass a pointer to the return value as arg 0 */
argspos = 1;
if (pvInstance)
{
args[0] = (DWORD_PTR)pvInstance; /* the This pointer is always the first parameter */
argspos++;
const FARPROC *vtable = *(FARPROC **)pvInstance;
func = vtable[oVft/sizeof(void *)];
args[argspos++] = (DWORD_PTR)pvInstance; /* the This pointer is always the first parameter */
}
else func = (void *)oVft;
for (i = 0; i < cActuals; i++)
{
@ -6097,24 +6135,13 @@ DispCallFunc(
switch (prgvt[i])
{
case VT_R4:
case VT_R8:
case VT_DATE:
FIXME(" floating point not supported properly\n" );
memcpy( &args[argspos], &V_R8(arg), sizeof(V_R8(arg)) );
argspos++;
break;
case VT_DECIMAL:
memcpy( &args[argspos], &V_DECIMAL(arg), sizeof(V_DECIMAL(arg)) );
argspos += sizeof(V_DECIMAL(arg)) / sizeof(DWORD_PTR);
break;
case VT_VARIANT:
memcpy( &args[argspos], arg, sizeof(*arg) );
argspos += sizeof(*arg) / sizeof(DWORD_PTR);
args[argspos++] = (ULONG_PTR)arg;
break;
case VT_BOOL: /* VT_BOOL is 16-bit but BOOL is 32-bit, needs to be extended */
args[argspos++] = V_BOOL(arg);
break;
case VT_RECORD:
FIXME("VT_RECORD not implemented\n");
/* fall through */
default:
args[argspos++] = V_UI8(arg);
break;
@ -6123,23 +6150,27 @@ DispCallFunc(
dump_Variant(arg);
}
if (pvInstance)
switch (vtReturn)
{
FARPROC *vtable = *(FARPROC**)pvInstance;
ret = call_method(vtable[oVft/sizeof(void *)], argspos, args);
case VT_R4:
V_R4(pvargResult) = call_double_method( func, argspos - 1, args + 1 );
break;
case VT_R8:
case VT_DATE:
V_R8(pvargResult) = call_double_method( func, argspos - 1, args + 1 );
break;
case VT_DECIMAL:
case VT_VARIANT:
args[0] = (DWORD_PTR)pvargResult; /* arg 0 is a pointer to the result */
call_method( func, argspos, args );
break;
default:
V_UI8(pvargResult) = call_method( func, argspos - 1, args + 1 );
break;
}
else
/* if we aren't invoking an object then the function pointer is stored
* in oVft */
ret = call_method((FARPROC)oVft, argspos, args);
if (pvargResult && (vtReturn != VT_EMPTY))
{
TRACE("Method returned 0x%lx\n",ret);
V_VT(pvargResult) = vtReturn;
V_UI8(pvargResult) = ret;
}
HeapFree(GetProcessHeap(),0,args);
HeapFree( GetProcessHeap(), 0, args );
if (vtReturn != VT_VARIANT) V_VT(pvargResult) = vtReturn;
TRACE("retval: "); dump_Variant(pvargResult);
return S_OK;
#else