How to get an error message box when LoadLibrary fails? - c++

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.

Related

Unregistering and uninstalling a Context menu driver

I am writing a software removal program, and want to uninstall a context menu driver. Just unregistering the driver while Windows File Explorer is open fails when trying to unregister the dll. Closing file explorer first and then running the program does not always work either.
Here is the unregister code I am using now:
HINSTANCE hLib = LoadLibraryW(szDllPath);
if (hLib == NULL)
{
DWORD dwErrorCode = GetLastError();
szError.Format(_T("\nWindows could not load library %s due to %s"),szDllName,getLastWinError(dwErrorCode));
return false;
}
typedef HRESULT (CALLBACK *HCRET)(void);
HCRET lpfnDllRegisterServer;
// Find the entry point
lpfnDllRegisterServer = (HCRET)GetProcAddress(hLib, "DllUnregisterServer");
if (lpfnDllRegisterServer == NULL)
{
szError = NEWLINE;
szError = ERR_PROCEDURE_ADDRESS;
return false;
}
// Call the function by function pointer..
if (FAILED((*lpfnDllRegisterServer)()))
{
szError.Format(_T("\nWindows could not unregister context menu driver %s!"),szDllName);
return false;
}
FreeLibrary(hLib);
What is the best way to go about this? It seems that even if the dll is unregistered by just deleting the relevant registry keys, File Explorer still will not let go of it.

ShellExecute(Ex) with 'properties' verb

I am trying to write a program, that opens the properties of a file, from the command-line. I read that it is possible to do, using either of the functions ShellExecute and ShellExecuteEx, with the 'properties' verb.
So, I wrote such a C++ program in Visual Studio for Windows 10. This is that program:
#include <windows.h>
#include <iostream>
void ShowFileProperties(char *);
int main(int argc, char **argv)
{
if (argc >= 2)
{
std::cout << argv[1] << std::endl;
ShowFileProperties(argv[1]);
}
std::cout << GetLastError();
return 0;
}
void ShowFileProperties(char *szPathName)
{
HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
SHELLEXECUTEINFO Sei;
ZeroMemory(&Sei,sizeof(SHELLEXECUTEINFO));
Sei.cbSize = sizeof(SHELLEXECUTEINFO);
Sei.lpFile = szPathName;
Sei.nShow = SW_SHOW;
Sei.fMask = SEE_MASK_INVOKEIDLIST;
Sei.lpVerb = "properties";
ShellExecuteEx(&Sei);
if (result == S_OK || result == S_FALSE)
CoUninitialize();
}
If I run the program from the command line with a valid filename (such as . or the name of the executable itself), all it outputs is the filename and a zero (there was no error), but the properties of the file don't open.
Now, I have seen that other people have this problem, i.e. that the 'properties' verb doesn't do anything or that they get a messagebox saying that the filetype doesn't have an associated program for the operation, but I have not been able to find a fix.
Is there anyone here that can help me?
Thanks in advance.
Pass the SEE_MASK_NOASYNC (0x00000100) flag in your SHELLEXECUTEINFO to tell ShellExecuteEx that you're calling it without a message loop and not to return until it has finished.
See the remarks in the SHELLEXECUTEINFO docs on MSDN.
Sleep() is neither necessary nor recommended.

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).

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

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.

How to get the process name in C++

How do I get the process name from a PID using C++ in Windows?
I guess the OpenProcess function should help, given that your process possesses the necessary rights. Once you obtain a handle to the process, you can use the GetModuleFileNameEx function to obtain full path (path to the .exe file) of the process.
#include "stdafx.h"
#include "windows.h"
#include "tchar.h"
#include "stdio.h"
#include "psapi.h"
// Important: Must include psapi.lib in additional dependencies section
// In VS2005... Project > Project Properties > Configuration Properties > Linker > Input > Additional Dependencies
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE Handle = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE,
8036 /* This is the PID, you can find one from windows task manager */
);
if (Handle)
{
TCHAR Buffer[MAX_PATH];
if (GetModuleFileNameEx(Handle, 0, Buffer, MAX_PATH))
{
// At this point, buffer contains the full path to the executable
}
else
{
// You better call GetLastError() here
}
CloseHandle(Handle);
}
return 0;
}
You can obtain the process name by using the WIN32 API GetModuleBaseName after having the process handle. You can get the process handle by using OpenProcess.
To get the executable name you can also use GetProcessImageFileName.
All the above methods require psapi.dll to be loaded (Read the remarks section) and iterating through process snapshot is an option one should not even consider for getting a name of the executable file from an efficiency standpoint.
The best approach, even according to MSDN recommendation, is to use QueryFullProcessImageName.
std::string ProcessIdToName(DWORD processId)
{
std::string ret;
HANDLE handle = OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION,
FALSE,
processId /* This is the PID, you can find one from windows task manager */
);
if (handle)
{
DWORD buffSize = 1024;
CHAR buffer[1024];
if (QueryFullProcessImageNameA(handle, 0, buffer, &buffSize))
{
ret = buffer;
}
else
{
printf("Error GetModuleBaseNameA : %lu", GetLastError());
}
CloseHandle(handle);
}
else
{
printf("Error OpenProcess : %lu", GetLastError());
}
return ret;
}
If you are trying to get the executable image name of a given process, take a look at GetModuleFileName.
Check out the enumprocess functions in the tool help library:
http://msdn.microsoft.com/en-us/library/ms682629(v=vs.85).aspx
Good example # http://msdn.microsoft.com/en-us/library/ms682623(v=vs.85).aspx
Try this function :
std::wstring GetProcName(DWORD aPid)
{
PROCESSENTRY32 processInfo;
processInfo.dwSize = sizeof(processInfo);
HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (processesSnapshot == INVALID_HANDLE_VALUE)
{
std::wcout << "can't get a process snapshot ";
return 0;
}
for(BOOL bok =Process32First(processesSnapshot, &processInfo);bok; bok = Process32Next(processesSnapshot, &processInfo))
{
if( aPid == processInfo.th32ProcessID)
{
std::wcout << "found running process: " << processInfo.szExeFile;
CloseHandle(processesSnapshot);
return processInfo.szExeFile;
}
}
std::wcout << "no process with given pid" << aPid;
CloseHandle(processesSnapshot);
return std::wstring();
}