Create document version for document already checked-out

Is there a function to create a new version of a document on check-in?

Parents
  • Yes, take a look at aaApi_DocumentCheckInActionDlg2().

    You may already be aware of the "problem" of finding what you are looking for in the ProjectWise SDK documentation.  A "trick" I have covered before, and cover in the ProjectWise SDK training that I teach, is to be "clever" on how you search for your item of interest.  Unfortunately, it's a bit of "you don't know what you don't know" as well as "words get in the way" type of problem, particularly if your native language is not English!

    For example, to find the function to MOVE a FOLDER, it isn't clear that the key word here is PARENT.  To move a folder, you need to change a folder's parent folder.

    That function is aaApi_SetParentProject().  And of course, in this context and this instance, a folder is called a project.  

    Another example, is how I found the function to create a version on check in, to answer the question in this post.

    What I did was to search the *.h and *.fdf files delivered with the SDK for text that I thought might be related to what I was looking for.

    To find the above function I used this search in Notepad++:

    Then by browsing through the results I noticed this:

    And clicking on line 2553 I then see this:

    Then by looking at the documentation in the help file, I found a function that will allow a new version to be created on check in!

    HTHs

  • Hi Dan, I have a issue with aaApi_DocumentCheckInActionDlg2.  

    as I test, I check in  one file  in pwc for testing, then I call  aaApi_DocumentCheckInActionDlg2, the form show up, looks all right. but  click any button, it not work fine and get a crash.  do u have some sample for this api  or any idea? thx.

    C++  code like below:


    AADOC_ITEM ai[1];
    ai[0].lProjectId = lpDocItems->lplProjectIds[lDocIdx];
    ai[0].lDocumentId= lpDocItems->lplDocumentIds[lDocIdx];
    BOOL nVer = false;
    LPWSTR strcom=L"D";

    aaApi_DocumentCheckInActionDlg2(NULL, L"DDD", 0, 0, ai, 1, &nVer, strcom, 2000);


    C# code like below:

    [DllImport("DMAWIN.DLL", CharSet = CharSet.Unicode)]
    public static extern int aaApi_DocumentCheckInActionDlg(IntPtr hWndParent, string strTitle, uint flags, IntPtr pDocuments, int count, ref bool pNewVersion, StringBuilder pComment, int commentLength);


    [DllImport("DMAWIN.DLL", CharSet = CharSet.Unicode)]
    public static extern int aaApi_DisplayDocumentCheckInActionDlg(
    IntPtr hWndParent,
    string lpctstrTitle,
    uint flags,
    int count,
    IntPtr pDocuments,
    ref Boolean pNewVersion,
    StringBuilder pComment,
    int commentLength);


    [DllImport("DMAWIN.DLL", CharSet = CharSet.Unicode)]
    public static extern int aaApi_DocumentCheckInActionDlg2(
    IntPtr hWndParent,
    string strTitle,
    uint flags,
    uint checkinFlags,
    IntPtr pDocuments,
    int count,
    ref bool pNewVersion,
    StringBuilder pComment,
    int commentLength
    );

    DocFun.AADOC_ITEM[] items = new DocFun.AADOC_ITEM[1];

    items[0].lProjectId = 2153;
    items[0].lDocumentId = 13;

    IntPtr iptdoc = MarshalHelper.IntPtrFromStuctArray<DocFun.AADOC_ITEM>(items);
    StringBuilder stCom = new StringBuilder();
    stCom.Append("xx");
    string title = "YY";

    Boolean bVer =false ;

    int t=DocFun.aaApi_DocumentCheckInActionDlg2(Process.GetCurrentProcess().Handle, title, 0, 0, iptdoc, 1, ref bVer, stCom, 4096);

  • My guess is your C++ code is crashing because you did not allocate enough memory for the second last parameter in aaApi_DocumentCheckInActionDlg2().

    You set strcom to just one character, but you tell the function that the allocated buffer length is 2000.

    Try allocating a string buffer large enough for you needs, and pass that length to the function and see if that fixes your problem.

    Keep in mind that "strings" in C and C++ can vary in their actual "type" (arrays of characters vs. an object).  In C+, "strings" are immutable and are always objects, and as you are showing, StringBuilder is a good choice when you want the object to behave like a buffer.

    I didn't check your C# code.  See if you can get the C++ code to work first.

  • Thank  u very much.  u r right.

    It work fine.  C++ code change below:

    AADOC_ITEM ai[1];
    ai[0].lProjectId = lpDocItems->lplProjectIds[lDocIdx];
    ai[0].lDocumentId= lpDocItems->lplDocumentIds[lDocIdx];
    BOOL nVer = false;
    LPTSTR strcom;
    ZeroMemory(&strcom, sizeof(strcom));
    aaApi_DocumentCheckInActionDlg2(NULL, L"DDD", 0, 0, ai, 1, &nVer, strcom, 2000);

    I will check C# code. thanks again.

  • Hmm.  I could be wrong as I'm not a world class programmer, but I'm not sure you understand what LPTSTR is.  Take a look at this page: https://docs.microsoft.com/en-us/windows/win32/intl/windows-data-types-for-strings 

    If I understand the code snippet that you posted, you allocate a pointer to a memory location that is typed to TCHAR. Then you zero out that memory location, which is I believe to be just one TCHAR wide.  Later, in your call to aaApi_DocumentCheckInActionDlg2(), you pass 2000 as the size of the buffer for the return of a comment.  Why? The ProjectWise API function doesn't allocate memory with that value, it is your way of telling the function how big the buffer is that you are pointing to.

    The "correct" way, or at least the "best practice" way, would be to pass the size of your buffer, strcom, the same/similar way that you pass the size to "ZeroMemory()", i.e. the sizeof(strcom) function, which I believe would return zero or perhaps one, since it is a pointer, not an array of TCHAR.

    Take a look at this page: https://docs.microsoft.com/en-us/cpp/cpp/sizeof-operator?view=msvc-160 

    If this is "working", i.e., not crashing, I suspect that it is by chance, but I could be wrong.

    Also, the prototype for aaApi_DocumentCheckInActionDlg2() looks like this:

    pComment is of type LPWSTR, not LPTSTR.  I think these are the same when using UNICODE, but since you are using UNICODE, why not be explicit about it?

    Here's a code snippet that is from an old program I did years ago where I'm allocating memory for a "string" and passing a pointer to that memory location for the function aaApi_GetLastLoginInfo() which is similar to the way aaApi_DocumentCheckInActionDlg2() is expecting a pointer to a buffer for a "string".  Here's the prototype for aaApi_GetLastLoginInfo():

    and a snippet showing how I allocated memory for the strings, and how I pass this onto the function:

    ...
    	WCHAR	szDSName[128];			// datasource name
    	WCHAR	szUserName[63];			// user who is logging in
    	WCHAR	szPassWord[128];		// user's password
    ...
    	// initalize the WCHAR types
    	wmemset(szDSName,   '\0', sizeof(szDSName)  / sizeof(WCHAR));
    	wmemset(szUserName, '\0', sizeof(szUserName)/ sizeof(WCHAR));
    	wmemset(szPassWord, '\0', sizeof(szPassWord)/sizeof(WCHAR));
    ...
    	// get the previous login info if any from the local pw registry
    	hPwReg = aaApi_GetLocalRegistry();
    	bRetCode = aaApi_GetLastLoginInfo
    		(
    		hPwReg,								// i  ProjectWise registry handle   
    		szDSName,							// io Datasource name               
    		sizeof(szDSName)/sizeof(WCHAR),		// i  Length of lptstrDataBase      
    		szUserName,							// o  User name                     
    		sizeof(szUserName)/sizeof(WCHAR),	// i  Length of lptstrUser          
    		NULL,								// o  Schema path                   
    		0									// i  Length of lptstrSchema        
    		);
    

    You might want to run your existing code with various sets of data, i.e. large comment strings, and see if it behaves as you expect.  And if not, try modifying to code to look a bit more like my sample snippet and see if that approach works for you.

    Any "world class programmers", or at least those who better understand the workings of "string" in C++ code, who can comment or advise on what I am suggesting?

  • Hi Dan

    Thank you for your guidance .

    For this problem,  C++ just for testing to check this aaApi_DocumentCheckInActionDlg2 api is ok.

    pComment is of type LPWSTR, that correct, i wrote wrong, but also work fine.

    the key point  is ZeroMemory(&strcom, sizeof(strcom)) , call this api, pComment need to be allocated memory, according the pComment length, if not, it will crash, i think 2000 just for api return comment  value when user fill comment in the ui.

    According to this feature , My C# also work fine now, new StringBuilder must be set memory, such as 

    StringBuilder stCom = new StringBuilder();
    stCom.Capacity = 2000; // if not set this value, c++ will crash.

    ---------------------------------------------------------------

    StringBuilder stCom = new StringBuilder();
    stCom.Capacity = 2000;
    string title = "YY";
    int bVer;
    int t = DocFun.aaApi_DocumentCheckInActionDlg2((IntPtr)null, title, 0, 0, iptdoc, 1, out bVer, stCom, 2000);

Reply
  • Hi Dan

    Thank you for your guidance .

    For this problem,  C++ just for testing to check this aaApi_DocumentCheckInActionDlg2 api is ok.

    pComment is of type LPWSTR, that correct, i wrote wrong, but also work fine.

    the key point  is ZeroMemory(&strcom, sizeof(strcom)) , call this api, pComment need to be allocated memory, according the pComment length, if not, it will crash, i think 2000 just for api return comment  value when user fill comment in the ui.

    According to this feature , My C# also work fine now, new StringBuilder must be set memory, such as 

    StringBuilder stCom = new StringBuilder();
    stCom.Capacity = 2000; // if not set this value, c++ will crash.

    ---------------------------------------------------------------

    StringBuilder stCom = new StringBuilder();
    stCom.Capacity = 2000;
    string title = "YY";
    int bVer;
    int t = DocFun.aaApi_DocumentCheckInActionDlg2((IntPtr)null, title, 0, 0, iptdoc, 1, out bVer, stCom, 2000);

Children
  • Just to be clear, aaApi_DocumentCheckInActionDlg2() does NOT allocate any memory for pComment, that is the caller's responsibility.  The purpose of commentLength is to inform the function of the size of the buffer that the caller has allocated for the string to be returned so that it doesn't overflow the buffer.

    In your C# version, you are using an object, i.e. stCom is a StringBuilder object where you have set the size of it as a buffer to be 2000, so I would expect your C# implementation to work, but not the C++ version of your code. 

    I didn't realize that ZeroMemory is a macro (Windows).  From what I can tell, it does what I thought it did, i.e. zeros out the buffer that you pass to it, and uses the size that you pass to it.  That size, i.e. what sizeof(strcom) returns, I don't see how it would return 2000, which is what you hard-coded in your call to the API function.  If the returned string is larger than what sizeof(strcom) returns, you should experience a buffer overflow.

    No need to continue  this conversation.  If it "works" for you, then I'm happy to hear it, I'm just not convinced that it would work for all possible returned values.

  • I got curious so I created a quick console application to see what what sizeof(strcom) returns.

    Here's the result of my test:

    It returns 4, so as long as the size of the return string is 4 or less, it would work, but if the string is larger, then your C++ code  should cause a buffer overflow.

    LPTSTR strcom;
    ZeroMemory(&strcom, sizeof(strcom));
    aaApi_DocumentCheckInActionDlg2(NULL, L"DDD", 0, 0, ai, 1, &nVer, strcom, 2000);

    And I'm not sure if sizeof() is returning the size in bytes or characters.  I suspect bytes, 4 bytes is 32 bits, so that is the size of a pointer in a 32 bit application, which could hold two 16 bit UNICODE characters, so your code might fail with fewer characters.  Of course, buffer overflows don't always cause an exception or a crash as it would depend on what is actually being overwritten.