Highlighted
s_vogel Absent Member.
Absent Member.
4799 views

[TUTORIAL] Context Menu to export EML files

Hello there folks!

I'm pretty new to the topic of C3PO, GW and all the Novell stuff and one of my tasks was to "code an export mechanism for GW8 thats lats us save e-mails to our storage system". Ok, that was a hammer. But wrapping my head around it and starting to error out the things got me pretty far and I guessed it was tutorial material. So here we go:

@Moderators: This is the thread that has everything in it. the other one can be deleted.

This tutorial is intendend for C# only. I don't like VB and I'm too dumb for C++ so if you need it for another dialect you need to work it out your self.

Agenda:

  1. Needed packages
  2. C3PO wizard
  3. Loading to Visual Studio 2010
  4. Needed Imports/References
  5. Simple MessageBoxing
  6. Export Code
  7. Registering and caching the .DLL
  8. Testing (please help me with a better way here)



    1. Needed packages

    • the novell-gwc3po-devel-2012.11.15.zip file (unzip this after downloading)
    • an installed version of Visual Studio 2012 C# (or if you want to work with a different dialect choose another)
    • cmd access to some of the registering tools:
      [INDENT]It may be the best thing to set tose paths up in you env variables. Allthough when running the cmd with administrator privileges you can't use regasm from env variables and need to cd to the directory.

      • RegAsm (regasm.exe): C:\Windows\Microsoft.NET\Framework\v4.0.30319 (the version depends on the target)
      • GACUtil (gacutil.exe): C:\Program Files(x86)\Micrsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\ (this path is also dependent on your target framework version, I chose .NET4)
      • StrongName (sn.exe): C:\Program Files(x86)\Micrsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\ (this path is also dependent on your target framework version, I chose .NET4)

      [/INDENT]
    • a good beverage 😄 (you should obtain multiple of these :D)



    2. The C3PO wizard
    In my case I wanted to add the functionality via the context menu. So the code executes when right-clicking on one or multiple messages displays another menu item and is clickable.

    This is pretty easy to realize via the C3PO wizard. You'll find it in the downloaded and extracted novell-gwc3po-devel-2012.11.15.zip from above. Start it (it is located in extracted-zip-folder/gwc3po-FILES/C3POWizard/C3POWizard.exe) and setup your project:
    Setup the project in the wizard step 1
    I usually setup the Wizard inside my Visual Studio 2010 projects folder, create a new folder there with the name of the project and check the options i want to have.

    In the next step I chose which type of View should display my custom context menu. Since I was only interested in exporting and working with e-mails I chose "GW.MESSAGE.MAIL" and added it to the bottom list via, you guessed it, "Add".
    Setup theView that invokes the new context menu item

    In the next step you I had to setup a new entry for the context menu. You could make side-droppable menus here etc. But for me a simple "Add Menu" was enough. Give it a name of your choice (beware: I'm yet to find out where to change this setting in the source files).
    Creating a Menu Item in step 3

    Click through next and the wizard will sum up you choices. In the next dialog window you will be prompted to specify the language you want the code to be generated. I chose .NET C#.
    In the prompt after that you will be asked if the wizard should create a .DLL-project. You click yes.
    Quit the wizard with the "Done" button.

    3. Loading to Visual Studio 2010
    Open up your Visual Studio and go to File -> Open Project. Navigate to the folder where you just created the files with the C3PO-Wizard. and open up the .csproj file.

    All the files get loaded and it seems quite well. but now it's time for some other stuff: Signing, or better, providing a key for signing.

    Allthough the README.txt (also in your project folder) states this is not neccessarily needed I did not get it to work without a key file.
    Open up a terminal and tpye in sn /? to see if the environment variables work. If not you can yuse the abolute path to sn (see: 1: Needed packages). If everything works as expected you can generate your keyfile with sn -k <PathToYourProject>\Archive.snk.

    In Visual Studio, go to Project -> <ProjectName>-Properties -> Signing -> Sign assembly -> Search and pick the .snk-file you just created.

    Good. A first compilation of the project with F6 should rumble through without problems. Go to <ProjectFolder>\bin\Release and copy the .dll files to <GroupWiseInstallPath>.

    After that you need to open a cmd windows as administrator and cd to the RegAsm.exe directory and execute the following: regasm "<GroupWiseInstallPath>\<TheDllName>.dll". Then execute gacutil -i "<GroupWiseInstallPath>\<TheDllName>.dll".

    RegAsm will register the extension to the Windows registry and GACUtil will cache the .dll content to make it available to GroupWise.

    You need to re-cache the .dll everytime you compile in VS. So basically the workflow is Compile -> Copy dll to GroupWise directory -> re-cache with gacutil -i -> Start Groupwise

    I have not found a method to post-build execute a script that does that. Problem is the copying and the gacutil caching (both must be done as administrator).

    IIf everything worked you see a new entry in the context menu when right-clicking a mail in Groupwise. When you click it, there will appear a message box.

    The MessageBox is defined in GWCommand.cs L. ~125

    4. Needed Imports/References
    Since we got the skeleton to compile and function properly, it's time to get our own code in there. FOr rapid prototyping I do all the stuff in GWCommand.cs.

    Go to Project -> add Reference -> COM and select "C3POTypeLibrary", "GroupWareTypeLibrary, "GroupWiseCommander", "GroupWiseConnectorLibrary" and click OK. The selected entries now appear in the project explorer.

    5. Simple MessageBoxing
    A thing I like to do (because I'm not a very good programmer) is to get all sorts of infos to get displayed with
    MessageBox.Show();
    .

    Just fling it in the code and see what get's where etc. An important thing is allready in the comments of the file.
    It is this line:
     C3POTypeLibrary.IGWClientState6 myCL = (C3POTypeLibrary.IGWClientState6)WIASSArchivButton.g_C3POManager.ClientState;
    . Uncomment it and play around with the myCL-object in your code.

    The myCL has some properties we will use later on such as myCL.SelectedMessages which is exactly what we need for our archive functionality.

    6. Export Code
    Now we get to the code:

    With the
    ClientState
    dug up in the code we can pass the
    SelectedMessages
    into a
    MessageList
    . Over this MessageList we will iterate and save each
    Message
    with the so called
    GroupWiseCommander
    to our disk. well that sounds simple. And, well after digging through a lot of threads here on the forum and the documentation, it is.

    Here is the Execute() method from GWCommand.cs:
    It has comments that should serve as a documentation.

    public void Execute()
    {
    try
    {
    switch (m_PersistentID)
    {
    case WIASSArchivButton.vWIASS:
    //C3PO WIZARD Put execute command code here for WIASS Context menu.


    /* this was in the comments and is essential!
    * the myCL object provides us everything we need to interact with the messages */
    C3POTypeLibrary.IGWClientState6 myCL = (C3POTypeLibrary.IGWClientState6)WIASSArchivButton.g_C3POManager.ClientState;

    // get the selected messages
    object o = myCL.SelectedMessages;
    // and convert the SelectedMessages to a MessagesList
    MessageList ml = (MessageList)o;

    // iterate over all the selected Messages
    // this was tricky: the index of the MessageList starts by 1 and not at 0
    for (int i = 1; i <= ml.Count; i++)
    {
    // the .Item() method expects either a string or a long
    // see http://www.novell.com/documentation/developer/groupwise_sdk/gwsdk_gwobjapi/data/h20s5bdo.html
    long index = (long)i;

    // instantiate a Message object to get access to the different properties like subject, sender etc
    GroupwareTypeLibrary.Message oMessage = (GroupwareTypeLibrary.Message)ml.Item(index);

    // instantiate a GroupWiseCommander
    // this is the interface to the TOKEN API
    // TOKENS: https://www.novell.com/developer/documentation/gwtoken/index.html
    GroupWiseCommander.GWCommander cmdr = new GroupWiseCommander.GWCommander();

    // the GWCommander has an Execute() method that is able to take certain tokens kind of like SQL
    // lets build the token (the complete list is huge and awesome) to save our Messages
    // ItemSaveMessage(): https://www.novell.com/developer/documentation/gwtoken/gwtokens/data/hbt0bd7x.html
    string tokenCommand = "ItemSaveMessage(\"" + oMessage.MessageID + "\"; \"C:\\archiv\\" + oMessage.MessageID + ".eml\"; 900)";

    /* what happens here ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ is that we build us a TOKEN command that the
    * GWCommander is able to execute.
    * the actual command is ItemSaveMassge()
    * everything between the semi-colons are the parameters:
    * \"" + oMessage.MessageID + "\" : builds an ANSISTRING of the MessageID which we get from the oMessage onject
    * \"C:\\archiv\\" + oMessage.MessageID + ".eml\" : build an ANSISTRING of the output filename
    * 900 is the type we want to export. 900 stands for Mime
    *
    * CAUTION:In this example I use C:\archive\ as the destination folder. It must exist and be writable to the program
    */


    // now that we have setup our command we can get it executed by the commander
    // the result is sort of a callback variable
    string result ="";
    cmdr.Execute(tokenCommand, out result);

    /* here can the error handling be done with the result string
    }

    break;

    default:
    MessageBox.Show("Unsupported Case", "Error", MessageBoxButtons.OK);
    break;
    }

    //A way to get the GroupWise client state with newest interface
    //C3POTypeLibrary.IGWClientState6 myCL = (C3POTypeLibrary.IGWClientState6)WIASSArchivButton.g_C3POManager.ClientState;

    //uncomment the code below to unblock the base command
    //IGWCommand baseCmd = (IGWCommand)WIASSArchivButton.g_C3POManager.CreateGWCommand(m_objBaseCmd);
    //baseCmd.Execute();
    }
    catch (Exception e)
    {
    MessageBox.Show("Error Executing GWCommand: " + m_PersistentID.ToString() + " Error: " + e.Message);
    }

    return;
    }


    7. Registering and caching the .DLL
    After that you need to open a cmd windows as administrator and cd to the RegAsm.exe directory and execute the following: regasm "<GroupWiseInstallPath>\<TheDllName>.dll". Then execute gacutil -i "<GroupWiseInstallPath>\<TheDllName>.dll".

    RegAsm will register the extension to the Windows registry and GACUtil will cache the .dll content to make it available to GroupWise.

    You need to re-cache the .dll everytime you compile in VS. So basically the workflow is Compile -> Copy dll to GroupWise directory -> re-cache with gacutil -i -> Start Groupwise

    8. Testing (please help me with a better way here)
    Is there a good way to hook every thing up together to jsut stay in VS , compile, files get copied, registered, cached and GW starts?

    Thanks for reading!

    I wrote this up to have a documentation for myself and others. please let em know if you need help or anything is missing or not clear. It's certainly not a total noob guide and I expect a bit of knowledge to be honest.

    Regards
    Sebastian
Labels (1)
0 Likes
7 Replies
s_vogel Absent Member.
Absent Member.

Re: [TUTORIAL] Context Menu to export EML files

And here is a bugfix for all those that get the problem that the ContextMenuItem gets put multiple times when multiple messages are selected:

Surround the Add() method with an if-clause checking the length. Can be found here

I rebuild my app so that i get a MenuItem in the ContextMenu that represents our company and added a sub menu item for archivating files. So be careful with copy & paste
0 Likes
s_vogel Absent Member.
Absent Member.

Re: [TUTORIAL] Context Menu to export EML files

Forgot something for our beginners:
after you added the references like in step 4 you need to make the namespaces available to the code. Therefore add this in your GWCommand.cs add the top:

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using C3POTypeLibrary;
using GroupwareTypeLibrary;
using GroupWiseCommander;
0 Likes
Username951 Absent Member.
Absent Member.

Re: [TUTORIAL] Context Menu to export EML files

Hi s_vogel,

thanks for your great tutorial!

I followed it step-by-step but it doesnt work for me.
(Win7, 64bit, VS 2013, Groupwise 2012 which has already a "Archive" context menu so I used: "GWSaveToDatabase")

The wizard created a compileable project within .NET Framework 2.0
So I used nearly your pathes to "sn", "regasm and gacutil - and not the newest

Some remarks

1.) Why sn? The project can be build right after the wizard created it. No need to sign it for this purpose!?
Or: Must the "*.snk" file aslo be copied to the GroupWise installation path ?

2.) Why adding "GroupWiseCommander" and "GroupWiseConnectorLibrary"?
(the "using GroupWiseCommander;" can be removed)

3.) "case WIASSArchivButton.vWIASS:"
"WIASSArchivButton" throws an error
The name 'WIASSArchivButton' does not exist in the current context.
Seems you use finally a button and not a context menu entry, or ?

PLEASE: Zip your whole project and add it to this thread.

Thx in advance!
0 Likes
s_vogel Absent Member.
Absent Member.

Re: [TUTORIAL] Context Menu to export EML files

Hey there Username951!

to first answer your questions:
1.) The sn is required on my machine to be able to cache the .dll's with gac
2.) Yes those can be removed actually
3.) I will upload a zip with the complete project

0 Likes
s_vogel Absent Member.
Absent Member.

Re: [TUTORIAL] Context Menu to export EML files

What I realized today after tinkering with the problem: You need to gacutil -i all the dlls in the output directory. So for this particular case you need to Build -> gacutil -i GWSaveToDB.dll -> gacutil -i Interop.C3POTypeLibrary.dll -> gacutil -i GroupwareTypeLibrary.dll -> gacutil -i Interop.GrouWiseConnector.dll

This all was tested today on Windows 7 x64 SP1, GroupWise 12.0.3.HP1 (4th December build), VisualStudio 2013 Community Edition.
0 Likes
Username951 Absent Member.
Absent Member.

Re: [TUTORIAL] Context Menu to export EML files

Hi s_vogel,

first: thxs for your fast answer!

I figured it out in the meanwhile!
Now its working well for me.
But to say it clear:
YOU saved me a lot of time due the fact that your tutorial put me on rails!

But I would like to share my find-outs as follows - hoping that its helpful for someone.

Please note that my goal was to add a context menu "Save to database".

Multiple email selection should be possible, but only those emails that are fitting some requirements should be stored finally in database.
One requirement is for example that a keyword like "ISSUE" appears in the email subject
(followed by a ":", a "space" and some characters that can be converted to an integer value),
multiple, leading "Fwd: " and/or "Re: " should be handled well,
subject should be handled case-in-sensitive.


So here are my find outs, remarks, etc.:

1.) Visual Studio should be started under admin. rights.
Then you can write a post-build event (batch) that copies, "regasm"s and "gacutil"s everything.
As said this works fine for me.
But note that unfortunately the paths to "regasm" and "gacutil" changed
(compared to the time where you wrote your tutorial).

2.) The "Novell C3PO" wizard was downloaded and worked out as described in our tutorial.
One important step was to use "GW.MESSAGE.MAIL" and not "...BROWSER..." or something else.

The wizard created finally the basic C# (.NET framework 2.0) project.
This project was loaded in Visual Studio 2013, automatically converted to "newest version"
and finally was a ".sln" made.

There is NO need to change the solution to a newer .NET framework.

At that point contained the solution/project already the "C3POTypeLibrary" reference,
but it was necessary to add also these references:
"GroupwareTypeLibrary"
and
"GroupWiseCommander"
-> Note that the ".dll" has the name "GroupWise Client Library"!
and
"oracle.dataaccess"
-> Note that the "Copy Local" property must be set to "true"!
(This property will be reset to "false" after a successful (re)build.
So check this and change it to "true" for the first build!
This must be made only once because after a successful build is this .dll known;
keywords: GAC -> cached
But note that "successful" means also that the post-build event ran flawless!)

2.) regasm.exe needs strong names.
So a "cmd" with admin. right was opened,
a
"C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\sn.exe" -k "C:\Users\<username>\Documents\Visual Studio 2013\Projects\GWSaveToDatabase\GWSaveToDatabase.snk"
fired
and the created "GWSaveToDatabase.snk" file added to the solution.




(Development) Remarks

1.) While I used the "C3PO" wizard first time I used "Add Menu" item - as you said in your tutorial! 😞
And that is definitely wrong!
See:
The result was a C# project that does not show any new context menu entry.
So I tried at the next wizard run "Add Menu Item".
The wizard created again a C# project but still no new context menu entry in the GroupWise client.
(And that after all needed steps
like
copy to GroupWise installation path,
regasm and gacutil over all .dlls
etc.
were successful be made).

It took a complete day to get the idea to "merge" the two wizard created projects!
Why merging?
Because the second project contained a "const" which were used in the switch statement of the "Execute()" method
(with the same meaning like your "WIASSArchivButton.vWIASS" - see your code snippet above!)
and the "CustomizeContextMenu(...)" method in "CommandFactory.cs" looked also different
while the first project does not contained something similar.
( For example:
The "CustomizeContextMenu(...)" method had more statements.
And that made more sense to me compared to the first wizeard created C# project.
)

So I ASSUME that the second project would work but it does not because of regasm / gacutil behaviour.
Means I believe it would work when all
with regasm registered
and
with gacutil to the cache added "things"
would be "un-registerd" and "un-cached".

So, finally I took the second, wizard created C# project and copied the "const", adjusted the "Execute()"
and "CustomizeContextMenu(...)" methods, etc.

After that the context menu were shown in the GroupWise client!

2.) The next issue was that the context menu was added as often as many emails were selected.
Means: For example: Three selected emails ends up in three time added context menu.
Solution:
Checking
var existsAlready = menuItems.Item("...");
if (existsAlready != null)
{
return;
}

in "CustomizeContextMenu(...)" method and leaving the method under shown circumstances.

3.) The by the wizard created registry path contained the version number "5.0".
This may confuse but it is finally ok. No need to change here anything!
On the other side:
It will NOT work when the registry entry
"SOFTWARE\\Novell\\GroupWise\\5.0\\C3PO\\DataTypes\\...."
will be changed/"adjusted to that GroupWise client version you are currently using"!

Hope this helps someone. 🙂
0 Likes
s_vogel Absent Member.
Absent Member.

Re: [TUTORIAL] Context Menu to export EML files

Username951;2343658 wrote:


Multiple email selection should be possible, but only those emails that are fitting some requirements should be stored finally in database.
One requirement is for example that a keyword like "ISSUE" appears in the email subject
(followed by a ":", a "space" and some characters that can be converted to an integer value),
multiple, leading "Fwd: " and/or "Re: " should be handled well,
subject should be handled case-in-sensitive.

This sounds like you should implement some sort of SelectedMessagesValidator class just to keep it clean.

Username951;2343658 wrote:


So here are my find outs, remarks, etc.:

1.) Visual Studio should be started under admin. rights.
Then you can write a post-build event (batch) that copies, "regasm"s and "gacutil"s everything.
As said this works fine for me.
But note that unfortunately the paths to "regasm" and "gacutil" changed
(compared to the time where you wrote your tutorial).

Definitely. That way, as you mentioned, the post build scripts integrate very well.

Username951;2343658 wrote:


2.) The "Novell C3PO" wizard was downloaded and worked out as described in our tutorial.
One important step was to use "GW.MESSAGE.MAIL" and not "...BROWSER..." or something else.


I can not figure out, where you have the GW.BROWSER thing from, but in my examples I allways used GW.MESSAGES.MAIL

Username951;2343658 wrote:

The wizard created finally the basic C# (.NET framework 2.0) project.
This project was loaded in Visual Studio 2013, automatically converted to "newest version"
and finally was a ".sln" made.

Yes. You can leave it at 2.0. I just have the 4.5 installed so i will target this version

Username951;2343658 wrote:

"oracle.dataaccess"
-> Note that the "Copy Local" property must be set to "true"!
(This property will be reset to "false" after a successful (re)build.
So check this and change it to "true" for the first build!
This must be made only once because after a successful build is this .dll known;
keywords: GAC -> cached
But note that "successful" means also that the post-build event ran flawless!)


This is quite specific to your case since my example on exports a flat EML file to the hard drive

Username951;2343658 wrote:

2.) regasm.exe needs strong names.
So a "cmd" with admin. right was opened,
a
"C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\sn.exe" -k "C:\Users\<username>\Documents\Visual Studio 2013\Projects\GWSaveToDatabase\GWSaveToDatabase.snk"
fired
and the created "GWSaveToDatabase.snk" file added to the solution.

I don't want to be picky, but it's gacutil that needs the strong names. ragasm is not complaining


Username951;2343658 wrote:

(Development) Remarks

1.) While I used the "C3PO" wizard first time I used "Add Menu" item - as you said in your tutorial! 😞
And that is definitely wrong!
See:
The result was a C# project that does not show any new context menu entry.
So I tried at the next wizard run "Add Menu Item".
The wizard created again a C# project but still no new context menu entry in the GroupWise client.
(And that after all needed steps
like
copy to GroupWise installation path,
regasm and gacutil over all .dlls
etc.
were successful be made).

It took a complete day to get the idea to "merge" the two wizard created projects!
Why merging?
Because the second project contained a "const" which were used in the switch statement of the "Execute()" method
(with the same meaning like your "WIASSArchivButton.vWIASS" - see your code snippet above!)
and the "CustomizeContextMenu(...)" method in "CommandFactory.cs" looked also different
while the first project does not contained something similar.
( For example:
The "CustomizeContextMenu(...)" method had more statements.
And that made more sense to me compared to the first wizeard created C# project.
)

Since I uploaded a better example this is obsolete.

Username951;2343658 wrote:

So I ASSUME that the second project would work but it does not because of regasm / gacutil behaviour.
Means I believe it would work when all
with regasm registered
and
with gacutil to the cache added "things"
would be "un-registerd" and "un-cached".

This is, as I assume, due to the Interop.C3POTypeLibrary.dll. This must me cached every time the project is build. maybe you could use gacutil -i Interop.C3POTypeLibrary.dll -f to force the recaching


Username951;2343658 wrote:

So, finally I took the second, wizard created C# project and copied the "const", adjusted the "Execute()"
and "CustomizeContextMenu(...)" methods, etc.

After that the context menu were shown in the GroupWise client!

Thats is correct. But I never had to do this. The thing is, that the "Add Menu Item" is giving you the opputunity to specify a command, which the "Add Menu" doesn't.

Username951;2343658 wrote:

2.) The next issue was that the context menu was added as often as many emails were selected.
Means: For example: Three selected emails ends up in three time added context menu.
Solution:
Checking
var existsAlready = menuItems.Item("...");
if (existsAlready != null)
{
return;
}

in "CustomizeContextMenu(...)" method and leaving the method under shown circumstances.

I added a fix for this in the second post, but it isn't working in GW2012 anymore. I have a very ubly fix for that in my new code.

Username951;2343658 wrote:

3.) The by the wizard created registry path contained the version number "5.0".
This may confuse but it is finally ok. No need to change here anything!
On the other side:
It will NOT work when the registry entry
"SOFTWARE\\Novell\\GroupWise\\5.0\\C3PO\\DataTypes\\...."
will be changed/"adjusted to that GroupWise client version you are currently using"!


This is all part of the official documentation and wasn't touched by Novell since quite a long time.

I think i will make a github repository in the futer as a proof of concept and kind of a accessable documentation for everyone.
0 Likes
The opinions expressed above are the personal opinions of the authors, not of Micro Focus. By using this site, you accept the Terms of Use and Rules of Participation. Certain versions of content ("Material") accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company. As of September 1, 2017, the Material is now offered by Micro Focus, a separately owned and operated company. Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.