Calling a COM dll from c++, "Class Not Registered" - c++

I have created a COM dll in Microsoft Visual Basic 2008. I am trying to call this dll from a C++ project. In the C++ I used "#import U:\path...\MyComDll.tlb" I then used the following code to the DisplayMessage() method.
FB::variant CmdAccessAPI::filePSV(std::string file)
{
CoInitialize(NULL);
try
{
_MyComClassPtr spIMyComClass;
HRESULT hr = spIMyComClass.CreateInstance(__uuidof(_MyComClass));
if (FAILED(hr)) throw _com_error(hr);
spIMyComClass->DisplayMessage();
}
catch (std::exception& e)
{
CString strMsg(e.what());
MessageBox(NULL, strMsg, L"Error", MB_OK);
}
catch (_com_error& e)
{
CString strMsg;
strMsg = (TCHAR*) e.Description();
strMsg += _T("\n");
strMsg += (TCHAR*) e.ErrorMessage();
MessageBox(NULL, strMsg, L"COM Error", MB_OK);
}
CoUninitialize();
return "test";
}
When I call this function I get a Class Not Registered error. I have tried to register the dll using regsvr32 and I get the message "MyComDll.dll was loaded, but the DLLREgeisterServer entry point was not found. This file can not be registered."
How do I register the class and get this to work?

Try registering it using admin privileges,
Right click on the Command prompt and choose run as administrator, and give the systems 32 path.
Then use regsvr32 xyz.dll

If you can't register it, there are two possibilities
You are missing a dependent library. Download dependency walker and see what's missing. Some of the ones that are marked missing are red herrings (ieshims.dll for example), but one of them may be yours
Something went way off the rails in your COM setup. You can debug your dll as its being registered and dig into the ATL code where its failing.

You need to use regsvr32 to register the DLL, as you suspect.
However, the error its giving suggests that either the DLL doesn't have the stub included (and so cannot register itself) or you're trying to register the library, not the stub.
Make sure your VS project and the code is set up to include the proxy/stub functions in your DLL, or make sure you register the proxy DLL.

**include the below code** and export these functions with .def file
wchar_t *convertCharArrayToLPCWSTR(const char* charArray)
{
wchar_t* wString=new wchar_t[4096];
MultiByteToWideChar(CP_ACP, 0, charArray, -1, wString, 4096);
return wString;
}
BOOL Register( HKEY mainKey,const char *subKey,LPCTSTR val_name, DWORD dwType,char * chardata,DWORD dwDataSize)
{
HKEY hk;
if (ERROR_SUCCESS != RegCreateKey(mainKey,convertCharArrayToLPCWSTR(subKey),&hk) )
return FALSE;
LPCTSTR data=convertCharArrayToLPCWSTR(chardata);
if (ERROR_SUCCESS != RegSetValueEx(hk,val_name,0,dwType,(CONST BYTE *)data,2*dwDataSize))
return FALSE;
if (ERROR_SUCCESS != RegCloseKey(hk))
return FALSE;
return TRUE;
}
HRESULT __stdcall DllRegisterServer(void)
{
WCHAR *lpwszClsid;
char szBuff[MAX_PATH]="new multiplication Algorithm";
char szClsid[MAX_PATH]="", szInproc[MAX_PATH]="",szProgId[MAX_PATH];
char szDescriptionVal[256]="";
StringFromCLSID(CLSID_MultiplicationObject,&lpwszClsid);
sprintf(szClsid,"%S",lpwszClsid);
sprintf(szInproc,"%s\\%s\\%s","clsid",szClsid,"InprocServer32");
sprintf(szProgId,"%s\\%s\\%s","clsid",szClsid,"ProgId");
sprintf(szDescriptionVal,"%s\\%s","clsid",szClsid);
Register (HKEY_CLASSES_ROOT,szDescriptionVal,NULL,REG_SZ,szBuff,strlen(szBuff));
//InprocServer32
GetModuleFileNameA(g_hModule,szBuff,sizeof(szBuff));
Register (HKEY_CLASSES_ROOT,szInproc,NULL,REG_SZ,szBuff,strlen((szBuff)));
//ProgId
strcpy(szBuff,multiplicationObjProgId);
Register (HKEY_CLASSES_ROOT,szProgId,NULL,REG_SZ,szBuff,strlen(szBuff));
return 1;
}
HRESULT __stdcall DllUnregisterServer(void)
{
WCHAR *lpwszClsid;
char szBuff[MAX_PATH]="new multiplication Algorithm";
char szClsid[MAX_PATH]="", szInproc[MAX_PATH]="",szProgId[MAX_PATH];
char szDescriptionVal[256]="";
StringFromCLSID(CLSID_MultiplicationObject,&lpwszClsid);
sprintf(szClsid,"%S",lpwszClsid);
sprintf(szInproc,"%s\\%s\\%s","clsid",szClsid,"InprocServer32");
sprintf(szProgId,"%s\\%s\\%s","clsid",szClsid,"ProgId");
sprintf(szDescriptionVal,"%s\\%s","clsid",szClsid);
RegDeleteKey(HKEY_CLASSES_ROOT,convertCharArrayToLPCWSTR(szInproc));
RegDeleteKey(HKEY_CLASSES_ROOT,convertCharArrayToLPCWSTR(szProgId));
RegDeleteKey(HKEY_CLASSES_ROOT,convertCharArrayToLPCWSTR(szDescriptionVal));
return 1;
}

This kind of dependency problem can happen when there is a mismatch in vendor architecture.
For example, you may have registered the 32bit DLL but your program is 64bit or vice-versa.

Related

How to get an error message box when LoadLibrary fails?

I want the proc to show detailed information such as which dependency is missing, whether the dll is invalid...
int main(int argc, char *argv[]) {
// Record original error mode
UINT prevErrorMode = GetErrorMode();
::SetErrorMode(0);
std::wstring wstr;
// Get library absolute path and store into wstr.
// ...
typedef int (*EntryFun)(int, char *[]);
HINSTANCE hDLL = ::LoadLibraryW(wstr.data());
int res = -1;
if (hDLL != NULL) {
EntryFun fun = (EntryFun)::GetProcAddress(hDLL, "main_entry");
if (fun != NULL) {
// Restore error mode
SetErrorMode(prevErrorMode);
res = fun(argc, argv);
} else {
res = ::GetLastError();
::MessageBoxW(nullptr, TO_UNICODE("Failed to find entry!"), TO_UNICODE("Error"),
MB_OK | MB_ICONERROR);
}
::FreeLibrary(hDLL);
} else {
res = ::GetLastError();
::MessageBoxW(nullptr, TO_UNICODE("Failed to load main module!"), TO_UNICODE("Error"),
MB_OK | MB_ICONERROR);
}
return res;
}
I use SetErrorMode but it doesn't seem to work, there's no message box after LoadLibrary returns NULL.
Also, FormatMessage doesn't help because it cannot provide information about which dependency is missing.
LoadLibrary reports error through GetLastError. The advantage of Run-Time Dynamic Linking is Run-time dynamic linking enables the process to continue running even if a DLL is not available. You may write your own loader to walk through the dependencies yourself until you find what is missing as #RemyLebeau said. There is a MemoryModule repository which loads a DLL completely from memory and you can refer to.
But for Load-Time Dynamic Linking, the system simply terminates the process if it cannot find the DLL.

Calling COM server, C++ CoCreateInstance throws "No such interface supported", C# works without issue

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.

loadFromRemoteSources in a C++ project

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.

Advanced Installer serial validation DLL

I am working on an installer project in Advanced Installer 10.2. I found out that I can use a DLL for serial validation then I found this resource on their website.
I succeeded in building that DLL, here is my code:
// SerialValidationLib.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "SerialValidationLib.h"
#include <Msi.h>
#include <MsiQuery.h>
#include <MsiDefs.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// The one and only application object
CWinApp theApp;
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
HMODULE hModule = ::GetModuleHandle(NULL);
if (hModule != NULL)
{
// initialize MFC and print and error on failure
if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
_tprintf(_T("Fatal Error: MFC initialization failed\n"));
nRetCode = 1;
}
else
{
// TODO: code your application's behavior here.
}
}
else
{
// TODO: change error code to suit your needs
_tprintf(_T("Fatal Error: GetModuleHandle failed\n"));
nRetCode = 1;
}
return nRetCode;
}
UINT __stdcall ValidateSerial_Sample(MSIHANDLE hInstall)
{
TCHAR szPidKey[256];
DWORD dwLen = sizeof(szPidKey)/sizeof(szPidKey[0]);
//retrive the text entered by the user
UINT res = MsiGetProperty(hInstall, _T("PIDKEY"), szPidKey, &dwLen);
if(res != ERROR_SUCCESS)
{
//fail the installation
return 1;
}
bool snIsValid = false;
//validate the text from szPidKey according to your algorithm
//put the result in snIsValid
TCHAR * serialValid;
if(snIsValid)
serialValid = _T("TRUE");
else
{
//eventually say something to the user
MessageBox(0, _T("Serial invalid!"), _T("Message"), MB_ICONSTOP);
serialValid = _T("FALSE");
}
res = MsiSetProperty(hInstall, _T("SERIAL_VALIDATION"), serialValid);
if(res != ERROR_SUCCESS)
{
return 1;
}
//the validation succeeded - even the serial is wrong
//if the SERIAL_VALIDATION was set to FALSE the installation
//will not continue
return 0;
}
I also imported it to Advanced Installer, look here:
But when I run the installer, and try to proceed with the installation, after serial insertion point, I get this error message:
Where is my mistake? Does anybody know a good tutorial about this? I searched on the internet, but nothing helps me...
You could have two problems:
either you have typed the method name instead of picking it from the combo loaded by Advanced Installer. In this case the installer fails to call the method from the DLL, as it cannot find it.
or, there is a problem with your code, in which case you need to debug it, as you would do with a normal custom action, attaching from VS (add a mesagebox with a breakpoint after it).

ChangeServiceConfig2 using GetProcAddress to support multiple version of windows

I'm using ChangeServiceConfig2 API of windows to change the service description. Since the same api is not supported in windows 98, ME I have used LoadLibraray and GetProcAddress to prevent static linking of the API in the exe. Please refer the code for more details:
typedef BOOL (*ChgSvcDesc) (SC_HANDLE hService, DWORD dwInfoLevel, LPVOID lpInfo);
eBool ServiceConfigNT::Install(IN tServiceDesc * pServiceDesc){
SC_HANDLE hservice;
SC_HANDLE hservicemgr;
SERVICE_DESCRIPTION desc;
ULong starttype;
HMODULE hmod;
ChgSvcDesc fpsvcdesc;
// Opens the Service Control Manager
hservicemgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if(!hservicemgr){
vPrepareError(_TEXT("Failed to open service control manager!!"));
goto err;
}
// Set start method of service.
starttype = (pServiceDesc->uAutoStart == TRUE)? SERVICE_AUTO_START : SERVICE_DEMAND_START;
// Create the service
hservice = CreateService(hservicemgr, vServiceName, pServiceDesc->uDisplayName, SERVICE_ALL_ACCESS,
pServiceDesc->uServiceType, starttype, SERVICE_ERROR_NORMAL,pServiceDesc->uExePath,
NULL, NULL, NULL, NULL, NULL);
if(!hservice) {
vPrepareError(_TEXT("Failed to create service.!!"));
goto err;
}
// Set the description string
if(pServiceDesc->uServiceDescription && *pServiceDesc->uServiceDescription) {
desc.lpDescription = pServiceDesc->uServiceDescription;
// This cannot be executed since it is not supported in Win98 and ME OS
//(Void)ChangeServiceConfig2 (hservice, SERVICE_CONFIG_DESCRIPTION, &desc);
hmod = LoadLibrary(_TEXT("Advapi32.dll"));
if(hmod) {
// _UNICODE macro is set, hence im using the "W" version of the api
fpsvcdesc = (ChgSvcDesc)GetProcAddress(hmod, "ChangeServiceConfig2W");
if(fpsvcdesc)
// On execution of the below statement, I get the error handle is invalid
fpsvcdesc(hservice, SERVICE_CONFIG_DESCRIPTION, &desc);
}
CloseServiceHandle(hservice);
CloseServiceHandle(hservicemgr);
return TRUE;
err:
if(hservicemgr)
CloseServiceHandle(hservicemgr);
return FALSE;
}
I debugged the code many times to find out why I am getting handle is invalid error ? On calling the API directly the description of service is changing but using the function pointer it gives the error.
I think that the API is writing something to the service handle of the SCM, but I have no clues as to why ?
Can somebody help me with this ?
Your function pointer is declared with no calling convention specified. The default calling convention is __cdecl. But Windows API functions are __stdcall. You need to add the calling convention to your function pointer type.
typedef BOOL (__stdcall *ChgSvcDesc) (SC_HANDLE hService,
DWORD dwInfoLevel, LPVOID lpInfo);