Assigning a file type to an Application on Windows: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
m (Registers, not registeres)
m (Cosmetic changes to make the code readable)
 
Line 4: Line 4:
== Background / technical stuff ==
== Background / technical stuff ==


File types in windows are defined in the Windows registry. First there is the entry of "<extension>" in HKCR which contains the file type name. In HKCRamp;lt;file type name> the different actions are defined.
File types in Windows are defined in the Windows registry. First, there is the entry of <extension>in HKCR which contains the file type name. In HKCR, <file type name> the different actions are defined.


This means, there is a file extension '''gidoc''', whose files are called "MDI Text Editor Document". These documents should be opened in an application, which is located here: E:.exe. The file itself should be a parameter.
This means, there is a file extension <code>.gidoc</code>, whose files are called “MDI Text Editor Document”. These documents should be opened in an application, which is located here: <code>E:.exe</code>. The file itself should be a parameter.


Using this pattern, each double click on a file opens a new Process which gets the file as parameter. This is fine for SDI (Single Document Interface) applications.
Using this pattern, each double click on a file opens a new Process which gets the file as parameter. This is fine for SDI (Single Document Interface) applications.
Line 12: Line 12:
Example below:
Example below:


<code>
    HKEY_CLASSES_ROOT
HKEY_CLASSES_ROOT
    .gidoc
idoc
      "Default"="GiMdi.Document"
"Default"="GiMdi.Document"
      "NullFile"=""
"NullFile"=""
   
 
    .Document
.Document
      "Default"="MDI Text Editor Document"
"Default"="MDI Text Editor Document"
      "Default"="E:.exe,0"
]
      "Default"="E:.exe %1"
"Default"="E:.exe,0"
"Default"="E:.exe %1"
</code>


For MDI (Multi Document Interfaces), it should be opened in the existing Process. To achieve this, windows uses the old DDE mechanism. There must be some additional entries to achieve this:
For MDI (Multi Document Interfaces), it should be opened in the existing Process. To achieve this, windows uses the old DDE mechanism. There must be some additional entries to achieve this:


<code>
    HKEY_CLASSES_ROOT
HKEY_CLASSES_ROOT
    .Document
.Document
      "Default"="[open(\"%1\")]"
"Default"="[open(quot;%1quot;)]"
      "Default"="SimpleCryptIoDevided"
]
      "Default"="system"
"Default"="SimpleCryptIoDevided"
"Default"="system"
</code>


If this is also added, Windows starts the process, which is given in command and then sends a WM_DDE_INITIATE broadcast message to windows. The application must react on this, then handle WM_DDE_EXECUTE messages and, if requested, WM_DDE_TERMINATE message. If the reaction on the DDE messages is not correc, Windows displays some error messages.
If this is also added, Windows starts the process, which is given in command and then sends a <code>WM_DDE_INITIATE</code> broadcast message to windows. The application must react on this, then handle <code>WM_DDE_EXECUTE</code> messages and, if requested, <code>WM_DDE_TERMINATE</code> message. If the reaction on the DDE messages is not correct, Windows displays some error messages.


== How to do it ==
== How to do it ==


Normally, your application registers the file types (changes the registry) during startup. If the DDE Mdi stuff should be used, it then has to create some ATOMS (::GlobalAddAtom). Then it starts up the rest and waits fo user input.
Normally, your application registers the file types (changes the registry) during startup. If the DDE MDI stuff should be used, it then has to create some ATOMS (<code>::GlobalAddAtom</code>). Then it starts up the rest and waits for user input.


If the file type is registered, and no dde is set, the application is started with the file path as parameter.
If the file type is registered, and no DDE is set, the application is started with the file path as parameter.


If ddeexec keys are set in the registry, the application is started and the the DDE messages are send. While handling these messages, WM_DDE_ACK messages are send back to the caller and the messages must be handled.
If ddeexec keys are set in the registry, the application is started and the the DDE messages are send. While handling these messages, <code>WM_DDE_ACK</code> messages are send back to the caller and the messages must be handled.


This behaviour and the code would always look the same, only the registration keys and the handling of the DDE commands differ from application to application. There fore I created a class, that does all this and can be used as base class instead of QMainWindow: '''DocumentWindow'''.
This behavior and the code would always look the same, only the registration keys and the handling of the DDE commands differ from application to application. Therefore, I created a class that does all this and can be used as base class instead of <code>QMainWindow</code>: <code>DocumentWindow</code>.


== Use the DocumentWindow class ==
== Use the <code>DocumentWindow</code> class ==


If you want to create an (MDI) editor application, you can derive your main window class from DocumentWindow instead of QMainWindow. If you implement an MDI application, you should also reimplement the virtual method <code>ddeOpenFile</code>.
If you want to create an (MDI) editor application, you can derive your main window class from <code>DocumentWindow</code> instead of <code>QMainWindow</code>. If you implement an MDI application, you should also re-implement the virtual method <code>ddeOpenFile</code>.
The example code is based on the [http://doc.qt.io/qt-4.8/qt-mainwindows-mdi-example.html Qt MDI Example]
The example code is based on the [http://doc.qt.io/qt-4.8/qt-mainwindows-mdi-example.html Qt MDI Example].


<code>
    class MainWindow : public DocumentWindow
class MainWindow : public DocumentWindow
    {
{
       
        protected:
protected:
            virtual void ddeOpenFile(const QString& filePath);
virtual void ddeOpenFile(const QString& filePath);
    }
}
</code>


all other things that need to be done is extending your constructor
All other things that need to be done is extending your constructor:


<code>
    MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) :
MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) :
        DocumentWindow(parent, flags)
DocumentWindow(parent, flags)
    {
{
       
        registerFileType("GiMdi.Document",           // Document type name
registerFileType("GiMdi.Document", // Document type name
                        "MDI Text Editor Document", // User readable file type name
"MDI Text Editor Document",// User readable file type name
                        ".gidoc",                   // file extension
".gidoc", // file extension
                        0,                         // index of the icon to use for the files.
0, // index of the icon to use for the files.
                        true);                     // register for DDE events
true); // register for DDE events
        enableShellOpen();
enableShellOpen();
       
    }
}
</code>


and reimplement ddeFileOpen
and re-implement <code>ddeFileOpen</code>:


<code>
    void MainWindow::ddeOpenFile(const QString& filePath)
void MainWindow::ddeOpenFile(const QString& filePath)
    {
{
        MdiChild *child = createMdiChild();
MdiChild '''child = createMdiChild();
        if (child->loadFile(filePath)) {
if (child->loadFile(filePath))
            statusBar()->showMessage(tr("File loaded"), 2000);
{
            child->show();
statusBar()->showMessage(tr("File loaded"), 2000);
        }
child->show();
        else {
}
            child->close();
else
        }
{
    }
child->close();
}
}
</code>


That's it. Now it should work to just start the application, and the file type ".gidoc" is registered with your application. If you double click on it in the Windows explorer, it should be opened in the currently running Process or, if no instance of your application is running, start a new one. If your application has a windows icon in the resource (not in the qrc file! a windows resource file <file>.rc), this icon should be used for the files.
That's it. Now it should work to just start the application, and the file type <code>.gidoc</code> is registered with your application. If you double click on it in the Windows explorer, it should be opened in the currently running Process or, if no instance of your application is running, start a new one. If your application has a windows icon in the resource (not in the <code>.qrc</code> file! a windows resource file <code><file>.rc</code>), this icon should be used for the files.


==Use the DocumentWindow sources==
==Use the <code>DocumentWindow</code> sources==


===DocumentWindow.h===
===<code>DocumentWindow.h</code>===


<code>
    // ————————————————————————————————-
// ————————————————————————————————-
    /**
/**
    * @file
* @file
    * @brief
* @brief
    * @author Gerolf Reinwardt
* @author Gerolf Reinwardt
    * @date 30. march 2011
* @date 30. march 2011
    *
*
    * Copyright © 2011, Gerolf Reinwardt. All rights reserved.
* Copyright © 2011, Gerolf Reinwardt. All rights reserved.
    *
*
    * Simplified BSD License
* Simplified BSD License
    *
*
    * Redistribution and use in source and binary forms, with or without modification, are
* Redistribution and use in source and binary forms, with or without modification, are
    * permitted provided that the following conditions are met:
* permitted provided that the following conditions are met:
    *
*
    * 1. Redistributions of source code must retain the above copyright notice, this list of
* 1. Redistributions of source code must retain the above copyright notice, this list of
    * conditions and the following disclaimer.
* conditions and the following disclaimer.
    *
*
    * 2. Redistributions in binary form must reproduce the above copyright notice, this list
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
    * of conditions and the following disclaimer in the documentation and/or other materials
* of conditions and the following disclaimer in the documentation and/or other materials
    * provided with the distribution.
* provided with the distribution.
    *
*
    * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY EXPRESS OR IMPLIED
* THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY EXPRESS OR IMPLIED
    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
    * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    *
*
    * The views and conclusions contained in the software and documentation are those of the
* The views and conclusions contained in the software and documentation are those of the
    * authors and should not be interpreted as representing official policies, either expressed
* authors and should not be interpreted as representing official policies, either expressed
    * or implied, of Gerolf Reinwardt.
* or implied, of Gerolf Reinwardt.
    */
*/
    // ————————————————————————————————-
// ————————————————————————————————-
   
 
    #ifndef WIDGET_H
#ifndef WIDGET_H
    #define WIDGET_H
#define WIDGET_H
   
 
    // —— general includes —————————————————————————
// —— general includes —————————————————————————
    #include <windows.h>
#include <windows.h>
    #include <QMainWindow>
#include <QMainWindow>
   
 
    // —— local includes —————————————————————————
// —— local includes —————————————————————————-
    // —— predefines ——————————————————————————
// —— pre defines ——————————————————————————-
   
 
    // —— class definition —————————————————————————
// —— class definition —————————————————————————
    /**
/**
    * @short This class implements a main window with the ability to register file types on MS Windows and
* @short This class implements a main window with the ability to register file types on MS Windows and
    * react on the corresponding DDE events to open / print the files in an Windows MDI typical manner.
* react on the corresponding DDE events to open / print the files in an Windows MDI typical manner.
    *
*
    * The usage is fairly easy. Derive your own MainWindow class from DocumentWindow instead of QMainWindow.
* The usage is fairly easy. Derive your own MainWindow class from DocumentWindow instead of QMainWindow.
    * Inside your constructor, call registerFileType and enableShellOpen. The axample is build on top of the
* Inside your constructor, call registerFileType and enableShellOpen. The axample is build on top of the
    * Qt MDI Example (http://doc.qt.nokia.com/4.7/mainwindows-mdi.html)
* Qt MDI Example (http://doc.qt.nokia.com/4.7/mainwindows-mdi.html)
    *
*
    * @code
* @code
    MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) :
MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) :
        DocumentWindow(parent, flags)
DocumentWindow(parent, flags)
    {
{
       
        registerFileType("GiMdi.Document", "MDI Text Editor Document", ".gidoc", 0, true);
 
        enableShellOpen();
registerFileType("GiMdi.Document", "MDI Text Editor Document", ".gidoc", 0, true);
   
enableShellOpen();
        setWindowTitle(tr("MDI"));
 
        setUnifiedTitleAndToolBarOnMac(true);
setWindowTitle(tr("MDI"));
    }
setUnifiedTitleAndToolBarOnMac(true);
   
}
    *
    * Aditionally, the actions must be performed by overwriting one or more of:
*
    * @li ddeOpenFile
* Aditionally, the actions must be performed by overwriting one or more of:
    * @li ddeNewFile
* @li ddeOpenFile
    * @li ddePrintFile
* @li ddeNewFile
    * @li executeUnknownDdeCommand
* @li ddePrintFile
    *
* @li executeUnknownDdeCommand
    *
*
   
*  
    void MainWindow::ddeOpenFile(const QString& filePath)
 
    {
void MainWindow::ddeOpenFile(const QString& filePath)
        MdiChild *child = createMdiChild();
{
        if (child->loadFile(filePath)) {
MdiChild *child = createMdiChild();
            statusBar()->showMessage(tr("File loaded"), 2000);
if (child->loadFile(filePath))
            child->show();
{
        }
statusBar()->showMessage(tr("File loaded"), 2000);
        else {
child->show();
            child->close();
}
        }
else
    }
{
   
child->close();
    *
}
    */
}
    class DocumentWindow : public QMainWindow
 
    {
*
        Q_OBJECT
*/
        public:
class DocumentWindow : public QMainWindow
            // —— enums ———————————————————————————
{
            /**
Q_OBJECT
            * Known DDE command. These commands are typically used
public:
            */
// —— enums ———————————————————————————
            enum DdeCommand {
/**
                DDEOpen = 0x0001, // < open a file via explorer
* Known DDE command. These commands are typically used
                DDENew = 0x0002,   // < create a new file via explorer
*/
                DDEPrint = 0x0004, // < print a file via explorer
enum DdeCommand {
            };
DDEOpen = 0x0001, /< open a file via explorer*/
            Q_DECLARE_FLAGS(DdeCommands, DdeCommand)
DDENew = 0x0002, /**< create a new file via explorer*/
   
DDEPrint = 0x0004, /**< print a file via explorer*/
                // —— construction —————————————————————————
};
                /**
Q_DECLARE_FLAGS(DdeCommands, DdeCommand)
                * Constructor.
 
                *
// —— construction —————————————————————————
                * Creates a DocumentWindow with a given @arg parent and @arg flags.
/**
                */
* Constructor.
                explicit DocumentWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0);
*
   
* Creates a DocumentWindow with a given @arg parent and @arg flags.
            /**
*/
            * Destructor
explicit DocumentWindow(QWidget* parent = 0, Qt::WindowFlags flags = 0);
            */
 
            ~DocumentWindow();
/**
   
* Destructor
            // —— operators ——————————————————————————
*/
            // —— methods ——————————————————————————
~DocumentWindow();
            // —— accessors ——————————————————————————
 
            // —— members ——————————————————————————
// —— operators ——————————————————————————
   
// —— methods ——————————————————————————-
        protected:
// —— accessors ——————————————————————————
            // —— events ———————————————————————————
// —— members ——————————————————————————-
            /**
 
            * reimpl as DDE events come as windows events and are not translated by Qt.
protected:
            */
// —— events ———————————————————————————
            virtual bool winEvent(MSG *message, long *result);
/**
   
* reimpl as DDE events come as windows events and are not translated by Qt.
            // —— helpers for the file registration ——————————————————
*/
            /**
virtual bool winEvent(MSG *message, long *result);
            * virtual function that must be implemented by the derived class to react to the open command
 
            * from Windows.
// —— helpers for the file registration ——————————————————
            *
/**
            * @param filePath file that was selected in the explorer
* virtual function that must be implemented by the derived class to react to the open command
            */
* from Windows.
            virtual void ddeOpenFile(const QString& filePath);
*
   
* @param filePath file that was selected in the explorer
            /**
*/
            * virtual function that must be implemented by the derived class to react to the new command
virtual void ddeOpenFile(const QString& filePath);
            * from Windows.
 
            *
/**
            * @param filePath file that was selected in the explorer
* virtual function that must be implemented by the derived class to react to the new command
            */
* from Windows.
            virtual void ddeNewFile(const QString& filePath);
*
   
* @param filePath file that was selected in the explorer
            /**
*/
            * virtual function that must be implemented by the derived class to react to the print command
virtual void ddeNewFile(const QString& filePath);
            * from Windows.
 
            *
/**
            * @param filePath file that was selected in the explorer
* virtual function that must be implemented by the derived class to react to the print command
            */
* from Windows.
            virtual void ddePrintFile(const QString& filePath);
*
   
* @param filePath file that was selected in the explorer
            /**
*/
            * virtual function that must be implemented by get custom DDE commands from the explorer. If, e.g.
virtual void ddePrintFile(const QString& filePath);
            * a print or copy command should be available in explorer and they are registered via registerCommand,
 
            * this function is called.
/**
            *
* virtual function that must be implemented by get custom dde commands from the explorer. If, e.g.
            * @param command name of the command
* a printo or copy command should be available in explorer and they are registered via registerCommand,
            * @param params parameter string, containing the hole stuff, also " and commas.
* this function is called.
            *
*
            * @note a command like this [compare("%1")] will result in the parameters:
* @param command name of the command
            command = "compare";
* @param params parameter string, containing the hole stuff, also " and commas.
            params = "\"<filepath>\"";
*
            */
* @note a command like this [compare("%1")] will result in the parameters:
            virtual void executeUnknownDdeCommand(const QString& command, const QString& params);
command = "compare";
   
params = "quot;<filepath>quot;";
            /**
*/
            * Call this method to register the file type in Windows. It creates the default command
virtual void executeUnknownDdeCommand(const QString& command, const QString& params);
            * which means the app is started with the file to open as parameter. If @arg registerForDDE
            * is true, the DDE events are also created so the opening of a file is done in typical MDI
            * behavior (all files open in the same app).
            *
            * @param documentId id of the document, typically <Application>.Document —> see in registry, e.g. "GiMdi.Document"
            * @param fileTypeName free name of the file type, e.g. "MDI Text Editor Document"
            * @param fileExtension File extension, including the dot (e.g. ".gidoc")
            * @param appIconIndex index of the app icon to use for the file in the windows explorer, typically the application icon
            * @param registerForDDE true if DDE should be used (typical for MDI apps), typically false for SDI apps.
            * @param commands a combination of the commands to install.
            *
            * @note If more then the default commands are needed, then subsequent calls of registerCommand are needed.
            *
            * @note DDEOpen leads to the DDE command: [open("%1")]
            DDENew leads to the DDE command: [new("%1")]
            DDEPrint leads to the DDE command: [print("%1")]
            */
            void registerFileType(const QString& documentId,
                    const QString& fileTypeName,
                    const QString& fileExtension,
                    qint32 appIconIndex = 0,
                    bool registerForDDE = false,
                    DdeCommands commands = DDEOpen);
   
            /**
            * registers one command for a given file type. It is called for the predefined DDE command
            * types from registerFileType. if more then the normal commands are needed, it can be called
            * in addition to registerFileType.
            *
            * @code
            MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) :
            DocumentWindow(parent, flags)
            {
                …
                registerFileType("GiMdi.Document", "MDI Text Editor Document", ".gidoc", 0, true);
                registerCommand("fancyCommand", "GiMdi.Document", "-fancy %1", "[fancy(\"%1\")]");
                enableShellOpen();
                …
            }
            @endcode
            */
            void registerCommand(const QString& command,
                    const QString& documentId,
                    const QString cmdLineArg = QString::null,
                    const QString ddeCommand = QString::null);
   
            /**
            * Call this method to enable the user to open data files associated with your application
            * by double-clicking the files in the windows file manager.
            *
            * Use it together with registerFileType to register the fspezified file types or provida a
            * registry file (*.reg) which does this.
            */
            void enableShellOpen();
   
   
        private:
            // —— private helpers ————————————————————————
            /**
            * implementation of the WM_DDE_INITIATE windows message
            */
            bool ddeInitiate(MSG* message, long* result);
   
            /**
            * implementation of the WM_DDE_EXECUTE windows message
            */
            bool ddeExecute(MSG* message, long* result);
   
            /**
            * implementation of the WM_DDE_TERMINATE windows message
            */
            bool ddeTerminate(MSG* message, long* result);
   
            /**
            * Sets specified value in the registry under HKCU\Software\Classes, which is mapped to HKCR then.
            */
            bool SetHkcrUserRegKey(QString key, const QString& value, const QString& valueName = QString::null);
   
            /**
            * this method is called to do the DDE command handling. It does argument splitting and then calls
            * ddeOpenFile, ddeNewFile, ddePrintFile or executeUnknownDdeCommand.
            */
            void executeDdeCommand(const QString& command, const QString& params);
   
            // —— members ——————————————————————————-
            bool m_registerForDDE; /< used to identfy, if the dde commands should be written to the registry*/
                QString m_appAtomName; /**< the name of the application, without file extension*/
            QString m_systemTopicAtomName; /**< the name of the system topic atom, typically "System"*/
            ATOM m_appAtom; /**< The windows atom needed for DDE communication*/
            ATOM m_systemTopicAtom; /**< The windows system topic atom needed for DDE communication*/
            // —— not allowed members ——————————————————————-
    };
   
    Q_DECLARE_OPERATORS_FOR_FLAGS(DocumentWindow::DdeCommands)
   
    #endif // WIDGET_H


/**
* Call this method to register the file type in Windows. It creates the default command
* which means the app is started with the file to open as parameter. If @arg registerForDDE
* is true, the dde events are also created so the opening of a file is done in typical MDI
* behavior (all files open in the same app).
*
* @param documentId id of the document, typically <Application>.Document —> see in registry, e.g. "GiMdi.Document"
* @param fileTypeName free name of the file type, e.g. "MDI Text Editor Document"
* @param fileExtension File extension, including the dot (e.g. ".gidoc")
* @param appIconIndex index of the app icon to use for the file in the windows explorer, typically the application icon
* @param registerForDDE true if DDE should be used (typical for MDI apps), typically false for SDI apps.
* @param commands a combination of the commands to install.
*
* @note If more then the default commands are needed, then subsequent calls of registerCommand are needed.
*
* @note DDEOpen leads to the DDE command: [open("%1)]
DDENew leads to the DDE command: [new("%1)]
DDEPrint leads to the DDE command: [print("%1)]
*/
void registerFileType(const QString& documentId,
const QString& fileTypeName,
const QString& fileExtension,
qint32 appIconIndex = 0,
bool registerForDDE = false,
DdeCommands commands = DDEOpen);
/**
* registers one command for a given file type. It is called for the pre defined DDE command
* types from registerFileType. if more then the normal commands are needed, it can be called
* in addition to registerFileType.
*
* @code
MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) :
DocumentWindow(parent, flags)
{
registerFileType("GiMdi.Document", "MDI Text Editor Document", ".gidoc", 0, true);
registerCommand("fancyCommand", "GiMdi.Document", "-fancy %1", "[fancy(quot;%1quot;)]");
enableShellOpen();
}
@endcode
*/
void registerCommand(const QString& command,
const QString& documentId,
const QString cmdLineArg = QString::null,
const QString ddeCommand = QString::null);
/**
* Call this method to enable the user to open data files associated with your application
* by double-clicking the files in the windows file manager.
*
* Use it together with registerFileType to register the fspezified file types or provida a
* registry file (*.reg) which does this.
*/
void enableShellOpen();
private:
// —— privat helpers ————————————————————————
/**
* implementation of the WM_DDE_INITIATE windows message
*/
bool ddeInitiate(MSG* message, long* result);
/**
* implementation of the WM_DDE_EXECUTE windows message
*/
bool ddeExecute(MSG* message, long* result);
/**
* implementation of the WM_DDE_TERMINATE windows message
*/
bool ddeTerminate(MSG* message, long* result);
/**
* Sets specified value in the registry under HKCU\Software\Classes, which is mapped to HKCR then.
*/
bool SetHkcrUserRegKey(QString key, const QString& value, const QString& valueName = QString::null);
/**
* this method is called to do the DDE command handling. It does argument splitting and then calls
* ddeOpenFile, ddeNewFile, ddePrintFile or executeUnknownDdeCommand.
*/
void executeDdeCommand(const QString& command, const QString& params);
// —— members ——————————————————————————-
bool m_registerForDDE; /< used to identfy, if the dde commands should be written to the registry*/
QString m_appAtomName; /**< the name of the application, without file extension*/
QString m_systemTopicAtomName; /**< the name of the system topic atom, typically "System"*/
ATOM m_appAtom; /**< The windows atom needed for DDE communication*/
ATOM m_systemTopicAtom; /**< The windows system topic atom needed for DDE communication*/
// —— not allowed members ——————————————————————-
};
Q_DECLARE_OPERATORS_FOR_FLAGS(DocumentWindow::DdeCommands)
#endif // WIDGET_H
</code>


=== DocumentWindow.cpp ===
=== DocumentWindow.cpp ===


<code>
    // ————————————————————————————————-
// ————————————————————————————————-
    /**
/**
    * @file
* @file
    * @brief
* @brief
    * @author Gerolf Reinwardt
* @author Gerolf Reinwardt
    * @date 30.01.2011
* @date 30.01.2011
    *
*
    * Copyright © 2011, Gerolf Reinwardt. All rights reserved.
* Copyright © 2011, Gerolf Reinwardt. All rights reserved.
    *
*
    * Simplified BSD License
* Simplified BSD License
    *
*
    * Redistribution and use in source and binary forms, with or without modification, are
* Redistribution and use in source and binary forms, with or without modification, are
    * permitted provided that the following conditions are met:
* permitted provided that the following conditions are met:
    *
*
    * 1. Redistributions of source code must retain the above copyright notice, this list of
* 1. Redistributions of source code must retain the above copyright notice, this list of
    * conditions and the following disclaimer.
* conditions and the following disclaimer.
    *
*
    * 2. Redistributions in binary form must reproduce the above copyright notice, this list
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
    * of conditions and the following disclaimer in the documentation and/or other materials
* of conditions and the following disclaimer in the documentation and/or other materials
    * provided with the distribution.
* provided with the distribution.
    *
*
    * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY EXPRESS OR IMPLIED
* THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY EXPRESS OR IMPLIED
    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
    * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    *
*
    * The views and conclusions contained in the software and documentation are those of the
* The views and conclusions contained in the software and documentation are those of the
    * authors and should not be interpreted as representing official policies, either expressed
* authors and should not be interpreted as representing official policies, either expressed
    * or implied, of Gerolf Reinwardt.
* or implied, of Gerolf Reinwardt.
    */
*/
    // ————————————————————————————————-
// ————————————————————————————————-
   
 
    // —— general includes —————————————————————————
// —— general includes —————————————————————————
    #include <windows.h>
#include <windows.h>
    #include <QMessageBox>
#include <QMessageBox>
    #include <QApplication>
#include <QApplication>
    #include <QDir>
#include <QDir>
    #include <QFileInfo>
#include <QFileInfo>
    #include <QRegExp>
#include <QRegExp>
   
 
    // —— local includes —————————————————————————-
// —— local includes —————————————————————————-
    #include "documentwindow.h"
#include "documentwindow.h"
   
 
   
 
    // —— construction ——————————————————————————
// —— construction ——————————————————————————
    DocumentWindow::DocumentWindow(QWidget* parent, Qt::WindowFlags flags) :
DocumentWindow::DocumentWindow(QWidget''' parent, Qt::WindowFlags flags) :
        QMainWindow(parent, flags),
QMainWindow(parent, flags),
        m_registerForDDE(false),
m_registerForDDE(false),
        m_appAtomName(),
m_appAtomName(),
        m_systemTopicAtomName("system"),
m_systemTopicAtomName("system"),
        m_appAtom(0),
m_appAtom(0),
        m_systemTopicAtom(0)
m_systemTopicAtom(0)
    {
{
        QFileInfo fi(qApp->applicationFilePath());
QFileInfo fi(qApp->applicationFilePath());
        m_appAtomName = fi.baseName();
m_appAtomName = fi.baseName();
    }
}
   
 
    DocumentWindow::~DocumentWindow()
DocumentWindow::~DocumentWindow()
    {
{
        if (0 != m_appAtom) {
if(0 != m_appAtom)
            ::GlobalDeleteAtom(m_appAtom);
{
            m_appAtom = 0;
::GlobalDeleteAtom(m_appAtom);
        }
m_appAtom = 0;
        if (0 != m_systemTopicAtom) {
}
            ::GlobalDeleteAtom(m_systemTopicAtom);
if(0 != m_systemTopicAtom)
            m_systemTopicAtom = 0;
{
        }
::GlobalDeleteAtom(m_systemTopicAtom);
    }
m_systemTopicAtom = 0;
   
}
    // —— operators ———————————————————————————
}
    // —— methods ————————————————————————————
 
    // —— accessors ———————————————————————————
// —— operators ———————————————————————————
    // —— public slots ——————————————————————————
// —— methods ————————————————————————————
    // —— protected slots —————————————————————————
// —— accessors ———————————————————————————
    // —— events ————————————————————————————
// —— public slots ——————————————————————————
    bool DocumentWindow::winEvent(MSG *message, long *result)
// —— protected slots —————————————————————————
    {
// —— events ————————————————————————————
        switch(message->message)
bool DocumentWindow::winEvent(MSG *message, long '''result)
        {
{
            case WM_DDE_INITIATE:
switch(message->message)
                return ddeInitiate(message, result);
{
                break;
case WM_DDE_INITIATE:
            case WM_DDE_EXECUTE:
return ddeInitiate(message, result);
                return ddeExecute(message, result);
break;
                break;
case WM_DDE_EXECUTE:
            case WM_DDE_TERMINATE:
return ddeExecute(message, result);
                return ddeTerminate(message, result);
break;
                break;
case WM_DDE_TERMINATE:
        }
return ddeTerminate(message, result);
   
break;
        return QMainWindow::winEvent(message, result);
}
    }
 
   
return QMainWindow::winEvent(message, result);
    void DocumentWindow::ddeOpenFile(const QString&)
}
    {
 
        // this method will be overwritten, if the application uses the dde open command
void DocumentWindow::ddeOpenFile(const QString&)
    }
{
   
// this method will be overwritten, if the application uses the dde open command
    void DocumentWindow::ddeNewFile(const QString&)
}
    {
 
        // this method will be overwritten, if the application uses the dde new command
void DocumentWindow::ddeNewFile(const QString&)
    }
{
   
// this method will be overwritten, if the application uses the dde new command
    void DocumentWindow::ddePrintFile(const QString&)
}
    {
 
        // this method will be overwritten, if the application uses the dde print command
void DocumentWindow::ddePrintFile(const QString&)
    }
{
   
// this method will be overwritten, if the application uses the dde print command
    void DocumentWindow::executeUnknownDdeCommand(const QString&, const QString&)
}
    {
 
        // this method will be overwritten, if the application uses other commands,
void DocumentWindow::executeUnknownDdeCommand(const QString&, const QString&)
        // then open, new and print via DDE and Explorer
{
    }
// this method will be overwritten, if the application uses other commands,
   
// then open, new and print via DDE and Explorer
    void DocumentWindow::registerFileType(const QString& documentId,
}
            const QString& fileTypeName,
 
            const QString& fileExtension,
void DocumentWindow::registerFileType(const QString& documentId,
            qint32 appIconIndex,
const QString& fileTypeName,
            bool registerForDDE,
const QString& fileExtension,
            DdeCommands commands)
qint32 appIconIndex,
    {
bool registerForDDE,
        // first register the type ID of our server
DdeCommands commands)
        if (!SetHkcrUserRegKey(documentId, fileTypeName)) {
{
            return;
// first register the type ID of our server
        }
if (!SetHkcrUserRegKey(documentId, fileTypeName))
   
return;
        if (!SetHkcrUserRegKey(QString("%1DefaultIcon").arg(documentId),
 
                    QString("\"%1\",%2").arg(QDir::toNativeSeparators(qApp->applicationFilePath())).arg(appIconIndex))) {
if (!SetHkcrUserRegKey(QString("%1DefaultIcon").arg(documentId),
            return;
QString("quot;%1quot;,%2").arg(QDir::toNativeSeparators(qApp->applicationFilePath())).arg(appIconIndex)))
        }
return;
   
 
        m_registerForDDE = registerForDDE;
m_registerForDDE = registerForDDE;
   
 
        if (commands & DDEOpen) {
if(commands & DDEOpen)
            registerCommand("Open", documentId, "\"%1\"", "[open(\"%1\")]");
registerCommand("Open", documentId, "quot;%1quot;", "[open(quot;%1quot;)]");
        }
if(commands & DDENew)
        if (commands & DDENew) {
registerCommand("New", documentId, "-new quot;%1quot;", "[new(quot;%1quot;)]");
            registerCommand("New", documentId, "-new \"%1\"", "[new(\"%1\")]");
if(commands & DDEPrint)
        }
registerCommand("Print", documentId, "-print quot;%1quot;", "[print(quot;%1quot;)]");
        if (commands & DDEPrint) {
 
            registerCommand("Print", documentId, "-print \"%1\"", "[print(\"%1\")]");
LONG lSize = _MAX_PATH * 2;
        }
wchar_t szTempBuffer[_MAX_PATH * 2];
   
LONG lResult = ::RegQueryValue(HKEY_CLASSES_ROOT,
        LONG lSize = _MAX_PATH * 2;
(const wchar_t*)fileExtension.utf16(),
        wchar_t szTempBuffer[_MAX_PATH * 2];
szTempBuffer,
        LONG lResult = ::RegQueryValue(HKEY_CLASSES_ROOT,
&lSize);
                (const wchar_t*)fileExtension.utf16(),
 
                szTempBuffer,
QString temp = QString::fromUtf16((unsigned short*)szTempBuffer);
                &lSize);
 
   
if (lResult != ERROR_SUCCESS || temp.isEmpty() || temp == documentId)
        QString temp = QString::fromUtf16((unsigned short*)szTempBuffer);
{
   
// no association for that suffix
        if (lResult != ERROR_SUCCESS || temp.isEmpty() || temp == documentId) {
if (!SetHkcrUserRegKey(fileExtension, documentId))
            // no association for that suffix
return;
            if (!SetHkcrUserRegKey(fileExtension, documentId)) {
 
                return;
SetHkcrUserRegKey(QString("%1ShellNew").arg(fileExtension), QLatin1String(""), QLatin1String("NullFile"));
            }
}
   
}
            SetHkcrUserRegKey(QString("%1ShellNew").arg(fileExtension), QLatin1String(""), QLatin1String("NullFile"));
 
        }
void DocumentWindow::registerCommand(const QString& command,
    }
const QString& documentId,
   
const QString cmdLineArg,
    void DocumentWindow::registerCommand(const QString& command,
const QString ddeCommand)
            const QString& documentId,
{
            const QString cmdLineArg,
QString commandLine = QDir::toNativeSeparators(qApp->applicationFilePath());
            const QString ddeCommand)
commandLine.prepend(QLatin1String("quot;"));
    {
commandLine.append(QLatin1String("quot;"));
        QString commandLine = QDir::toNativeSeparators(qApp->applicationFilePath());
 
        commandLine.prepend(QLatin1String("\""));
if(!cmdLineArg.isEmpty())
        commandLine.append(QLatin1String("\""));
{
   
commandLine.append(QChar(' '));
        if (!cmdLineArg.isEmpty()) {
commandLine.append(cmdLineArg);
            commandLine.append(QChar(' '));
}
            commandLine.append(cmdLineArg);
 
        }
if (!SetHkcrUserRegKey(QString("%1shell%2command").arg(documentId).arg(command), commandLine))
   
return; // just skip it
        if (!SetHkcrUserRegKey(QString("%1shell%2command").arg(documentId).arg(command), commandLine)) {
 
            return; // just skip it
if(m_registerForDDE)
        }
{
   
if (!SetHkcrUserRegKey(QString("%1shell%2ddeexec").arg(documentId).arg(command), ddeCommand))
        if (m_registerForDDE) {
return;
            if (!SetHkcrUserRegKey(QString("%1shell%2ddeexec").arg(documentId).arg(command), ddeCommand)) {
 
                return;
if (!SetHkcrUserRegKey(QString("%1shell%2ddeexecapplication").arg(documentId).arg(command), m_appAtomName))
            }
return;
   
 
            if (!SetHkcrUserRegKey(QString("%1shell%2ddeexecapplication").arg(documentId).arg(command), m_appAtomName)) {
if (!SetHkcrUserRegKey(QString("%1shell%2ddeexectopic").arg(documentId).arg(command), m_systemTopicAtomName))
                return;
return;
            }
}
   
}
            if (!SetHkcrUserRegKey(QString("%1shell%2ddeexectopic").arg(documentId).arg(command), m_systemTopicAtomName)) {
 
                return;
void DocumentWindow::enableShellOpen()
            }
{
        }
if((0 != m_appAtom) || (0 != m_systemTopicAtom))
    }
return;
   
 
    void DocumentWindow::enableShellOpen()
m_appAtom = ::GlobalAddAtomW((const wchar_t''')m_appAtomName.utf16());
    {
m_systemTopicAtom = ::GlobalAddAtomW((const wchar_t*)m_systemTopicAtomName.utf16());
        if ((0 != m_appAtom) || (0 != m_systemTopicAtom)) {
}
            return;
 
        }
// —— private slots ——————————————————————————
   
// —— private helpers —————————————————————————
        m_appAtom = ::GlobalAddAtomW((const wchar_t*)m_appAtomName.utf16());
bool DocumentWindow::ddeInitiate(MSG* message, long* result)
        m_systemTopicAtom = ::GlobalAddAtomW((const wchar_t*)m_systemTopicAtomName.utf16());
{
    }
if( (0 != LOWORD (message->lParam)) &&
   
(0 != HIWORD (message->lParam)) &&
    // —— private slots ——————————————————————————
(LOWORD (message->lParam)  m_appAtom) &&
    // —— private helpers —————————————————————————
        (HIWORD(message->lParam)  m_systemTopicAtom))
    bool DocumentWindow::ddeInitiate(MSG* message, long* result)
{
    {
// make duplicates of the incoming atoms (really adding a reference)
        if ( (0 != LOWORD (message->lParam)) &&
wchar_t atomName[_MAX_PATH];
                (0 != HIWORD (message->lParam)) &&
Q_ASSERT(::GlobalGetAtomNameW(m_appAtom, atomName, _MAX_PATH - 1) != 0);
                (LOWORD (message->lParam)  m_appAtom) &&
Q_ASSERT(::GlobalAddAtomW(atomName)  m_appAtom);
                (HIWORD(message->lParam)  m_systemTopicAtom)) {
        Q_ASSERT(::GlobalGetAtomNameW(m_systemTopicAtom, atomName, _MAX_PATH - 1) != 0);
            // make duplicates of the incoming atoms (really adding a reference)
        Q_ASSERT(::GlobalAddAtomW(atomName)  m_systemTopicAtom);
            wchar_t atomName[_MAX_PATH];
 
            Q_ASSERT(::GlobalGetAtomNameW(m_appAtom, atomName, _MAX_PATH - 1) != 0);
// send the WM_DDE_ACK (caller will delete duplicate atoms)
            Q_ASSERT(::GlobalAddAtomW(atomName)  m_appAtom);
::SendMessage((HWND)message->wParam, WM_DDE_ACK, (WPARAM)winId(), MAKELPARAM (m_appAtom, m_systemTopicAtom));
            Q_ASSERT(::GlobalGetAtomNameW(m_systemTopicAtom, atomName, _MAX_PATH - 1) != 0);
}
            Q_ASSERT(::GlobalAddAtomW(atomName)  m_systemTopicAtom);
'''result = 0;
   
return true;
            // send the WM_DDE_ACK (caller will delete duplicate atoms)
}
            ::SendMessage((HWND)message->wParam, WM_DDE_ACK, (WPARAM)winId(), MAKELPARAM (m_appAtom, m_systemTopicAtom));
 
        }
bool DocumentWindow::ddeExecute(MSG''' message, long* result)
        *result = 0;
{
        return true;
// unpack the DDE message
    }
UINT_PTR unused;
   
HGLOBAL hData;
    bool DocumentWindow::ddeExecute(MSG* message, long* result)
//IA64: Assume DDE LPARAMs are still 32-bit
    {
Q_ASSERT(::UnpackDDElParam(WM_DDE_EXECUTE, message->lParam, &unused, (UINT_PTR*)&hData));
        // unpack the DDE message
 
        UINT_PTR unused;
QString command = QString::fromWCharArray((LPCWSTR)::GlobalLock(hData));
        HGLOBAL hData;
::GlobalUnlock(hData);
        //IA64: Assume DDE LPARAMs are still 32-bit
 
        Q_ASSERT(::UnpackDDElParam(WM_DDE_EXECUTE, message->lParam, &unused, (UINT_PTR*)&hData));
// acknowledge now - before attempting to execute
   
::PostMessage((HWND)message->wParam, WM_DDE_ACK, (WPARAM)winId(),
        QString command = QString::fromWCharArray((LPCWSTR)::GlobalLock(hData));
//IA64: Assume DDE LPARAMs are still 32-bit
        ::GlobalUnlock(hData);
ReuseDDElParam(message->lParam, WM_DDE_EXECUTE, WM_DDE_ACK, (UINT)0x8000, (UINT_PTR)hData));
   
 
        // acknowledge now - before attempting to execute
// don't execute the command when the window is disabled
        ::PostMessage((HWND)message->wParam, WM_DDE_ACK, (WPARAM)winId(),
if (!isEnabled())
                //IA64: Assume DDE LPARAMs are still 32-bit
{
                ReuseDDElParam(message->lParam, WM_DDE_EXECUTE, WM_DDE_ACK, (UINT)0x8000, (UINT_PTR)hData));
'''result = 0;
   
return true;
        // don't execute the command when the window is disabled
}
        if (!isEnabled()) {
 
            *result = 0;
QRegExp regCommand("<sup>$");
            return true;
if(regCommand.exactMatch(command))
        }
{
   
executeDdeCommand(regCommand.cap(1), regCommand.cap(2));
        QRegExp regCommand("$");
}
        if (regCommand.exactMatch(command)) {
 
            executeDdeCommand(regCommand.cap(1), regCommand.cap(2));
'''result = 0;
        }
return true;
   
}
        *result = 0;
 
        return true;
bool DocumentWindow::ddeTerminate(MSG''' message, long* result)
    }
{
   
// The client or server application should respond by posting a WM_DDE_TERMINATE message.
    bool DocumentWindow::ddeTerminate(MSG* message, long* result)
::PostMessageW((HWND)message->wParam, WM_DDE_TERMINATE, (WPARAM)winId(), message->lParam);
    {
return true;
        // The client or server application should respond by posting a WM_DDE_TERMINATE message.
}
        ::PostMessageW((HWND)message->wParam, WM_DDE_TERMINATE, (WPARAM)winId(), message->lParam);
 
        return true;
bool DocumentWindow::SetHkcrUserRegKey(QString key, const QString& value, const QString& valueName)
    }
{
   
HKEY hKey;
    bool DocumentWindow::SetHkcrUserRegKey(QString key, const QString& value, const QString& valueName)
 
    {
key.prepend("SoftwareClasses");
        HKEY hKey;
 
   
LONG lRetVal = RegCreateKey(HKEY_CURRENT_USER,
        key.prepend("SoftwareClasses");
(const wchar_t*)key.utf16(),
   
&hKey);
        LONG lRetVal = RegCreateKey(HKEY_CURRENT_USER,
 
                (const wchar_t*)key.utf16(),
if(ERROR_SUCCESS == lRetVal)
                &hKey);
{
   
LONG lResult = ::RegSetValueExW(hKey,
        if (ERROR_SUCCESS == lRetVal) {
valueName.isEmpty() ? 0 : (const wchar_t*)valueName.utf16(),
            LONG lResult = ::RegSetValueExW(hKey,
0,
                    valueName.isEmpty() ? 0 : (const wchar_t*)valueName.utf16(),
REG_SZ,
                    0,
(CONST BYTE*)value.utf16(),
                    REG_SZ,
(value.length() + 1) * sizeof(wchar_t));
                    (CONST BYTE*)value.utf16(),
 
                    (value.length() + 1) * sizeof(wchar_t));
if(::RegCloseKey(hKey)  ERROR_SUCCESS && lResult  ERROR_SUCCESS)
   
return true;
            if (::RegCloseKey(hKey)  ERROR_SUCCESS && lResult  ERROR_SUCCESS) {
 
                return true;
QMessageBox::warning(0, QString("Error in setting Registry values"),
            }
QString("registration database update failed for key '%s'.").arg(key));
   
}
            QMessageBox::warning(0, QString("Error in setting Registry values"),
else
                    QString("registration database update failed for key '%s'.").arg(key));
{
        }
wchar_t buffer[4096];
        else {
::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, 0, lRetVal, 0, buffer, 4096, 0);
            wchar_t buffer[4096];
QString szText = QString::fromUtf16((const ushort*)buffer);
            ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, 0, lRetVal, 0, buffer, 4096, 0);
QMessageBox::warning(this, QString("Error in setting Registry values"), szText);
            QString szText = QString::fromUtf16((const ushort*)buffer);
}
            QMessageBox::warning(this, QString("Error in setting Registry values"), szText);
return false;
        }
}
        return false;
 
    }
void DocumentWindow::executeDdeCommand(const QString& command, const QString& params)
   
{
    void DocumentWindow::executeDdeCommand(const QString& command, const QString& params)
QRegExp regCommand("</sup>quot;(.''')quot;$");
bool singleCommand = regCommand.exactMatch(params);
if((0  command.compare("open", Qt::CaseInsensitive)) && singleCommand)
     {
     {
         ddeOpenFile(regCommand.cap(1));
         QRegExp regCommand("</sup>\"(.*)\"$");
        bool singleCommand = regCommand.exactMatch(params);
        if ((0  command.compare("open", Qt::CaseInsensitive)) && singleCommand) {
            ddeOpenFile(regCommand.cap(1));
        }
        else if ((0  command.compare("new", Qt::CaseInsensitive)) && singleCommand) {
            ddeNewFile(regCommand.cap(1));
        }
        else if ((0 == command.compare("print", Qt::CaseInsensitive)) && singleCommand) {
            ddePrintFile(regCommand.cap(1));
        }
        else {
            executeUnknownDdeCommand(command, params);
        }
     }
     }
    else if((0  command.compare("new", Qt::CaseInsensitive)) && singleCommand)
{
ddeNewFile(regCommand.cap(1));
}
else if((0 == command.compare("print", Qt::CaseInsensitive)) && singleCommand)
{
ddePrintFile(regCommand.cap(1));
}
else
{
executeUnknownDdeCommand(command, params);
}
}
</code>


You may use this code without any warranty.
You may use this code without any warranty.

Latest revision as of 16:12, 11 April 2021

En Ar Bg De El Es Fa Fi Fr Hi Hu It Ja Kn Ko Ms Nl Pl Pt Ru Sq Th Tr Uk Zh

Background / technical stuff

File types in Windows are defined in the Windows registry. First, there is the entry of “<extension>” in HKCR which contains the file type name. In HKCR, <file type name> the different actions are defined.

This means, there is a file extension

.gidoc

, whose files are called “MDI Text Editor Document”. These documents should be opened in an application, which is located here:

E:.exe

. The file itself should be a parameter.

Using this pattern, each double click on a file opens a new Process which gets the file as parameter. This is fine for SDI (Single Document Interface) applications.

Example below:

   HKEY_CLASSES_ROOT
    .gidoc
     "Default"="GiMdi.Document"
     "NullFile"=""
   
    .Document
     "Default"="MDI Text Editor Document"
     "Default"="E:.exe,0"
     "Default"="E:.exe %1"

For MDI (Multi Document Interfaces), it should be opened in the existing Process. To achieve this, windows uses the old DDE mechanism. There must be some additional entries to achieve this:

   HKEY_CLASSES_ROOT
    .Document
     "Default"="[open(\"%1\")]"
     "Default"="SimpleCryptIoDevided"
     "Default"="system"

If this is also added, Windows starts the process, which is given in command and then sends a

WM_DDE_INITIATE

broadcast message to windows. The application must react on this, then handle

WM_DDE_EXECUTE

messages and, if requested,

WM_DDE_TERMINATE

message. If the reaction on the DDE messages is not correct, Windows displays some error messages.

How to do it

Normally, your application registers the file types (changes the registry) during startup. If the DDE MDI stuff should be used, it then has to create some ATOMS (

::GlobalAddAtom

). Then it starts up the rest and waits for user input.

If the file type is registered, and no DDE is set, the application is started with the file path as parameter.

If ddeexec keys are set in the registry, the application is started and the the DDE messages are send. While handling these messages,

WM_DDE_ACK

messages are send back to the caller and the messages must be handled. This behavior and the code would always look the same, only the registration keys and the handling of the DDE commands differ from application to application. Therefore, I created a class that does all this and can be used as base class instead of

QMainWindow

:

DocumentWindow

.

Use the
DocumentWindow
class

If you want to create an (MDI) editor application, you can derive your main window class from

DocumentWindow

instead of

QMainWindow

. If you implement an MDI application, you should also re-implement the virtual method

ddeOpenFile

.

The example code is based on the Qt MDI Example.

   class MainWindow : public DocumentWindow
   {
       …
       protected:
           virtual void ddeOpenFile(const QString& filePath);
   }

All other things that need to be done is extending your constructor:

   MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) :
       DocumentWindow(parent, flags)
   {
       …
       registerFileType("GiMdi.Document",           // Document type name
                        "MDI Text Editor Document", // User readable file type name
                        ".gidoc",                   // file extension
                        0,                          // index of the icon to use for the files.
                        true);                      // register for DDE events
       enableShellOpen();
       …
   }

and re-implement

ddeFileOpen

:

   void MainWindow::ddeOpenFile(const QString& filePath)
   {
       MdiChild *child = createMdiChild();
       if (child->loadFile(filePath)) {
           statusBar()->showMessage(tr("File loaded"), 2000);
           child->show();
       }
       else {
           child->close();
       }
   }

That's it. Now it should work to just start the application, and the file type

.gidoc

is registered with your application. If you double click on it in the Windows explorer, it should be opened in the currently running Process or, if no instance of your application is running, start a new one. If your application has a windows icon in the resource (not in the

.qrc

file! a windows resource file

<file>.rc

), this icon should be used for the files.

Use the
DocumentWindow
sources

DocumentWindow.h

   // ————————————————————————————————-
   /**
    * @file
    * @brief
    * @author Gerolf Reinwardt
    * @date 30. march 2011
    *
    * Copyright © 2011, Gerolf Reinwardt. All rights reserved.
    *
    * Simplified BSD License
    *
    * Redistribution and use in source and binary forms, with or without modification, are
    * permitted provided that the following conditions are met:
    *
    * 1. Redistributions of source code must retain the above copyright notice, this list of
    * conditions and the following disclaimer.
    *
    * 2. Redistributions in binary form must reproduce the above copyright notice, this list
    * of conditions and the following disclaimer in the documentation and/or other materials
    * provided with the distribution.
    *
    * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS AND ANY EXPRESS OR IMPLIED
    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
    * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    *
    * The views and conclusions contained in the software and documentation are those of the
    * authors and should not be interpreted as representing official policies, either expressed
    * or implied, of Gerolf Reinwardt.
    */
   // ————————————————————————————————-
   
   #ifndef WIDGET_H
   #define WIDGET_H
   
   // —— general includes —————————————————————————
   #include <windows.h>
   #include <QMainWindow>
   
   // —— local includes —————————————————————————
   // —— predefines ——————————————————————————
   
   // —— class definition —————————————————————————
   /**
    * @short This class implements a main window with the ability to register file types on MS Windows and
    * react on the corresponding DDE events to open / print the files in an Windows MDI typical manner.
    *
    * The usage is fairly easy. Derive your own MainWindow class from DocumentWindow instead of QMainWindow.
    * Inside your constructor, call registerFileType and enableShellOpen. The axample is build on top of the
    * Qt MDI Example (http://doc.qt.nokia.com/4.7/mainwindows-mdi.html)
    *
    * @code
    MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) :
        DocumentWindow(parent, flags)
    {
        …
        registerFileType("GiMdi.Document", "MDI Text Editor Document", ".gidoc", 0, true);
        enableShellOpen();
   
        setWindowTitle(tr("MDI"));
        setUnifiedTitleAndToolBarOnMac(true);
    }
   
    *
    * Aditionally, the actions must be performed by overwriting one or more of:
    * @li ddeOpenFile
    * @li ddeNewFile
    * @li ddePrintFile
    * @li executeUnknownDdeCommand
    *
    *
   
    void MainWindow::ddeOpenFile(const QString& filePath)
    {
        MdiChild *child = createMdiChild();
        if (child->loadFile(filePath)) {
            statusBar()->showMessage(tr("File loaded"), 2000);
            child->show();
        }
        else {
            child->close();
        }
    }
   
    *
    */
   class DocumentWindow : public QMainWindow
   {
       Q_OBJECT
       public:
           // —— enums ———————————————————————————
           /**
            * Known DDE command. These commands are typically used
            */
           enum DdeCommand {
               DDEOpen = 0x0001,  // < open a file via explorer
               DDENew = 0x0002,   // < create a new file via explorer
               DDEPrint = 0x0004, // < print a file via explorer
           };
           Q_DECLARE_FLAGS(DdeCommands, DdeCommand)
   
               // —— construction —————————————————————————
               /**
                * Constructor.
                *
                * Creates a DocumentWindow with a given @arg parent and @arg flags.
                */
               explicit DocumentWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0);
   
           /**
            * Destructor
            */
           ~DocumentWindow();
   
           // —— operators ——————————————————————————
           // —— methods ——————————————————————————
           // —— accessors ——————————————————————————
           // —— members ——————————————————————————
   
       protected:
           // —— events ———————————————————————————
           /**
            * reimpl as DDE events come as windows events and are not translated by Qt.
            */
           virtual bool winEvent(MSG *message, long *result);
   
           // —— helpers for the file registration ——————————————————
           /**
            * virtual function that must be implemented by the derived class to react to the open command
            * from Windows.
            *
            * @param filePath file that was selected in the explorer
            */
           virtual void ddeOpenFile(const QString& filePath);
   
           /**
            * virtual function that must be implemented by the derived class to react to the new command
            * from Windows.
            *
            * @param filePath file that was selected in the explorer
            */
           virtual void ddeNewFile(const QString& filePath);
   
           /**
            * virtual function that must be implemented by the derived class to react to the print command
            * from Windows.
            *
            * @param filePath file that was selected in the explorer
            */
           virtual void ddePrintFile(const QString& filePath);
   
           /**
            * virtual function that must be implemented by get custom DDE commands from the explorer. If, e.g.
            * a print or copy command should be available in explorer and they are registered via registerCommand,
            * this function is called.
            *
            * @param command name of the command
            * @param params parameter string, containing the hole stuff, also " and commas.
            *
            * @note a command like this [compare("%1")] will result in the parameters:
            command = "compare";
            params = "\"<filepath>\"";
            */
           virtual void executeUnknownDdeCommand(const QString& command, const QString& params);
   
           /**
            * Call this method to register the file type in Windows. It creates the default command
            * which means the app is started with the file to open as parameter. If @arg registerForDDE
            * is true, the DDE events are also created so the opening of a file is done in typical MDI
            * behavior (all files open in the same app).
            *
            * @param documentId id of the document, typically <Application>.Document —> see in registry, e.g. "GiMdi.Document"
            * @param fileTypeName free name of the file type, e.g. "MDI Text Editor Document"
            * @param fileExtension File extension, including the dot (e.g. ".gidoc")
            * @param appIconIndex index of the app icon to use for the file in the windows explorer, typically the application icon
            * @param registerForDDE true if DDE should be used (typical for MDI apps), typically false for SDI apps.
            * @param commands a combination of the commands to install.
            *
            * @note If more then the default commands are needed, then subsequent calls of registerCommand are needed.
            *
            * @note DDEOpen leads to the DDE command: [open("%1")]
            DDENew leads to the DDE command: [new("%1")]
            DDEPrint leads to the DDE command: [print("%1")]
            */
           void registerFileType(const QString& documentId,
                   const QString& fileTypeName,
                   const QString& fileExtension,
                   qint32 appIconIndex = 0,
                   bool registerForDDE = false,
                   DdeCommands commands = DDEOpen);
   
           /**
            * registers one command for a given file type. It is called for the predefined DDE command
            * types from registerFileType. if more then the normal commands are needed, it can be called
            * in addition to registerFileType.
            *
            * @code
            MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) :
            DocumentWindow(parent, flags)
            {
                …
                registerFileType("GiMdi.Document", "MDI Text Editor Document", ".gidoc", 0, true);
                registerCommand("fancyCommand", "GiMdi.Document", "-fancy %1", "[fancy(\"%1\")]");
                enableShellOpen();
                …
            }
            @endcode
            */
           void registerCommand(const QString& command,
                   const QString& documentId,
                   const QString cmdLineArg = QString::null,
                   const QString ddeCommand = QString::null);
   
           /**
            * Call this method to enable the user to open data files associated with your application
            * by double-clicking the files in the windows file manager.
            *
            * Use it together with registerFileType to register the fspezified file types or provida a
            * registry file (*.reg) which does this.
            */
           void enableShellOpen();
   
   
       private:
           // —— private helpers ————————————————————————
           /**
            * implementation of the WM_DDE_INITIATE windows message
            */
           bool ddeInitiate(MSG* message, long* result);
   
           /**
            * implementation of the WM_DDE_EXECUTE windows message
            */
           bool ddeExecute(MSG* message, long* result);
   
           /**
            * implementation of the WM_DDE_TERMINATE windows message
            */
           bool ddeTerminate(MSG* message, long* result);
   
           /**
            * Sets specified value in the registry under HKCU\Software\Classes, which is mapped to HKCR then.
            */
           bool SetHkcrUserRegKey(QString key, const QString& value, const QString& valueName = QString::null);
   
           /**
            * this method is called to do the DDE command handling. It does argument splitting and then calls
            * ddeOpenFile, ddeNewFile, ddePrintFile or executeUnknownDdeCommand.
            */
           void executeDdeCommand(const QString& command, const QString& params);
   
           // —— members ——————————————————————————-
           bool m_registerForDDE; /< used to identfy, if the dde commands should be written to the registry*/
               QString m_appAtomName; /**< the name of the application, without file extension*/
           QString m_systemTopicAtomName; /**< the name of the system topic atom, typically "System"*/
           ATOM m_appAtom; /**< The windows atom needed for DDE communication*/
           ATOM m_systemTopicAtom; /**< The windows system topic atom needed for DDE communication*/
           // —— not allowed members ——————————————————————-
   };
   
   Q_DECLARE_OPERATORS_FOR_FLAGS(DocumentWindow::DdeCommands)
   
   #endif // WIDGET_H


DocumentWindow.cpp

   // ————————————————————————————————-
   /**
    * @file
    * @brief
    * @author Gerolf Reinwardt
    * @date 30.01.2011
    *
    * Copyright © 2011, Gerolf Reinwardt. All rights reserved.
    *
    * Simplified BSD License
    *
    * Redistribution and use in source and binary forms, with or without modification, are
    * permitted provided that the following conditions are met:
    *
    * 1. Redistributions of source code must retain the above copyright notice, this list of
    * conditions and the following disclaimer.
    *
    * 2. Redistributions in binary form must reproduce the above copyright notice, this list
    * of conditions and the following disclaimer in the documentation and/or other materials
    * provided with the distribution.
    *
    * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS AND ANY EXPRESS OR IMPLIED
    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
    * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    *
    * The views and conclusions contained in the software and documentation are those of the
    * authors and should not be interpreted as representing official policies, either expressed
    * or implied, of Gerolf Reinwardt.
    */
   // ————————————————————————————————-
   
   // —— general includes —————————————————————————
   #include <windows.h>
   #include <QMessageBox>
   #include <QApplication>
   #include <QDir>
   #include <QFileInfo>
   #include <QRegExp>
   
   // —— local includes —————————————————————————-
   #include "documentwindow.h"
   
   
   // —— construction ——————————————————————————
   DocumentWindow::DocumentWindow(QWidget* parent, Qt::WindowFlags flags) :
       QMainWindow(parent, flags),
       m_registerForDDE(false),
       m_appAtomName(),
       m_systemTopicAtomName("system"),
       m_appAtom(0),
       m_systemTopicAtom(0)
   {
       QFileInfo fi(qApp->applicationFilePath());
       m_appAtomName = fi.baseName();
   }
   
   DocumentWindow::~DocumentWindow()
   {
       if (0 != m_appAtom) {
           ::GlobalDeleteAtom(m_appAtom);
           m_appAtom = 0;
       }
       if (0 != m_systemTopicAtom) {
           ::GlobalDeleteAtom(m_systemTopicAtom);
           m_systemTopicAtom = 0;
       }
   }
   
   // —— operators ———————————————————————————
   // —— methods ————————————————————————————
   // —— accessors ———————————————————————————
   // —— public slots ——————————————————————————
   // —— protected slots —————————————————————————
   // —— events ————————————————————————————
   bool DocumentWindow::winEvent(MSG *message, long *result)
   {
       switch(message->message)
       {
           case WM_DDE_INITIATE:
               return ddeInitiate(message, result);
               break;
           case WM_DDE_EXECUTE:
               return ddeExecute(message, result);
               break;
           case WM_DDE_TERMINATE:
               return ddeTerminate(message, result);
               break;
       }
   
       return QMainWindow::winEvent(message, result);
   }
   
   void DocumentWindow::ddeOpenFile(const QString&)
   {
       // this method will be overwritten, if the application uses the dde open command
   }
   
   void DocumentWindow::ddeNewFile(const QString&)
   {
       // this method will be overwritten, if the application uses the dde new command
   }
   
   void DocumentWindow::ddePrintFile(const QString&)
   {
       // this method will be overwritten, if the application uses the dde print command
   }
   
   void DocumentWindow::executeUnknownDdeCommand(const QString&, const QString&)
   {
       // this method will be overwritten, if the application uses other commands,
       // then open, new and print via DDE and Explorer
   }
   
   void DocumentWindow::registerFileType(const QString& documentId,
           const QString& fileTypeName,
           const QString& fileExtension,
           qint32 appIconIndex,
           bool registerForDDE,
           DdeCommands commands)
   {
       // first register the type ID of our server
       if (!SetHkcrUserRegKey(documentId, fileTypeName)) {
           return;
       }
   
       if (!SetHkcrUserRegKey(QString("%1DefaultIcon").arg(documentId),
                   QString("\"%1\",%2").arg(QDir::toNativeSeparators(qApp->applicationFilePath())).arg(appIconIndex))) {
           return;
       }
   
       m_registerForDDE = registerForDDE;
   
       if (commands & DDEOpen) {
           registerCommand("Open", documentId, "\"%1\"", "[open(\"%1\")]");
       }
       if (commands & DDENew) {
           registerCommand("New", documentId, "-new \"%1\"", "[new(\"%1\")]");
       }
       if (commands & DDEPrint) {
           registerCommand("Print", documentId, "-print \"%1\"", "[print(\"%1\")]");
       }
   
       LONG lSize = _MAX_PATH * 2;
       wchar_t szTempBuffer[_MAX_PATH * 2];
       LONG lResult = ::RegQueryValue(HKEY_CLASSES_ROOT,
               (const wchar_t*)fileExtension.utf16(),
               szTempBuffer,
               &lSize);
   
       QString temp = QString::fromUtf16((unsigned short*)szTempBuffer);
   
       if (lResult != ERROR_SUCCESS || temp.isEmpty() || temp == documentId) {
           // no association for that suffix
           if (!SetHkcrUserRegKey(fileExtension, documentId)) {
               return;
           }
   
           SetHkcrUserRegKey(QString("%1ShellNew").arg(fileExtension), QLatin1String(""), QLatin1String("NullFile"));
       }
   }
   
   void DocumentWindow::registerCommand(const QString& command,
           const QString& documentId,
           const QString cmdLineArg,
           const QString ddeCommand)
   {
       QString commandLine = QDir::toNativeSeparators(qApp->applicationFilePath());
       commandLine.prepend(QLatin1String("\""));
       commandLine.append(QLatin1String("\""));
   
       if (!cmdLineArg.isEmpty()) {
           commandLine.append(QChar(' '));
           commandLine.append(cmdLineArg);
       }
   
       if (!SetHkcrUserRegKey(QString("%1shell%2command").arg(documentId).arg(command), commandLine)) {
           return;  // just skip it
       }
   
       if (m_registerForDDE) {
           if (!SetHkcrUserRegKey(QString("%1shell%2ddeexec").arg(documentId).arg(command), ddeCommand)) {
               return;
           }
   
           if (!SetHkcrUserRegKey(QString("%1shell%2ddeexecapplication").arg(documentId).arg(command), m_appAtomName)) {
               return;
           }
   
           if (!SetHkcrUserRegKey(QString("%1shell%2ddeexectopic").arg(documentId).arg(command), m_systemTopicAtomName)) {
               return;
           }
       }
   }
   
   void DocumentWindow::enableShellOpen()
   {
       if ((0 != m_appAtom) || (0 != m_systemTopicAtom)) {
           return;
       }
   
       m_appAtom = ::GlobalAddAtomW((const wchar_t*)m_appAtomName.utf16());
       m_systemTopicAtom = ::GlobalAddAtomW((const wchar_t*)m_systemTopicAtomName.utf16());
   }
   
   // —— private slots ——————————————————————————
   // —— private helpers —————————————————————————
   bool DocumentWindow::ddeInitiate(MSG* message, long* result)
   {
       if ( (0 != LOWORD (message->lParam)) &&
               (0 != HIWORD (message->lParam)) &&
               (LOWORD (message->lParam)  m_appAtom) &&
               (HIWORD(message->lParam)  m_systemTopicAtom)) {
           // make duplicates of the incoming atoms (really adding a reference)
           wchar_t atomName[_MAX_PATH];
           Q_ASSERT(::GlobalGetAtomNameW(m_appAtom, atomName, _MAX_PATH - 1) != 0);
           Q_ASSERT(::GlobalAddAtomW(atomName)  m_appAtom);
           Q_ASSERT(::GlobalGetAtomNameW(m_systemTopicAtom, atomName, _MAX_PATH - 1) != 0);
           Q_ASSERT(::GlobalAddAtomW(atomName)  m_systemTopicAtom);
   
           // send the WM_DDE_ACK (caller will delete duplicate atoms)
           ::SendMessage((HWND)message->wParam, WM_DDE_ACK, (WPARAM)winId(), MAKELPARAM (m_appAtom, m_systemTopicAtom));
       }
       *result = 0;
       return true;
   }
   
   bool DocumentWindow::ddeExecute(MSG* message, long* result)
   {
       // unpack the DDE message
       UINT_PTR unused;
       HGLOBAL hData;
       //IA64: Assume DDE LPARAMs are still 32-bit
       Q_ASSERT(::UnpackDDElParam(WM_DDE_EXECUTE, message->lParam, &unused, (UINT_PTR*)&hData));
   
       QString command = QString::fromWCharArray((LPCWSTR)::GlobalLock(hData));
       ::GlobalUnlock(hData);
   
       // acknowledge now - before attempting to execute
       ::PostMessage((HWND)message->wParam, WM_DDE_ACK, (WPARAM)winId(),
               //IA64: Assume DDE LPARAMs are still 32-bit
               ReuseDDElParam(message->lParam, WM_DDE_EXECUTE, WM_DDE_ACK, (UINT)0x8000, (UINT_PTR)hData));
   
       // don't execute the command when the window is disabled
       if (!isEnabled()) {
           *result = 0;
           return true;
       }
   
       QRegExp regCommand("$");
       if (regCommand.exactMatch(command)) {
           executeDdeCommand(regCommand.cap(1), regCommand.cap(2));
       }
   
       *result = 0;
       return true;
   }
   
   bool DocumentWindow::ddeTerminate(MSG* message, long* result)
   {
       // The client or server application should respond by posting a WM_DDE_TERMINATE message.
       ::PostMessageW((HWND)message->wParam, WM_DDE_TERMINATE, (WPARAM)winId(), message->lParam);
       return true;
   }
   
   bool DocumentWindow::SetHkcrUserRegKey(QString key, const QString& value, const QString& valueName)
   {
       HKEY hKey;
   
       key.prepend("SoftwareClasses");
   
       LONG lRetVal = RegCreateKey(HKEY_CURRENT_USER,
               (const wchar_t*)key.utf16(),
               &hKey);
   
       if (ERROR_SUCCESS == lRetVal) {
           LONG lResult = ::RegSetValueExW(hKey,
                   valueName.isEmpty() ? 0 : (const wchar_t*)valueName.utf16(),
                   0,
                   REG_SZ,
                   (CONST BYTE*)value.utf16(),
                   (value.length() + 1) * sizeof(wchar_t));
   
           if (::RegCloseKey(hKey)  ERROR_SUCCESS && lResult  ERROR_SUCCESS) {
               return true;
           }
   
           QMessageBox::warning(0, QString("Error in setting Registry values"),
                   QString("registration database update failed for key '%s'.").arg(key));
       }
       else {
           wchar_t buffer[4096];
           ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, 0, lRetVal, 0, buffer, 4096, 0);
           QString szText = QString::fromUtf16((const ushort*)buffer);
           QMessageBox::warning(this, QString("Error in setting Registry values"), szText);
       }
       return false;
   }
   
   void DocumentWindow::executeDdeCommand(const QString& command, const QString& params)
   {
       QRegExp regCommand("\"(.*)\"$");
       bool singleCommand = regCommand.exactMatch(params);
       if ((0  command.compare("open", Qt::CaseInsensitive)) && singleCommand) {
           ddeOpenFile(regCommand.cap(1));
       }
       else if ((0  command.compare("new", Qt::CaseInsensitive)) && singleCommand) {
           ddeNewFile(regCommand.cap(1));
       }
       else if ((0 == command.compare("print", Qt::CaseInsensitive)) && singleCommand) {
           ddePrintFile(regCommand.cap(1));
       }
       else {
           executeUnknownDdeCommand(command, params);
       }
   }

You may use this code without any warranty.

Future extensions

The class might be extended so it also works on Linux or Mac. The code of the class and a demo project are located on gitorious in the following repository: qtdevnet-registereditorfiletype

Versions

The current code is version 1 of the code.

Version 1

This marks the first public release.

Feedback