How to interact with internet explorer C++ - c++

I have a school project that I am working on and the outcome is pointless it seems, but it's got more to do with the experience gained through this I believe.
What I am trying to do is submit an initial URL, then pull all the URLs on that page and visit them in order and do this until I tell it to stop. All of the URLs will be recorded in a text file.
So far, I am able to open a window in IE and launch a webpage of my choosing. So now I need to know how to send IE to a new webpage using the same session and also how I can scan and pull data from the websites I visit. Thanks for any help!
Here is my code so far:
#include <string>
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
using namespace std;
int main( int argc, TCHAR *argv[] )
{
std::string uRL, prog;
int length, count;
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
//if( argc != 2 )
//{
// printf("Usage: %s [cmdline]\n", argv[0]);
// system("PAUSE");
// return 0;
//}
std::cout << "Enter URL: ";
std::cin >> uRL;
prog = ("C:\\Program Files\\Internet Explorer\\iexplore.exe ") + uRL;
char *cstr = new char[prog.length() + 1];
strcpy(cstr, prog.c_str());
// Start the child process.
if( !CreateProcess(NULL, // No module name (use command line)
_T(cstr), // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
{
printf( "CreateProcess failed (%d).\n", GetLastError() );
system("PAUSE");
return 0;
}
cout << HRESULT get_Count(long *Count) << endl;
//cout << count << endl;
system("PAUSE");
// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
delete [] cstr;
return 0;
}

If you want to crawl a webpage launching Internet Explorer is not going to work very well. I also don't recommend attempting to parse the HTML page yourself unless you are prepared for a lot of heartache and hassle. Instead I recommend that you create an instance of an IWebBrowser2 object and use it to navigate to the webpage, grab the appropriate IHTMLDocument2 object and iterate through the elements picking out the URL's. It's far easier and is a common approach using components that are already installed on Windows. The example below should get your started and on your way to crawling the web like proper spider should.
#include <comutil.h> // _variant_t
#include <mshtml.h> // IHTMLDocument and IHTMLElement
#include <exdisp.h> // IWebBrowser2
#include <atlbase.h> // CComPtr
#include <string>
#include <iostream>
#include <vector>
// Make sure we link in the support library!
#pragma comment(lib, "comsuppw.lib")
// Load a webpage
HRESULT LoadWebpage(
const CComBSTR& webpageURL,
CComPtr<IWebBrowser2>& browser,
CComPtr<IHTMLDocument2>& document)
{
HRESULT hr;
VARIANT empty;
VariantInit(&empty);
// Navigate to the specifed webpage
hr = browser->Navigate(webpageURL, &empty, &empty, &empty, &empty);
// Wait for the load.
if(SUCCEEDED(hr))
{
READYSTATE state;
while(SUCCEEDED(hr = browser->get_ReadyState(&state)))
{
if(state == READYSTATE_COMPLETE) break;
}
}
// The browser now has a document object. Grab it.
if(SUCCEEDED(hr))
{
CComPtr<IDispatch> dispatch;
hr = browser->get_Document(&dispatch);
if(SUCCEEDED(hr) && dispatch != NULL)
{
hr = dispatch.QueryInterface<IHTMLDocument2>(&document);
}
else
{
hr = E_FAIL;
}
}
return hr;
}
void CrawlWebsite(const CComBSTR& webpage, std::vector<std::wstring>& urlList)
{
HRESULT hr;
// Create a browser object
CComPtr<IWebBrowser2> browser;
hr = CoCreateInstance(
CLSID_InternetExplorer,
NULL,
CLSCTX_SERVER,
IID_IWebBrowser2,
reinterpret_cast<void**>(&browser));
// Grab a web page
CComPtr<IHTMLDocument2> document;
if(SUCCEEDED(hr))
{
// Make sure these two items are scoped so CoUninitialize doesn't gump
// us up.
hr = LoadWebpage(webpage, browser, document);
}
// Grab all the anchors!
if(SUCCEEDED(hr))
{
CComPtr<IHTMLElementCollection> urls;
long count = 0;
hr = document->get_all(&urls);
if(SUCCEEDED(hr))
{
hr = urls->get_length(&count);
}
if(SUCCEEDED(hr))
{
for(long i = 0; i < count; i++)
{
CComPtr<IDispatch> element;
CComPtr<IHTMLAnchorElement> anchor;
// Get an IDispatch interface for the next option.
_variant_t index = i;
hr = urls->item( index, index, &element);
if(SUCCEEDED(hr))
{
hr = element->QueryInterface(
IID_IHTMLAnchorElement,
reinterpret_cast<void **>(&anchor));
}
if(SUCCEEDED(hr) && anchor != NULL)
{
CComBSTR url;
hr = anchor->get_href(&url);
if(SUCCEEDED(hr) && url != NULL)
{
urlList.push_back(std::wstring(url));
}
}
}
}
}
}
int main()
{
HRESULT hr;
hr = CoInitialize(NULL);
std::vector<std::wstring> urls;
CComBSTR webpage(L"http://cppreference.com");
CrawlWebsite(webpage, urls);
for(std::vector<std::wstring>::iterator it = urls.begin();
it != urls.end();
++it)
{
std::wcout << "URL: " << *it << std::endl;
}
CoUninitialize();
return 0;
}

To scan and pull data from the websites, you'll want to capture the HTML and iterate through it looking for all character sequences matching a certain pattern. Have you ever used regular expressions? Regular expressions would by far be the best here, but if you understand them (just look up a tutorial on the basics) then you can manually apply the pattern-recognition concepts to this project.
So what you're looking for is something like http(s)://.. It's more complex though, because domain names are a rather intricate pattern. You'll probably want to use a third-party HTML parser or regular expression library, but it's doable without it, although pretty tedious to program.
Here's a link about regular expressions in c++:
http://www.johndcook.com/cpp_regex.html

Related

Changing the title of uwp process

I want to change the title bar of calc.exe. I read that it's done via SetWindowTextA() but when I used this it only change the title of the preview (1) and I want to change title at (2) as well.
Can anyone explain for me why it change the title at (1) not (2) and how can I change the title at (2)
The Calculator title is Text Control Type retrieved using UI Automation. However according to Text Control Type, the IValueProvider is never supported by text controls. So you can’t.
Edit:
#include <Windows.h>
#include <UIAutomation.h>
#include <wchar.h>
int Element(IUIAutomation* automation)
{
// Get the element under the cursor
// Use GetPhysicalCursorPos to interact properly with
// High DPI
POINT pt;
GetPhysicalCursorPos(&pt);
IUIAutomationElement* pAtMouse;
HRESULT hr = automation->ElementFromPoint(pt, &pAtMouse);
if (FAILED(hr))
return hr;
// Get the element's name and print it
BSTR name;
hr = pAtMouse->get_CurrentName(&name);
if (SUCCEEDED(hr))
{
IUIAutomationTextPattern* pattern;
pAtMouse->GetCurrentPatternAs(UIA_TextPatternId, IID_IUIAutomationTextPattern,(void**)&pattern);
//TODO
wprintf(L"Element's Name: %s \n", name);
SysFreeString(name);
}
// Get the element's Control Type (in the current languange)
// and print it
BSTR controlType;
hr = pAtMouse->get_CurrentLocalizedControlType(&controlType);
if (SUCCEEDED(hr))
{
wprintf(L"Element's Control Type: %s \n", controlType);
SysFreeString(controlType);
}
// Clean up our COM pointers
pAtMouse->Release();
return hr;
}
int main(int argc, TCHAR* argv[])
{
// Initialize COM and create the main Automation object
IUIAutomation* g_pAutomation;
CoInitialize(NULL);
HRESULT hr = CoCreateInstance(__uuidof(CUIAutomation), NULL,
CLSCTX_INPROC_SERVER, __uuidof(IUIAutomation),
(void**)&g_pAutomation);
if (FAILED(hr))
return (hr);
bool quit = false;
while (!quit)
{
SHORT leftControlMod = GetAsyncKeyState(VK_LCONTROL);
if (leftControlMod != 0)
{
Element(g_pAutomation);
}
quit = GetAsyncKeyState(VK_ESCAPE);
}
g_pAutomation->Release();
CoUninitialize();
return 0;
}

WINAPI Network Discovery without SMBv1

I need to get a list of available shared folders on the local network, the way they appear in the "Network" tab in File Explorer. Earlier, I used combination of NetServerEnum/NetShareEnum functions to obtain it, but they are using SMBv1 protocol, which is now disabled by default in windows, so now i'm getting error 1231 from NetServerEnum. But File Explorer still cat obtain this list. I tried use Process Monitor to determine, which API it use, but failed. So, is there any way to get list of available shared folders in local network without using API, that requires SMBv1?
You can use windows shell api and use FOLDERID_NetworkFolder to get the KNOWNFOLDERID of "network".
The following sample can get folders, nonfolders, and hidden items in the "network" folder.
#include <windows.h>
#include <Shobjidl.h>
#include <Shlobj.h>
#include <iostream>
void wmain(int argc, TCHAR* lpszArgv[])
{
IShellItem* pShellItem;
IEnumShellItems* pShellEnum = NULL;
HRESULT hr = S_OK;
hr = CoInitialize(NULL);
if (FAILED(hr))
{
printf("CoInitialize error, %x\n", hr);
return;
}
hr = SHGetKnownFolderItem(FOLDERID_NetworkFolder, KF_FLAG_DEFAULT, NULL, IID_PPV_ARGS(&pShellItem));
if (FAILED(hr))
{
printf("SHGetKnownFolderItem error, %x\n", hr);
return;
}
hr = pShellItem->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&pShellEnum));
if (FAILED(hr))
{
printf("BindToHandler error, %x\n", hr);
return;
}
do {
IShellItem* pItem;
LPWSTR szName = NULL;
hr = pShellEnum->Next(1, &pItem, nullptr);
if (hr == S_OK && pItem)
{
HRESULT hres = pItem->GetDisplayName(SIGDN_NORMALDISPLAY, &szName);
std::wcout << szName << std::endl;
CoTaskMemFree(szName);
}
} while (hr == S_OK);
CoUninitialize();
}

Console app vs Win32 app - DirectSound capture-device enumeration gives different results

I am looking for a reliable method to map a DirectShow capture device GUID to its corresponding waveID value.
I found the following project by Chris_P:
The solution works great, and it relies on an a rather obscure IKsPropertySet interface to retrieve the mapping.
Unfortunately, if I attempt the same technique from a C++/CLI library, the code fails with E_NOTIMPL (this behavior has been described on this question, - see the answer by Vladimir Hmelyoff)
So, I figured that I could write a simple console-based auxiliary app to retrieve the mappings and print them. My library could then launch this auxiliary app and parse the redirected output to obtain the mappings.
The console program runs fine, however, the GUIDs that are being passed to the enumeration callback are completely different to the ones passed by Chris_P's solution.
In fact they all share the same basic structure:
lpGuid = 0x02ad0808 {BDF35A00-B9AC-11D0-A619-00AA00A7C000}
The only variation occurs in the last digits of the GUID, where coincidentally, they match the mapped waveId value.
Another weird thing is that the capture device description is truncated to 31 characters, as if the enumeration was being performed using WaveIn APIs!
It would almost seem that some DirectSound facade is now wrapping the WaveIn API.
Any pointers on what could be happening?, Can I disable this behavior and enumerate the same GUIDS that the WIN32 app is enumerating?
Here is the code for the console application:
#include "stdafx.h"
#include <mmreg.h>
#include <initguid.h>
#include <Dsound.h>
#include <dsconf.h>
static BOOL CALLBACK DSEnumCallback(
LPGUID lpGuid,
LPCTSTR lpcstrDescription,
LPCTSTR lpcstrModule,
LPVOID lpContext
);
static BOOL GetInfoFromDSoundGUID(GUID i_sGUID, DWORD &dwWaveID);
static HRESULT DirectSoundPrivateCreate(OUT LPKSPROPERTYSET * ppKsPropertySet);
typedef WINUSERAPI HRESULT(WINAPI *LPFNDLLGETCLASSOBJECT) (const CLSID &, const IID &, void **);
BOOL GetInfoFromDSoundGUID(GUID i_sGUID, DWORD &dwWaveID) {
LPKSPROPERTYSET pKsPropertySet = NULL;
HRESULT hr;
BOOL retval = FALSE;
PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA psDirectSoundDeviceDescription = NULL;
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA sDirectSoundDeviceDescription;
memset(&sDirectSoundDeviceDescription, 0, sizeof(sDirectSoundDeviceDescription));
hr = DirectSoundPrivateCreate(&pKsPropertySet);
if( SUCCEEDED(hr) ) {
ULONG ulBytesReturned = 0;
sDirectSoundDeviceDescription.DeviceId = i_sGUID;
// On the first call the final size is unknown so pass the size of the struct in order to receive
// "Type" and "DataFlow" values, ulBytesReturned will be populated with bytes required for struct+strings.
hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice,
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION,
NULL,
0,
&sDirectSoundDeviceDescription,
sizeof(sDirectSoundDeviceDescription),
&ulBytesReturned
);
if( ulBytesReturned ) {
// On the first call it notifies us of the required amount of memory in order to receive the strings.
// Allocate the required memory, the strings will be pointed to the memory space directly after the struct.
psDirectSoundDeviceDescription = (PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA)new BYTE[ulBytesReturned];
*psDirectSoundDeviceDescription = sDirectSoundDeviceDescription;
hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice,
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION,
NULL,
0,
psDirectSoundDeviceDescription,
ulBytesReturned,
&ulBytesReturned
);
dwWaveID = psDirectSoundDeviceDescription->WaveDeviceId;
delete[] psDirectSoundDeviceDescription;
retval = TRUE;
}
pKsPropertySet->Release();
}
return retval;
}
HRESULT DirectSoundPrivateCreate(OUT LPKSPROPERTYSET * ppKsPropertySet) {
HMODULE hLibDsound = NULL;
LPFNDLLGETCLASSOBJECT pfnDllGetClassObject = NULL;
LPCLASSFACTORY pClassFactory = NULL;
LPKSPROPERTYSET pKsPropertySet = NULL;
HRESULT hr = DS_OK;
// Load dsound.dll
hLibDsound = LoadLibrary(TEXT("dsound.dll"));
if( !hLibDsound ) {
hr = DSERR_GENERIC;
}
// Find DllGetClassObject
if( SUCCEEDED(hr) ) {
pfnDllGetClassObject =
(LPFNDLLGETCLASSOBJECT)GetProcAddress(hLibDsound, "DllGetClassObject");
if( !pfnDllGetClassObject ) {
hr = DSERR_GENERIC;
}
}
// Create a class factory object
if( SUCCEEDED(hr) ) {
hr = pfnDllGetClassObject(CLSID_DirectSoundPrivate, IID_IClassFactory, (LPVOID *)&pClassFactory);
}
// Create the DirectSoundPrivate object and query for an IKsPropertySet
// interface
if( SUCCEEDED(hr) ) {
hr = pClassFactory->CreateInstance(NULL, IID_IKsPropertySet, (LPVOID *)&pKsPropertySet);
}
// Release the class factory
if( pClassFactory ) {
pClassFactory->Release();
}
// Handle final success or failure
if( SUCCEEDED(hr) ) {
*ppKsPropertySet = pKsPropertySet;
} else if( pKsPropertySet ) {
pKsPropertySet->Release();
}
FreeLibrary(hLibDsound);
return hr;
}
BOOL CALLBACK DSEnumCallback(
LPGUID lpGuid,
LPCTSTR lpcstrDescription,
LPCTSTR lpcstrModule,
LPVOID lpContext
) {
LPWSTR psz = NULL;
StringFromCLSID(*lpGuid, &psz);
DWORD WaveID = 0xFFFFFFFF;
if( lpGuid ) {
GUID i_guid = *lpGuid;
GetInfoFromDSoundGUID(i_guid, WaveID);
}
if( WaveID != 0xFFFFFFFF )
wprintf(_T("%d %s\r\n"), WaveID, psz);
if( psz ) {
CoTaskMemFree(psz);
}
return TRUE;
}
int main()
{
DirectSoundCaptureEnumerate(DSEnumCallback, NULL);
Sleep(10000);
return 0;
}
It turns out I was not initializing COM.
I added the following snippet at the beginning of my main() procedure and the program retrieved the expected GUIDs:
HRESULT hr = NULL;
hr = CoInitialize(NULL);
if( FAILED(hr) ) {
printf("Failed to initialize COM");
return -1;
}
So I guess that if COM is not initialized, the DirectSound engine falls back to the WaveIn API (creating a DirectShow facade around it).

ImpersonateLoggedOnUser is successful but secondary process is still run in the initial context

I have an installer that tries to (re)start my application in the current user context after the installation is done.
The installer runs in the SYSTEM context and before launching the application it attempts (and theoretically succeeds) to impersonate the current user. However, when I look in the task manager, I see that my application is running in the SYSTEM context.
This is (a snippet from) my code:
TCHAR szUsername[128] = _T("");
DWORD dwUsernameSize = 128;
GetUserName(szUsername, &dwUsernameSize);
// Lets the calling process impersonate the security context of a logged-on user.
if (!ImpersonateLoggedOnUser(hToken))
{
throw Win32Exception(GetLastError(), _T("Failed to impersonate current user"));
}
TCHAR szUsername2[128] = _T("");
DWORD dwUsernameSize2 = 128;
GetUserName(szUsername2, &dwUsernameSize2);
MLOGD(_T("ProcessUtils::StartProcessInCurrentUserContext: Successfully impersonated %s"), szUsername2);
ProcessUtils::StartProcess(sExeName, lstParams, sWorkingDir, bWaitToFinish, errCode);
ProcessUtils::StartProcess is a wrapper around CreateProcess.
szUsername contains SYSTEM and szUsername2 contains the current user. So ImpersonateLoggedOnUser is successful.
However, as mentioned above, the process is started in the SYSTEM context, not the current user one.
I'm not sure how helpful this might be, but my installer is written in NSIS and it's calling the function that contains the code from above via a plugin written in C/C++.
Does anyone know why my application doesn't start in the current user context?
Win32 CreateProcess creates a process in the same security context as the caller which is SYSTEM (even though you are impersonating).
Think you need to be calling CreateProcessAsUser.
I had a very similar problem a couple of years ago when I was also
working on an installer application. After A LOT of frustration, caused
by failed attempts to start an application in the context of the current
user using CreateProcessAsUser, I've finally given up. After a thorough
search on the web, I've found a briliant implementation that uses
IShellDispatch2 interface. Here is an example:
#include <Windows.h>
#include <exdisp.h>
#include <Shobjidl.h>
#include <Shlwapi.h>
#include <comutil.h>
#include <SHLGUID.h>
#include <cstdlib>
#include <iostream>
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "comsuppw.lib")
bool ShellExecuteAsCurrentUser(const TCHAR *pcOperation, const TCHAR *pcFileName, const TCHAR *pcParameters,
const TCHAR *pcsDirectory, const DWORD dwShow)
{
bool bSuccess = false;
IShellWindows *psw = NULL;
HRESULT hr = CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw));
if(SUCCEEDED(hr))
{
HWND hwnd = 0;
IDispatch* pdisp = NULL;
_variant_t vEmpty;
if(S_OK == psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, reinterpret_cast<long*>(&hwnd), SWFO_NEEDDISPATCH, &pdisp))
{
if((hwnd != NULL) && (hwnd != INVALID_HANDLE_VALUE))
{
IShellBrowser *psb;
hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb));
if(SUCCEEDED(hr))
{
IShellView *psv = NULL;
hr = psb->QueryActiveShellView(&psv);
if(SUCCEEDED(hr))
{
IDispatch *pdispBackground = NULL;
HRESULT hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground));
if(SUCCEEDED(hr))
{
IShellFolderViewDual *psfvd = NULL;
hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd));
if(SUCCEEDED(hr))
{
IDispatch *pdisp = NULL;
hr = psfvd->get_Application(&pdisp);
if(SUCCEEDED(hr))
{
IShellDispatch2 *psd;
hr = pdisp->QueryInterface(IID_PPV_ARGS(&psd));
if(SUCCEEDED(hr))
{
_variant_t verb(pcOperation);
_variant_t file(pcFileName);
_variant_t para(pcParameters);
_variant_t dir(pcsDirectory);
_variant_t show(dwShow);
if(SUCCEEDED(psd->ShellExecute(file.bstrVal, para, vEmpty, verb, show)))
bSuccess = true;
psd->Release();
psd = NULL;
}
pdisp->Release();
pdisp = NULL;
}
}
pdispBackground->Release();
pdispBackground = NULL;
}
psv->Release();
psv = NULL;
}
psb->Release();
psb = NULL;
}
}
pdisp->Release();
pdisp = NULL;
}
psw->Release();
psw = NULL;
}
return bSuccess;
}
int main(int argc, char *argv[])
{
CoInitialize(NULL);
if(ShellExecuteAsCurrentUser(L"open", L"notepad", nullptr, nullptr, SW_SHOWNORMAL))
std::cout << "SUCCESS" << std::endl;
CoUninitialize();
return 0;
}
This is just a quick demo, the implementation of ShellExecuteAsCurrentUser can be
improved by using smart pointers for COM interfaces and some refactoring. This method
worked for me on versions WinXP SP3 - Win 8.1, not sure if it works on Windows 10. For
more details, check the authors github page:
https://github.com/lordmulder/stdutils/tree/master/Contrib/StdUtils
If you had read the documentation for CreateProcess, you would have found the answer to your question in the first three sentences:
Creates a new process and its primary thread. The new process runs in the security context of the calling process.
If the calling process is impersonating another user, the new process uses the token for the calling process, not the impersonation token.
There really isn't much else to say; the behaviour you describe is as documented. If you want to create a process as another user, you must use CreateProcessAsUser or one of the related functions.

Problems accessing a COM interface in C++

What I want to do is access a COM interface and then call the "Open" method of that interface.
I have a sample code in Visual Basic which works fine, but I need to write it in C++ and I can't seem to get it to work.
First of all, this is the working VB code:
Dim CANapeApplication As CANAPELib.Application
CANapeApplication = CreateObject("CANape.Application")
Call CANapeApplication.Open("C:\Users\Public\Documents\Vector\CANape\12\Project", 0)
CANape.Application is the ProgID which selects the interface I need.
After reading some docs at msdn.microsoft.com and this question, I wrote this code:
void ErrorDescription(HRESULT hr); //Function to output a readable hr error
int InitCOM();
int OpenCANape();
// Declarations of variables used.
HRESULT hresult;
void **canApeAppPtr;
IDispatch *pdisp;
CLSID ClassID;
DISPID FAR dispid;
UINT nArgErr;
OLECHAR FAR* canApeWorkingDirectory = L"C:\\Users\\Public\\Documents\\Vector\\CANape\\12\\Project";
int main(){
// Instantiate CANape COM interface
if (InitCOM() != 0) {
std::cout << "init error";
return 1;
}
// Open CANape
if (OpenCANape() != 0) {
std::cout << "Failed to open CANape Project" << std::endl;
return 1;
}
CoUninitialize();
return 0;
}
void ErrorDescription(HRESULT hr) {
if(FACILITY_WINDOWS == HRESULT_FACILITY(hr))
hr = HRESULT_CODE(hr);
TCHAR* szErrMsg;
if(FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&szErrMsg, 0, NULL) != 0)
{
_tprintf(TEXT("%s"), szErrMsg);
LocalFree(szErrMsg);
} else
_tprintf( TEXT("[Could not find a description for error # %#x.]\n"), hr);
}
int InitCOM() {
// Initialize OLE DLLs.
hresult = OleInitialize(NULL);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
// Get CLSID from ProgID
//hresult = CLSIDFromProgID(OLESTR("CANape.Application"), &ClassID);
hresult = CLSIDFromProgID(OLESTR("CanapeCom.CanapeCom"), &ClassID);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
// OLE function CoCreateInstance starts application using GUID/CLSID
hresult = CoCreateInstance(ClassID, NULL, CLSCTX_LOCAL_SERVER,
IID_IDispatch, (void **)&pdisp);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
// Call QueryInterface to see if object supports IDispatch
hresult = pdisp->QueryInterface(IID_IDispatch, (void **)&pdisp);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
std::cout << "success" << std::endl;
return 0;
}
int OpenCANape() {
//Method name
OLECHAR *szMember = L"Open";
// Retrieve the dispatch identifier for the Open method
// Use defaults where possible
DISPID idFileExists;
hresult = pdisp->GetIDsOfNames(
IID_NULL,
&szMember,
1,
LOCALE_SYSTEM_DEFAULT,
&idFileExists);
if (!SUCCEEDED(hresult)) {
std::cout << "GetIDsOfNames: ";
ErrorDescription(hresult);
return 1;
}
unsigned int puArgErr = 0;
VARIANT VarResult;
VariantInit(&VarResult);
DISPPARAMS pParams;
memset(&pParams, 0, sizeof(DISPPARAMS));
pParams.cArgs = 2;
VARIANT Arguments[2];
VariantInit(&Arguments[0]);
pParams.rgvarg = Arguments;
pParams.cNamedArgs = 0;
pParams.rgvarg[0].vt = VT_BSTR;
pParams.rgvarg[0].bstrVal = SysAllocString(canApeWorkingDirectory);
pParams.rgvarg[1].vt = VT_INT;
pParams.rgvarg[1].intVal = 0; // debug mode
// Invoke the method. Use defaults where possible.
hresult = pdisp->Invoke(
dispid,
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD,
&pParams,
&VarResult,
NULL,
&puArgErr
);
SysFreeString(pParams.rgvarg[0].bstrVal);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
return 0;
}
There are several problems with this.
Using the ClassID received from CLSIDFromProgID as the first parameter of CoCreateInstance does not work, it returns the error: class not registered
If i use the ProgID CanapeCom.CanapeCom (I found it by looking in the Registry), CoCreateInstance works. However, when I use pdisp->GetIDsOfNames I get the error message: Unkown name. Which I think means that the method was not found. That seems logical because I've used a different ProgID, but I just can't figure out how to get to the interface I'm looking for.
I have also tried to use the resulting CLSID from CLSIDFromProgID(OLESTR("CANape.Application"), &ClassID); as the 4th argument of CoCreateInstance but that resulted in a "No such interface supported" error.
Do I need the dll file of the software? In the VB example the dll file is used to get the interface and then create a new object using the ProgID. I'm not sure if I need to do the same in C++ or how this should work.
I'm really stuck here and hope that someone can help me.
Thanks for your comments.
I've fixed the problem, although the solution is kind of embarrassing...
In my defense, I'm still a student and new to this kind of stuff.
I've used the Process Monitor to check what happens when I execute the VB script.
I saw that the CLSID used there is the ID returned by CLSIDFromProgID(OLESTR("CANape.Application"), &ClassID);, which meant that this had to be the right one and the problem had to be somewhere else. I've looked again at the CoCreateInstance and then took a look at the other parameters. Turns out that the context CLSCTX_LOCAL_SERVER was wrong, it has to be CLSCTX_INPROC_SERVER. I don't know why I've set it to local_server in the first place or why I've never questioned it. I wrote that part of the code a few days ago and then focused too much on the CLSID and IID rather than on the other parameters.
I've also taken the first comment from Alex into account and created a tlb file.
This is a simplified version of the code that works:
#import "CANape.tlb"
int _tmain(int argc, _TCHAR* argv[])
{
_bstr_t path = "C:\\Users\\Public\\Documents\\Vector\\CANape\\12\\Project";
CLSID idbpnt;
CoInitialize(NULL);
HRESULT hr = CLSIDFromProgID (L"CANape.Application", &idbpnt);
CANAPELib::IApplication *app;
hr = CoCreateInstance(idbpnt,NULL,CLSCTX_INPROC_SERVER,__uuidof(CANAPELib::IApplication),(LPVOID*)&app );
app->Open(path,0);
CoUninitialize();
return 0;
}