Solved

OLE and MS Project

Posted on 1997-03-25
14
523 Views
Last Modified: 2013-11-25
I try to write a program in Visual C++ 5.0 using MFC which can remotely control MS Project using OLE.

I found out that I have to write an OLE automation client.

I would like to know, how to find out the names of objects or functions MS Project offers to the outside??

If I have to do this via a ".tbl" file, can someone tell me the name of this file which MS Project 95 version 4.1a offers??

Thanks.
0
Comment
Question by:re
  • 8
  • 6
14 Comments
 
LVL 1

Accepted Solution

by:
MFCGuy earned 50 total points
ID: 1300664
Do a search for Pj4en32.olb, either within your MSProject
directory or on microsoft.com.

This will generate the Project classes.
0
 
LVL 1

Expert Comment

by:MFCGuy
ID: 1300665
In response to your acceptance of my answer, where you asked how to use the file:

After you have generated the classes using "New Class -> From TypeLib" in ClassWizard, you can do something like this, where I am setting the start date value of the first task
(I named all the classes from the TypeLib ProjectXXX, where XXX
is a class generated from the TypeLib):

ProjectApplication* dispatch = new ProjectApplication;

// create the object that we'll drive through OLE automation
COleException e;
CLSID clsid;
if (CLSIDFromProgID(OLESTR("MSProject.Application"), &clsid) != NOERROR)
{
      AfxMessageBox("Could not create MSProject object");
      return FALSE;
}

// try to get the active object before creating a new one
LPUNKNOWN lpUnk;
LPDISPATCH lpDispatch;
if (GetActiveObject(clsid, NULL, &lpUnk) == NOERROR)
{
      HRESULT hr = lpUnk->QueryInterface(IID_IDispatch,
            (LPVOID*)&lpDispatch);
      lpUnk->Release();
      if (hr == NOERROR)
            dispatch->AttachDispatch(lpDispatch, TRUE);
}


// if not dispatch ptr attached yet, need to create one
if (dispatch->m_lpDispatch == NULL &&
      !dispatch->CreateDispatch(clsid, &e))
{
      AfxMessageBox("Could not create Project Object");
      return FALSE;
}

char title[128];
BOOL found = FALSE;
CWnd * pWnd = AfxGetMainWnd();
while (pWnd != NULL)
{
      pWnd->GetWindowText(title,sizeof(title));
      if((CString(title).Find("Microsoft Project")) != -1)
      {
            found = TRUE;
            break;
      }
      pWnd = pWnd->GetWindow(GW_HWNDNEXT);
}
if(!found)//try another method if it is a brand-new window
{
      pWnd = CWnd::FindWindow(NULL,"Microsoft Project");
      if(pWnd == NULL) return FALSE;
}

//Bring the window to the front
if(pWnd || found)
{
      pWnd->ShowWindow(SW_SHOWMAXIMIZED);
      pWnd->UpdateWindow();
      pWnd->BringWindowToTop();

      //Open the file
      dispatch->FileOpen(      COleVariant(fileName) );

      //Fill out template

      ProjectProject project;
      ProjectTasks tasks;

      tmpDisp = dispatch->GetActiveProject();
      project.AttachDispatch(tmpDisp, TRUE);

      tmpDisp = project.GetTasks();
      tasks.AttachDispatch(tmpDisp, TRUE);

      int count = ( tasks.Count() ).iVal;//get # of tasks in plan
      if(!count) return FALSE;

      //Set Project Start Date
      project.SetProjectStart(COleVariant(startDate));

      //Reactivate in case of template errors
      pWnd->ShowWindow(SW_SHOWMAXIMIZED);
      pWnd->UpdateWindow();
      pWnd->BringWindowToTop();
}
else
{
      AfxMessageBox("Project could not be started!");
      return FALSE;
}


I hope that gives you more information as well as giving you a better impression of Experts Exchange.  I would have been glad to provide this information up front if I had realized you needed it.  I will try and be more proactive in my answers in the future.

Regards,
MFCGuy
0
 

Author Comment

by:re
ID: 1300666
This comment seems to be very useful.

Thanks a lot, Ralf
0
 

Author Comment

by:re
ID: 1300667
Hi MFCGuy,

I couldn't find out your email-address, so I write a message on this way.

I have a problem belonging to your source code in your answer. In the first line you declare a variable of type "ProjectApplication" but VC++ 5.0 doesn't know that type, I can't find the word in the online help either. So can you please tell me where to finnd this type (in a header file or class library or...)

Thanks, re
0
 
LVL 1

Expert Comment

by:MFCGuy
ID: 1300668
When you generate the classes by doing NewClass->FromTypeLib
in ClassWizard, ClassWizard will generate a class called "Application" that represents the MS Project application VBA object.  I changed the name to "ProjectApplication" during that
import process.

I actually put "Project" in front of all the classes it generated to simplify the listing in the ClassView and to not conflict with the Application class generated by some other TypeLibs I am using.

Hope I helped.  Please respond if the above did not make sense.

MFCGuy
0
 

Author Comment

by:re
ID: 1300669
I was a little bit confused about your last comment. I searched for the "New class" in ClassWizard. The entry is "AddClass". I think that's what you meant. I think now I can try to type in your code and find out if it works. I let you know about the results.

Thanks so far, re
0
 
LVL 1

Expert Comment

by:MFCGuy
ID: 1300670
Yes "Add Class" is correct.  I was going from memory.

Sorry.  Let me know what happens.

MFCGuy
0
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 

Author Comment

by:re
ID: 1300671
I'm sorry to ask again something but I found some "errors" in your code.

Your code works very fine until Project starts and the project window comes to the front.

There are three places where the compiler finds something:
1) The variable "fileName" is undeclared. I think it can be declared as type "char[]" and then directly assigned with a value.

2) The call to function "FileOpen" is wrong. You use only one parameter but the header file shows to specify many more. Can you please correct this call and tell me how to find out which type a parameter must be. The header file shows all parameters of type VARIANT, which is only used to transport the value, I think.

3) The variable "startDate" isn't declared. Can you please tell me, which type to use for this variable.

Sorry, but it would be useful for me if you could change the code, so that it directly works and post it as a new comment. Please don't forget the explanations. Sorry for this much work without extra bonus points.

I hope this was all about your code and thanks for your help so far, re
0
 
LVL 1

Expert Comment

by:MFCGuy
ID: 1300672
1.fileName is the File name you want to open, e.g.
CString fileName = "C:\\MyPlan.prj"
or something like that.

3.startDate is a date you want to set - this was just an example
so use COleDateTime::GetCurrentTime() if you want to use today's date.
COleDateTime startDate = COleDateTime::GetCurrentTime();
startDate.SetDate(startDate.GetYear(),startDate.GetMonth(),startDate.GetDay());  //used to zero out the time

2. Wrong?  How dare you!  :-)
I forgot about that...  I overrode the FileOpen function so I didn't have to use all those parameters.  Here is the code for the class generated from ClassWizard:
in the .h file
--------------
void FileOpen(const VARIANT& Name);
Put it under the normal declaration, e.g.
void FileOpen(const VARIANT& Name, const VARIANT& ReadOnly, const VARIANT& Merge, const VARIANT& TaskInformation, const VARIANT& Table, const VARIANT& Sheet, const VARIANT& NoAuto);

in the .cpp file
----------------
void ProjectApplication::FileOpen(const VARIANT& Name)
{
      static BYTE parms[] =
            VTS_VARIANT;
      InvokeHelper(0x66, DISPATCH_METHOD, VT_EMPTY, NULL, parms, &Name);
}
Put it under the standard declaration, e.g.:
void ProjectApplication::FileOpen(const VARIANT& Name, const VARIANT& ReadOnly, const VARIANT& Merge, const VARIANT& TaskInformation, const VARIANT& Table, const VARIANT& Sheet, const VARIANT& NoAuto)
{
      static BYTE parms[] =
            VTS_VARIANT VTS_VARIANT VTS_VARIANT VTS_VARIANT VTS_VARIANT VTS_VARIANT VTS_VARIANT;
      InvokeHelper(0x66, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
             &Name, &ReadOnly, &Merge, &TaskInformation, &Table, &Sheet, &NoAuto);
}

In the .cpp File, remember to replace the class I have (ProjectApplication) with the class name you generated (probably Application).

Remember, my example sets the start date of the first task, so make sure the file you are opening has at least one task (or remove that code).

Regards,
MFCGuy

P.S. Don't worry about the points, I only need your thanks (which I've already gotten).

0
 

Author Comment

by:re
ID: 1300673
I forgot something in my last comment.

The variable "tmpDisp" was also not declared. I think it was right to use the type "ProjectApplication" for this variable (I renamed the class "Application" also to "ProjectApplication").

If this was right, I don't understand an exception which occurs in my program. The exception is described as an access vialation. This happens when the program tries to set the project start. My project file includes two tasks and is opened properly. Maybe you have some ideas about that.

Thanks, re
0
 
LVL 1

Expert Comment

by:MFCGuy
ID: 1300674
LPDISPATCH tmpDisp;

That is probably yuor access exception.
0
 
LVL 1

Expert Comment

by:MFCGuy
ID: 1300675
LPDISPATCH tmpDisp;

That is probably your access exception.
0
 

Author Comment

by:re
ID: 1300676
Thank you that was the correct data type and now it works. I don't know why my first task doesn't move, if the current time is used for project start and it was set before to another date but that doesn't matter now. Maybe I'm not familiar enough with MS Project.

There is still a last question:
How can I find out which datatype to use for a parameter because the .h file with the class definition from the server application show all the parameters with "VARIANT" as type. It would be nice if you could also answer this last question.

Thanks, re
0
 
LVL 1

Expert Comment

by:MFCGuy
ID: 1300677
All function parameters and all OLE calls are explained in the
MSProject on-line VBA help. (VBA_pj.hlp)

In my experience, which is very limited with MS Project OLE calls, it is best to override any function (like I did with FileOpen) that has optional parameters that are useless under
normal circumstances.  Maybe someday ClassWizard will generate all the overrides that the MS Products provide using VBA directly...

Good Luck!
0

Featured Post

What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

Join & Write a Comment

Suggested Solutions

Introduction: Database storage, where is the exe actually on the disc? Playing a game selected randomly (how to generate random numbers).  Error trapping with try..catch to help the code run even if something goes wrong. Continuing from the seve…
After several hours of googling I could not gather any information on this topic. There are several ways of controlling the USB port connected to any storage device. The best example of that is by changing the registry value of "HKEY_LOCAL_MACHINE\S…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now