ProjectNode.CopyPaste contains the following code to recursively add all of the siblings when copying a folder within a project:
// Then look at siblings
currentItemID = itemId;
while(currentItemID != VSConstants.VSITEMID_NIL)
variant = null;
ErrorHandler.ThrowOnFailure(sourceHierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_NextVisibleSibling, out variant));
currentItemID = (uint)(int)variant;
WalkSourceProjectAndAdd(sourceHierarchy, currentItemID, targetNode, true);
But to properly walk the list of siblings it needs to pass currentItemId to sourceHierarchy.GetProperty instead of passing itemId. By passing itemId it is repeatedly adding the same node multiple times. Ultimately this results in AddItemWithSpecific returning
ADDRESULT_Cancel because of this check:
// If the file to be added is an existing file part of the hierarchy then continue.
if (NativeMethods.IsSamePath(file, newFileName))
result = VSADDRESULT.ADDRESULT_Cancel;
And then AddNodeIfTargetExistInStorage throws an Exception with no message which propagates out to the user.
Changing to itemId to currentItemId causes the copy and paste to succeed.