Is there a function to create a new version of a document on check-in?
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!
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; ai.lProjectId = lpDocItems->lplProjectIds[lDocIdx]; ai.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;
items.lProjectId = 2153; items.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; ai.lProjectId = lpDocItems->lplProjectIds[lDocIdx]; ai.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; // datasource name
WCHAR szUserName; // user who is logging in
WCHAR szPassWord; // 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?
Thank you for your guidance .
For this problem, C++ just for testing to check this aaApi_DocumentCheckInActionDlg2 api is ok.
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);
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.
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.