I have written a core service method to do this recently. You will need to pass in a string array of Publish Target URIs. Also, this works up to Tridion 2013, but not in Sites 8 / 9, as they change the publishing API there to include the new Business Process Types (BPT). The idea is to get all the pages, and then ask the core service for each page, if it is published. It is not very fast, but it works.
private List<TridionPage> GetPublishedPages(CoreServiceClient client, string rootSgUri, string[] pubTargets)
{
OrganizationalItemItemsFilterData filter = new OrganizationalItemItemsFilterData();
filter.ItemTypes = new ItemType[] { ItemType.Page };
filter.Recursive = true;
XElement pageElements = client.GetListXml(rootSgUri, filter);
int count = pageElements.DescendantNodes().Count();
List<TridionPage> publishedItems = new List<TridionPage>();
List<TridionPage> notPublishedItems = new List<TridionPage>();
foreach (XElement pageXml in pageElements.Elements())
{
string pageUri = pageXml.Attribute("ID").Value;
foreach (var pubTargetUri in pubTargets)
{
bool isPagePublishedToTarget = client.IsPublished(pageUri, pubTargetUri, true); // isPublishedInContext = true
if(isPagePublishedToTarget)
{
TridionPage pageItem = GetPageItem(client, pageUri, pubTargetUri);
// Add the Publish Target Info
if (pageItem.PublishDetails != null)
{
publishedItems.Add(pageItem);
}
else
{
notPublishedItems.Add(pageItem);
}
}
}
}
Also, the same logic can be used for Components, just search in the Building Blocks folder and also change the filter as below:
// Below 3 lines to filter for Components
OrganizationalItemItemsFilterData filter = new OrganizationalItemItemsFilterData();
filter.ItemTypes = new ItemType[] { ItemType.Component };
filter.Recursive = true;
// End Comp filter