I developed an out-of-proc server and trying to marshal input params, but it fails.
My .idl file is:
[
uuid(6a4791c0-f69f-4d78-a591-f549c90c0620),
object,
oleautomation
]
interface IBrowserInterraction : IUnknown
{
HRESULT Unlock([in] BSTR str, [in] int value);
};
[
uuid(4d36f1c6-9ee6-4d9a-b0aa-6b7195cc5666),
version(1.0)
]
library BrowserInterractionInterfaceLibrary
{
[
uuid(cdebf15b-f12c-4559-a6ac-81620c5056c4)
]
coclass BrowserInterractionInterface
{
interface IBrowserInterraction;
};
};
My server code:
m_bpFactory = boost::make_shared<BrowserPluginInterractionClassFactory>(m_synchronizer);
IUnknown* pUnk;
m_bpFactory->QueryInterface(__uuidof(IUnknown), (void**)&pUnk);
CoRegisterClassObject(__uuidof(BrowserInterractionInterface), pUnk, CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &magic);
My client code:
std::string data("some value")
std::wstring wstr = std::wstring(password.begin(), password.end());
BSTR bs = SysAllocStringLen(wstr.data(), wstr.size());
IBrowserInterraction* interraction = NULL;
CoCreateInstance(__uuidof(BrowserInterractionInterface), NULL, CLSCTX_ALL, __uuidof(IBrowserInterraction), (void**)&interraction);
interraction->Unlock(bs, 123);
I debugged this client call it sends the right value. But server receives this:
I created a proxy\stub library, registered it with regsvr, and included _i.c and _h.h files in client and server code, and everythong was fine.
And client after call of Unlock functions shows me this error:
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
Related
I am working with WebAssembly and try to make an HTTPS request from C++. I have seen the solution of Emscripten FETCH API and tried to use it.
To test it, I created a Test class where I send the request like this :
void Test::sendRequest() {
emscripten_fetch_attr_t attr;
emscripten_fetch_attr_init(&attr);
strcpy(attr.requestMethod, "GET");
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
attr.onsuccess = &Test::onSuccess;
attr.onerror = &Test::onError;
emscripten_fetch(&attr, "http://127.0.0.1:5000/");
}
And my onSuccess callback look like this :
void Test::onSuccess(struct emscripten_fetch_t *fetch) {
printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url);
setText(QString::fromUtf8(fetch->data));
emscripten_fetch_close(fetch); // Free data associated with the fetch.
}
But, when I try to compile I have an error saying :
error: assigning to 'void (*)(struct emscripten_fetch_t *)' from incompatible type 'void
(Test::*)(struct emscripten_fetch_t *)'
attr.onsuccess = &Test::onSuccess;
^~~~~~~~~~~~~~~~
It seems that I can't put the callback function in the class, but I need to access the class in order to modify the text attribute of the instance with the response.
I tried to define the Test class with the Singleton pattern and to remove the callback function from the class. With this method I can modify the text attribute getting the unique instance of the class but I would like to put callback function in the class directly if possible.
You can't use a non-static member function as a callback directly.
However, most callback interfaces have a "user data" field somewhere for communicating back to the originator.
emscripten_fetch_attr_t has a void* userData member where you can store any pointer you want.
This pointer is passed as userData in the argument to the callbacks, and you just need to cast it back to the correct type.
So you can use a free function as a wrapping callback, with the object as "user data":
void onSuccess(struct emscripten_fetch_t *fetch) {
auto test = static_cast<Test*>(fetch->userData);
test->onSuccess(fetch);
}
void Test::sendRequest() {
emscripten_fetch_attr_t attr;
emscripten_fetch_attr_init(&attr);
strcpy(attr.requestMethod, "GET");
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
attr.userData = this;
attr.onsuccess = onSuccess;
// ...
And make sure that the object is alive when the callback fires.
So I asked this question in a C# context and I have set bounty over there.
I have written an equivalent fragment of C++ code (to be housed within an ATL DLL project) to tap C++ developers experience as well.
IDispatch has a method called GetTypeInfo() with which one can acquire a pointer to ITypeInfo. ITypeInfo itself has a method called GetContainingTypeLib which gets the containing ITypeLib (as it says). This is useful when given a CoClass instance once can get to all the other CoClasses in a given DLL, very useful for reflection.
I had wondered if Excel VBA would co-operate in a similar fashion and return a ITypeLib for the containing VBA project and equally allow me to iterate over all the types found therein. So I wrote some test C# code and that is linked. Here I give equivalent C++ code to tap C++ experience.
The IDL...
[
object,
uuid(ddc4e135-49d6-49f8-ad57-ded4180095fd),
dual,
nonextensible,
pointer_default(unique)
]
interface ICoClassGetContainingTypeLib : IDispatch
{
HRESULT GetContainingTypeLib(IDispatch* vbaClass, IUnknown** iunkTypeLib);
};
[
uuid(bc8410a6-802a-4f91-a73b-c03179bb402b)
]
coclass CoClassGetContainingTypeLib
{
[default] interface ICoClassGetContainingTypeLib;
};
The cpp method implementation
STDMETHODIMP CCoClassGetContainingTypeLib::GetContainingTypeLib(IDispatch* vbaClass, IUnknown** iunkTypeLib)
{
CComPtr<IDispatch> itfDispatch(vbaClass);
CComPtr<ITypeInfo> pITypeInfo;
HRESULT hr = S_OK;
HRESULT hr2 = S_OK;
try
{
hr = itfDispatch->GetTypeInfo(0, 0, &pITypeInfo);
if (SUCCEEDED(hr))
{
UINT* idx(0);
CComQIPtr<ITypeLib> pTLib;
HRESULT hr2 = pITypeInfo->GetContainingTypeLib(&pTLib, idx);
return hr2; // throws exception Exception 0x800A88C1
// which ends u[p at 35009
//TODO If successful copy to ByRef param iunkTypeLib
}
}
catch (std::exception ex)
{
return E_FAIL;
}
return S_OK;
}
And the header declaration
STDMETHOD(GetContainingTypeLib)(IDispatch* vbaClass, IUnknown** iunkTypeLib);
And some client VBA
Sub TestGetContainingTypeLib()
Dim oTR As ToolsReferences '* could be ANY VBA class
Set oTR = New ToolsReferences '* could be ANY VBA class
'* instantiate my ATL project
Dim oTest As GetContainingTypeLibTestLib.CoClassGetContainingTypeLib
Set oTest = New GetContainingTypeLibTestLib.CoClassGetContainingTypeLib
Dim iunkTypeLib As IUnknown
Set iunkTypeLib = Nothing '* not strictly required
Call oTest.GetContainingTypeLib(oTR, iunkTypeLib)
End Sub
There is very little on the Internet to explain the exception number of 0x800A88C1 . I think this the Excel team saying no.
Why this error is occurred? References count is increased, threading models is single appartment. Coll-object and EmptyColl-function both located inside one dll. Default calling conversion of ATL project is __stdcall. Same error occured with other objects inside this dll.
VariantClear throws exception when clearing VARIANT with NULL-object:
Exception thrown at 0x75C14974 (oleaut32.dll) in VB6.EXE: 0xC0000005:
Access violation reading location 0x00000008.
frmMain.frm (error, see below why):
Private Sub Form_Load()
Dim c As Coll
Set c = EmptyColl
'error when ends here with variable "c" in the watch window.
End Sub
frmMain.frm (no error):
Private Sub Form_Load()
Dim c2 As Coll 'instead of Coll can be any object of same library
Set c2 = New Coll 'creation
Set c2 = Nothing 'destroying (optionaly)
Dim c As Coll
Set c = EmptyColl
'no error
End Sub
filyus.idl:
[
object,
uuid(6FA7FAEB-5CE3-4A80-9288-2667EE5E7596),
dual,
nonextensible,
pointer_default(unique)
]
interface IColl : IDispatch{
//some methods
};
[
uuid(157F3D2F-A427-4D5A-B908-87868297EA43),
version(1.0),
]
library Filyus
{
importlib("stdole2.tlb");
[
dllname("Filyus")
]
module Filyus{
[entry("EmptyColl")]
HRESULT EmptyColl([out, retval] IColl** Coll);
}
};
filyus.def:
LIBRARY
EXPORTS
DllCanUnloadNow PRIVATE
DllGetClassObject PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
DllInstall PRIVATE
EmptyColl
ole.h:
extern HRESULT EmptyColl(IColl** Coll);
ole.cpp:
HRESULT EmptyColl(IColl** Coll) {
HRESULT hr; CComObject<CColl>* Object;
if (Coll != nullptr) {
hr = CComObject<CColl>::CreateInstance(&Object);
if (hr == S_OK) {
Object->AddRef();
*Coll = Object; //same error with using QueryInterface
}
}
else hr = E_POINTER;
return hr;
}
EmptyColl() needs to use the __stdcall calling convention:
extern HRESULT __stdcall EmptyColl(IColl** Coll);
HRESULT __stdcall EmptyColl(IColl** Coll) {
//...
}
Alternatively, use the STDMETHODCALLTYPE macro, which resolves to __stdcall:
extern HRESULT STDMETHODCALLTYPE EmptyColl(IColl** Coll);
HRESULT STDMETHODCALLTYPE EmptyColl(IColl** Coll) {
//...
}
Without a calling convention declared, by default a C/C++ compiler will use __cdecl instead unless configured differently. __cdecl and __stdcall manage the call stack differently. You will corrupt the call stack if you don't use the correct calling convention. COM standards require __stdcall, and that is what VB expects.
The error is occurred because of wrong access to the object.
CComPtr is for client side, CComObject is for server side (direct access, obtain it only when you already created any object of this library).
Correct ole.cpp:
HRESULT EmptyColl(IColl** Coll) {
HRESULT hr; CComPtr<IColl> Object;
if (Coll != nullptr) {
hr = Object.CoCreateInstance(CLSID_Coll);
if (hr == S_OK) {
Object.CopyTo(Coll);
}
}
else hr = E_POINTER;
return hr;
}
The old C++ error module implements one interface:
[
object,
uuid(7286455D-A48B-11D1-8C7E-006097089085),
dual,
helpstring("IDQSEror Interface"),
pointer_default(unique)
]
interface IDQSEror : IDispatch
{
[id(1), helpstring("method getLastError")] HRESULT getLastError([out] BSTR
...
When everything was in C++ this interface could be passed between other COM modules without problem. However, we've converted a middle module to VB.NET and returning the IDQSEror required casting:
NAVInitServr (VB.NET COM)
References
MainModule.PIA (interop assembly #include IDQError)
DQSError.PIA (interop assembly defines IDQError)
NavInit.vb
- Is called by MainModule
- Creates a DQSEror class
- Returns IDQSEror to the MainModule
- Requires casting:
Dim error as DQSError = new DQSEror()
return DirectCast(error, MainModule.PIA.IDQSerror)
Otherwise there is a casting error. We have rewritten DQSError in VB.NET:
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)
Public Interface IDQSEror
DispId(1)
Sub getLastError()
...
End Interface
The interface has the same GUID and DispId as the one in C++. The casting to return the error to the MainModule does not work anymore:
Dim error as DQSError = new DQSEror()
return DirectCast(error, MainModule.PIA.IDQSerror)
I am trying to call a .net webservice from a C++ ATL console Application. This is how my webmethod looks like:
[WebMethod]
public string[] GetFieryIP(string companyname)
{
IpAddress = new string[]{"1","2","3","4"};
return IpAddress;
}
In the c++ application, I have added a web reference.This is how i am accessing the webmethod:
BSTR str = SysAllocString(L"");
BSTR *ptr = new BSTR[4];
int ptr1;
HRESULT hr = ws.GetFieryIP(str, &ptr, &ptr1);
ws is the webservice proxy.
Is this correct? If yes, How do i get the IpAddress from the *ptr. I am new to C++ and dont have much idea abt pointers and COM.Please help.