I'm using Visual C++ Professional Version 2015 update 3. I have a program that automates Excel and works fine in our Windows 7 computers. However, on Windows 10, Excel doesn't even launch (CreateDispatch() returns 0). There is a problem with a Windows Function, CLSIDFromProgID(L"Excel.Application", &clsid), which returns error 0x800401fe, which has the explanation: “Application was launched but it didn't register a class factory”. This is a curious explanation because all the function does is pick up the CLSID from the registry (it doesn’t launch Excel). And “Excel.Application” is in the registry for the Windows 10 computer just like for our Windows 7 computers. I was thinking that maybe Windows 10 doesn’t allow automation to work from clients that are from an “unknown publisher”, but then it should work with UAC off. (And when I ran it with UAC set to "never notify", the warning message did not appear when starting PI_Dats. Maybe it’s not really disabled, it just doesn’t issue the warning?) On the Local Disk Properties Security tab, Administrators have full control and I set Users also to full control. Any ideas on how to automate Excel from Windows 10 would be greatly appreciated. Thanks, Gary
garyflet
Posts
-
Can't automate Excel from Windows 10 -
Need help with managed C++ dll so unmanaged C++ program can use third party c# dllActually, all I need to do is make the tcb variable static, as in static TcbDriver::TCB tcb; Gary
-
Need help with managed C++ dll so unmanaged C++ program can use third party c# dllUsing Windows 7 and VS 2010 (soon 2015). I’m writing a C++ managed code dll (managed.dll)which will be an intermediary between our unmanaged C++ code (program.exe) and a third party C# driver dll (tcbDriver.dll). I don’t have access to the code for tcbDriver.dll. However, in the following example of managed.dll code, I use a TcbDriver class which accesses the tcbDriver.dll. So program.exe calls "__declspec(dllexport) int ConnectTCB()" in managed.dll, which in turns calls Connect() in tcbDriver.dll. It works. The problem is, I don’t know how get the managed.dll code to preserve the same instance of "work" or "tcb" for other methods that program.exe will call. For example, in this case tcbDriver.dll will eventually make tcb.Initialized equal to "true", indicating it is done initializing the hardware. However, managed.dll needs to use the same instance of "work" or "tcb" in another exposed function that it used to call Connect(). The exposed function "__declspec(dllexport) bool IsInitialized()" makes a new instance, so tcbDriver.dll doesn't ever make tcb.Initialized equal to "true", even after TcbDriver is done initializing.
namespace ManagedTCB
{
public ref class DoWork
{
TcbDriver::TCB tcb;
public:int ConnectTCB()
{
try
{
tcb.Connect();
}
catch(...)
{
return 0;
}
return 1;
}
public:bool IsInitialized()
{
return tcb.Initialized;
}
};
}__declspec(dllexport) int ConnectTCB()
{
ManagedTCB::DoWork work;
return work.ConnectTCB();
}
__declspec(dllexport) bool IsInitialized()
{
ManagedTCB::DoWork work;
return work.IsInitialized();
}How do I use the same instance of the tcb class in different exported functions? Thanks for any help, Gary
-
How to program Excel named ranges with automationThese classes were generated years ago from the Excel type library using Class Wizard. I note that these classes can be generated using Class Wizard on Visual Studio 2010 as well: MFC Class Wizard->Add Class (arrow)->MFC Class from Typelib...->Add Class from Typelib Wizard->Available Type libraries: Microsoft Excel 15.0 Object Library<1.8>. Then you add the classes, using whatever names you want and the files are automatically generated. However, the classes in my legacy code are a bit different than later versions of Excel. Also if you know where to find documentation for these classes, it would be much appreciated.
-
How to program Excel named ranges with automationUsing Excel Automation in Visual Studio 2010 C++, how do I define a name? As a user of Excel, you can enter a name that then can be used in formulas to refer to a range of cells. The range changes as the user inserts columns or rows. My program has worked with various versions of Excel dating back to the early 2000’s. I have a bunch of classes derived from COleDispatchDriver: CXLApplication, CXLRange, CXLWorkbook, CXLWorkbooks, and CXLWorksheet that have many functions, but I have no documentation (anybody have any clues on where to find documentation?). Anyone know how to define a named range using functions of these classes (probably CXLRange)?
-
Excel automation: SetValue() function throws exception when user changes sheetsUsing Visual C++ 6 on Windows XP and automation to send data to Excel 2007. With my software there are occasions in which I repeatedly open an Excel file, write to a specified sheet, then close without saving. However, if the user clicks on a different sheet, when my code gets to SetValue(), it throws a COleDispatchException. The description of the error is a blank string. Why doesn't Excel just keep writing to the original sheet? How can I make that happen? A less desirable workaround would be to somehow disable Excel while my code is writing to Excel. Making Excel invisible while writing is not acceptable. Incidentally, there was no problem with Excel 2000.
//worksheet wrapper class
CXLWorksheet worksheet;
//range wrapper class
CXLRange range;
CString strRange;VARIANT v, vRet, vNotPassed, vBOOL;
VariantInit(&v);
VariantInit(&vRet);
VariantInit(&vNotPassed);
VariantInit(&vBOOL);
V_VT(&vNotPassed) = VT_ERROR;
V_ERROR(&vNotPassed) = DISP_E_PARAMNOTFOUND;
V_VT(&vBOOL) = VT_BOOL;LPDISPATCH pXLAppDispatch = NULL; LPUNKNOWN lpUnk; CLSID clsid; if( S\_OK == ::CLSIDFromProgID(L"Excel.Application", &clsid) ) { if( S\_OK == ::GetActiveObject(clsid, NULL, &lpUnk) ) { VERIFY(lpUnk->QueryInterface(IID\_IDispatch, (void\*\*)&pXLAppDispatch) == S\_OK); //m\_XLApp is Excel application class m\_XLApp.AttachDispatch( pXLAppDispatch ); lpUnk->Release(); } } V\_BOOL(&vBOOL) = TRUE; m\_XLApp.SetVisible(vBOOL); VARIANT vFilename; V\_VT(&vFilename) = VT\_BSTR;
//strFileName is full path of Excel file name
int Len = strFileName.GetLength();
V_BSTR(&vFilename) = SysAllocString(
T2COLE(strFileName.GetBuffer(Len+1)) );//m_workbooks is workbooks wrapper class
if( !m_workbooks.m_lpDispatch )
{
m_workbooks.AttachDispatch( m_XLApp.GetWorkbooks() );TRY { vRet = m\_workbooks.Open(vFilename, vNotPassed,vNotPassed,vNotPassed,vNotPassed, vNotPassed,vNotPassed,vNotPassed,vNotPassed, vNotPassed,vNotPassed,vNotPassed ); } CATCH\_ALL(e) { //\* Create a new one instead vRet = m\_workbooks.Add(vNotPassed); } END\_CATCH\_ALL //m\_workbook is workbook wrapper class if( V\_VT(&vRet) == VT\_DISPATCH ) m\_workbook.AttachDispatch( V\_DISPATCH(&vRet) ); } LPDISPATCH lpDisp; //for this example, nXLWorksheetNo == 1 lpDisp = m\_workbook.GetWorksheets(nXLWorksheetNo); worksheet.AttachDispatch(lpDisp); //in this example, nCols ==
-
Tab key doesn't work rightThanks for your reply. Your references are to the point; although maybe not since none of them seem to apply to property pages embedded with an OCX. In the dialog box, I set the Tab Control to WS_EX_CONTROLPARENT with WS_TABSTOP, but it made no difference. The property pages are embedded in the Tab Control using CoCreateInstance to get a ISpecifyPropertyPages pointer, which is used to get a CAUUID, which is used to get an array of IPropertyPage pointers from more calls to CoCreateInstance, one for each property page. Probably what I need to do is trap the Tab message in the dialog box, then somehow see if the property page with the edit controls is open, see if the focus is in an edit control, and if it is, send the Tab message to the property page. I don't know how to do that yet, but your answer stimulated some thinking. It seems like a lot of work for an apparently simple task..
-
Tab key doesn't work rightThanks for your efforts, Josh, but you don't seem to understand the question. As I mentioned, this case involves property pages embedded in the dialog box using an OCX. (Maybe the correct term is ActiveX control?) So there is an rc file for the property pages in the OCX and a separate rc file for the dialog box in my main program. The tab order for the property page in the OCX rc file is already set up to go from edit control to edit control. This tab order is ignored. No matter which one of the ten edit controls has the focus, pressing the tab button causes the focus to jump to the OK button in the dialog box. Clearly the tab order for the dialog box in the rc file of the main program is the one used by the system. In the rc file for the dialog box, there are the three buttons, OK, Help, Cancel and a Tab Control in which the OCX property pages are embedded. I can set the Tab Stop property of the Tab control to TRUE, then the tab button causes the focus to jump between the three button and the title tab of the property page open. But I don't know how to make the tab button jump between the edit controls as desired.
-
Tab key doesn't work rightUsing Visual C++ 6, service pack 6 on XP. I have a set of COlePropertyPages controlled by an OCX embedded in a dialog box. On one property page I have 10 edit controls. I want the user to be able to jump from control to control using the Tab button. However, when the focus is in a control and the user hits the tab button, the focus jumps to the OK button in the dialog box. From that point on, repeatedly pressing the Tab button merely jumps between the Help, OK, and Cancel buttons on the dialog box. Trapping the WM_KEYDOWN message in the property page doesn't help, the code isn't reached. I tried subclassing one of the edit controls. Even when that edit control has the focus, and the Tab button is pressed, the WM_KEYDOWN message isn't reached. I tried using PreTranslateMessage() in the subclass, the code isn't reached (I tested all these using a break point.) How can I make it so that the Tab button jumps from edit control to edit control? Thanks!
-
Trouble with VirtualAllocThank you very much for your prompt reply. My overall goal is to be able to have my application access memory beyond the 2GB boundary so that it can allocate physical memory beyond the 2GB boundary and I am able to allocate 2 to 3 (maybe more?) GBs of memory. One solution you mentioned (the 3GB extension) is described here: http://msdn.microsoft.com/en-us/library/ms189334(SQL.100).aspx This solution however affects the allocation of memory for every application on the system, and is not something I really want to do. I just want my particular application to be able to allocate beyond the boundary. Note that the link above mentions AWE, which I am trying to use. Note that the diagram shows the that memory allocated in physical memory is not contiguous, which addresses your concern. The link posted in my first post is the code I am using to use AWE. As I mentioned, the only difference is that I'm using LoadLibrary to access the allocation and mapping functions. This link discusses AWE in more detail: http://msdn.microsoft.com/en-us/library/aa366527(VS.85).aspx Included in description for the AWE functions in the documentation is the following: "Physical pages can reside at any physical address. You should make no assumptions about the contiguity of the physical pages." So the hitch at this point to making this work is VirtualAlloc(). The previous function in the example, "AllocateUserPhysicalPages()" works fine.
-
Trouble with VirtualAllocI'm using Visual C++ 6 service pack 6 on XP. According to the documentation for that version, Advanced Windows Extentions (AWE) allows users to access physical memory beyond the 2 GB boundary for a process. There is an example in the documentation: http://msdn.microsoft.com/en-us/library/aa366531(VS.85).aspx I've had trouble linking to kernel32.dll to make this work. However, the kernel32.dll(2009) in Windows/system32 has the functions so I used LoadLibrary() to explicitly link to that kernel32.dll. Unfortunately, even on a system with 2.96 GB of RAM, running ONLY the simple example, I cannot get VirtualAlloc() to successfully reserve memory over 1100 MB (it does work at 1100 MB and below). I am using the VirtualAlloc from the 2009 kernel32.dll. ***** typedef LPVOID (WINAPI *LPVIRT)(LPVOID lpAddress,SIZE_T dwSize,DWORD flAllocationType,DWORD flProtect); LPVIRT lpVirt; HMODULE hHandle = LoadLibrary("kernel32.dll"); // Reserve the virtual memory. lpVirt = (LPVIRT)GetProcAddress(hHandle, "VirtualAlloc"); lpMemReserved = (*lpVirt)( NULL,m_uMBytes*0x100000,MEM_RESERVE | MEM_PHYSICAL,PAGE_READWRITE ); if( lpMemReserved == NULL ) { TRACE("Cannot reserve memory.\n"); return; } ***** With 2.96 GB, shouldn't I be able to reserve more than 1100 MB? How can I do it?
-
Enabling SE_LOCK_MEMORY_NAME privilegeI found the setting at Control panel->Local Security Policy->Local policies->User Rights Assignment->Lock pages in memory
-
Enabling SE_LOCK_MEMORY_NAME privilegeI'm working with Visual C++ service pack 6 and attempting to use AWE, the Addressing Windows Extensions on Windows XP. I'm using the example at http://msdn.microsoft.com/en-us/libr...31(VS.85).aspx which has the following code: ****** // Adjust the privilege. Result = AdjustTokenPrivileges ( Token, FALSE, (PTOKEN_PRIVILEGES) &Info, 0, NULL, NULL); // Check the result. if( Result != TRUE ) { printf ("Cannot adjust token privileges (%u)\n", GetLastError() ); return FALSE; } else { if( GetLastError() != ERROR_SUCCESS ) { printf ("Cannot enable the SE_LOCK_MEMORY_NAME privilege; "); printf ("please check the local policy.\n"); return FALSE; } } ***** I get the "Cannot enable the SE_LOCK_MEMORY_NAME privilege". I'm assuming there's a way for a user with administrative privileges to allow the program to enable it. How do I do it?
-
List Control doesn't refresh when dialog box disappearsI'm using Visual C++ 6 on Windows XP. My program uses COlePropertyPage property pages. One of them has a List Control. When the user clicks a certain part of my List Control, a dialog box appears, and when the user is done with it, it disappears. However, if the dialog box is moved over part of my List Control, when the box is closed that part is erased and set to gray, the color of the underlying COlePropertyPage. I want the List Control to refresh itself when the dialog box disappears. The control will refresh itself if another window is placed on top and then removed. Of course, I don't want the user to have to do that! The List Control should refresh immediately when the dialog disappears. I'm trying Invalidate(), UpdateWindow() and RedrawItems() one at a time and all three at once. It makes no difference. Actually, I note that my DrawItem function is called even without calling those repaint functions. Calling them just repeats the DrawItem function being called. However, the DrawItem call has no effect. Also I've tried setting the control to dirty with COlePropertyPage::SetControlStatus(). Nothing works. Thanks for any help..
-
Bigger OLE property pagesAt work I use Visual C++, programming for W2K and XP. We have a program that uses lots of "ocx"s, nearly all of which have property pages using COlePropertyPage. Nowadays, people have a lot of pixels on their screens and the property pages are getting a bit small. My boss asked me to look into enlarging the property pages. I did this on one of the ocxs and got the warning message box: "Dimensions should be 250x62 or 250x110 dialog units." This is from the _ValidatePageDialog() function in ctlppg.cpp in the MFC/SRC directory. This debug only function checks for those dimensions using hard-wired numbers! And although I enlarged the property pages in the resource file, they remained the same size when shown. So I'm wondering if anyone has a work-around for this: how can I make the property pages larger for an ocx? Thanks for your help....
-
OLE Automation server's knowledge of client(s)Thanks very much for your reply.
Roger Stoltz wrote:
I may be misinterpreting your question, but this is done by reference counting. The server will in general not know anything about its clients, it only knows that it has one or more clients attached to it.
That's exactly my question. I don't know how to do reference counting! My MDI server has a CCmdTarget derived class: CAutoApp. I noted that CAutoApp gets created with every new client connection, so I put in an array to capture the LPUNKNOWN return from GetInterface(&IID_IUnknown). However, I still don't know how to use that to tell me if the client has disconnected.
Roger Stoltz wrote:
From your question I assume you're talking about an application that runs as an out-of-process COM server. I wouldn't expect the server to close, or even the WM_CLOSE message being sent, unless the reference count reaches zero. Popping a dialogue asking the user whether to shut down the server or not seems a bit odd since there shouldn't be any clients left.
After any client calls ReleaseDispatch(), my server gets a WM_CLOSE message whether or not there are any clients left. I wouldn't even have to do a reference count if this were not the case. Maybe that's the real question, why do I get a WM_CLOSE message even when there are clients left? I'm not sure how to investigate that.
-
OLE Automation server's knowledge of client(s)Using Visual C++ Service Pack 6 on Windows 2000. When my automation client calls COleDispatchDriver::ReleaseDispatch(), thus releasing the server, the server receives a WM_CLOSE message. If there are other clients still connected, the server,if visible, is supposed to display a message to that effect and offer to close. If invisible, the server simly doesn't close. How does the server know which clients, if any, are still attached? Thanks, GF
-
Automation Server QuestionsI'm using VC++ 6 service pack 6 on Windows 2000. I'm working with an automation server MDI application with the following requirements. 1) Can be started by user with visible interface. 2) Only one instance at a time. 3) If controlled by client, runs in background (invisible) initially. 4) Client(s) has option to make application visible. 5) If visible and user attempts to close with client(s) connections, warning is given with choice to close or not. 6) Has menu item that allows user to disconnect from all clients. 7) If invisible, closes when last client disconnects. I have no problem with 1-4, the questions arise with 5-7. In order to implement these, the server needs to know if there are clients connected to it. My main automation class (CAutoApp) is instantiated every time a client contacts it. In the constructor I add to a static array of LPUNKOWNs (m_lpUnk[MAXNUMCONN]) using GetInterface(&IID_IUnknown):
LPUNKNOWN CAutoApp::m_lpUnk[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
CAutoApp::CAutoApp()
{
EnableAutomation();
for (int i=0; i < MAXNUMCONN && NULL != m_lpUnk[i]; i++);
if (i < MAXNUMCONN)
m_lpUnk[i] = GetInterface(&IID_IUnknown);
}Whenever a client closes, CMainFrame::OnClose is called in the server. So I have the following code to implement 5 and 7 in OnClose():
//see if any clients are still connected int i=0; while(i < MAXNUMCONN && NULL != CAutoApp::m\_lpUnk\[i\]) { try { CAutoApp::m\_lpUnk\[i\]->AddRef(); CAutoApp::m\_lpUnk\[i++\]->Release(); } catch(...) { //connection no longer exists, remove from array for(int j=i; j<MAXNUMCONN-1 && NULL != CAutoApp::m\_lpUnk\[j\]; j++) CAutoApp::m\_lpUnk\[j\] = CAutoApp::m\_lpUnk\[j+1\]; } } //if connected to at least one client if (NULL != CAutoApp::m\_lpUnk\[0\]) { if (IsWindowVisible()) { if (IDNO == AfxMessageBox("One or more remote connections is still active. Exit anyway?", MB\_YESNO)) //user decides not to close application return; } else //a client disconnected but do not close because other client(s) remain return; } ..... CMDIFrameWnd::OnClose();
This works, although maybe it's a no-no to use a try-catch this way. If there's a better way to do it, I'd like to know. To implement (6), I have the following:
void CMainFrame::OnCloseconnections()
{
if (IDNO == AfxMessageBox("This will close all connections. Are y -
How to create a shortcut within C++ programI want to create a shortcut within my Visual C++ program and put it in the All Users Startup directory. Anyone know how? Thanks, GAF