C++ Variables for wix - c++

Im currently working with Visual Studio 2010 C++ Custom Action Project
I have a custom action like this:
extern "C" UINT __stdcall RegProductName(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
char szLocalPath[MAX_PATH];
hr = WcaInitialize(hInstall, "RegProductName");
ExitOnFailure(hr, "Failed to initialize");
WcaLog(LOGMSG_STANDARD, "Initialized.");
strcpy(szLocalPath, Orc_Get_Product_Name());
MsiSetProperty(hInstall, "ProductName", szLocalPath);
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
It does not throw up any errors but I'm not sure if i'm doing what I want correctly.
In this line:
strcpy(szLocalPath, Orc_Get_Product_Name());
I'm getting the Product name from the function and copying it to szLocalPath. However I want to use this variable many times in other functions and also in my wix project.
Is this the correct way?
MsiSetProperty(hInstall, "ProductName", szLocalPath);
Many thanks

I can see no issue here but potential buffer overflow in strcpy. If your function Orc_Get_Product_Name() returns path longer than MAX_PATH. However, you'd better use TCHAR type instead of plain char to support Unicode/ANSI builds, and you should really build with Unicode enabled.
And I'm sure you want to assign the result of MsiSetProperty to hr variable.
Additionally, it looks strange that you copy Product Name into variable called Local Path.
And I can't see where the label LExit is used.
The function ExitOnFailure does not seem to return control: if WcaInitialize was unsuccessful, you should simply return ERROR_INSTALL_FAILURE or another error code back to MSI engine (rather than terminate the process where your custom action is running).

Related

How can I create a JavaScript object in Internet Explorer from C++

I'm using an external C++ application to control Internet Explorer (version is 11, but that seems unimportant for this example). Part of this control is being able to run arbitrary JavaScript in the browser's context. To enable that control, I'd like to be able to create a JavaScript object (or array, or function, etc.), represented by an appropriate IDispatch variant, that I can use in subsequent script calls. There are some examples online, including some from Microsoft, that would indicate such a thing should be possible, by finding and invoking the Object constructor.
Below is some sample code, cribbed from the aforementioned examples, that should do the trick. However, when I execute the code, I get an E_INVALIDARG ("One or more arguments are invalid") HRESULT returned. What am I doing wrong, and how do I fix the issue?
// Example assumes that you're using an ATL project which give access to
// the "CCom" wrapper classes, and that you have the ability to retrieve
// an IHTMLDocument2 object pointer.
int CreateJavaScriptObject(IHTMLDocument2* script_engine_host, CComVariant* created_object) {
// NOTE: Proper return code checking and error handling
// has been omitted for brevity
int status_code = 0;
CComPtr<IDispatch> script_dispatch;
HRESULT hr = script_engine_host->get_Script(&script_dispatch);
CComPtr<IDispatchEx> script_engine;
hr = script_dispatch->QueryInterface<IDispatchEx>(&script_engine);
// Create the variables we need
DISPPARAMS no_arguments_dispatch_params = { NULL, NULL, 0, 0 };
CComVariant created_javascript_object;
DISPID dispatch_id;
// Find the javascript object using the IDispatchEx of the script engine
std::wstring item_type = L"Object";
CComBSTR name(item_type.c_str());
hr = script_engine->GetDispID(name, 0, &dispatch_id);
// Create the jscript object by calling its constructor
// The below InvokeEx call returns E_INVALIDARG in this case
hr = script_engine->InvokeEx(dispatch_id,
LOCALE_USER_DEFAULT,
DISPATCH_CONSTRUCT,
&no_arguments_dispatch_params,
&created_javascript_object,
NULL,
NULL);
*created_object = created_javascript_object;
return status_code;
}
Potential answerers desiring a fully compilable solution can find a version of the above code in a GitHub repo. The repo includes a Visual Studio 2017 solution that creates a console application which will reproduce the issue, including launching Internet Explorer and obtaining a reference to the required IHTMLDocument2 object. Note that the version in the repo has somewhat more complete error handling than the above sample, in which it was omitted for brevity.

ATL structures break on COM objects from dll

So, we are currently upgrading an ancient program from visual studio 2008 (where everything works) to 2017. We use a dll, whose classes the application connects to through ATL and a project-dependency.
Unfortunately all attempts at calling functions from these classes return exceptions, presumably because it can't find them.
The dll's classes are successfully added to the registry through regedit when built, and the uuids correspond correctly with the registered values. The classes can also be found in the OLE/COM-viewer.
It also only breaks when attempting to call from one of our classes. An attempt to call functions from IDispatch, which the classes in question inherits from, worked correctly.
In the below code the first attempt 'm_pRenderer' throws the exception. The second attempt 'test2' does not enter it's if-statement as CoCreateInstance returns a bad variable type-error
HRESULT res = CoCreateInstance(__uuidof(CBSNullRenderer), NULL, CLSCTX_INPROC_SERVER, __uuidof(IDispatch), (void**)&m_pRenderer);
Log("\nCreateInstance: %ld", res);
ICBSNullRendererPtr test2 = NULL;
HRESULT res2 = CoCreateInstance(__uuidof(CBSNullRenderer), NULL, CLSCTX_INPROC_SERVER, __uuidof(ICBSNullRenderer), (void**)&test2);
Log("\nres2: %ld", res2);
wireHWND mainHwnd = (wireHWND)GetParent(p_hWnd);
if(mainHwnd == NULL)
Log("\nWARNING mainHWND is NULL!");
try {
if (test2)
{
Log("\nDid create NullRenderer!");
test2->SetMainWnd(mainHwnd);
}
if (m_pRenderer)
{
Log("\nDid create NullRenderer!");
m_pRenderer->SetMainWnd(mainHwnd);
}
}
catch (...)
{
}
Which breaks on line 2 for m_pRenderer for:
inline HRESULT ICBSNullRenderer::SetMainWnd ( wireHWND hwnd ) {
HRESULT _hr = raw_SetMainWnd(hwnd);
if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
return _hr;
}
In the dll's .tli file.
The error creates an "Exception Thrown" dialog with the following:
Exception thrown at 0x00007FF9153ED7F2 (oleaut32.dll) in
AnimgramPro.exe: 0xC0000005: Access violation executing location
0x00007FF9153ED7F2
We also attempted to use QueryInterface on the m_pRenderer with the nullrenderer's uuid. This ended in another bad variable type-error.
Any advice or information of the errors which could be associated would be greatly appreciated.
So, I solved the problem. Apparently properly calling a dll-function in my program requires that 'Common Language Runtime Support' is turned off and that 'Whole Program Optimization' is set to 'Use Link Time Code Generation'

Hook ishellfolder enumobjects

I am trying to hook the function enumobjects in Ishellfolder .
I am doing it because I want to display the user non existing files in explorer.
I was success to hook FindNextFile and FindFirstFile but unfortunately this function not always call by explorer according to this question Which APIs are used by explorer.exe in Windows 7 to list files?
Now I try to hook IShellFolder::EnumObjects so I hook
MyCoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv)
And inside this function I have the following code:
if (IsEqualCLSID(rclsid, (REFGUID) __uuidof (IShellFolder)) ||
IsEqualCLSID(rclsid, (REFGUID) __uuidof (IShellFolder2)) ||
IsEqualCLSID(rclsid, (REFGUID) CLSID_ShellDesktop) ||
IsEqualCLSID(rclsid, (REFGUID) IID_IShellFolder) )
{
PDEBUG(L"IID_IShellFolder.2");
IShellFolderCast *shellFolder = (IShellFolderCast *) *ppv;
orig_EnumObjects = (type_EnumObjects) GetInterfaceMethod(shellFolder->lpVtbl, 4);
if (!Mhook_SetHook((void **) &orig_EnumObjects, MyEnumObjects))
{
PDEBUG(L". CoCreateInstance. Failed to set EnumObjects!");
}else
{
PDEBUG(L". CoCreateInstance. success to set EnumObjects!");
}
}
but it never go inside that if
anyone know why?
The following lays out how the windows API enumerates files in a directory. Look here.
[EDIT]
Missed the intent of your question on my first entry. You want to know how to trap an event when iShellFolder is accessed? You have probably already Looked Here?. It has some example code, and discusses topics around what I think may be useful.
Just change to
if (IsEqualCLSID(rclsid, (REFGUID) CLSID_ShellFSFolder) )
and now it works

OLE automation problems with C++ - "Class not registered"

I'm trying to port a LabView program to C++, and the OLE calls it contains are giving me some trouble.
The LabView program starts out by doing an "Automation open", i.e. getting a reference to the interface "XLib.XInterface" (LabView calls this expression the "ActiveX class"), then calls the method QA found in the interface and finally closes the reference again. I think LabView gets its info on the interface from the type library, but I'm not entierly sure.
I've tried to adapt some code for Word automation I found: http://www.codeproject.com/KB/office/MSOfficeAuto.aspx
CoInitialize(NULL);
CLSID clsid;
HRESULT hr = CLSIDFromProgID(L"XConfig.XInterface", &clsid);
IDispatch *pWApp;
if(SUCCEEDED(hr))
{
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER,
IID_IDispatch, (void **)&pWApp);
}
// etc.
The program is successful in looking up the CLSID, but CoCreateInstance fails, claiming that the class is not registered. I've also tried entering the CLSID from the type library directly, bypassing CLSIDFromProgID, but yielding the same result. Needless to say that the LabView program works fine, and the C++ code I'm using has no trouble at all to create an instance of Word when using the progID "Word.Application". The interface in question looks like this:
[
odl,
uuid(33AAA2DA-70EB-48EE-ACA7-DD0D1F5CAF2D),
helpstring("XInterface Interface"),
dual,
oleautomation
]
interface XInterface : IDispatch {
[id(0x00000001), helpstring("method QA")]
HRESULT QA();
[id(0x00000002), helpstring("method LoadFromDisk")]
HRESULT LoadFromDisk();
...
As you may have noticed, OLE is kind of new to me (most likely, that's a part of the problem). Any hints would be greatly appreciated. Thanks.
OK, I think I figured it out by myself, even though I don't fully understand my solution. Anyway, when I use
hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IDispatch,
(void **)&pWApp);
it seems to work; at least I don't get the "class not registered" error anymore. The difference is replacing the argument CLSCTX_LOCAL_SERVER with CLSCTX_ALL. I think it's got something to do with the fact that I'm using a dll. Does anyone have a more insightful explaination?

COM: how to get more details about COM errors?

Greets,
When working with DirectX, you get this nice header to #include called DxErr9.h which has really helpful functions like:
DXGetErrorString9
and
DXGetErrorDescription9
They tell you everything you need to know about the error given the HR.
But now working with COM and OLE, I find I'm kind of on my own with the HRESULTS that come back from COM functions. Is it really just me and MSDN at this point, or are there similar helper functions in OLE DB that I just haven't come across yet?
Additionally, you should look at the error info. Part of the COM system is the concept of the error information, which is a per-thread global which can be set and cleared at various times. You query for it in response to an error, and if it is set, it will have more useful information than just looking at the HRESULT.
HRESULT hr=something();
if (FAILED(hr))
{
CComPtr<IErrorInfo> err;
::GetErrorInfo(0, &err);
if (err)
{
CComBSTR description;
err->GetDescription(&description);
// description will be a more descriptive error message than just formatting the
// HRESULT because it is set by the COM server code at the point of the error
}
}
Use _com_error to get a meaningful string:
#include <comdef.h>
HRESULT hr = SomeComFunc();
if ( FAILED(hr) )
{
_com_error err(hr);
LPTCSTR szErrMsg = err.ErrorMessage();
// log szErrMsg or whatever
}