I'm trying to convert System::Windows::Forms::IDataObject into the native IDataObject defined in the ObjIdl.h. Unfortunately the following source code doesn't return the native IDataObject.
IntPtr pData = System::Runtime::InteropServices::Marshal::GetIUnknownForObject(e->Data);
::IDataObject* data = (::IDataObject*) pData.ToPointer();
How can I get the native IDataObject in a Windows Forms application during Drag and Drop?
Casting a COM interface pointer with a C-style cast is not legal. You have to ask nicely with the QueryInterface() method. Like this:
System::Windows::Forms::IDataObject^ obj = Clipboard::GetDataObject();
::IUnknown* punk = (::IUnknown*)System::Runtime::InteropServices::Marshal::GetIUnknownForObject(obj).ToPointer();
::IDataObject* pdata = nullptr;
HRESULT hr = punk->QueryInterface(__uuidof(::IDataObject), (void**)&pdata);
if (SUCCEEDED(hr)) {
// etc..
}
Related
i want to pass a com object instance as a variant parameter to another active x object function, for that i need to convert the idispatch pointer to a variant? i am not sure.
hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
if (FAILED(hr))
{
return;
}
hr = CLSIDFromProgID(objectName.c_str(), &clsid);
if (FAILED(hr))
{
return;
}
hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pApp));
if (FAILED(hr) || pApp == nullptr) {
return;
}
this is the instance creating code, after that i am using this :
VARIANT v;
VariantInit(&v);
v.pdispVal = pApp;
v.ppdispVal = &pApp;
v.vt = VT_DISPATCH;
return v;
and passing it to an active x method, but it is giving access violation after invoke.
what i am doing wrong?
If you want to use the VARIANT raw structure, you can code it like this:
VARIANT v;
VariantInit(&v);
pApp->AddRef();
v.pdispVal = pApp;
v.vt = VT_DISPATCH;
...
// later on, some code (this code or another code) will/should call this
VariantClear(&v); // implicitely calls pdispVal->Release();
Or, if you're using the Visual Studio development environment, then you can just use the _variant_t or CComVariant (ATL) smart wrappers which I recommend. In this case, you can just call it like this:
IDispatch *pApp = ...
// both wrappers will call appropriate methods
// and will release what must be, when destroyed
CComVariant cv = pApp;
// or
_variant_t vt = pApp;
PS: don't use both wrapper classes, make your choice. If a project uses ATL, I uses CComVariant, otherwise _variant_t, for example.
We have a COM API for our application (which is written in VC++) which exposes a few functionalities so that the users can automate their tasks. Now, I'm required to add a new method in that, which should return a list/array/vector of strings. Since I'm new to COM, I was looking at the existing methods in the .idl file for that interface.
One of the existing methods in that idl file looks like this:
interface ITestApp : IDispatch
{
//other methods ..
//...
//...
//...
[id(110), helpstring("method GetFileName")] HRESULT GetFileName([out, retval] BSTR *pFileName);
//...
//...
//...
};
My task is to write a similar new method, but instead of returning one BSTR string, it should return a list/array/vector of them.
How can I do that?
Thanks!
Since yours is an automation-compatible interface, you need to use safearrays. Would go something like this:
// IDL definition
[id(42)]
HRESULT GetNames([out, retval] SAFEARRAY(BSTR)* names);
// C++ implementation
STDMETHODIMP MyCOMObject::GetNames(SAFEARRAY** names) {
if (!names) return E_POINTER;
SAFEARRAY* psa = SafeArrayCreateVector(VT_BSTR, 0, 2);
BSTR* content = NULL;
SafeArrayAccessData(psa, (void**)&content);
content[0] = SysAllocString(L"hello");
content[1] = SysAllocString(L"world");
SafeArrayUnaccessData(psa);
*names = psa;
return S_OK;
}
Error handling is left as an exercise for the reader.
I am trying to write a CLI wrapper around some low-level COM-related calls. One of the operations that I need to do specifically is to get a specific value from a PROPVARIANT, i.e.:
pwszPropName = varPropNames.calpwstr.pElems[dwPropIndex];
where pwszPropName is documented to be an LPWSTR type and dwPropIndex is a DWORD value passed into the function by the user.
I have a native function defined as follows:
HRESULT CMetadataEditor::GetPropertyNameByID(DWORD ID, wchar_t *PropertyName)
I would like to return the value of pwszPropName via *PropertyName.
Is the wchar_t* type the best way to do this, and would I need to pin *PropertyName in my CLI to ensure it does not move in memory? Do I need to define the length of *PropertyName before passing it to native code (buffer)?
If wchar_t* is the right variable type to pass into the native function, what is the proper conversion of LPWSTR to whar_t*, and how then would you convert that value to System::String?
I have tried a number of different techniques over the past few days and can't seem to get anything right.
------------UPDATE------------
Here is my full code. First, the CLI:
String^ MetadataEditor::GetPropertyNameByID(unsigned int ID)
{
LPWSTR mPropertyName = L"String from CLI";
m_pCEditor->GetPropertyNameByID(ID, mPropertyName);
//Convert return back to System::String
String^ CLIString = gcnew String(mPropertyName);
return CLIString;
}
And the native code:
HRESULT CMetadataEditor::GetPropertyNameByID(DWORD ID, LPWSTR PropertyName)
{
HRESULT hr = S_OK;
LPWSTR myPropName;
PROPVARIANT varNames;
PropVariantInit(&varNames);
hr = m_pMetadata->GetAllPropertyNames(&varNames);
if(hr != S_OK)
{
PropVariantClear(&varNames);
return hr;
}
myPropName = varNames.calpwstr.pElems[ID];
PropertyName = myPropName;
PropVariantClear(&varNames);
return hr;
}
It doesn't seem like the value (myPropName) is set properly and/or sustained back into the CLI function because the CLI returns the value I set on mPropertyName before calling the native function.. I'm not sure why or how to fix this.
UPDATE!!!!
I suspected my problem had something to do with variables going out of scope. So I changed the C++ function definition as follows:
LPWSTR GetPropertyNameByID(DWORD ID, HRESULT ErrorCode);
After adjusting the CLI as well, I now get a value returned, but the first character is incorrect, and in fact can be different with every call. I tried using ZeroMemory() in the native class before assigning the output of the PROPVARIANT to the variable (ZeroMemory(&myPropName, sizeof(myPropName +1)); but still no luck.
You can design unmanaged function by the following way:
HRESULT CMetadataEditor::GetPropertyNameByID(DWORD ID, LPWSTR PropertyName, size_t size)
{
....
wcscpy(PropertyName, varNames.calpwstr.pElems[ID]); // or wcsncpy
...
}
PropertyName is the buffer allocated by caller, size is its size. Inside the function wcscpy or wcsncpy the string varNames.calpwstr.pElems[ID] to PropertyName. Client code:
WCHAR mPropertyName[100];
m_pCEditor->GetPropertyNameByID(ID, mPropertyName, sizeof(mPropertyName)/sizeof(mPropertyName[0]));
Think, for example, how GetComputerName API is implemented, and do the same
I am trying to use the IFileSystemImage2 interface to create an ISO with multiple boot records using Imapi2.
To do this, I should be able to use put_BootImageOptionsArray passing in SAFEARRAY* of VT_DISPATCH type, i.e. COM pointers of type IBootOptions for each boot options configuration. As a short demo, I have the following code (I only created one IBootOptions in this case):
SAFEARRAYBOUND bounds[1];
bounds[0].cElements = 1;
bounds[1].lLbound = 0;
IBootOptions* BootOptionsArrayData = NULL;
SAFEARRAY* Array = SafeArrayCreateEx(VT_DISPATCH,
1,
bounds,
(void*) &IID_IBootOptions);
hr = SafeArrayAccessData(Array,
reinterpret_cast<void**>(&BootOptionsArrayData));
BootOptionsArrayData = BootOptions; // BootOptions = IBootOptions*
hr = SafeArrayUnaccessData(Array);
hr = IsoImage->put_BootImageOptionsArray(Array);
However, every time I call put_BootImageOptionsArray I get E_NOINTERFACE returned.
IsoImage is being created as you'd expect:
hr = CoCreateInstance(CLSID_MsftFileSystemImage,
NULL,
CLSCTX_ALL,
__uuidof(IFileSystemImage2),
(void**) &IsoImage);
Using IFileSystemImage2 any inherited functionality from IFileSystemImage works fine. Likewise, I can CoCreateInstance a IFileSystemImage instead, and this interface can be used just fine.
I have attached to my process in WinDbg and set a breakpoint in CMsftFileSystemImage::put_BootOptionsArray, however, this function (the underlying implementation) simply isn't being called.
My question, therefore is simple: the implementation appears to be there, but I don't seem to be able to call it. Does anyone have any experience of using this particular bit of functionality and if so how did you get it to work?
The documentation stipulates the SAFEARRAY must be an array of VARIANT that contain IDispatch interface pointers, so you could do something like this (I'm using smart pointers which is easier...):
CComPtr<IFileSystemImage2> image;
CComPtr<IBootOptions> options;
image.CoCreateInstance(CLSID_MsftFileSystemImage);
options.CoCreateInstance(CLSID_BootOptions);
// set various options here...
options->put_Manufacturer(CComBSTR(L"joe"));
// create a SAFEARRAY of VARIANT
CComSafeArray<VARIANT> a(1);
// create a VARIANT of type VT_UNKNONW (or VT_DISPATCH)
CComVariant v(options);
// put it in the array
a.SetAt(0, v);
HRESULT hr = pImage->put_BootImageOptionsArray(a.m_psa);
Using ATL (VS2008) how can I enumerate the available methods available on a given IDispatch interface (IDispatch*)? I need to search for a method with a specific name and, once I have the DISPID, invoke the method (I know the parameters the method takes.) Ideally I would like to do this using smart COM pointers (CComPtr<>).
Is this possible?
You can enumerate the methods an IDispatch exposes through the type info. There are two ways to get the type info:
through the type library (if any) for the dispinterface.
through calling IDispatch::GetTypeInfo.
Unfortunately, an IDispatch implementation is not obligated to provide type info about the methods and properties it implements.
If it does, however, the basic enumerating involves calling ITypeInfo::GetTypeAttr to get the TYPEATTR for the interface and looking at the number of implemented methods (cFuncs) and variables (cVars) and looping over these and calling ITypeInfo::GetFuncDesc() or ITypeInfo::GetVarDesc(). Of course, there are lot more details you will have to deal with as I can list here, but this should be a good starting point for your exploration.
Here's a nice article explaining the process in more details with code in VB.Net.
Here's some code that does the enumeration (it inserts the [Dispatch ID]-[Method Name] pairs in a map, but that's easy to change).
///
/// \brief Returns a map of [DispId, Method Name] for the passed-in IDispatch object
///
HRESULT COMTools::GetIDispatchMethods(_In_ IDispatch * pDisp,
_Out_ std::map<long, std::wstring> & methodsMap)
{
HRESULT hr = S_OK;
CComPtr<IDispatch> spDisp(pDisp);
if(!spDisp)
return E_INVALIDARG;
CComPtr<ITypeInfo> spTypeInfo;
hr = spDisp->GetTypeInfo(0, 0, &spTypeInfo);
if(SUCCEEDED(hr) && spTypeInfo)
{
TYPEATTR *pTatt = nullptr;
hr = spTypeInfo->GetTypeAttr(&pTatt);
if(SUCCEEDED(hr) && pTatt)
{
FUNCDESC * fd = nullptr;
for(int i = 0; i < pTatt->cFuncs; ++i)
{
hr = spTypeInfo->GetFuncDesc(i, &fd);
if(SUCCEEDED(hr) && fd)
{
CComBSTR funcName;
spTypeInfo->GetDocumentation(fd->memid, &funcName, nullptr, nullptr, nullptr);
if(funcName.Length()>0)
{
methodsMap[fd->memid] = funcName;
}
spTypeInfo->ReleaseFuncDesc(fd);
}
}
spTypeInfo->ReleaseTypeAttr(pTatt);
}
}
return hr;
}
You can't enumerate all the available methods unless the object implements IDispatchEx.
However, if you know the name of the method you want to call, you can use GetIDsOfNames to map the name to the proper DISPID.
HRESULT hr;
CComPtr<IDispatch> dispatch;
DISPID dispid;
WCHAR *member = "YOUR-FUNCTION-NAME-HERE";
DISPPARAMS* dispparams;
// Get your pointer to the IDispatch interface on the object here. Also setup your params in dispparams.
hr = dispatch->GetIDsOfNames(IID_NULL, &member, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
if (SUCCEEDED(hr)) {
hr = dispatch->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, dispparams, &varResult, NULL, NULL);
}
Edit: For completeness, I suspect there is a way to interrogate the ITypeInfo2 interface (assuming there is a type library for the object) that you get from IDispatch::GetTypeInfo for a list of methods, but I've not done it. See the other answer.