托管CLR:原生C++反射调用含Enum参数的.NET构造方法失败如何解决
解决方案
完全可以通过反射调用含Enum参数的构造方法或方法,问题出在你直接传入Enum的基础数值类型(VT_I4),而没有将其包装为对应的Enum类型实例。CLR反射在匹配方法签名时要求参数类型严格匹配,Enum是独立的值类型,并非其基础整数类型的别名,因此直接传int会导致找不到对应构造方法。
具体实现步骤
要正确传递Enum参数,需要先将整数数值转换为对应的Enum类型实例,再将该实例作为参数传入反射调用:
获取目标Enum类型的Type对象
通过AppDomain->GetType_2方法获取System.IO.FileAccess的Type实例:BSTR enumTypeName = SysAllocString(L"System.IO.FileAccess"); ITypeInfo* pEnumTypeInfo = nullptr; IUnknown* pEnumTypeUnk = nullptr; HRESULT hr = pAppDomain->GetType_2(enumTypeName, &pEnumTypeUnk); pEnumTypeUnk->QueryInterface(IID_ITypeInfo, (void**)&pEnumTypeInfo); SysFreeString(enumTypeName);将整数转换为Enum实例
调用Enum::ToObject静态方法,把基础数值(比如FileAccess::Read对应的0)转换为Enum类型对象:// 获取Enum类型的ToObject方法DISPID BSTR methodName = SysAllocString(L"ToObject"); DISPID dispidToObject; hr = pEnumTypeInfo->GetIDsOfNames(IID_NULL, &methodName, 1, LOCALE_USER_DEFAULT, &dispidToObject); SysFreeString(methodName); // 准备ToObject的参数:第一个是Type对象,第二个是int值 VARIANT argsToObj[2]; VariantInit(&argsToObj[0]); argsToObj[0].vt = VT_DISPATCH; pEnumTypeUnk->QueryInterface(IID_IDispatch, (void**)&argsToObj[0].pdispVal); VariantInit(&argsToObj[1]); argsToObj[1].vt = VT_I4; argsToObj[1].lVal = 0; // 对应FileAccess::Read // 调用ToObject方法得到Enum实例 VARIANT enumObj; VariantInit(&enumObj); DISPPARAMS dp = { argsToObj, nullptr, 2, 0 }; hr = pEnumTypeInfo->Invoke(nullptr, dispidToObject, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dp, &enumObj, nullptr, nullptr); // 释放临时资源 VariantClear(&argsToObj[0]); VariantClear(&argsToObj[1]); pEnumTypeInfo->Release(); pEnumTypeUnk->Release();构造FileStream的参数数组
将文件路径字符串和转换后的Enum实例放入SAFEARRAY:SAFEARRAY* pArgs = SafeArrayCreateVector(VT_VARIANT, 0, 2); LONG indices[1]; // 第一个参数:文件路径 indices[0] = 0; VARIANT filePathVar; VariantInit(&filePathVar); filePathVar.vt = VT_BSTR; filePathVar.bstrVal = SysAllocString(L"C:\\test.txt"); SafeArrayPutElement(pArgs, indices, &filePathVar); VariantClear(&filePathVar); // 第二个参数:FileAccess Enum实例 indices[0] = 1; SafeArrayPutElement(pArgs, indices, &enumObj); VariantClear(&enumObj);调用CreateInstance_3创建FileStream
此时传入的参数类型严格匹配构造方法签名,就能成功创建对象:BSTR assemblyName = SysAllocString(L"mscorlib"); BSTR typeName = SysAllocString(L"System.IO.FileStream"); IUnknown* result = nullptr; HRESULT hr = pAppDomain->CreateInstance_3(assemblyName, typeName, VARIANT_TRUE, BindingFlags_Default, NULL, pArgs, NULL, NULL, NULL, &result); // 释放资源 SysFreeString(assemblyName); SysFreeString(typeName); SafeArrayDestroy(pArgs);
关键说明
- Enum类型在CLR中是继承自
System.Enum的值类型,反射调用时必须传递该类型的实例,而非其基础整数类型。 - 所有含Enum参数的方法/构造方法,都可以用上述流程处理:先将整数转换为对应Enum实例,再作为参数传入。
内容的提问来源于stack exchange,提问作者Smashery
相关产品推荐
相关产品推荐

