I was looking into modifying Microsoft Office OneNote contents via c++ programming. Specifically at using Quick Filing Dialog Box Interfaces. But all the examples available in there are for C#, I want to know whether API for C++ is available, if so where can I start to learn them. I just wanted to use that dialog box get the OneNote section and insert my content as a page in that section.
As Daniel Escapa writes:
Open Visual Studio and create a new C++ project. For my example I am creating a Win32 Console App:
Now I want to add the OneNote2007 header file, OneNote12.h. You can find that attached with this document. Make sure to copy it to the folder with your C++ source files as well as add it to your header files like this:
Add a new #include for OneNote:
#include "OneNote12.h"
Now just connect to OneNote like you would any other COM API. Please see here for an example:
CoInitialize(NULL);
IApplication* piOneNote;
CoCreateInstance(__uuidof(Application), NULL,
CLSCTX_LOCAL_SERVER, __uuidof(IApplication), (void**)&piOneNote);
if(piOneNote)
{
BSTR temp;
HRESULT hr = piOneNote->GetHierarchy(NULL, hsNotebooks, &temp);
wprintf(L"%s", temp);
}
Related
I know that windows common dialog have a IShellBrowser interface and its class name is 'SHELLDLL_DefView'. Refer this.
How to get the IShellBrowser interface of a file open dialog?
(The file open dialog is outside's, not my application's.)
This is the same as my question but is delphi(?) ver.
I don't know delphi code. How to solve this problem as C++ or MFC code?
Here is a C/C++ translation of the Delphi code:
const UINT CWM_GETISHELLBROWSER = WM_USER + 7;
IShellBrowser *ShellBrowser = (IShellBrowser*) SendMessage(aDialog, CWM_GETISHELLBROWSER, 0, 0);
if (ShellBrowser) {
ShellBrowser->AddRef();
// use ShellBrowser as needed...
ShellBrowser->Release();
}
You might also want to read the following article, which provides a slightly more official (ie, more reliable, but more complicated) way to get an IShellBrowser for a given HWND:
Querying information from an Explorer window
Start with the ShellWindows object which represents all the open shell windows. You can enumerate through them all with the Item property... From each item, we can ask it for its window handle and see if it's the one we want... Okay, now that we have found the folder via its IWebBrowserApp, we need to get to the top shell browser. This is done by querying for the SID_STopLevelBrowser service and asking for the IShellBrowser interface.
Problem
I'm trying to open an MFC program that reads a Microsoft Access database (.mdb) and allows the user to view or modify the data. This is an existing program (and source code) given to me by a group in another lab where the program opens and works just fine.
In our lab, I have yet to see it load properly. When run, it pops up a dialog box that says, "Attempted an unsupported operation". Windows then offers me a chance to debug and such before it crashes.
Environment
In the other lab, they use Windows 7 and Microsoft Office 2010, and it works.
In our lab, I've tried Windows 7 with Office 2013 and Windows XP with Office 2010. The latter crashes without giving me the dialog box. I don't know if we have a Win7/MSO2010 machine.
The Function
I have the source code for the program. The solution file implies it was last developed in VS2010, which the computers I tested on had installed as well. Running it out of Visual Studio 2010 or straight from the executable yields the same results.
I have added additional debug dialog boxes to the code that narrow down the problem to this function call, which the code never gets past:
CwinApp:OpenDocumentFile(LPCTSTR lpszPathName)
The single string passed into the function is a path and filename for the MS Access database to be opened. It exists in a temporary directory created by another program. This is on a drive other than C, though I've tested some there, as well. Problems with programs related to this one often stem from files with "read only" status, but I continually check the temporary files created, and they are write-able.
Documentation
I found this information titled "Breaking Changes in Visual C++" for VS2010 through another SO question:
A new virtual function was added to the CDocTemplate class. This new virtual function is CDocTemplate::OpenDocumentFile. The previous version of OpenDocumentFile had two parameters. The new version has three parameters. To support the restart manager, any class derived from CDocTemplate must implement the version that has three parameters. For more information about the function, see CDocTemplate::OpenDocumentFile. The new parameter is bAddToMRU.
Code
I feel this might be the answer! But I don't have a strong idea of exactly what to change to get this to work. Here's where I stopped:
Program.cpp
CDocument* ProgramApp::OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToLRU, BOOL bMakeVisible)
{
// Add specialized code here and/or call base class
// Debug messages added
CDocument* tempDoc;
AfxMessageBox(lpszFileName);
tempDoc = CWinApp::OpenDocumentFile(lpszFileName, bAddToMRU);
AfxMessageBox("Opened database!");
return tempDoc;
}
Program.h
class ProgramApp : public CWinApp
{
public:
...
virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToMRU, BOOL bMakeVisible);
afxwin.h
class CWinApp : public CWinThread
{
...
virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName);
virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToMRU);
Changing the Program.cpp call to be from a "Template" class caused errors, but I wouldn't be surprised if that's towards the answer. This solution looks similar, but I'm not sure exactly what to do.
My hope is that this problem has a simple solution that someone more knowledgeable can give me. I would be much appreciative, and additional context would help a lot.
Edit: Debugging
I drilled down into the Windows code to see what was precisely going wrong. It seemed too dense to understand, but a coworker and I may have clues based on it. The failure message happens here:
dlgdata.cpp
// Could be a windowless OCX
pSite = m_pDlgWnd->GetOldControlSite(nIDC);
if (pSite == NULL)
{
TRACE(traceAppMsg, 0, "Error: no data exchange control with ID 0x%04x.\n", nIDC);
ASSERT(FALSE);
AfxThrowNotSupportedException();
}
...Although we are seeing debugger issues here:
occcont.cpp
COleControlSiteOrWnd *pemp = new COleControlSiteOrWnd(hwndCtrl, pOccDlgInfo->m_pItemInfo[i].bAutoRadioButton);
ASSERT(IsWindow(pTemp->m_hWnd));
if (IsWindow(pTemp->m_hWnd))
{
hwndStart = pTemp->m_hWnd;
...
My coworker believes this could have little to do with the opening of this document as I suspected and more to do with objects/controls we don't have on our lab computers trying to be used for the program.
I have faced the same problem in opendocumentfile(), there was a control in CFormView class which i was not using so i commented it out but forget to delete from .rc file. Once i remove the control entry from .rc file the problem disappear.
there should be no control variable uninitialize , please check that also
Does anyone know which relation may exist between registration-free COM and drag/drop functionality?
Specifically, we have a huge C++ CAD/CAM application comprising a number of EXEs and several hundreds DLLs. Many of them serve as COM servers (both in-proc and out-of-proc) and/or clients, and also implement ActiveX controls.
The most of ActiveX controls and the main CMDIFrameWnd-based window of one of EXEs implement drag/drop functionality. ActiveX controls implement the both drop source and drop target, and the main window is only drop target, in particular, for files from Windows Explorer.
The drag/drop implementation is pretty standard and based on two data members derived from COleDataSource and COleDropTarget for drop source and drop target respectively. The COleDropTarget-derived member is registered with respective window in the window's OnCreate method. It also overrides OnDragEnter, OnDragOver and OnDrop methods in a similar way. Namely, the system-supplied COleDataObject parameter is asked for specific format (in particular, CF_HDROP), and in the case of positive answer, the data (e.g., file path) is extracted from the clipboard. The code looks like the following:
static FORMATETC g_FileFmt = {CF_HDROP, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
....
// Inside OnDragEnter, OnDragOver or OnDrop method
STGMEDIUM stgmedium = {0,0,0};
if (pDataObject->IsDataAvailable(g_FileFmt.cfFormat))
{
HRESULT hr = pDataObject->GetData(g_FileFmt.cfFormat, &stgmedium);
HDROP hdrop = (HDROP)GlobalLock(stgmedium.hGlobal);
if (hdrop != 0)
{
int FilesCount = DragQueryFile(hdrop, (UINT)-1, 0, 0);
if (FilesCount != 0)
{
TCHAR FileName[_MAX_PATH];
DragQueryFile(hdrop, 0, FileName, _MAX_PATH);
// Check file extension and store the file name for farther use.
}
GlobalUnlock(hdrop);
}
}
The drop source implementation is also straightforward and looks like the following:
void CDmDocListCtrl::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if (pNMListView->iItem != -1 && m_pOleDataSource && prv_BeginDrag())
{
DROPEFFECT DE = m_pOleDataSource->DoDragDrop(
DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK, 0);
}
*pResult = 0;
}
where prv_BeginDrag() function collects dragged data, packs it and puts on the clipboard by calling SetData method from the m_pOleDataSource object's IDataObject interface.
The all this stuff worked perfectly until it was decided to make the whole application registration-free. It took me three months to force the application run isolated (without registration of COM components) by embedding manifests, launching out-of-proc COM servers on demand and altering CLSID of some classes in order to separate instances of the same server launched from different folders. At last it begins to work - but without drag/drop functionality, despite it wasn't even touched by my changes.
On the drop target side, when I drag file from Windows Explorer, depicted above call to COleDataObject::IsDataAvailable returns false, although before my changes returned true. At the same time, if I add a single line of code "DragAcceptFiles();" to the main window's OnCreate method, drag/drop begins working via the standard CFrameWnd's WM_DROPFILE message handler.
On the drop source side, the dragged data are successfully packed and placed on the clipboard, but COleDataSource::DoDragDrop method fails, because a call to ::DoDragDrop API inside MFC implementation returns REGDB_E_CLASSNOTREG "Class not registered" result.
It means, that COM activation changes somehow influence drag/drop behavior. How?
P.S. 1) The EXE, to which I drag files from Windows Explorer, has in its project properties "UAC Execution Level = asInvoker". As far as I understand, it tells that the EXE will run at the same UAC level as Windows Explorer when launched by double-click on the file.
2) Quite surprisingly, although drag/drop stopped working with symptoms described above, Copy/Paste continues work well, despite the both technologies have similar implementation.
3) I believe, that if find out when ::DoDragDrop API returns "Class not registered" error, and which class it is looking for, it would be possible to solve the problem.
Thanks for help,
Ilia.
Following to MartinBa advice, I solved the problem with the help of Process Monitor. The Process Monitor showed me that while I drag an item in the ActiveX control (mentioned in the question), the system unsuccessfully tries get access to a class ID in the Registry. Looking for that ID, I found that it is really not class ID, but IDataObject interface ID. It was referenced in one of my manifest files.
The most of manifests I have written by hand, but a few, especially at the beginning of the project having no experience in the area, I generated automatically by Visual Studio from existing type library. In one of them Studio included the comInterfaceExternalProxyStub statement for a couple of system interfaces, in which proxyStubClsid32 element was (erroneously) equal to the interface ID.
I'm still not sure whether those system interfaces should present in the manifest; for example, the IDataObject is only mentioned as a method's parameter in one of IDL definitions. Anyway, I corrected only the proxyStubClsid32 value, and the problem disappeared...
The moral of this very painful for me story is to always check output of automatic tools...
In my application I can programmatically open explorer and select a file using the following code:
void BrowseToFile(LPCTSTR filename)
{
CString strArgs;
strArgs = _T("/select,\"");
strArgs += filename;
strArgs += _T("\"");
ShellExecute(0, _T("open"), _T("explorer.exe"), strArgs, 0, SW_NORMAL);
}
My problem is that if I call this function a second time with a different file, but in the same folder, the selection in explorer does not change to the new file, but remains on the previous file.
For example, if I call my function with C:\path\to\file1.txt, a new explorer window will open and file1.txt will be selected. If I call my function a second time with C:\path\to\file2.txt, the existing explorer window will be activated, but the selection will still be on file1.txt.
Is there a way to force explorer to update the selection or a better way to accomplish this?
EDIT:
The behavior mentioned above was on Windows XP. It seems the behavior on Vista / Win7 is different. Each call will open a new instance of explorer and select the file.
My main goal is to replicate the Visual Studio option to Open Containing Folder of a document. This feature in Visual Studio behaves the same on XP, Vista, and Win7. It will not create a new instance if another instance with the same folder is already open, but it will update the selection to the new file.
If anybody knows how Visual Studio accomplishes this I would love to know about it.
Found the answer to my question. I need to use the shell function SHOpenFolderAndSelectItems. Here is the code for the function if anybody is ever interested:
void BrowseToFile(LPCTSTR filename)
{
ITEMIDLIST *pidl = ILCreateFromPath(filename);
if(pidl) {
SHOpenFolderAndSelectItems(pidl,0,0,0);
ILFree(pidl);
}
}
Try the '/n' option. This will, however, open a new folder - perhaps already opened. But, at least, the file you specify is selected.
/n,/select,<path_and_filename>
SHOpenFolderAndSelectItems always fails in my case and I can't figure out why. Btw, you must call CoInitialize/CoInitializeEx before calling this one.
In the case you outlined it appears the file window only selects the file when it's initialized instead of when activated.
Although this feels like a kludge, you could detect XP and only for that OS close the dialog using its handle and open a new one to target another file with.
I have some code which shows a simple dialog box and handles user action (written using plain WinAPI).
// Display dialog and handle user action
LRESULT choice = DialogBoxParam(NULL, MAKEINTRESOURCE(AP_IDD_DIALOG), NULL, (DLGPROC)DialogCallback, NULL);
Is there any way to hardcode the resource file dialog.rc, which is used to build the dialog ?(I would like to get rid of .rc files and I'm pretty sure there is a way, yet I don't know what it is :)
Edit
Also, does someone have any ideas on converting existing .rc files into hardcoded resources? Is this possible?
*.rc (resource) files are source code, they are compiled with the resource compiler and linked into your object (.exe/.dll)
You don't need to ship the resource file or have it present with your app to run it.
If you want to move to programmatically defined windows rather than templates then you might want to be looking at QT/wxWidgets. But thats a fair chunk of overhead for 1 dialog!
I'm surprised I couldn't find an existing app to do this sort of thing, enough hits on google with people trying to do this.
Ok, so the DLGTEMPLATE is a variable length blob of data, normally you let the dialog function pull it from the resource bundle for you, but instead you want to store it in your program.
You need to change your static lib to have a new function to decode some 'blob' back into the dlgtemplate, and you need to generate the blob. (or add the blob in your code without decoding which I don't want to think about right now)
The following code will give you the DLGTemplate data you need to imbed in your app. (cut from larger project)
HGLOBAL LoadResourceImpl(wchar_t *resource, wchar_t *type)
{
HRSRC handle = FindResource(hInstance, resource,type);
if (handle)
{
HGLOBAL hResource = LoadResource(hInstance, handle);
if (hResource)
return LockResource(hResource);
}
return 0;
}
DLGTEMPLATE * LoadDialog(wchar_t *resource)
{
return (DLGTEMPLATE *) LoadResourceImpl(resource,RT_DIALOG);
}
DLGTEMPLATE * LoadDialog(int resource)
{
return (DLGTEMPLATE *) LoadResourceImpl(MAKEINTRESOURCE(resource),RT_DIALOG);
}
Make an app that includes your resource - use the appropriate LoadDialog to get the data.
Now "write out" that blob in a format to include in your app -
step 1 - find out how much data there is by traversing the structure to find the total size including all the controls (control count is in DLGTEMPLATE::cdit)
step 2 - convert the data to something you can compile into your code - like HEX
Add to your static library a new 'HEX' to DLGTEMPLATE method and the hex string you made using the other app.
Can we hard code the .res file into the program?
the resource compiler converts .rc into .res
use a hex dump tool (eg. winhex) to translate the .res into bytes array
(represented in C source code).
add the source code file in the project and compile in the executable.
locate the dialog resource position from the array and use DialogBoxIndirect.
DialogBoxParamIndirect can be used instead. It takes as a parameter the dialog template. Raymond Chen's blog has an example of building a dialog box at runtime rather than from a resource using the DialogBox*Indirect API's.
Per MSDN, dialog box resources are basically composed of the DLGTEMPLATE and DLGITEMTEMPLATE structures. So you should be able to use the resource API's (FindResource, LoadResource, and LockResource) to get at the underlying bits of an existing dialog resource, and embed that within your code.
Note that this is a lot more painful than using the .rc file. It's much more difficult to make changes to your layout, and it's also much less localizable, since localization would now require a code change to update the template in code.
If it's a simple dialog, why use the DLGTEMPLATE at all?
Nothing stops you from simply doing ::CreateWindow'ing those controls directly. If it's a simple dialog with 2-3 buttons and a couple text fields, simply call ::CreateWindow, passing in the window class of whatever common control you're using.
This is essentially what the DialogXxxxx functions do anyway. DLGTEMPLATE is a convenience for declaratively laying out your forms, and having the boilerplate make the appropriate CreateWindow calls, etc.