I've created a C++ DLL (it must be in C++) which dinamically links a .Net DLL to host a Web Service Server. The .Net DLL passes the web service calls to the C++ DLL, which as evaluated and responded also through the .Net DLL.
Something like that:
HSPWebService.DLL
-----------------------------------
HSPProxy.DLL | HSPWebServiceLib.DLL
-----------------------------------
HSPSendData.DLL
HSPWebService.DLL - the proper C++ DLL.
HSPProxy.DLL - proxy generated by the MIDL compiler over the HSP.DLL MIDL interface.
HSPWebServiceLib.DLL - typelib generated by the tlmbimp over the HSP.DLL.
HSPSendData.DLL - .Net DLL which hosts the web service server
Everything works like a charm. The problem is when the DLL files are in a network share (//myPc/share). My application log shows the error 0x80131515:
CreateAssemblyInstance ERROR: Could not create an assembly instance. (hr=80131515)
On my research I've found that the 0x80131515 error occurs because the .Net framework won't load assemblies from external sources by default. For .Net projects the option can be set into the project settings. But I have a C++ project on Visual Studio 2010 and I have no clue about using this configuration on my project (or my code). Any ideas?
The CreateAssemblyInstance function:
HRESULT CHSPWebServiceObjectHost::CreateAssemblyInstance(_AppDomain* pDefAppDomain, CComPtr<IDispatch>& spDisp, LPCTSTR pszAsseblyName, LPCTSTR pszClassNameWithNamespace) const
{
spDisp = NULL;
REQUIRE_IN_POINTER(pDefAppDomain);
try
{
_bstr_t _bstrAssemblyName(pszAsseblyName);
_bstr_t _bstrszClassNameWithNamespace(pszClassNameWithNamespace);
//Creates an Assembly instance
CComPtr<_ObjectHandle> spObjectHandle;
HRESULT hr = pDefAppDomain->CreateInstanceFrom(_bstrAssemblyName, _bstrszClassNameWithNamespace, &spObjectHandle);
if (FAILED(hr))
{
Log(logDriver, _T("CHSPWebServiceObjectHost::CreateAssemblyInstance ERROR: Could not create an assembly instance. (hr=%08X)"), hr);
return hr;
}
CComVariant VntUnwrapped;
hr = spObjectHandle->Unwrap(&VntUnwrapped);
if (FAILED(hr))
{
Log(logDriver, _T("CHSPWebServiceObjectHost::CreateAssemblyInstance ERROR: Could not unwrap assembly object. (hr=%08X)"), hr);
return hr;
}
spDisp = VntUnwrapped.pdispVal;
}
catch (_com_error& e)
{
return e.Error();
}
return S_OK;
}
It is called by the StartCLR function:
HRESULT CHSPWebServiceObjectHost::StartCLR(CComPtr<ICorRuntimeHost>& spRuntimeHost, CComPtr<IDispatch>& spDispHost) const
{
spRuntimeHost = NULL;
spDispHost = NULL;
//Retrieve a pointer to the ICorRuntimeHost interface
HRESULT hr = CorBindToRuntimeEx(L"v4.0.30319",
L"wks",
STARTUP_LOADER_SAFEMODE | STARTUP_CONCURRENT_GC,
CLSID_CorRuntimeHost,
IID_ICorRuntimeHost,
(void**)&spRuntimeHost);
if (FAILED(hr))
{
Log(logDriver, _T("CHSPWebServiceObjectHost::StartCLR ERROR: Could not load CLR into unmanaged host process. ( hr=%08X)"), hr);
return hr;
}
//Start the CLR
hr = spRuntimeHost->Start();
if (FAILED(hr))
{
Log(logDriver, _T("CHSPWebServiceObjectHost::StartCLR ERROR: Could not start CLR. (hr=%08X)"), hr);
return hr;
}
//Retrieve the IUnknown default AppDomain
CComPtr<IUnknown> spUnknown;
hr = spRuntimeHost->GetDefaultDomain(&spUnknown);
if (FAILED(hr))
{
Log(logDriver, _T("CHSPWebServiceObjectHost::StartCLR ERROR: Could not retrieve pointer to domain interface. ( hr=%08X)"), hr);
return hr;
}
CComQIPtr<_AppDomain> spDefAppDomain(spUnknown);
if (spDefAppDomain == NULL)
return E_NOINTERFACE;
CString strAssemblyFullPath = _T(".\\HSPSendData.dll");
return CreateAssemblyInstance(spDefAppDomain, spDispHost, strAssemblyFullPath, _T("Elipse.HSPWebService. HSPWebServiceHost"));
}
Given that your assemblies are located on a network share, you need to add a <loadFromRemoteSources> element to your app.config file. For example:
<configuration>
<runtime>
<loadFromRemoteSources enabled="true"/>
</runtime>
</configuration>
You don't mention your host process, but assuming it is MyService.exe this would go in the file MyService.exe.config.
Aparently the .NET updates have resolved this issue. I've tried to backtrack which update had it done but I wasn't able to find it.
Related
I'm trying to rewrite some code that calls a local COM Server from C# to C++. The C# code works without issue. The key part is:
Guid lr_FactoryGuid = Guid.Parse("AE7CFA4B-985A-4F76-8CC6-2011649FC8A9");
Guid lr_FactoryClass = Guid.Parse("1CA0D073-4ABB-4D06-B318-BFFDE38E4903");
IntPtr lk_FactoryPtr = new IntPtr();
CoGetClassObject(
ref lr_FactoryClass,
4,
new IntPtr(),
ref lr_FactoryGuid,
out lk_FactoryPtr);
if (lk_FactoryPtr == IntPtr.Zero)
{
MessageBox.Show("lk_FactoryPtr == IntPtr.Zero");
return false;
}
I've tried to rewrite this into C++ and I can't get any further than here, the error is give as "No such interface supported":
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CLSID clsid;
HRESULT hr = CLSIDFromString(L"{1CA0D073-4ABB-4D06-B318-BFFDE38E4903}", &clsid);
CLSID iid;
hr = CLSIDFromString(L"{AE7CFA4B-985A-4F76-8CC6-2011649FC8A9}", &iid);
void* pIFace;
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, iid, &pIFace);
if (!SUCCEEDED(hr))
{
_com_error err(hr);
LPCTSTR errMsg = err.ErrorMessage();
MessageBox(NULL, errMsg, L"SiteKiosk demo", MB_ICONEXCLAMATION | MB_OK);
}
There is a .tlb file that I used to generate the interop DLL for C# and to import into the C++, however it's currently commented out of the C++ in an attempt to keep the code smaller and I still get this error from CoCreateInstance.
The COM application I'm calling is a 32 bit app, so both my C# and C++ clients applications are also 32 bit. Both of the clients are Windows Console applications.
Is there anything else I need to set/do to get the C++ working?
The suggestion by Hans solved the problem, I used CoGetClassObject and the rest of the code then clicked into place.
I am new to Qt and I want to use the AudioSessionManager2 from the Windows SDK. In Qt Creator using this example I've written appended code below.
When compiling I get the messages
undefined reference to `__imp_CoCreateInstance'
undefined reference to `IID_IAudioSessionManager2'
In the documentation for the core audio APIs, Microsoft states the APIs are implemented in Mmdevapi.dll and Audioses.dll. While I hoped to find two matching .lib files the SDK I downloaded with Visual Studio, I only found mmdevapi.lib. After copying it to my project and adding it to the qt project file like below I still had no success with the same error message.
How am I supposed to know which lib files to import for which functions?
How do I get these lib files?
Did I import the lib file correctly?
audiomanager.h:
#ifndef AUDIOMANAGER_H
#define AUDIOMANAGER_H
#include <mmdeviceapi.h>
#include <audiopolicy.h>
#include "utils/SafeRelease.h"
class AudioManager {
public:
AudioManager();
~AudioManager();
private:
IAudioSessionManager2 *pSessionManager = nullptr;
HRESULT init();
};
#endif // AUDIOMANAGER_H
audiomanager.cpp
#include "audiomanager.h"
AudioManager::AudioManager () {
this->init();
}
AudioManager::~AudioManager () {
SafeRelease(&pSessionManager);
}
HRESULT AudioManager::init () {
HRESULT hr = S_OK;
// initialize needed vars
IMMDeviceEnumerator *pDeviceEnumerator = nullptr;
IMMDevice *pDevice = nullptr;
// get device enumerator
hr = CoCreateInstance(
__uuidof(MMDeviceEnumerator),
nullptr,
CLSCTX_ALL,
IID_PPV_ARGS(&pDeviceEnumerator)
);
if (FAILED(hr))
goto done;
// get the default audio output (for consoles)
// !!! only for consoles? (games, system, etc.), no music, video
pDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
if (FAILED(hr))
goto done;
// get the session manager
hr = pDevice->Activate(
IID_IAudioSessionManager2,
CLSCTX_ALL,
nullptr,
reinterpret_cast<void**>(&pSessionManager)
);
done:
SafeRelease(&pDeviceEnumerator);
SafeRelease(&pDevice);
return hr;
}
Line from Qt Project file
LIBS += "$$PWD\..\libs\mmdevapi.lib"
After hours of trying I figured out it would work with
// get the session manager
hr = pDevice->Activate(
__uuidof(IAudioSessionManager2),
CLSCTX_ALL,
nullptr,
reinterpret_cast<void**>(&pSessionManager)
);
instead of
// get the session manager
hr = pDevice->Activate(
IID_IAudioSessionManager2,
CLSCTX_ALL,
nullptr,
reinterpret_cast<void**>(&pSessionManager)
);
I don't know, why I cannot find proper libraries for the IID_IAudioSessionManager, nor why the Microsoft source code example lists this, but the other provided option seems to work.
For a web application, in the msi installer, I used a custom dll and trying the following C++ code to enable 32-bit application on win64 for an application pool, the hr result shows success but still the Enable32BitAppOnWin64 is false for the application pool. pSiteElem is IAppHostElement type and already validated by getting the name of the application pool successfully.
VARIANT vtEnable32Bit;
vtEnable32Bit.vt = VT_BOOL;
vtEnable32Bit.boolVal = true;
hr = pSiteElem->SetMetadata(L"Enable32BitAppOnWin64", vtEnable32Bit);
if(FAILED(hr))
{
MessageBox(NULL, L"Enable32BitAppOnWin64: ",L"FAILED", MB_OK);
}
else
{
MessageBox(NULL, L"Enable32BitAppOnWin64: ",L"success", MB_OK);
}
How can i open the AutoCAD application and send commands to that in C++?
In the VB it's possible by CreateObject and GetObject functions.
In C++, you need to use CoCreateInstance instead of CreateObject and CoGetObject instead of GetObject.
Here is some sample code, adapted from this sample of Microsoft:
// Initialize COM for this thread...
CoInitialize(NULL);
// Get CLSID for our server...
CLSID clsid;
HRESULT hr = CLSIDFromProgID(L"AutoCAD.Application", &clsid);
if(FAILED(hr)) {
return -1;
}
// Start server and get IDispatch...
IDispatch *pAcadApp;
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&pAcadApp);
if(FAILED(hr)) {
return -2;
}
You may want to use C# instead of C++, the .Net syntax is much more friendly than the C++ if you start to deal with COM pointers.
Here is an example. You can always create a .Net CLI library and wrap your C++ code so you can use it from .Net.
void LaunchACAD()
{
try
{
//Connect to a running instance
AcadApp = (AcadApplication)System.Runtime.InteropServices.Marshal.GetActiveObject(
"AutoCAD.Application");
}
catch(Exception ex)
{
// starts last run acad version
System.Type acType = System.Type.GetTypeFromProgID("AutoCAD.Application", true);
// ("AutoCAD.Application.17.1"); // starts 2008
// ("AutoCAD.Application.17.2"); // starts 2009
AcadApp = (AcadApplication)System.Activator.CreateInstance(acType);
}
AcadApp.Visible = true; // by the time this is reached AutoCAD is fully functional
}
I’m stuck with an issue and I’m pretty sure that this forum will be able to help me for this.
I’m trying to add few automation objects to the scripts (hosted via a separate process (ATL EXE server)).
Also I am trying to sink the events from these automation objects inside my ATL EXE script engine.
To achive this I’m using CDispExSinkConnector as described in http://www.codeproject.com/Articles/3326/Extending-the-Internet-Explorer-Scripting-Engine
As described I’m using IDispatchEx::GetDispID()to create new members inside the scripting engine. Once the member DISPID has been created, I’m using InvokeEx()to set the property value.
But these method calls return RPC_E_SYS_CALL_FAILED.
Note- Here I get the IDispEx pointer from an OUT-OF-PROC IDispatch pointer.
More detailed descriptions:
I do have a BrowserCtrl hosted by CAxWindow in an ATL EXE server which I’m using to create HTML plug-ins for my MFC client application.
The ATL EXE server provides wrapper methods to IWebBrowser2 methods so that I can call methods like IWebBrowser2Ptr->Navigate2().
Also I can subscribe to IWebBrowser2 events like OnDocumentComplete(….)in this ATL EXE server and ultimately forward those to my MFC Client application to handle those there.
Below are few code snippets for my ATL EXE server (BrowserCtrl):
1st is CreateControl() which creates the CAxWindow, creates the WebBrowser Control and host the control on internal default CAxHostWindow.
STDMETHODIMP BrowserCtrl::CreateControl(ULONG hwndParent, LONG left,LONG top, LONG right, LONG bottom)
{
CRect rc(left, top, right, bottom);
// Create the AX host window. m_wndBrowserCtrl is CAxWindow object
HWND hwndAxHost = m_wndBrowserCtrl.Create(reinterpret_cast<HWND>(hwndParent), &rc, L"Shell.Explorer", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, ID_BROWSERCTRL );
//Obtain a pointer to the container object
CComQIPtr<IAxWinHostWindow> ptrWinHost;
HRESULT hr = m_wndBrowserCtrl.QueryHost(&ptrWinHost);
LPOLESTR lpszCLSID = NULL;
hr = StringFromCLSID(CLSID_WebBrowser, &lpszCLSID);
// Create the WebBrowser Ctrl
CComPtr<IUnknown> cpIUnknown = NULL;
hr = ptrWinHost->CreateControlEx(lpszCLSID, m_wndBrowserCtrl, NULL, &cpIUnknown, __uuidof(SHDocVw::DWebBrowserEvents2), reinterpret_cast<IUnknown*>(reinterpret_cast<DWebBrowserEvents2Impl*>(this)));
// Get the IWebBrowser2 interface pointer for the control
// CComQIPtr<IWebBrowser2> m_cpIBrowser;
m_cpIBrowser = cpIUnknown;
return S_OK;
}
//Now the wrapper method for IWebBrowser2 methods
STDMETHODIMP BrowserCtrl::Navigate2(VARIANT *pwszUrl, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers)
{
HRESULT hr = m_cpIBrowser->Navigate2(pwszUrl, Flags,TargetFrameName,PostData,Headers);
}
//Now the event handlers for the IWebBrowser2 events:
void __stdcall BrowserCtrl::OnDocumentComplete(IDispatch* pDisp, VARIANT* pvaURL)
{
Fire_OnDocumentComplete(pDisp , pvaURL);// forwarding to the MGC client APP
}
Now the issue:
1.On the MFC Client APP and in the event handler function OnDocumentComplete() I do the following:
void MFCClientApp::HandleDocumentComplete(LPDISPATCH pIDisp, VARIANT* pvaURL)
{
CComPtr<IDispatch> pDispDoc;
CComQIPtr<IDispatch> pScriptDisp;
CComQIPtr<IDispatchEx> pDispEx;
CComQIPtr<IHTMLDocument> pHTMLDoc;
CComQIPtr<IWebBrowser2> pWebBrowser;
if (pIDisp != NULL)
{
pWebBrowser = pIDisp;
//CComPtr<IDispatch> ptrIDisp;
//BOOL bResult = GetOPBrowser()->GetDisp(&ptrIDisp);
//pWebBrowser = ptrIDisp;
pWebBrowser->get_Document(&pDispDoc);
if (pDispDoc != NULL)
{
// Get the IDispatchEx interface
pHTMLDoc = pDispDoc;
if (pHTMLDoc != NULL)
{
pHTMLDoc->get_Script(&pScriptDisp);
pDispEx = pScriptDisp;
if (pDispEx != NULL)
{
CDispExSinkConnector pDispExSinkConnector = new CDispExSinkConnector();
Hr = pDispExSinkConnector->SetDispEx(pDispEx); // here the Hr returned is 0x80010100 “System Call Failed” i.e. RPC_E_SYS_CALL_FAILED
……
……..
………
}
Here is the SetDispEx() and internal related functions:
void CDispExSinkConnector::SetDispEx(IDispatchEx *pDispEx)
{
CComBSTR bstrThisGuid("AC0B188C-6B55-408f-9E8C-821B9B5467CB"); // #!I8NIGNORE
HRESULT hr = m_pDispExGIT.Attach(pDispEx);//add it to the GIT Table
ASSERT(pDispEx);
// We need to add ourselves to the script engine
AddNamedObject(bstrThisGuid, this);
}
HRESULT CDispExSinkConnector::AddNamedObject(BSTR bsName, IDispatch *pDisp)
{
HRESULT hr = 0;
CComPtr<IDispatchEx> pDispEx;
GetDispEx(&pDispEx);
if(pDispEx == NULL)
return E_FAIL;
if(!bsName || SysStringLen(bsName) == 0 || !pDisp)
return E_INVALIDARG;
// Also, add our root objects into the script namespace
DISPID dispIdThis = 0;
hr = pDispEx->GetDispID(bsName, fdexNameEnsure | fdexNameCaseSensitive,&dispIdThis); // here the Hr returned is 0x80010100 “System Call Failed” i.e. RPC_E_SYS_CALL_FAILED
if (hr == DISP_E_UNKNOWNNAME)
…
..
}
Please help me to find out the root cause and a solution to resolve this issue.
Your quick help will be much appreciated.
Thanks,
SP